From 4142143bc86adb5548fc002ba930f49cd0f51b0e Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 26 Mar 2025 18:28:51 -0400 Subject: [PATCH] Move Zone IPC over to the new JSON opcodes file --- resources/opcodes.json | 234 +++++++++++++++++++++++++++++++++ src/bin/kawari-world.rs | 17 +-- src/world/chat_handler.rs | 2 +- src/world/connection.rs | 3 +- src/world/ipc/mod.rs | 263 ++++++++------------------------------ 5 files changed, 296 insertions(+), 223 deletions(-) diff --git a/resources/opcodes.json b/resources/opcodes.json index 1acddaa..cfabcdf 100644 --- a/resources/opcodes.json +++ b/resources/opcodes.json @@ -1,4 +1,238 @@ { + "ServerZoneIpcType": [ + { + "name": "InitializeChat", + "opcode": 2, + "size": 8 + }, + { + "name": "InitZone", + "opcode": 699, + "size": 103 + }, + { + "name": "ActorControlSelf", + "opcode": 367, + "size": 32 + }, + { + "name": "PlayerStats", + "opcode": 401, + "size": 224 + }, + { + "name": "PlayerSetup", + "opcode": 218, + "size": 2784 + }, + { + "name": "UpdateClassInfo", + "opcode": 119, + "size": 48 + }, + { + "name": "PlayerSpawn", + "opcode": 817, + "size": 664 + }, + { + "name": "InitResponse", + "opcode": 547, + "size": 16 + }, + { + "name": "LogOutComplete", + "opcode": 105, + "size": 8 + }, + { + "name": "ActorSetPos", + "opcode": 136, + "size": 24 + }, + { + "name": "ServerChatMessage", + "opcode": 406, + "size": 776 + }, + { + "name": "Unk8", + "opcode": 308, + "size": 808 + }, + { + "name": "LinkShellInformation", + "opcode": 564, + "size": 456 + }, + { + "name": "Unk11", + "opcode": 342, + "size": 32 + }, + { + "name": "PrepareZoning", + "opcode": 364, + "size": 16 + }, + { + "name": "Unk15", + "opcode": 652, + "size": 8 + }, + { + "name": "Unk16", + "opcode": 939, + "size": 136 + }, + { + "name": "ActorControl", + "opcode": 520, + "size": 24 + }, + { + "name": "ActorMove", + "opcode": 345, + "size": 16 + }, + { + "name": "Unk17", + "opcode": 673, + "size": 104 + }, + { + "name": "SocialList", + "opcode": 876, + "size": 1136 + }, + { + "name": "NpcSpawn", + "opcode": 319, + "size": 648 + }, + { + "name": "StatusEffectList", + "opcode": 839, + "size": 384 + }, + { + "name": "WeatherChange", + "opcode": 373, + "size": 8 + }, + { + "name": "ItemInfo", + "opcode": 850, + "size": 64 + }, + { + "name": "ContainerInfo", + "opcode": 566, + "size": 16 + } + ], + "ClientZoneIpcType": [ + { + "name": "InitRequest", + "opcode": 683, + "size": 120 + }, + { + "name": "FinishLoading", + "opcode": 427, + "size": 72 + }, + { + "name": "Unk1", + "opcode": 868, + "size": 32 + }, + { + "name": "Unk2", + "opcode": 472, + "size": 16 + }, + { + "name": "Unk3", + "opcode": 687, + "size": 8 + }, + { + "name": "Unk4", + "opcode": 376, + "size": 8 + }, + { + "name": "SetSearchInfoHandler", + "opcode": 522, + "size": 8 + }, + { + "name": "Unk5", + "opcode": 547, + "size": 8 + }, + { + "name": "SocialListRequest", + "opcode": 417, + "size": 16 + }, + { + "name": "Unk7", + "opcode": 693, + "size": 32 + }, + { + "name": "UpdatePositionHandler", + "opcode": 561, + "size": 24 + }, + { + "name": "LogOut", + "opcode": 538, + "size": 8 + }, + { + "name": "Disconnected", + "opcode": 864, + "size": 8 + }, + { + "name": "ChatMessage", + "opcode": 375, + "size": 1056 + }, + { + "name": "GameMasterCommand", + "opcode": 905, + "size": 32 + }, + { + "name": "EnterZoneLine", + "opcode": 447, + "size": 24 + }, + { + "name": "ActionRequest", + "opcode": 865, + "size": 32 + }, + { + "name": "Unk16", + "opcode": 392, + "size": 6 + }, + { + "name": "Unk17", + "opcode": 466, + "size": 32 + }, + { + "name": "Unk18", + "opcode": 221, + "size": 8 + } + ], "ServerLobbyIpcType": [ { "name": "LobbyError", diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 4c3a132..08249f0 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -6,14 +6,15 @@ use kawari::common::{Position, determine_initial_starting_zone, get_citystate, g use kawari::config::get_config; use kawari::lobby::CharaMake; use kawari::oodle::OodleNetwork; +use kawari::opcodes::ServerZoneIpcType; use kawari::packet::{ CompressionType, ConnectionType, PacketSegment, PacketState, SegmentType, send_keep_alive, send_packet, }; use kawari::world::ipc::{ ClientZoneIpcData, CommonSpawn, DisplayFlag, GameMasterCommandType, GameMasterRank, ObjectKind, - OnlineStatus, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType, - SocialListRequestType, StatusEffect, + OnlineStatus, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, SocialListRequestType, + StatusEffect, }; use kawari::world::{ ChatHandler, Inventory, Zone, ZoneConnection, @@ -611,9 +612,6 @@ async fn main() { } } } - ClientZoneIpcData::Unk12 { .. } => { - tracing::info!("Recieved Unk12!"); - } ClientZoneIpcData::EnterZoneLine { exit_box_id, position, @@ -693,12 +691,6 @@ async fn main() { connection.change_zone(new_territory).await; } - ClientZoneIpcData::Unk13 { .. } => { - tracing::info!("Recieved Unk13!"); - } - ClientZoneIpcData::Unk14 { .. } => { - tracing::info!("Recieved Unk14!"); - } ClientZoneIpcData::ActionRequest(request) => { tracing::info!("Recieved action request: {:#?}!", request); @@ -752,9 +744,6 @@ async fn main() { .await; } } - ClientZoneIpcData::Unk15 { .. } => { - tracing::info!("Recieved Unk15!"); - } ClientZoneIpcData::Unk16 { .. } => { tracing::info!("Recieved Unk16!"); } diff --git a/src/world/chat_handler.rs b/src/world/chat_handler.rs index 6f07abd..200f289 100644 --- a/src/world/chat_handler.rs +++ b/src/world/chat_handler.rs @@ -1,11 +1,11 @@ use crate::{ common::{CustomizeData, ObjectId, ObjectTypeId, Position, timestamp_secs}, config::get_config, + opcodes::ServerZoneIpcType, packet::{PacketSegment, SegmentType}, world::ipc::{ ActorControl, ActorControlCategory, BattleNpcSubKind, CommonSpawn, DisplayFlag, NpcSpawn, ObjectKind, PlayerSpawn, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, - ServerZoneIpcType, }, }; diff --git a/src/world/connection.rs b/src/world/connection.rs index 34e1176..5580ac4 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -2,6 +2,7 @@ use tokio::net::TcpStream; use crate::{ common::{Position, timestamp_secs}, + opcodes::ServerZoneIpcType, packet::{ CompressionType, ConnectionType, PacketSegment, PacketState, SegmentType, parse_packet, send_packet, @@ -12,7 +13,7 @@ use super::{ Inventory, Item, Zone, ipc::{ ActorSetPos, ClientZoneIpcSegment, ContainerInfo, ContainerType, InitZone, ItemInfo, - ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType, UpdateClassInfo, WeatherChange, + ServerZoneIpcData, ServerZoneIpcSegment, UpdateClassInfo, WeatherChange, }, }; diff --git a/src/world/ipc/mod.rs b/src/world/ipc/mod.rs index bc6deb1..9fcc187 100644 --- a/src/world/ipc/mod.rs +++ b/src/world/ipc/mod.rs @@ -56,6 +56,8 @@ pub use item_info::ItemInfo; use crate::common::Position; use crate::common::read_string; use crate::common::write_string; +use crate::opcodes::ClientZoneIpcType; +use crate::opcodes::ServerZoneIpcType; use crate::packet::IpcSegment; use crate::packet::ReadWriteIpcSegment; @@ -64,32 +66,7 @@ pub type ClientZoneIpcSegment = IpcSegment impl ReadWriteIpcSegment for ClientZoneIpcSegment { fn calc_size(&self) -> u32 { // 16 is the size of the IPC header - 16 + match self.op_code { - ClientZoneIpcType::InitRequest => 120, - ClientZoneIpcType::FinishLoading => 72, - ClientZoneIpcType::Unk1 => 32, - ClientZoneIpcType::Unk2 => 16, - ClientZoneIpcType::Unk3 => 8, - ClientZoneIpcType::Unk4 => 8, - ClientZoneIpcType::SetSearchInfoHandler => 8, - ClientZoneIpcType::Unk5 => 8, - ClientZoneIpcType::SocialListRequest => 16, - ClientZoneIpcType::Unk7 => 32, - ClientZoneIpcType::UpdatePositionHandler => 24, - ClientZoneIpcType::LogOut => 8, - ClientZoneIpcType::Disconnected => 8, - ClientZoneIpcType::ChatMessage => 1056, - ClientZoneIpcType::GameMasterCommand => 32, - ClientZoneIpcType::Unk12 => todo!(), - ClientZoneIpcType::EnterZoneLine => 24, - ClientZoneIpcType::Unk13 => todo!(), - ClientZoneIpcType::Unk14 => todo!(), - ClientZoneIpcType::ActionRequest => 32, - ClientZoneIpcType::Unk15 => todo!(), - ClientZoneIpcType::Unk16 => 6, - ClientZoneIpcType::Unk17 => 32, - ClientZoneIpcType::Unk18 => 8, - } + 16 + self.op_code.calc_size() } } @@ -112,35 +89,7 @@ pub type ServerZoneIpcSegment = IpcSegment impl ReadWriteIpcSegment for ServerZoneIpcSegment { fn calc_size(&self) -> u32 { // 16 is the size of the IPC header - 16 + match self.op_code { - ServerZoneIpcType::InitializeChat => 8, - ServerZoneIpcType::InitZone => 103, - ServerZoneIpcType::ActorControlSelf => 32, - ServerZoneIpcType::PlayerStats => 224, - ServerZoneIpcType::PlayerSetup => 2784, - ServerZoneIpcType::UpdateClassInfo => 48, - ServerZoneIpcType::PlayerSpawn => 664, - ServerZoneIpcType::InitResponse => 16, - ServerZoneIpcType::LogOutComplete => 8, - ServerZoneIpcType::ActorSetPos => 24, - ServerZoneIpcType::ServerChatMessage => 776, - ServerZoneIpcType::Unk8 => 808, - ServerZoneIpcType::LinkShellInformation => 456, - ServerZoneIpcType::Unk9 => 24, - ServerZoneIpcType::Unk11 => 32, - ServerZoneIpcType::Unk15 => 8, - ServerZoneIpcType::Unk16 => 136, - ServerZoneIpcType::ActorControl => 24, - ServerZoneIpcType::ActorMove => 16, - ServerZoneIpcType::Unk17 => 104, - ServerZoneIpcType::SocialList => 1136, - ServerZoneIpcType::PrepareZoning => 16, - ServerZoneIpcType::NpcSpawn => 648, - ServerZoneIpcType::StatusEffectList => 384, - ServerZoneIpcType::WeatherChange => 8, - ServerZoneIpcType::ItemInfo => 64, - ServerZoneIpcType::ContainerInfo => 16, - } + 16 + self.op_code.calc_size() } } @@ -177,142 +126,38 @@ pub enum GameMasterCommandType { ChangeTerritory = 0x58, } -#[binrw] -#[brw(repr = u16)] -#[derive(Clone, PartialEq, Debug)] -pub enum ServerZoneIpcType { - /// Sent by the server to Initialize something chat-related? - InitializeChat = 0x2, - /// Sent by the server that tells the client which zone to load - InitZone = 0x2BB, - /// Sent by the server for... something - ActorControlSelf = 0x16F, - /// Sent by the server containing character stats - PlayerStats = 0x191, - /// Sent by the server to setup the player on the client - PlayerSetup = 0xDA, - // Sent by the server to setup class info - UpdateClassInfo = 0x77, - // Sent by the server to spawn the player in - PlayerSpawn = 0x331, - /// Sent by the server as response to ZoneInitRequest. - InitResponse = 0x223, - // Sent by the server to indicate the log out is complete - LogOutComplete = 0x69, - // Sent by the server to modify the client's position - ActorSetPos = 0x88, - // Sent by the server when they send a chat message - ServerChatMessage = 0x196, - // Unknown, server sends to the client before player spawn - Unk8 = 0x134, - // Unknown, but seems to contain information on cross-world linkshells - LinkShellInformation = 0x234, - // Unknown, server sends to the client before player spawn - Unk9 = 0x189, - // Unknown, server sends this in response to Unk7 - Unk11 = 0x156, - // Sent by the server when it wants the client to... prepare to zone? - PrepareZoning = 0x16C, - // Sent by the server??? - Unk15 = 0x28C, - // Sent by the server before init zone??? - Unk16 = 0x3AB, - // Sent by the server - ActorControl = 0x208, - // Sent by the server - ActorMove = 0x159, - // Sent by the server - Unk17 = 0x2A1, - // Sent by the server in response to SocialListRequest - SocialList = 0x36C, - // Sent by the server to spawn an NPC - NpcSpawn = 0x13F, - // Sent by the server to update an actor's status effect list - StatusEffectList = 0x347, - // Sent by the server when it's time to change the weather - WeatherChange = 0x175, - // Sent to inform the client of an inventory item - ItemInfo = 0x352, - // Sent to inform the client of container status - ContainerInfo = 0x236, -} - -#[binrw] -#[brw(repr = u16)] -#[derive(Clone, PartialEq, Debug)] -pub enum ClientZoneIpcType { - /// Sent by the client when they successfully initialize with the server, and they need several bits of information (e.g. what zone to load) - InitRequest = 0x2AB, - // Sent by the client when they're done loading and they need to be spawned in - FinishLoading = 0x1AB, - // FIXME: 32 bytes of something from the client, not sure what yet - Unk1 = 0x364, - // FIXME: 16 bytes of something from the client, not sure what yet - Unk2 = 0x1D8, - // FIXME: 8 bytes of something from the client, not sure what yet - Unk3 = 0x2AF, - // FIXME: 8 bytes of something from the client, not sure what yet - Unk4 = 0x178, - SetSearchInfoHandler = 0x20A, - // FIXME: 8 bytes of something from the client, not sure what yet - Unk5 = 0x223, - // Sent by the client when it requests the friends list and other related info - SocialListRequest = 0x1A1, - // FIXME: 32 bytes of something from the client, not sure what yet - Unk7 = 0x2B5, - UpdatePositionHandler = 0x231, - // Sent by the client when the user requests to log out - LogOut = 0x21A, - // Sent by the client when it's actually disconnecting - Disconnected = 0x360, - // Sent by the client when they send a chat message - ChatMessage = 0x177, - // Sent by the client when they send a GM command. This can only be sent by the client if they are sent a GM rank. - GameMasterCommand = 0x389, - // Unknown, client sends this for ??? - Unk12 = 0x0E9, - // Sent by the client when the character walks into a zone transistion - EnterZoneLine = 0x1BF, - // Sent by the client after we sent a InitZone in TravelToZone?? - // TODO: Actually, I don't think is real... - Unk13 = 0x2EE, - // Sent by the client for unknown reasons - Unk14 = 0x87, - // Sent by the client when a character performs an action - ActionRequest = 0x361, - /// Sent by the client for unknown reasons, it's a bunch of numbers? - Unk15 = 0x10B, - /// 8 unknown bytes sent by the client for unknown reason - Unk16 = 0x188, - // FIXME: 32 bytes of something from the client, not sure what yet - Unk17 = 0x1D2, - // FIXME: 8 bytes of something from the client, not sure what yet - Unk18 = 0xDD, -} - #[binrw] #[br(import(_magic: &ServerZoneIpcType))] #[derive(Debug, Clone)] pub enum ServerZoneIpcData { - InitializeChat { - unk: [u8; 8], - }, + /// Sent by the server to Initialize something chat-related? + InitializeChat { unk: [u8; 8] }, + /// Sent by the server as response to ZoneInitRequest. InitResponse { unk1: u64, character_id: u32, unk2: u32, }, + /// Sent by the server that tells the client which zone to load InitZone(InitZone), + /// Sent by the server for... something ActorControlSelf(ActorControlSelf), + /// Sent by the server containing character stats PlayerStats(PlayerStats), + /// Sent by the server to setup the player on the client PlayerSetup(PlayerSetup), + /// Sent by the server to setup class info UpdateClassInfo(UpdateClassInfo), + /// Sent by the server to spawn the player in PlayerSpawn(PlayerSpawn), + /// Sent by the server to indicate the log out is complete LogOutComplete { // TODO: guessed unk: [u8; 8], }, + /// Sent by the server to modify the client's position ActorSetPos(ActorSetPos), + /// Sent by the server when they send a chat message ServerChatMessage { unk: u8, // channel? #[brw(pad_after = 775)] @@ -321,43 +166,44 @@ pub enum ServerZoneIpcData { #[bw(map = write_string)] message: String, }, - Unk8 { - unk: [u8; 808], - }, - LinkShellInformation { - unk: [u8; 456], - }, - Unk9 { - unk: [u8; 24], - }, + /// Unknown, server sends to the client before player spawn + Unk8 { unk: [u8; 808] }, + /// Unknown, but seems to contain information on cross-world linkshells + LinkShellInformation { unk: [u8; 456] }, + /// Unknown, server sends to the client before player spawn + Unk9 { unk: [u8; 24] }, + /// Unknown, server sends this in response to Unk7 Unk11 { timestamp: u32, #[brw(pad_after = 24)] // empty bytes unk: u32, }, - PrepareZoning { - unk: [u32; 4], - }, - Unk15 { - unk: u32, - player_id: u32, - }, - Unk16 { - unk: [u8; 136], - }, + /// Sent by the server when it wants the client to... prepare to zone? + PrepareZoning { unk: [u32; 4] }, + /// Sent by the server??? + Unk15 { unk: u32, player_id: u32 }, + /// Sent by the server before init zone??? + Unk16 { unk: [u8; 136] }, + /// Sent by the server ActorControl(ActorControl), + /// Sent by the server ActorMove { #[brw(pad_after = 4)] // empty pos: Position, }, - Unk17 { - unk: [u8; 104], - }, + /// Sent by the server + Unk17 { unk: [u8; 104] }, + /// Sent by the server in response to SocialListRequest SocialList(SocialList), + /// Sent by the server to spawn an NPC NpcSpawn(NpcSpawn), + /// Sent by the server to update an actor's status effect list StatusEffectList(StatusEffectList), + /// Sent by the server when it's time to change the weather WeatherChange(WeatherChange), + /// Sent to inform the client of an inventory item ItemInfo(ItemInfo), + /// Sent to inform the client of container status ContainerInfo(ContainerInfo), } @@ -365,31 +211,37 @@ pub enum ServerZoneIpcData { #[br(import(magic: &ClientZoneIpcType))] #[derive(Debug, Clone)] pub enum ClientZoneIpcData { + /// Sent by the client when they successfully initialize with the server, and they need several bits of information (e.g. what zone to load) #[br(pre_assert(*magic == ClientZoneIpcType::InitRequest))] InitRequest { // TODO: full of possibly interesting information unk: [u8; 120], }, + /// Sent by the client when they're done loading and they need to be spawned in #[br(pre_assert(*magic == ClientZoneIpcType::FinishLoading))] FinishLoading { // TODO: full of possibly interesting information unk: [u8; 72], }, + /// 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], }, + /// FIXME: 16 bytes of something from the client, not sure what yet #[br(pre_assert(*magic == ClientZoneIpcType::Unk2))] Unk2 { // TODO: full of possibly interesting information unk: [u8; 16], }, + /// FIXME: 8 bytes of something from the client, not sure what yet #[br(pre_assert(*magic == ClientZoneIpcType::Unk3))] Unk3 { // TODO: full of possibly interesting information unk: [u8; 8], }, + /// FIXME: 8 bytes of something from the client, not sure what yet #[br(pre_assert(*magic == ClientZoneIpcType::Unk4))] Unk4 { // TODO: full of possibly interesting information @@ -400,13 +252,16 @@ pub enum ClientZoneIpcData { // TODO: full of possibly interesting information unk: [u8; 8], }, + /// FIXME: 8 bytes of something from the client, not sure what yet #[br(pre_assert(*magic == ClientZoneIpcType::Unk5))] Unk5 { // TODO: full of possibly interesting information unk: [u8; 8], }, + /// Sent by the client when it requests the friends list and other related info #[br(pre_assert(*magic == ClientZoneIpcType::SocialListRequest))] SocialListRequest(SocialListRequest), + /// FIXME: 32 bytes of something from the client, not sure what yet #[br(pre_assert(*magic == ClientZoneIpcType::Unk7))] Unk7 { // TODO: full of possibly interesting information @@ -422,18 +277,22 @@ pub enum ClientZoneIpcData { #[brw(pad_after = 4)] // empty position: Position, }, + /// Sent by the client when the user requests to log out #[br(pre_assert(*magic == ClientZoneIpcType::LogOut))] LogOut { // TODO: full of possibly interesting information unk: [u8; 8], }, + /// Sent by the client when it's actually disconnecting #[br(pre_assert(*magic == ClientZoneIpcType::Disconnected))] Disconnected { // TODO: full of possibly interesting information unk: [u8; 8], }, + /// Sent by the client when they send a chat message #[br(pre_assert(*magic == ClientZoneIpcType::ChatMessage))] ChatMessage(ChatMessage), + /// Sent by the client when they send a GM command. This can only be sent by the client if they are sent a GM rank. #[br(pre_assert(*magic == ClientZoneIpcType::GameMasterCommand))] GameMasterCommand { // TODO: incomplete @@ -442,10 +301,7 @@ pub enum ClientZoneIpcData { arg: u32, unk: [u8; 24], }, - #[br(pre_assert(*magic == ClientZoneIpcType::Unk12))] - Unk12 { - unk: [u8; 8], // TODO: unknown - }, + /// Sent by the client when the character walks into a zone transistion #[br(pre_assert(*magic == ClientZoneIpcType::EnterZoneLine))] EnterZoneLine { exit_box_id: u32, @@ -453,18 +309,9 @@ pub enum ClientZoneIpcData { #[brw(pad_after = 4)] // empty landset_index: i32, }, - #[br(pre_assert(*magic == ClientZoneIpcType::Unk13))] - Unk13 { - unk: [u8; 16], // TODO: unknown - }, - #[br(pre_assert(*magic == ClientZoneIpcType::Unk14))] - Unk14 { - unk: [u8; 8], // TODO: unknown - }, + /// Sent by the client when a character performs an action #[br(pre_assert(*magic == ClientZoneIpcType::ActionRequest))] ActionRequest(ActionRequest), - #[br(pre_assert(*magic == ClientZoneIpcType::Unk15))] - Unk15 { unk: [u8; 632] }, #[br(pre_assert(*magic == ClientZoneIpcType::Unk16))] Unk16 { unk: [u8; 8], // TODO: unknown @@ -485,6 +332,8 @@ mod tests { use binrw::BinWrite; + use crate::opcodes::ServerZoneIpcType; + use super::*; /// Ensure that the IPC data size as reported matches up with what we write