stamp the tick on the snapshot that carries it

the snapshot is built inside the tick loop but the clock was written
after it, so every pushed state went out carrying the tick from the
previous call. LogicGameMode::decode compares that number against the
last one it saw and ignores anything older, so a stale stamp is the
difference between a state being applied and being dropped on the floor.
the clock advances with the tick now.

the sector command log gains the summoner mana, how many snapshots have
gone out, and the type of the command that arrived, so the next run says
whether the push is happening at all rather than leaving it to inference.
This commit is contained in:
WiseDev 2026-08-23 13:58:48 +03:00
parent dbbd162bed
commit f51d76f780
2 changed files with 23 additions and 1 deletions

View file

@ -22,6 +22,7 @@ pub struct BattleSession {
next_bot_emote: i32, next_bot_emote: i32,
next_snapshot: i32, next_snapshot: i32,
next_bot_play: i32, next_bot_play: i32,
pub pushed_snapshots: u32,
pending_bot_play: Option<(LogicDataRef, LogicVector2, i32)>, pending_bot_play: Option<(LogicDataRef, LogicVector2, i32)>,
} }
impl BattleSession { impl BattleSession {
@ -39,6 +40,7 @@ impl BattleSession {
next_bot_emote, next_bot_emote,
next_snapshot: SNAPSHOT_INTERVAL_TICKS, next_snapshot: SNAPSHOT_INTERVAL_TICKS,
next_bot_play: 0, next_bot_play: 0,
pushed_snapshots: 0,
pending_bot_play: None, pending_bot_play: None,
} }
} }
@ -88,6 +90,18 @@ impl BattleSession {
pub fn pushes_snapshots(&self) -> bool { pub fn pushes_snapshots(&self) -> bool {
self.mode.battle.battle_type == BATTLE_TYPE_PVP self.mode.battle.battle_type == BATTLE_TYPE_PVP
} }
pub fn mana(&self) -> Option<i32> {
self.mode.battle.objects.objects.iter().find_map(|entry| {
match &entry.body {
logic::battle::LogicObjectBody::Summoner(summoner)
if entry.owner_index() == 0 =>
{
Some(summoner.mana)
}
_ => None,
}
})
}
fn snapshot_message(&mut self) -> Option<WireMessage> { fn snapshot_message(&mut self) -> Option<WireMessage> {
let snapshot = self.mode.snapshot().ok()?; let snapshot = self.mode.snapshot().ok()?;
let report = verify_snapshot(&snapshot); let report = verify_snapshot(&snapshot);
@ -162,11 +176,13 @@ impl BattleSession {
pub fn advance_to(&mut self, tick: i32) { pub fn advance_to(&mut self, tick: i32) {
while self.tick < tick { while self.tick < tick {
self.tick += 1; self.tick += 1;
self.mode.time.tick = self.tick;
self.release_queued(self.tick); self.release_queued(self.tick);
self.mode.battle.tick(self.tick); self.mode.battle.tick(self.tick);
if self.pushes_snapshots() && self.tick >= self.next_snapshot { if self.pushes_snapshots() && self.tick >= self.next_snapshot {
self.next_snapshot = self.tick + SNAPSHOT_INTERVAL_TICKS; self.next_snapshot = self.tick + SNAPSHOT_INTERVAL_TICKS;
if let Some(message) = self.snapshot_message() { if let Some(message) = self.snapshot_message() {
self.pushed_snapshots += 1;
self.outbound.push(message); self.outbound.push(message);
} }
} }
@ -187,7 +203,6 @@ impl BattleSession {
} }
} }
} }
self.mode.time.tick = self.tick;
} }
fn release_queued(&mut self, tick: i32) { fn release_queued(&mut self, tick: i32) {
let mut due = Vec::new(); let mut due = Vec::new();
@ -323,6 +338,8 @@ impl BattleRegistry {
objects: session.object_count(), objects: session.object_count(),
towers: session.towers_standing(), towers: session.towers_standing(),
checksum: session.checksum(), checksum: session.checksum(),
mana: session.mana(),
pushed: session.pushed_snapshots,
}) })
} }
pub async fn play<F>( pub async fn play<F>(
@ -361,4 +378,6 @@ pub struct BattleProgress {
pub objects: usize, pub objects: usize,
pub towers: (usize, usize), pub towers: (usize, usize),
pub checksum: Option<i32>, pub checksum: Option<i32>,
pub mana: Option<i32>,
pub pushed: u32,
} }

View file

@ -512,9 +512,12 @@ impl GameApi for GameService {
server_checksum, server_checksum,
agrees = server_checksum == Some(sector.client_checksum), agrees = server_checksum == Some(sector.client_checksum),
objects = progress.map(|state| state.objects), objects = progress.map(|state| state.objects),
mana = progress.and_then(|state| state.mana),
pushed = progress.map(|state| state.pushed),
towers = ?progress.map(|state| state.towers), towers = ?progress.map(|state| state.towers),
stars = ?progress.map(|state| state.stars), stars = ?progress.map(|state| state.stars),
spawned, spawned,
command = sector.command.as_ref().map(|command| command.command_type()),
"sector command" "sector command"
); );
Ok(()) Ok(())