core: extract the client surface — and close a CI gate hole that made this class of change unguarded (#92)
The scattered one. `usermsg`/`admin`/`bans` sat in contiguous runs; the client natives are cut into
six pieces by entity, translations, voice and server code, so this moved by SYMBOL rather than by line
range. `v8host.rs` 18,308 -> 17,833; `core/src/client.rs` is 557 lines.
Moved: `CLIENT_MUX`, `dispatch_client_event`/`replay_client_event`, the slot accessors
(valid/userid/signon/find_by_userid/name/language/steamid/address), the slot actions
(print/consolePrint/kick/command/fakeCommand), `__s2_client_subscribe`, and the owner-scoped teardown.
THE BOUNDARY IS NOT THE PREFIX. `CLIENT_CMD_SUBS`, `__s2_client_command_listen` and
`dispatch_command_listeners` stayed with the COMMANDS cluster despite the shared `client_` prefix.
They implement `Commands.onClientCommand`, whose semantics are defined by contrast with `CONCOMMANDS`
("a matching ConCommand supersedes; a listener observes"), and `dispatch_client_command` is a
`CONCOMMANDS` lookup that calls `dispatch_concommand`. Moving them here because of the prefix would
have put commands code in the clients module. Same call for the tests: the listener tests and
`client_print_and_chat_degrade_without_ops` (a `@s2script/chat` test that happens to touch
`__s2_client_print`) stayed put.
THE CI HOLE. `check-core-js-lint.sh` — the gate that catches a renamed or moved native breaking the
prelude — runs only in ci-js, and ci-js was NOT triggered by `core/**`. So a Rust-only change that
moves a native broke that gate and CI went green: ci-native ran (it does not lint JS) and ci-js never
started. #91 was exactly that shape — 27 natives moved, one check reported. It passed only because #90
had already made the eslint configs scan core/src directory-wide instead of just v8host.rs.
`core/**` is now in both of ci-js's path filters, with the reasoning recorded in the workflow header
so it does not read as an over-broad copy-paste. ci-native.yml already listed `core/**`; both suites
need it, for different reasons.
Two things checked rather than assumed while moving:
- `schema_offset_cached` looked like a client dependency in a line-range grep; it is not (that was
`s2_entity_target` in the same range), so the `pub(crate)` it briefly got was reverted.
- `FAKE_CMD_CALLS` is shared with a v8host re-entrancy test, so the client-command mock ops stay in
`frame_tests` as shared infra rather than being duplicated or dragged along.
Behaviour-neutral: 542 tests before and after; 218 natives before and after (v8host 169 + client 14 +
cookies 12 + admin 9 + usermsg 8 + bans 6); 9 build warnings before and after. `CI=1 make ci` green. G
Gabriel Hirakawa committed
0195cef74928c39ed15ad4e96484a6f69393ecbd
Parent: 45bb265
Committed by GitHub <noreply@github.com>
on 8/4/2026, 8:50:47 AM