Repository navigation
refactor(gateway): split WorldStore into one Store per Protocol Family - #644
Conversation
WorldStore had 185 own methods, 85 of them with default bodies. A Fake silently answered with a default for any method it did not write. Each method now sits on the family trait whose handler calls it: ShardRouting, Session, Character, Transfer, Party, Mail, Social, Npc, Trainer, Bank, Combat, Death and Trade are new, and LootWindowStore and ChatActionStore grow. WorldStore is a method-less umbrella with a blanket impl. Every default body is gone. The Coordinator adapter moves to one file per family under stdb/store, and InMemoryStore implements each family in its own block, with the seven answers it took from defaults now written out. Two methods had no caller once the defaults went: home_shard and publish_shard_index. They leave the traits, the Fake keeps them as inherent helpers, and the production code only they reached is deleted: realm_core::settle_shard_index, realm_core::publish_shard_index, config::resolve_home_shard, HomeShard, and the tests of those functions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The World Session carried a home pin, a generic store and the on_home_shard! macro that picked between them at each call. Handlers were bound on all of WorldStore, so a signature did not show which families a handler needed. RoutedStore now holds the listener's handle and the pinned Home Shard. route_home pins only when settle_home_shard names a new shard, and leave_world clears the pin. run_world_session takes an Arc<dyn WorldStore>, and each frame dispatches against RoutedStore::current(). Every old-style handler and helper is bound on exactly the Store families it calls. Helpers that use their own store as the Realm-core handle on a single-database gateway keep the WorldStore bound. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LootWindowStore carried the eight Loot Roll methods and ChatActionStore the three Speech methods, so the loot window, chat and channel Fakes held unreachable stubs for calls their dispatchers never make. LootRollStore (world/loot.rs) now holds the vote, the master looter's assignment and the promotion relay's reads and requests. SpeechStore (handlers/chat.rs) holds say, yell and `/e` lines, text emotes and the GM dot-command. Each caller is bounded on the families it calls, and the Coordinator adapters move to stdb/store. Speech joins both glossaries. The Module comments that named the deleted home_shard resolver now name locate_home_shard, which settle_home_shard runs at every world entry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…by Store family The shared Fake held 223 flat fields. Each field now sits in the state struct of the Store family whose methods read it, so a test reads `store.<family>.x`. Three fixtures several families read stay on WorldFake itself: characters, npc_refuses and trade_error. The field moves and the struct literals were rewritten mechanically and checked by the compiler. No test changes what it asserts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
WorldFake and its fixtures leave the 13,000-line world/tests.rs. Each Store family's state struct and impl now sit together in test_support/world_fake/<family>.rs, and shard routing sits in test_support/topology.rs. A later change to one family's Fake edits one file. Code motion only: items gain pub(crate) so the test modules reach them, and paths that named a parent module through `super::` now name it from the crate root. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Forty-seven socket tests read a fixed 13 frames (12 on a world-port) to get past world entry, so a change to the login sequence edited every one of them. drain_world_entry reads up to and including the SMSG_WEATHER that always closes the entry batch, and fails after 64 frames. Tests that inspect the batch iterate the drained frames, so their assertions are unchanged. WORLD_ENTRY_PACKETS and WORLD_REENTRY_PACKETS are gone. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
world/tests.rs held 194 socket tests across every family. They now live in one file per family (auction, bank, cast, channel, character, chat, combat, death, guild, item, loot_window, melee, npc, quest, social, taxi, trainer, vendor, weather) or join the existing party, loot, who and whisper files. session_tests.rs takes the handshake, admission, world entry, movement, coalescing and logout-order tests. The world-entry helpers (handshake, tester_store, quest_store, enter_world) move to test_support/entry.rs. tests.rs keeps the shared imports, the module list and the helpers several files use. Each test keeps its name and body. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Death requests run through handle_loot against HandleLootFake, which models who is alive and asserts outcomes. Loot window socket tests become dispatcher tests on InMemoryLootWindow. The socket tests that remain check GameObject type routing. WorldFake keeps only the loot window state that session and routing tests read. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Each family drives its handler through a local run helper against a Fake that implements only the Stores the handler is bounded on. Tests assert the packets sent or the Fake's resulting state, not a call log. The four families lose their state from WorldFake, except the fields other tests still read. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t the socket Guild, chat and channel opcode behaviour was already covered by the InMemory*Actions dispatcher tests. Delete the socket duplicates, add the one guild membership opcode test the dispatcher lacked, and test weather through zone_weather_message with a local Fake. Session-level guild, chat and cleanup tests stay on WorldFake. Remove the channel state and the Fake fields no test reads. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… unused Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…Fakes with the family tests The handler test modules become reachable from the World Session tests. The vendor, taxi, melee and cast Fakes model state a test can read back (buyback ring, accepted route, engagements, auras, pending casts), so a test can assert an outcome and not a call log. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…sts at the handler Each family's behaviour tests call its dispatcher through a local run helper against the family Fake. Session-level tests stay on WorldFake: the buyback replay at world entry, the raw auction browse, the auction refusal followed by WHO, and the two taxi gossip tests. WorldFake loses the item and melee state and most of the vendor, taxi and cast state. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…way-store-families # Conflicts: # gateway/src/world/test_support/world_fake/mod.rs
…way-store-families
…way-store-families # Conflicts: # gateway/src/world/tests.rs
…ness Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review of efff177: Standards and SpecThis review has two axes. The Standards axis checks the code against Priority: P1 fix before merge · P2 should fix, or state it as a known gap · P3 optional. StandardsCorrectness. I found no production regression. Documented standards (hard)
Baseline smells (judgement calls)
Spec (#635 Phase 3)(a) Missing or partial
(b) Not asked for
(c) Implemented, but check
Summary. Standards: 9 findings. The worst is the Protocol Family definition, which says handlers take only their own family's Store when several do not. Spec: 8 findings. The worst is that Generated by Claude Code |
…s over a World Session The World Fake now records use_gameobject and take_loot calls. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A Gateway adapter had to satisfy about 290 methods, because
WorldStorecarried 185 methods of its own and 15 family traits as supertraits. 85 methods had default bodies, and 78 of those never ran. Every family change edited one 4,800-line shared Fake inworld/tests.rs, and every test that entered the world counted a fixed 13 packets.This is phase 3 of #635. It changes no behaviour.
Store
WorldStorehas no methods now. It is an umbrella over one Store trait per Protocol Family (session, shard routing, character, transfer, party, mail, social, npc, trainer, bank, combat, death, trade, loot roll, speech, and the existing action families), with a blanket impl.gateway/src/stdb/store/<family>.rs. The bodies are moved verbatim; phase 4 collapses them.RoutedStoreinstead ofhomepluson_home_shard!. ANonerouting answer keeps the current pin, and leaving the world clears it, as before.home_shard,publish_shard_index,realm_core::settle_shard_index,realm_core::publish_shard_index,config::resolve_home_shardandHomeShard, with the 11 tests that only covered them.locate_home_shardkeeps its own tests of the same rules.Tests
gateway/src/world/test_support/asWorldFake, with one state struct per family inworld_fake/<family>.rs. It keeps the multi-shard topology that party, transfer, mail, presence, who, whisper, loot rolls and shard routing need.drain_world_entryreads up to theSMSG_WEATHERthat closes the world-entry batch. The fixed packet counts are gone.world/tests.rsis split into one test file per family plussession_tests.rs.WorldFake, because their handlers are bound on many families. Phase 5 narrows them.Glossary: adds Protocol Family and Speech.
Local gates: fmt, clippy with
-D warnings, the five crate test commands and the wasm check pass.🤖 Generated with Claude Code (Claude Opus 5.5 lead, with Opus 5.5 and Sonnet 5.5 subagents)