diff --git a/crates/game-service/src/battle.rs b/crates/game-service/src/battle.rs index 817c725..614b5d4 100644 --- a/crates/game-service/src/battle.rs +++ b/crates/game-service/src/battle.rs @@ -5,7 +5,7 @@ use logic::battle::{ LogicGameObjectRef, LogicHitpointComponent, LogicMovementComponent, LogicObjectBody, LogicSummoner, LogicSummonerDeck, LogicTilemap, - LogicTime, LogicVector2, BATTLE_TYPE_NPC, CHARACTER_OBJECT_TYPE, COMPONENT_PASSES, + LogicTime, LogicVector2, BATTLE_TYPE_PVP, CHARACTER_OBJECT_TYPE, COMPONENT_PASSES, DIRECTION_BOTTOM, DIRECTION_TOP, SUBTILE_UNITS, }; @@ -307,7 +307,7 @@ impl BattleBuilder { npc, arena, account_ids, - battle_type: BATTLE_TYPE_NPC, + battle_type: BATTLE_TYPE_PVP, decks, objects, leaders, diff --git a/crates/game-service/src/service.rs b/crates/game-service/src/service.rs index 4940400..5a56cc6 100644 --- a/crates/game-service/src/service.rs +++ b/crates/game-service/src/service.rs @@ -3,7 +3,7 @@ use logic::table; use logic::{ AvailableServerCommandMessage, EndClientTurnMessage, LogicCommandManager, LogicDataRef, LogicShopSeedChangedCommand, OutOfSyncMessage, OwnHomeDataMessage, SectorStateMessage, - SendBattleEventMessage, + SectorCommandMessage, SendBattleEventMessage, StartMissionMessage, StopHomeLogicMessage, }; use service_rpc::{ @@ -476,6 +476,49 @@ impl GameApi for GameService { self.running_battles.answer_emote(account).await; Ok(()) } + async fn sector_command(&self, account: AccountRef, payload: Vec) -> RpcResult<()> { + let Ok(sector) = SectorCommandMessage::from_bytes(&payload) else { + return Ok(()); + }; + let progress = self + .running_battles + .advance(account, sector.client_tick) + .await; + let server_checksum = progress.and_then(|state| state.checksum); + let mut spawned = 0; + if let Some(played) = sector + .command + .as_ref() + .and_then(|command| command.as_any().downcast_ref::()) + { + spawned = self + .running_battles + .play( + account, + played.header.executor_account_id, + played.deck_slot, + played.position, + played.header.execute_tick, + |card, position, owner, instance| { + self.battles.summon(card, position, owner, 0, instance) + }, + ) + .await; + } + tracing::info!( + %account, + tick = sector.client_tick, + checksum = sector.client_checksum, + server_checksum, + agrees = server_checksum == Some(sector.client_checksum), + objects = progress.map(|state| state.objects), + towers = ?progress.map(|state| state.towers), + stars = ?progress.map(|state| state.stars), + spawned, + "sector command" + ); + Ok(()) + } async fn cancel_matchmake(&self, account: AccountRef) -> RpcResult<()> { self.matchmaker.leave(account).await; Ok(()) @@ -516,6 +559,10 @@ impl RpcService for GameService { GameRequest::BattleTick { account } => Ok(GameResponse::messages( self.battle_tick(account).await?, )), + GameRequest::SectorCommand { account, payload } => { + self.sector_command(account, payload).await?; + Ok(GameResponse::Empty) + } GameRequest::CancelMatchmake { account } => { self.cancel_matchmake(account).await?; Ok(GameResponse::Empty) diff --git a/crates/gateway/src/backend.rs b/crates/gateway/src/backend.rs index 7990d23..089cdc7 100644 --- a/crates/gateway/src/backend.rs +++ b/crates/gateway/src/backend.rs @@ -141,6 +141,12 @@ impl GameApi for RemoteGame { .await?; Ok(()) } + async fn sector_command(&self, account: AccountRef, payload: Vec) -> RpcResult<()> { + self.client + .call(&GameRequest::SectorCommand { account, payload }) + .await?; + Ok(()) + } async fn disconnect(&self, account: AccountRef) -> RpcResult<()> { self.client .call(&GameRequest::Disconnect { account }) diff --git a/crates/gateway/src/message_manager.rs b/crates/gateway/src/message_manager.rs index d86d68a..2e1409e 100644 --- a/crates/gateway/src/message_manager.rs +++ b/crates/gateway/src/message_manager.rs @@ -99,6 +99,20 @@ impl MessageManager { self.stop_battle_ticker(); self.push_home(HomeRequestKind::GoHome).await } + message_type::SECTOR_COMMAND => { + let Some(account) = self.account else { + return Ok(()); + }; + if let Err(error) = self + .backends + .game + .sector_command(account, incoming.payload) + .await + { + tracing::warn!(peer = %self.peer, %error, "could not deliver the sector command"); + } + Ok(()) + } message_type::SEND_BATTLE_EVENT => { let Some(account) = self.account else { return Ok(()); diff --git a/crates/logic/src/messages/mod.rs b/crates/logic/src/messages/mod.rs index 1a49fa5..9e70c65 100644 --- a/crates/logic/src/messages/mod.rs +++ b/crates/logic/src/messages/mod.rs @@ -12,6 +12,7 @@ mod matchmake; mod out_of_sync; mod own_home_data; mod battle_event; +mod sector_command; mod sector_state; mod server_error; mod start_mission; @@ -34,6 +35,7 @@ pub use matchmake::{CancelMatchmakeDoneMessage, HomeLogicStoppedMessage, StopHom pub use out_of_sync::OutOfSyncMessage; pub use own_home_data::OwnHomeDataMessage; pub use battle_event::{BattleEventMessage, SendBattleEventMessage}; +pub use sector_command::SectorCommandMessage; pub use sector_state::SectorStateMessage; pub use server_error::ServerErrorMessage; pub use start_mission::StartMissionMessage; @@ -51,6 +53,7 @@ pub mod message_type { pub const START_MISSION: u16 = 14104; pub const HOME_LOGIC_STOPPED: u16 = 14105; pub const CANCEL_MATCHMAKE: u16 = 14107; + pub const SECTOR_COMMAND: u16 = 12904; pub const SEND_BATTLE_EVENT: u16 = 12951; pub const BATTLE_EVENT: u16 = 22952; pub const SERVER_HELLO: u16 = 20100; diff --git a/crates/logic/src/messages/sector_command.rs b/crates/logic/src/messages/sector_command.rs new file mode 100644 index 0000000..0b518e5 --- /dev/null +++ b/crates/logic/src/messages/sector_command.rs @@ -0,0 +1,12 @@ +use titan::Message; +use crate::commands::LogicCommand; +#[derive(Debug, Default, Message)] +#[message(id = 12904, direction = "client", name = "SectorCommandMessage")] +#[codec(partial)] +pub struct SectorCommandMessage { + #[codec(vint)] + pub client_tick: i32, + #[codec(vint)] + pub client_checksum: i32, + pub command: Option>, +} diff --git a/crates/service-rpc/src/game.rs b/crates/service-rpc/src/game.rs index 81816ab..b9c4982 100644 --- a/crates/service-rpc/src/game.rs +++ b/crates/service-rpc/src/game.rs @@ -56,6 +56,11 @@ pub enum GameRequest { BattleTick { account: AccountRef, }, + SectorCommand { + account: AccountRef, + #[serde(with = "crate::base64::serde_bytes")] + payload: Vec, + }, CancelMatchmake { account: AccountRef, }, @@ -111,5 +116,6 @@ pub trait GameApi: Send + Sync + 'static { async fn battle_tick(&self, account: AccountRef) -> RpcResult>; async fn battle_event(&self, account: AccountRef, payload: Vec) -> RpcResult<()>; async fn cancel_matchmake(&self, account: AccountRef) -> RpcResult<()>; + async fn sector_command(&self, account: AccountRef, payload: Vec) -> RpcResult<()>; async fn disconnect(&self, account: AccountRef) -> RpcResult<()>; }