From f4536f2cb78c41de52ffe94ab320f9d304f89adc Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 19 Mar 2025 00:28:47 -0400 Subject: [PATCH] Implement basic support for actions This only works for sprint, and it adds 30 sprint buffs. It also regularly crashes the server afterwards usually. --- src/bin/kawari-world.rs | 57 ++++++++++++++++++++++++++++++++- src/world/ipc/action_request.rs | 24 ++++++++++++++ src/world/ipc/mod.rs | 7 ++++ src/world/ipc/status_effect.rs | 8 ++--- 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 src/world/ipc/action_request.rs diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 30f7dd9..7e4c574 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -1,8 +1,9 @@ +use kawari::config::get_config; use kawari::oodle::OodleNetwork; use kawari::packet::{ConnectionType, PacketSegment, PacketState, SegmentType, send_keep_alive}; use kawari::world::ipc::{ ClientZoneIpcData, CommonSpawn, GameMasterCommandType, ObjectKind, ServerZoneIpcData, - ServerZoneIpcSegment, ServerZoneIpcType, SocialListRequestType, + ServerZoneIpcSegment, ServerZoneIpcType, SocialListRequestType, StatusEffect, }; use kawari::world::{ ChatHandler, Zone, ZoneConnection, @@ -15,6 +16,8 @@ use kawari::{ CHAR_NAME, CITY_STATE, CONTENT_ID, CUSTOMIZE_DATA, DEITY, NAMEDAY_DAY, NAMEDAY_MONTH, WORLD_ID, ZONE_ID, common::timestamp_secs, }; +use physis::common::{Language, Platform}; +use physis::gamedata::GameData; use tokio::io::AsyncReadExt; use tokio::net::TcpListener; @@ -594,6 +597,58 @@ async fn main() { ClientZoneIpcData::Unk14 { .. } => { tracing::info!("Recieved Unk14!"); } + ClientZoneIpcData::ActionRequest(request) => { + tracing::info!("Recieved action request: {:#?}!", request); + + let config = get_config(); + + let mut game_data = GameData::from_existing( + Platform::Win32, + &config.game_location, + ) + .unwrap(); + + let exh = + game_data.read_excel_sheet_header("Action").unwrap(); + let exd = game_data + .read_excel_sheet("Action", &exh, Language::English, 0) + .unwrap(); + + let action_row = &exd + .read_row(&exh, request.action_id as u32) + .unwrap()[0]; + + println!("Found action: {:#?}", action_row); + + // send new status list + { + let ipc = ServerZoneIpcSegment { + op_code: ServerZoneIpcType::StatusEffectList, + timestamp: timestamp_secs(), + data: ServerZoneIpcData::StatusEffectList( + kawari::world::ipc::StatusEffectList { + statues: [StatusEffect { + effect_id: 50, + param: 0, + duration: 50.0, + source_actor_id: connection.player_id, + }; + 30], + ..Default::default() + }, + ), + ..Default::default() + }; + + connection + .send_segment(PacketSegment { + source_actor: connection.player_id, + target_actor: connection.player_id, + segment_type: SegmentType::Ipc { data: ipc }, + }) + .await; + } + } } } SegmentType::KeepAlive { id, timestamp } => { diff --git a/src/world/ipc/action_request.rs b/src/world/ipc/action_request.rs new file mode 100644 index 0000000..3ea7b93 --- /dev/null +++ b/src/world/ipc/action_request.rs @@ -0,0 +1,24 @@ +use binrw::binrw; + +#[binrw] +#[derive(Debug, Eq, PartialEq, Clone, Default)] +#[brw(repr = u8)] +pub enum ActionKind { + #[default] + Nothing = 0x0, + Normal = 0x1, +} + +#[binrw] +#[derive(Debug, Clone, Default)] +pub struct ActionRequest { + pub exec_proc: u8, // what? + pub action_kind: ActionKind, + #[brw(pad_before = 2)] // this ISNT empty + pub action_id: u32, // See Action Excel sheet + pub request_id: u32, + pub dir: u16, + pub dir_target: u16, + pub target: u64, + pub arg: u32, +} diff --git a/src/world/ipc/mod.rs b/src/world/ipc/mod.rs index b93afe3..b6fce18 100644 --- a/src/world/ipc/mod.rs +++ b/src/world/ipc/mod.rs @@ -47,6 +47,9 @@ pub use status_effect_list::StatusEffectList; mod weather_change; pub use weather_change::WeatherChange; +mod action_request; +pub use action_request::ActionRequest; + use crate::common::read_string; use crate::common::write_string; use crate::packet::IpcSegment; @@ -234,6 +237,8 @@ pub enum ClientZoneIpcType { Unk13 = 0x2EE, // Sent by the client for unknown reasons Unk14 = 0x87, + // Sent by the client when a character performs an action + ActionRequest = 0x213, } #[binrw] @@ -403,6 +408,8 @@ pub enum ClientZoneIpcData { Unk14 { unk: [u8; 8], // TODO: unknown }, + #[br(pre_assert(*magic == ClientZoneIpcType::ActionRequest))] + ActionRequest(ActionRequest), } #[cfg(test)] diff --git a/src/world/ipc/status_effect.rs b/src/world/ipc/status_effect.rs index 63d7d5e..df2d5f5 100644 --- a/src/world/ipc/status_effect.rs +++ b/src/world/ipc/status_effect.rs @@ -3,8 +3,8 @@ use binrw::binrw; #[binrw] #[derive(Debug, Clone, Copy, Default)] pub struct StatusEffect { - effect_id: u16, - param: u16, - duration: f32, - source_actor_id: u32, + pub effect_id: u16, + pub param: u16, + pub duration: f32, + pub source_actor_id: u32, }