Commit graph

5 commits

Author SHA1 Message Date
WiseDev
7a0ba1b652 give the server its own battle clock and end condition
LogicBattle::isEndConditionMatched, transcribed: the battle is over when
end_counter is positive, when either king is dead, when tick/20 seconds
reach MatchLength + OvertimeSeconds, or - once past MatchLength - when
the crowns differ. the divisor is the 0x66666667/2^35 multiply in the
client, which is a divide by twenty, so the battle runs at 20 ticks a
second.

crowns come from LogicSummoner::getStars: three when the enemy king is
down, otherwise two minus the enemy princess towers still standing,
which is exactly what leader_towers holds.

BattleRegistry keeps the LogicGameMode we built for each account and
advances it on the tick the client reports in its turn message, so the
server now tracks the clock, the crowns and whether the battle is over,
and drops the session when the player goes home. the two isSummoner
guards in the overtime branch are left out: they only fire for an object
that is not a summoner, which a king tower always is.
2026-08-23 12:01:26 +03:00
WiseDev
5bca19da50 refill the hand, and decode SendBattleEventMessage
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.
2026-08-23 11:47:25 +03:00
WiseDev
e984aa7d15 verify the snapshot before sending it, and add two bisect switches
the client aborts in Debugger::error("Full update stream is corrupted!"), tail
called out of LogicGameMode::decode, which is why the stack blames
receiveSectorState. only one error path exists there so it is a sentinel.

verify_snapshot reads the snapshot back following the client decoders and
refuses to send anything that does not land both sentinels with no trailing
bytes. it passes today, so the layout matches what i believe the client reads.

checked every reader against the binary: LogicGameMode, LogicBattle,
LogicGameObjectManager, LogicGameObject, LogicCharacter, LogicSummoner, all
three components, LogicClientAvatar, readGlobalID, readDataReference,
readConstantSizeIntArray, readGameObjectReference, decodeComponent, and
setLevelIndex for the component set. all match.

SCROLL_BATTLE_SUMMONER=0 encodes the king towers as plain characters,
SCROLL_BATTLE_PRINCESS=0 drops the princess towers. one client run with each
splits the remaining hypotheses.
2026-08-23 10:50:03 +03:00
WiseDev
774e7c60cf build and send the battle sector state
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.
2026-08-23 10:24:20 +03:00
WiseDev
41eca5b620 battle model: LogicBattle, the object manager and BattleResultMessage
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.
2026-08-23 10:00:16 +03:00