switch the battle to type 0 and drive it from the server
type 0 turns out not to need a UDP transport. MessageManager::sendUdpMessage checks for a socket and a valid connection, and falls straight back to sendMessage when there is neither - we never send UdpConnectionInfoMessage, so the client has no socket and the sector traffic arrives on the tcp connection we already have. so SectorCommandMessage, 12904, is decoded now: a client tick, a client checksum and an optional command. a card played this way goes through the same summon path as before. the client stops sending EndClientTurnMessage in a battle - sendEndTurn asserts on isImmediateMessageExecution - so the checksum comparison moves onto the sector command, which carries the same two numbers. with the type at 0 the client no longer simulates. it renders what the snapshot says, which is why the snapshots start flowing again: the gate on them was the battle type all along. the bot's cards reach the player for the first time, because there is finally one simulation rather than two arguing.
This commit is contained in:
parent
ade049b29d
commit
498f2cb62d
7 changed files with 91 additions and 3 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<u8>) -> 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::<logic::LogicDoSpellCommand>())
|
||||
{
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -141,6 +141,12 @@ impl GameApi for RemoteGame {
|
|||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn sector_command(&self, account: AccountRef, payload: Vec<u8>) -> RpcResult<()> {
|
||||
self.client
|
||||
.call(&GameRequest::SectorCommand { account, payload })
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
async fn disconnect(&self, account: AccountRef) -> RpcResult<()> {
|
||||
self.client
|
||||
.call(&GameRequest::Disconnect { account })
|
||||
|
|
|
|||
|
|
@ -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(());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
12
crates/logic/src/messages/sector_command.rs
Normal file
12
crates/logic/src/messages/sector_command.rs
Normal file
|
|
@ -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<Box<dyn LogicCommand>>,
|
||||
}
|
||||
|
|
@ -56,6 +56,11 @@ pub enum GameRequest {
|
|||
BattleTick {
|
||||
account: AccountRef,
|
||||
},
|
||||
SectorCommand {
|
||||
account: AccountRef,
|
||||
#[serde(with = "crate::base64::serde_bytes")]
|
||||
payload: Vec<u8>,
|
||||
},
|
||||
CancelMatchmake {
|
||||
account: AccountRef,
|
||||
},
|
||||
|
|
@ -111,5 +116,6 @@ pub trait GameApi: Send + Sync + 'static {
|
|||
async fn battle_tick(&self, account: AccountRef) -> RpcResult<Vec<WireMessage>>;
|
||||
async fn battle_event(&self, account: AccountRef, payload: Vec<u8>) -> RpcResult<()>;
|
||||
async fn cancel_matchmake(&self, account: AccountRef) -> RpcResult<()>;
|
||||
async fn sector_command(&self, account: AccountRef, payload: Vec<u8>) -> RpcResult<()>;
|
||||
async fn disconnect(&self, account: AccountRef) -> RpcResult<()>;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue