diff --git a/crates/game-service/src/battle_session.rs b/crates/game-service/src/battle_session.rs index e961ea3..e1fe9a9 100644 --- a/crates/game-service/src/battle_session.rs +++ b/crates/game-service/src/battle_session.rs @@ -209,6 +209,9 @@ impl BattleSession { pub fn stars(&self) -> (i32, i32) { (self.mode.battle.stars(0), self.mode.battle.stars(1)) } + pub fn checksum(&self) -> Option { + self.mode.calculate_checksum().ok() + } pub fn towers_standing(&self) -> (usize, usize) { ( self.mode.battle.leader_towers[0].len(), @@ -319,6 +322,7 @@ impl BattleRegistry { stars: session.stars(), objects: session.object_count(), towers: session.towers_standing(), + checksum: session.checksum(), }) } pub async fn play( @@ -356,4 +360,5 @@ pub struct BattleProgress { pub stars: (i32, i32), pub objects: usize, pub towers: (usize, usize), + pub checksum: Option, } diff --git a/crates/game-service/src/service.rs b/crates/game-service/src/service.rs index 7ab5dcc..4940400 100644 --- a/crates/game-service/src/service.rs +++ b/crates/game-service/src/service.rs @@ -366,9 +366,14 @@ impl GameApi for GameService { .await; } let progress = self.running_battles.advance(account, turn.tick).await; + let server_checksum = progress.and_then(|state| state.checksum); + let agrees = server_checksum == Some(turn.checksum); tracing::info!( %account, tick = turn.tick, + checksum = turn.checksum, + server_checksum, + agrees, seconds = progress.map(|state| state.seconds), finished = progress.map(|state| state.finished), stars = ?progress.map(|state| state.stars), diff --git a/crates/logic/src/battle/logic_game_mode.rs b/crates/logic/src/battle/logic_game_mode.rs index aa1f5e2..facac68 100644 --- a/crates/logic/src/battle/logic_game_mode.rs +++ b/crates/logic/src/battle/logic_game_mode.rs @@ -23,10 +23,10 @@ impl LogicGameMode { Ok(writer.into_inner()) } } -impl Payload for LogicGameMode { - fn encode(&self, writer: &mut ByteStreamWriter) -> Result<()> { +impl LogicGameMode { + pub fn write(&self, writer: &mut ByteStreamWriter, with_commands: bool) -> Result<()> { writer.write_vint(self.server_tick); - writer.write_vint(0); + writer.write_checksum_checkpoint(); writer.write_vint(SECTION_BATTLE); self.time.encode(writer)?; self.random.encode(writer)?; @@ -37,10 +37,22 @@ impl Payload for LogicGameMode { } writer.write_vint(SECTION_TUTORIAL); self.tutorial_manager.encode(writer)?; - writer.write_vint(0); - writer.write_vint(0); + writer.write_checksum_checkpoint(); + if with_commands { + writer.write_vint(0); + } Ok(()) } + pub fn calculate_checksum(&self) -> Result { + let mut writer = ByteStreamWriter::new(); + self.write(&mut writer, false)?; + Ok(writer.checksum() as i32) + } +} +impl Payload for LogicGameMode { + fn encode(&self, writer: &mut ByteStreamWriter) -> Result<()> { + self.write(writer, true) + } fn decode(_reader: &mut titan::ByteStreamReader<'_>) -> Result { Err(titan::Error::Unsupported("LogicGameMode is encode only")) } diff --git a/crates/titan/src/io/writer.rs b/crates/titan/src/io/writer.rs index c89234c..71c3939 100644 --- a/crates/titan/src/io/writer.rs +++ b/crates/titan/src/io/writer.rs @@ -33,6 +33,10 @@ impl ByteStreamWriter { pub fn checksum(&self) -> u32 { self.checksum.value() } + pub fn write_checksum_checkpoint(&mut self) { + let running = self.checksum.value() as i32; + self.write_vint(running); + } pub fn write_byte(&mut self, value: u8) { self.checksum.write_byte(value as i8); self.bit_index = 0;