refill the hand, and decode SendBattleEventMessage

LogicSummoner::tick guards the whole hand-refill block on the first vint
of the deck block being at least 1 - it is the number of hand slots the
client scans for an empty one before pulling from the draw queue. we
sent zero, so the hand we dealt was the only one the player ever got.
named after what it is and set to four.

12951 is SendBattleEventMessage, the in-battle emotes and quick chat. it
carries a LogicBattleEvent: a type byte, the sender account and three
int lists - ticks, coordinate pairs and params. it decodes now instead
of being dropped with a warning. the client renders its own emote
locally, so nothing is echoed back yet; the relay belongs with a real
opponent.
This commit is contained in:
WiseDev 2026-08-23 11:47:25 +03:00
parent 3cba764841
commit 5bca19da50
6 changed files with 53 additions and 4 deletions

View file

@ -2,7 +2,8 @@ use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use logic::{ use logic::{
message_type, CancelMatchmakeDoneMessage, ClientCapabilitiesMessage, GoHomeMessage, message_type, CancelMatchmakeDoneMessage, ClientCapabilitiesMessage, GoHomeMessage,
KeepAliveServerMessage, LoginFailedMessage, LoginMessage, LoginOkMessage, ServerErrorMessage, KeepAliveServerMessage, LoginFailedMessage, LoginMessage, LoginOkMessage, SendBattleEventMessage,
ServerErrorMessage,
}; };
use service_rpc::{ use service_rpc::{
AccountRef, DeviceInfo, HomeRequestKind, LoginOutcome, Session as AuthSession, WireMessage, AccountRef, DeviceInfo, HomeRequestKind, LoginOutcome, Session as AuthSession, WireMessage,
@ -88,6 +89,17 @@ impl MessageManager {
let _ = incoming.downcast::<GoHomeMessage>(); let _ = incoming.downcast::<GoHomeMessage>();
self.push_home(HomeRequestKind::GoHome).await self.push_home(HomeRequestKind::GoHome).await
} }
message_type::SEND_BATTLE_EVENT => {
if let Some(sent) = incoming.downcast::<SendBattleEventMessage>() {
tracing::debug!(
peer = %self.peer,
event_type = sent.event.event_type,
params = ?sent.event.params,
"battle event"
);
}
Ok(())
}
message_type::CANCEL_MATCHMAKE => { message_type::CANCEL_MATCHMAKE => {
tracing::info!(peer = %self.peer, "client cancelled matchmaking"); tracing::info!(peer = %self.peer, "client cancelled matchmaking");
self.sender self.sender

View file

@ -0,0 +1,24 @@
use titan::{LogicLong, Payload};
#[derive(Debug, Default, Clone, PartialEq, Eq, Payload)]
pub struct LogicBattleEvent {
#[codec(byte)]
pub event_type: u8,
#[codec(vlong)]
pub account_id: LogicLong,
#[codec(vint)]
pub ticks: Vec<i32>,
#[codec(vint)]
pub coords: Vec<i32>,
#[codec(vint)]
pub params: Vec<i32>,
}
impl LogicBattleEvent {
pub fn num_coords(&self) -> usize {
self.coords.len() / 2
}
pub fn coord(&self, index: usize) -> Option<(i32, i32)> {
let x = *self.coords.get(index * 2)?;
let y = *self.coords.get(index * 2 + 1)?;
Some((x, y))
}
}

View file

@ -92,7 +92,7 @@ pub struct LogicSummoner {
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct LogicSummonerDeck { pub struct LogicSummonerDeck {
pub field_220: i32, pub hand_size: i32,
pub hand: [i32; SUMMONER_HAND_SIZE], pub hand: [i32; SUMMONER_HAND_SIZE],
pub draw_pile: Vec<i32>, pub draw_pile: Vec<i32>,
pub used_pile: Vec<i32>, pub used_pile: Vec<i32>,
@ -103,7 +103,7 @@ pub struct LogicSummonerDeck {
impl Default for LogicSummonerDeck { impl Default for LogicSummonerDeck {
fn default() -> Self { fn default() -> Self {
Self { Self {
field_220: 0, hand_size: 0,
hand: [-1; SUMMONER_HAND_SIZE], hand: [-1; SUMMONER_HAND_SIZE],
draw_pile: Vec::new(), draw_pile: Vec::new(),
used_pile: Vec::new(), used_pile: Vec::new(),
@ -120,6 +120,7 @@ impl LogicSummonerDeck {
*slot = index; *slot = index;
} }
Self { Self {
hand_size: SUMMONER_HAND_SIZE as i32,
hand, hand,
draw_pile: (SUMMONER_HAND_SIZE as i32..slot_count as i32).collect(), draw_pile: (SUMMONER_HAND_SIZE as i32..slot_count as i32).collect(),
..Self::default() ..Self::default()
@ -133,7 +134,7 @@ impl LogicSummoner {
None => writer.write_boolean(false), None => writer.write_boolean(false),
Some(deck) => { Some(deck) => {
writer.write_boolean(true); writer.write_boolean(true);
writer.write_vint(deck.field_220); writer.write_vint(deck.hand_size);
for slot in deck.hand { for slot in deck.hand {
writer.write_vint(slot); writer.write_vint(slot);
} }

View file

@ -1,4 +1,5 @@
mod logic_battle; mod logic_battle;
mod logic_battle_event;
mod logic_character; mod logic_character;
mod logic_component; mod logic_component;
mod logic_game_mode; mod logic_game_mode;
@ -20,6 +21,7 @@ pub use logic_component::{
LogicCharacterBuffComponent, LogicCombatComponent, LogicComponent, LogicHitpointComponent, LogicCharacterBuffComponent, LogicCombatComponent, LogicComponent, LogicHitpointComponent,
COMPONENT_BUFF, COMPONENT_COMBAT, COMPONENT_HITPOINT, COMPONENT_MOVEMENT, COMPONENT_BUFF, COMPONENT_COMBAT, COMPONENT_HITPOINT, COMPONENT_MOVEMENT,
}; };
pub use logic_battle_event::LogicBattleEvent;
pub use logic_game_mode::{LogicGameMode, SECTION_BATTLE, SECTION_TUTORIAL}; pub use logic_game_mode::{LogicGameMode, SECTION_BATTLE, SECTION_TUTORIAL};
pub use logic_game_object::{ pub use logic_game_object::{
LogicGameObject, LogicGameObjectEntry, LogicObjectBody, LogicVector2, CHARACTER_OBJECT_TYPE, LogicGameObject, LogicGameObjectEntry, LogicObjectBody, LogicVector2, CHARACTER_OBJECT_TYPE,

View file

@ -0,0 +1,7 @@
use titan::Message;
use crate::battle::LogicBattleEvent;
#[derive(Debug, Default, Clone, PartialEq, Eq, Message)]
#[message(id = 12951, direction = "client", name = "SendBattleEventMessage")]
pub struct SendBattleEventMessage {
pub event: LogicBattleEvent,
}

View file

@ -11,6 +11,7 @@ mod login_ok;
mod matchmake; mod matchmake;
mod out_of_sync; mod out_of_sync;
mod own_home_data; mod own_home_data;
mod battle_event;
mod sector_state; mod sector_state;
mod server_error; mod server_error;
mod start_mission; mod start_mission;
@ -32,6 +33,7 @@ pub use login_ok::LoginOkMessage;
pub use matchmake::{CancelMatchmakeDoneMessage, HomeLogicStoppedMessage, StopHomeLogicMessage}; pub use matchmake::{CancelMatchmakeDoneMessage, HomeLogicStoppedMessage, StopHomeLogicMessage};
pub use out_of_sync::OutOfSyncMessage; pub use out_of_sync::OutOfSyncMessage;
pub use own_home_data::OwnHomeDataMessage; pub use own_home_data::OwnHomeDataMessage;
pub use battle_event::SendBattleEventMessage;
pub use sector_state::SectorStateMessage; pub use sector_state::SectorStateMessage;
pub use server_error::ServerErrorMessage; pub use server_error::ServerErrorMessage;
pub use start_mission::StartMissionMessage; pub use start_mission::StartMissionMessage;
@ -49,6 +51,7 @@ pub mod message_type {
pub const START_MISSION: u16 = 14104; pub const START_MISSION: u16 = 14104;
pub const HOME_LOGIC_STOPPED: u16 = 14105; pub const HOME_LOGIC_STOPPED: u16 = 14105;
pub const CANCEL_MATCHMAKE: u16 = 14107; pub const CANCEL_MATCHMAKE: u16 = 14107;
pub const SEND_BATTLE_EVENT: u16 = 12951;
pub const SERVER_HELLO: u16 = 20100; pub const SERVER_HELLO: u16 = 20100;
pub const LOGIN_FAILED: u16 = 20103; pub const LOGIN_FAILED: u16 = 20103;
pub const LOGIN_OK: u16 = 20104; pub const LOGIN_OK: u16 = 20104;