From ff3313a0f9b693298dc16382ae2e4f3f84d59d42 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 29 Mar 2025 12:25:22 -0400 Subject: [PATCH] Add UpdateHpMpTp packet, make sprint take away debug monster health This is just for debugging, I want to make attack actions do this instead. --- resources/opcodes.json | 5 +++++ src/bin/kawari-world.rs | 33 ++++++++++++++++++++++++++++--- src/common/mod.rs | 2 +- src/world/actor.rs | 7 +++++++ src/world/chat_handler.rs | 9 ++++++++- src/world/connection.rs | 35 +++++++++++++++++++++++++++++++-- src/world/ipc/action_request.rs | 4 +++- src/world/ipc/mod.rs | 17 ++++++++++++++-- src/world/mod.rs | 3 +++ 9 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 src/world/actor.rs diff --git a/resources/opcodes.json b/resources/opcodes.json index 53944ab..9794bd1 100644 --- a/resources/opcodes.json +++ b/resources/opcodes.json @@ -139,6 +139,11 @@ "name": "EventStart", "opcode": 955, "size": 24 + }, + { + "name": "UpdateHpMpTp", + "opcode": 325, + "size": 8 } ], "ClientZoneIpcType": [ diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 20d3822..6f8df62 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -1,7 +1,10 @@ use std::sync::{Arc, Mutex}; use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType}; -use kawari::common::{Position, determine_initial_starting_zone, get_citystate, get_world_name}; +use kawari::common::{ + INVALID_OBJECT_ID, ObjectId, Position, determine_initial_starting_zone, get_citystate, + get_world_name, +}; use kawari::common::{get_racial_base_attributes, timestamp_secs}; use kawari::config::get_config; use kawari::lobby::CharaMake; @@ -77,6 +80,7 @@ async fn main() { inventory: Inventory::new(), status_effects: StatusEffects::default(), event: None, + actors: Vec::new(), }; let mut lua_player = LuaPlayer::default(); @@ -425,8 +429,18 @@ async fn main() { exit_position = None; exit_rotation = None; } - ClientZoneIpcData::Unk1 { .. } => { - tracing::info!("Recieved Unk1!"); + ClientZoneIpcData::Unk1 { + category, param1, .. + } => { + tracing::info!("Recieved Unk1! {category:#?}"); + + match category { + 3 => { + // set target + tracing::info!("Targeting actor {param1}"); + } + _ => {} + } } ClientZoneIpcData::Unk2 { .. } => { tracing::info!("Recieved Unk2!"); @@ -768,6 +782,18 @@ async fn main() { println!("Found action: {:#?}", action_row); + //if request.target.object_id == INVALID_OBJECT_ID { + if let Some(actor) = + connection.get_actor(ObjectId(0x106ad804)) + { + actor.hp -= 50; + + let actor = actor.clone(); + connection + .update_hp_mp(actor.id, actor.hp, 10000) + .await; + } + //} else { let lua = lua.lock().unwrap(); lua.scope(|scope| { let connection_data = scope @@ -782,6 +808,7 @@ async fn main() { Ok(()) }) .unwrap(); + //} } ClientZoneIpcData::Unk16 { .. } => { tracing::info!("Recieved Unk16!"); diff --git a/src/common/mod.rs b/src/common/mod.rs index 20688de..0ac56e4 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -20,7 +20,7 @@ pub use position::Position; #[binrw] #[brw(little)] -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ObjectId(pub u32); impl Default for ObjectId { diff --git a/src/world/actor.rs b/src/world/actor.rs new file mode 100644 index 0000000..059727e --- /dev/null +++ b/src/world/actor.rs @@ -0,0 +1,7 @@ +use crate::common::ObjectId; + +#[derive(Clone, Copy, Debug)] +pub struct Actor { + pub id: ObjectId, + pub hp: u32, +} diff --git a/src/world/chat_handler.rs b/src/world/chat_handler.rs index b3522be..7634597 100644 --- a/src/world/chat_handler.rs +++ b/src/world/chat_handler.rs @@ -4,7 +4,7 @@ use crate::{ opcodes::ServerZoneIpcType, packet::{PacketSegment, SegmentType}, world::{ - Event, + Actor, Event, ipc::{ ActorControl, ActorControlCategory, BattleNpcSubKind, CommonSpawn, DisplayFlag, EventStart, NpcSpawn, ObjectKind, OnlineStatus, PlayerSpawn, PlayerSubKind, @@ -235,6 +235,13 @@ impl ChatHandler { } } "!spawnmonster" => { + let actor = Actor { + id: ObjectId(0x106ad804), + hp: 100, + }; + + connection.add_actor(actor); + // spawn a tiny mandragora { let ipc = ServerZoneIpcSegment { diff --git a/src/world/connection.rs b/src/world/connection.rs index 9d18f14..50a9e7e 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -1,7 +1,7 @@ use tokio::net::TcpStream; use crate::{ - common::{Position, timestamp_secs}, + common::{ObjectId, Position, timestamp_secs}, opcodes::ServerZoneIpcType, packet::{ CompressionType, ConnectionType, PacketSegment, PacketState, SegmentType, parse_packet, @@ -10,7 +10,7 @@ use crate::{ }; use super::{ - Event, Inventory, Item, LuaPlayer, Zone, + Actor, Event, Inventory, Item, LuaPlayer, Zone, ipc::{ ActorSetPos, ClientZoneIpcSegment, ContainerInfo, ContainerType, InitZone, ItemInfo, ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo, @@ -77,6 +77,7 @@ pub struct ZoneConnection { pub status_effects: StatusEffects, pub event: Option, + pub actors: Vec, } impl ZoneConnection { @@ -337,4 +338,34 @@ impl ZoneConnection { self.status_effects.dirty = false; } } + + pub async fn update_hp_mp(&mut self, actor_id: ObjectId, hp: u32, mp: u16) { + let ipc = ServerZoneIpcSegment { + op_code: ServerZoneIpcType::UpdateHpMpTp, + timestamp: timestamp_secs(), + data: ServerZoneIpcData::UpdateHpMpTp { hp, mp, unk: 0 }, + ..Default::default() + }; + + self.send_segment(PacketSegment { + source_actor: actor_id.0, + target_actor: actor_id.0, + segment_type: SegmentType::Ipc { data: ipc }, + }) + .await; + } + + pub fn add_actor(&mut self, actor: Actor) { + self.actors.push(actor); + } + + pub fn get_actor(&mut self, id: ObjectId) -> Option<&mut Actor> { + for actor in &mut self.actors { + if actor.id == id { + return Some(actor); + } + } + + return None; + } } diff --git a/src/world/ipc/action_request.rs b/src/world/ipc/action_request.rs index 1f2f774..366ec00 100644 --- a/src/world/ipc/action_request.rs +++ b/src/world/ipc/action_request.rs @@ -1,5 +1,7 @@ use binrw::binrw; +use crate::common::ObjectTypeId; + #[binrw] #[derive(Debug, Eq, PartialEq, Clone, Default)] #[brw(repr = u8)] @@ -19,7 +21,7 @@ pub struct ActionRequest { pub request_id: u32, pub dir: u16, pub dir_target: u16, - pub target: u64, + pub target: ObjectTypeId, pub arg: u32, pub padding_prob: u32, } diff --git a/src/world/ipc/mod.rs b/src/world/ipc/mod.rs index b4d344e..73c3b97 100644 --- a/src/world/ipc/mod.rs +++ b/src/world/ipc/mod.rs @@ -216,6 +216,12 @@ pub enum ServerZoneIpcData { EventPlay(EventPlay), /// Sent to tell the client to load a scene, but not play it EventStart(EventStart), + /// Sent to update an actor's hp & mp values + UpdateHpMpTp { + hp: u32, + mp: u16, + unk: u16, // it's filled with... something + }, } #[binrw] @@ -237,8 +243,15 @@ pub enum ClientZoneIpcData { /// FIXME: 32 bytes of something from the client, not sure what yet #[br(pre_assert(*magic == ClientZoneIpcType::Unk1))] Unk1 { - // TODO: full of possibly interesting information - unk: [u8; 32], + // 3 = target + category: u32, + param1: u32, + param2: u32, + param3: u32, + param4: u32, + param5: u32, + param6: u32, + param7: u32, }, /// FIXME: 16 bytes of something from the client, not sure what yet #[br(pre_assert(*magic == ClientZoneIpcType::Unk2))] diff --git a/src/world/mod.rs b/src/world/mod.rs index 6121f82..3dd0d47 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -20,3 +20,6 @@ pub use lua::LuaPlayer; mod event; pub use event::Event; + +mod actor; +pub use actor::Actor;