the client never receives individual commands in a battle - SectorManager
has receiveSectorState, receiveCompressedSectorState and a heartbeat, and
nothing else. so everything the opponent does has to reach the player
inside server state, which means the server needs to be able to speak
first. it could not: the rpc only ever answered.
the gateway now runs a ticker for the length of a battle, calling a new
battle_tick on the service five times a second and writing whatever it
returns straight to the socket. the simulation stays in the service and
the socket stays in the gateway.
the first rider is emotes. the player's SendBattleEventMessage reaches
the service instead of being logged and dropped, and the bot answers with
a taunt of its own; it also sends one unprompted every twelve to thirty
seconds, drawn from the rows of taunts.csv that TauntMenu marks as
usable. the reply carries the opponent account so it renders on their
side of the arena.
LogicSummoner::tick guards the whole hand-refill block on the first vint
of the deck block being at least 1 - it is the number of hand slots the
client scans for an empty one before pulling from the draw queue. we
sent zero, so the hand we dealt was the only one the player ever got.
named after what it is and set to four.
12951 is SendBattleEventMessage, the in-battle emotes and quick chat. it
carries a LogicBattleEvent: a type byte, the sender account and three
int lists - ticks, coordinate pairs and params. it decodes now instead
of being dropped with a warning. the client renders its own emote
locally, so nothing is echoed back yet; the relay belongs with a real
opponent.
SectorManager::receiveSectorState reads one byte off the front of the
message body and branches on it: 1 goes to receiveCompressedSectorState,
anything else falls through to the plain decode. we never wrote that
byte, so the client ate the first byte of the snapshot as the flag and
then read every field one byte early - server tick 0, discard 11, and
the section sentinel landed on the LogicTime tick instead of 11.
drop the two bisect switches, they served their purpose: both settings
aborted identically, which is what ruled out the object payload.
537 only tells the client's ui to start searching, the handshake after it was
missing: server sends 24106 StopHomeLogic, client answers 14105, server sends
21903. that last step now builds the same snapshot the npc mission does, using
npcs row 0 as the opponent.
battle type stays 1 so it runs on the client's offline path. a real pvp battle
is type 0 and needs the udp sector channel, which does not exist here.
14107 CancelMatchmake now answers 24125 instead of being ignored.
npc missions used to get a ServerErrorMessage back. now StartMission builds a
LogicGameMode snapshot off the arena tilemap and answers 21903.
towers come from assets/locations/*.csv the way initDefaultSector does it: tile
coordinates times 500, leader index decided by which half of the map the tower
sits in. the two king towers must be there, the client dereferences them without
a null check. they live in buildings.csv, not characters.csv.
LogicCharacter puts the base object fields fourth, not first. the buff component
writes a fixed array sized by the character_buffs row count even with no buffs.
training_arena parses to 2 kings, 4 princess towers, 36x64 subtiles. snapshot is
602 bytes over 6 objects. the real client has not seen it yet.
field order taken straight off LogicBattle::encode. the object manager is
columnar, not object major: 6 counters, a count, then all data refs, then all
global ids, then all objects, then four component passes. global ids must be
ascending, the client binary searches them.
LogicRandom moved out of the shop into its own module. its abs was
checked_abs().unwrap_or(0), the binary uses NEGS so i32::MIN stays i32::MIN.
one seed in four billion, but it would have desynced the card shop.
per object subclass encodes and the component payloads are still missing, so no
snapshot can be sent yet.
14303 AskForJoinableAlliancesList and 14107 CancelMatchmake are both genuinely
empty payloads. 516 UpdateLastShownLevelUp is header only, 537 StartMatchmake
carries a vint and a bool. none of them move the home checksum.
that clears the unknown type warnings out of the log.