core: convert the last 7 subscribe natives; A4 complete (#60)
A4f — finishes A4. Stacked on #59.
Converts every remaining Channels-backed subscribe native onto subscribe_into.
These are the ones #59 deliberately left because they interleave logic between
the steps; each needed a judgement rather than a pattern match:
s2_ws_on / s2_net_on — the ownership gate (a plugin may only subscribe to a
connection it OWNS) must run before anything is stored, so `owner` is
resolved locally for the guard and the helper resolves it again. A refusal
must leave no row behind.
s2_usercmd_subscribe / s2_entity_listener_on / s2_event_subscribe_pre — each
samples WHOLE-STORE emptiness before subscribing, which is NOT the helper's
per-channel `was_first`: the input detour, the IEntityListener, and the
global FireEvent hook are each installed once for the process, not once per
channel. The is_empty() sample stays hoisted above the helper call.
s2_output_subscribe — key format only.
s2_usermsg_on — validates the handler argument BEFORE calling
usermsg_hook_sub, so a bad argument costs no engine call. That guard is kept
(the helper re-checks) purely to preserve the ordering, and the row is stored
under the CANONICAL message name while the caller's alias is only a lookup
key.
Three natives still carry the owner/generation lookup and are deliberately out
of scope: s2_concommand and s2_topmenu_add_item store into plain HashMaps rather
than Channels, so this helper does not apply to them.
A4 IS DONE, and the generic __s2_hook_on/_off pair plus the generic ffi dispatch
entry are being CLOSED OUT rather than left dangling — see the PR body. The
short version: they require consolidating the 17 Channels statics into one
namespaced keyed store, and that consolidation does not pay for itself. A
string-routed generic native still needs a per-family table for the engine-op
follow-ups (event_subscribe, the usermsg bitmap, three separate one-shot detour
installs — all of which this PR makes visible), namespaced keys add a format!
allocation to every dispatch including per-tick ones, and the ffi half moves the
shim boundary and cannot be trusted without a live gate. The duplication those
were meant to remove has now been removed directly instead.
cargo test -p s2script-core: 468 passed. CI=1 make ci: green. Behaviour-
identical throughout; the regression net is the existing per-capability tests. G
Gabriel Hirakawa committed
bf6c396c2bcc2a9f3ae3f4575218779dfbc7d0b0
Parent: 743d8ae
Committed by GitHub <noreply@github.com>
on 8/1/2026, 6:08:26 AM