From eb0e59f6f4e4f931b164bd630c55e30f7afb4d54 Mon Sep 17 00:00:00 2001 From: WiseDev <83840010+wisedevik@users.noreply.github.com> Date: Sun, 23 Aug 2026 14:36:16 +0300 Subject: [PATCH] give every object a new id once the client is live the client never asks for the state, so the objects it decoded before the battle screen installed its listener were stuck without models for the whole battle - and every later snapshot matched them by global id and reused them, so the "newly created" flag the visual depends on was never set again. the first sector command is the client telling us it is up. at that point every object is handed a fresh id: nothing matches, so the client builds them all from scratch and they get models. the leaders and the tower lists are remapped with them and the column is re-sorted, since the client binary searches it. the battle itself is complete as of this run: a princess tower fell and the first crown was scored, towers (2, 1) and stars (1, 0) in the log. --- crates/game-service/src/battle_session.rs | 51 ++++++++++++++++++++++- crates/game-service/src/service.rs | 3 ++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/crates/game-service/src/battle_session.rs b/crates/game-service/src/battle_session.rs index 788144b..6ed0570 100644 --- a/crates/game-service/src/battle_session.rs +++ b/crates/game-service/src/battle_session.rs @@ -4,7 +4,8 @@ pub const SNAPSHOT_INTERVAL_TICKS: i32 = 4; pub const MAX_CATCH_UP_TICKS: i32 = 40; use std::time::Instant; use logic::battle::{ - LogicGameMode, LogicGameObjectEntry, LogicVector2, BATTLE_TICKS_PER_SECOND, BATTLE_TYPE_PVP, + LogicGameMode, LogicGameObjectEntry, LogicGameObjectRef, LogicVector2, + BATTLE_TICKS_PER_SECOND, BATTLE_TYPE_PVP, CHARACTER_OBJECT_TYPE, }; use logic::battle::{verify_snapshot, LogicBattleEvent}; use logic::SectorStateMessage; @@ -25,6 +26,7 @@ pub struct BattleSession { next_bot_play: i32, pub pushed_snapshots: u32, next_instance: i32, + announced: bool, pending_bot_play: Option<(LogicDataRef, LogicVector2, i32)>, } impl BattleSession { @@ -53,9 +55,43 @@ impl BattleSession { next_bot_play: crate::bot::BOT_OPENING_DELAY_SECONDS * BATTLE_TICKS_PER_SECOND, pushed_snapshots: 0, next_instance: first_instance, + announced: false, pending_bot_play: None, } } + pub fn reannounce(&mut self) { + let mut remap: Vec<(LogicGameObjectRef, LogicGameObjectRef)> = Vec::new(); + for entry in self.mode.battle.objects.objects.iter_mut() { + let fresh = LogicGameObjectRef::of(CHARACTER_OBJECT_TYPE + 1, self.next_instance); + self.next_instance += 1; + remap.push((entry.global_id, fresh)); + entry.global_id = fresh; + } + let translate = |old: &LogicGameObjectRef| -> Option { + remap + .iter() + .find(|(was, _)| was == old) + .map(|(_, now)| *now) + }; + for leader in self.mode.battle.leaders.iter_mut() { + if let Some(now) = translate(leader) { + *leader = now; + } + } + for towers in self.mode.battle.leader_towers.iter_mut() { + for tower in towers.iter_mut() { + if let Some(now) = translate(tower) { + *tower = now; + } + } + } + self.mode + .battle + .objects + .objects + .sort_by_key(|entry| entry.global_id.0.map(|id| (id.class_id, id.instance_id))); + self.next_snapshot = self.tick; + } pub fn take_bot_play(&mut self) -> Option<(LogicDataRef, LogicVector2, i32)> { self.pending_bot_play.take() } @@ -347,8 +383,21 @@ impl BattleRegistry { pub async fn resend(&self, account: AccountRef) -> Option { let handle = self.session(account).await?; let mut session = handle.lock().await; + session.reannounce(); session.snapshot_message() } + pub async fn announce_once(&self, account: AccountRef) -> bool { + let Some(handle) = self.session(account).await else { + return false; + }; + let mut session = handle.lock().await; + if session.announced { + return false; + } + session.announced = true; + session.reannounce(); + true + } pub async fn send(&self, account: AccountRef, message: WireMessage) { if let Some(handle) = self.session(account).await { handle.lock().await.push_outbound(message); diff --git a/crates/game-service/src/service.rs b/crates/game-service/src/service.rs index 4f98fd0..7773d1d 100644 --- a/crates/game-service/src/service.rs +++ b/crates/game-service/src/service.rs @@ -488,6 +488,9 @@ impl GameApi for GameService { let Ok(sector) = SectorCommandMessage::from_bytes(&payload) else { return Ok(()); }; + if self.running_battles.announce_once(account).await { + tracing::info!(%account, "client is live, announcing the objects again"); + } let progress = self .running_battles .advance(account, sector.client_tick)