Commit graph

23 commits

Author SHA1 Message Date
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
fcb07bba64 fix the buff component array size, it desynced the snapshot
the client reads getTable(8)->getItemCount() entries there, table 8 is
damage_types with 5 rows. i was writing character_buffs, 12 rows, so every
snapshot carried 7 extra vints and 7 extra booleans and the stream slid.

that is what tripped the sentinel and aborted the client in
Debugger::error("Full update stream is corrupted!").

verified the rest field by field against the client decoders: LogicGameMode,
LogicBattle, LogicGameObjectManager, LogicGameObject, LogicCharacter,
LogicSummoner and LogicCombatComponent all match. the conditional character
fields stay off because ReloadAfterHits and ManaGenerateLimit are empty for both
tower rows.
2026-08-23 10:35:26 +03:00
WiseDev
6dc4caede9 matchmake against a bot instead of searching forever
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.
2026-08-23 10:28:28 +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
d439027735 pull the arena tilemaps out of the ipa
res/locations/*.csv, eight of them. these are what LogicBattle::initDefaultSector
walks to place the towers: training_arena has KingTower at tile 18,6 and 18,58
and four PrincessTowers, plus a 64x24 tile bitmask.

without these there is no way to build a battle snapshot, the leaders come from
here and the client dereferences them without a null check.
2026-08-23 10:13:37 +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
WiseDev
68e2b87a0a decode the four types the client kept sending
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.
2026-08-23 09:48:49 +03:00
WiseDev
c0713b50d7 supervised mode: run the services as child processes
SCROLL_MODE=supervised spawns auth-service, game-service and gateway, waits for
each rpc port before starting the next, and restarts whatever dies with backoff
from 500ms to 30s. the counter resets once a service has been up a minute.

single process mode is still the default and unchanged.

kill_on_drop means ctrl-c takes the children with it, no orphans.
2026-08-23 09:46:59 +03:00
WiseDev
e43f3fc02c unwind panics instead of aborting the process
a panic in a session task now kills that connection, tokio catches it and the
accept loop carries on. with abort it took the whole server down.

the data table RwLock is std so it poisons on panic, and every lookup went
through expect(). one panic would have bricked every later table read, which is
worse than the abort was. it reads the inner value now, the table is read only
once installed.
2026-08-23 09:42:44 +03:00
WiseDev
ca8e41c2c5 roll chest loot from the csv instead of a fixed config
RandomSpells / DifferentSpells / RareChance / EpicChance / MinGold / MaxGold all
come off the chest row now, gold is scaled by the player's arena. magic chest is
30 cards over 8 cards with 1 epic and 6 rares, like the client says it is.

arena chest rows inherit everything through BaseChest, so Free_Arena1 reads Free.
2026-08-23 09:30:38 +03:00
WiseDev
573d70ff08 deck swapping and achievement claims
500 LogicSwapSpellsCommand does the real work, 520 and 521 are no-ops in this
build so they just decode. 535 puts its data reference BEFORE the base header,
unlike every other command.

collection only shrinks when a card moves into an empty deck slot, that is the
one case where the home checksum moves.
2026-08-23 09:24:30 +03:00
WiseDev
9d10cc38f9 add MIT license 2026-08-23 09:13:19 +03:00
WiseDev
f218a45ed2 fix chest shop prices and gate the free chest
rounding is banded and the 5s band biases +3, so 22 goes to 25 not 20. numbers match the csv for arena 1 and 2.
free chest end timestamp needs migration 0002.
2026-08-23 09:02:49 +03:00
WiseDev
a2bf58a627 stop minting a new account on every login
if the client presents an id we don't know, create it under that id and bump the sequence past it.
also chest offers match through BaseChest, so one silver row covers every arena.
2026-08-23 08:54:43 +03:00
WiseDev
8a745d634c generate the daily card shop server side
prices come from the csv globals now, a json cost is just an override.
DATABASE_URL defaults to postgres:///scroll when unset.
2026-08-23 08:47:55 +03:00
WiseDev
ad5a1613e3 move players and accounts into postgres
json stores are gone, they nulled out every data ref on load and wrote the null straight back on save.
shop json and the purchase commands ended up in here too.
2026-08-23 08:40:13 +03:00
WiseDev
d25a6de423 move all crates into crates/, glob members
pure move, no code touched. readme and protocol.md paths fixed up.
2026-08-23 08:15:45 +03:00
WiseDev
972c61dae7 replace command enum with a dyn trait + registry
new logic-derive Command macro submits each command to inventory, manager decodes by vint type. titan-derive loses tagged enums since nothing uses them now. rest of the diff is rustfmt.
2026-08-23 08:05:58 +03:00
scroll
d8ed6ae2ee drop unused Payload import 2026-08-23 07:56:30 +03:00
scroll
2151d4ccbe auto-register messages via inventory
message_registry! is gone, the registry is a process-wide global now.
2026-08-23 07:56:09 +03:00
scroll
323450a977 home mode docs + --desync flag on the probe 2026-08-23 07:52:21 +03:00
scroll
20a75dbb25 nuke the tests and the dead code
data/ is runtime state, keep it out of git.
2026-08-23 07:51:04 +03:00
scroll
812052bc3e initial import, client gets to the lobby
titan engine, logic model, auth/game/gateway services.
csv tables pulled out of the ipa are checked in so it runs out of the box.
2026-08-23 07:50:31 +03:00