2025-03-16 17:43:29 -04:00
|
|
|
mod chat_message;
|
|
|
|
use binrw::binrw;
|
|
|
|
pub use chat_message::ChatMessage;
|
|
|
|
|
|
|
|
mod social_list;
|
|
|
|
pub use social_list::PlayerEntry;
|
|
|
|
pub use social_list::SocialList;
|
|
|
|
pub use social_list::SocialListRequest;
|
|
|
|
pub use social_list::SocialListRequestType;
|
|
|
|
|
|
|
|
mod player_spawn;
|
|
|
|
pub use player_spawn::PlayerSpawn;
|
|
|
|
|
|
|
|
mod status_effect;
|
|
|
|
pub use status_effect::StatusEffect;
|
|
|
|
|
|
|
|
mod update_class_info;
|
|
|
|
pub use update_class_info::UpdateClassInfo;
|
|
|
|
|
|
|
|
mod player_setup;
|
|
|
|
pub use player_setup::PlayerSetup;
|
|
|
|
|
|
|
|
mod player_stats;
|
|
|
|
pub use player_stats::PlayerStats;
|
|
|
|
|
2025-03-18 23:30:59 -04:00
|
|
|
mod actor_control;
|
2025-03-23 15:28:53 -04:00
|
|
|
pub use actor_control::{ActorControl, ActorControlCategory, ActorControlSelf};
|
2025-03-16 17:43:29 -04:00
|
|
|
|
|
|
|
mod init_zone;
|
|
|
|
pub use init_zone::InitZone;
|
|
|
|
|
2025-03-18 22:13:28 -04:00
|
|
|
mod npc_spawn;
|
|
|
|
pub use npc_spawn::NpcSpawn;
|
|
|
|
|
|
|
|
mod common_spawn;
|
2025-03-23 12:16:15 -04:00
|
|
|
pub use common_spawn::{
|
2025-03-23 12:54:04 -04:00
|
|
|
BattleNpcSubKind, CharacterMode, CommonSpawn, DisplayFlag, GameMasterRank, ObjectKind,
|
|
|
|
OnlineStatus, PlayerSubKind,
|
2025-03-23 12:16:15 -04:00
|
|
|
};
|
2025-03-18 22:13:28 -04:00
|
|
|
|
2025-03-18 23:30:59 -04:00
|
|
|
mod status_effect_list;
|
|
|
|
pub use status_effect_list::StatusEffectList;
|
|
|
|
|
2025-03-18 23:48:00 -04:00
|
|
|
mod weather_change;
|
|
|
|
pub use weather_change::WeatherChange;
|
|
|
|
|
2025-03-19 00:28:47 -04:00
|
|
|
mod action_request;
|
|
|
|
pub use action_request::ActionRequest;
|
|
|
|
|
2025-03-23 16:45:52 -04:00
|
|
|
mod container_info;
|
2025-03-23 16:49:48 -04:00
|
|
|
pub use container_info::{ContainerInfo, ContainerType};
|
2025-03-23 16:45:52 -04:00
|
|
|
|
|
|
|
mod item_info;
|
|
|
|
pub use item_info::ItemInfo;
|
|
|
|
|
2025-03-28 20:19:17 -04:00
|
|
|
mod event_play;
|
|
|
|
pub use event_play::EventPlay;
|
|
|
|
|
|
|
|
mod event_start;
|
|
|
|
pub use event_start::EventStart;
|
|
|
|
|
2025-03-22 22:01:32 -04:00
|
|
|
use crate::common::Position;
|
2025-03-16 17:43:29 -04:00
|
|
|
use crate::common::read_string;
|
|
|
|
use crate::common::write_string;
|
2025-03-26 18:28:51 -04:00
|
|
|
use crate::opcodes::ClientZoneIpcType;
|
|
|
|
use crate::opcodes::ServerZoneIpcType;
|
2025-03-16 17:43:29 -04:00
|
|
|
use crate::packet::IpcSegment;
|
2025-03-17 17:12:40 -04:00
|
|
|
use crate::packet::ReadWriteIpcSegment;
|
2025-03-16 17:43:29 -04:00
|
|
|
|
|
|
|
pub type ClientZoneIpcSegment = IpcSegment<ClientZoneIpcType, ClientZoneIpcData>;
|
|
|
|
|
2025-03-25 18:15:41 -04:00
|
|
|
impl ReadWriteIpcSegment for ClientZoneIpcSegment {
|
|
|
|
fn calc_size(&self) -> u32 {
|
|
|
|
// 16 is the size of the IPC header
|
2025-03-26 18:28:51 -04:00
|
|
|
16 + self.op_code.calc_size()
|
2025-03-25 18:15:41 -04:00
|
|
|
}
|
|
|
|
}
|
2025-03-16 17:43:29 -04:00
|
|
|
|
|
|
|
// TODO: make generic
|
|
|
|
impl Default for ClientZoneIpcSegment {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
unk1: 0x14,
|
|
|
|
unk2: 0,
|
|
|
|
op_code: ClientZoneIpcType::InitRequest,
|
|
|
|
server_id: 0,
|
|
|
|
timestamp: 0,
|
2025-03-25 18:15:41 -04:00
|
|
|
data: ClientZoneIpcData::InitRequest { unk: [0; 120] },
|
2025-03-16 17:43:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type ServerZoneIpcSegment = IpcSegment<ServerZoneIpcType, ServerZoneIpcData>;
|
|
|
|
|
2025-03-17 17:12:40 -04:00
|
|
|
impl ReadWriteIpcSegment for ServerZoneIpcSegment {
|
2025-03-16 17:43:29 -04:00
|
|
|
fn calc_size(&self) -> u32 {
|
|
|
|
// 16 is the size of the IPC header
|
2025-03-26 18:28:51 -04:00
|
|
|
16 + self.op_code.calc_size()
|
2025-03-16 17:43:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: make generic
|
|
|
|
impl Default for ServerZoneIpcSegment {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
unk1: 0x14,
|
|
|
|
unk2: 0,
|
|
|
|
op_code: ServerZoneIpcType::InitializeChat,
|
|
|
|
server_id: 0,
|
|
|
|
timestamp: 0,
|
|
|
|
data: ServerZoneIpcData::InitializeChat { unk: [0; 8] },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: move to their own files
|
|
|
|
#[binrw]
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
|
|
pub struct ActorSetPos {
|
|
|
|
pub unk: u32,
|
|
|
|
pub layer_id: u32,
|
|
|
|
pub position: Position,
|
|
|
|
pub unk3: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binrw]
|
|
|
|
#[brw(repr = u8)]
|
|
|
|
#[derive(Clone, PartialEq, Debug)]
|
|
|
|
pub enum GameMasterCommandType {
|
2025-03-18 23:48:00 -04:00
|
|
|
ChangeWeather = 0x6,
|
2025-03-23 15:28:53 -04:00
|
|
|
ToggleInvisibility = 0xD,
|
2025-03-16 17:43:29 -04:00
|
|
|
ChangeTerritory = 0x58,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binrw]
|
|
|
|
#[br(import(_magic: &ServerZoneIpcType))]
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum ServerZoneIpcData {
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server to Initialize something chat-related?
|
|
|
|
InitializeChat { unk: [u8; 8] },
|
|
|
|
/// Sent by the server as response to ZoneInitRequest.
|
2025-03-16 17:43:29 -04:00
|
|
|
InitResponse {
|
|
|
|
unk1: u64,
|
|
|
|
character_id: u32,
|
|
|
|
unk2: u32,
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server that tells the client which zone to load
|
2025-03-16 17:43:29 -04:00
|
|
|
InitZone(InitZone),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server for... something
|
2025-03-16 17:43:29 -04:00
|
|
|
ActorControlSelf(ActorControlSelf),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server containing character stats
|
2025-03-16 17:43:29 -04:00
|
|
|
PlayerStats(PlayerStats),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server to setup the player on the client
|
2025-03-16 17:43:29 -04:00
|
|
|
PlayerSetup(PlayerSetup),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server to setup class info
|
2025-03-16 17:43:29 -04:00
|
|
|
UpdateClassInfo(UpdateClassInfo),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server to spawn the player in
|
2025-03-16 17:43:29 -04:00
|
|
|
PlayerSpawn(PlayerSpawn),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server to indicate the log out is complete
|
2025-03-16 17:43:29 -04:00
|
|
|
LogOutComplete {
|
|
|
|
// TODO: guessed
|
|
|
|
unk: [u8; 8],
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server to modify the client's position
|
2025-03-16 17:43:29 -04:00
|
|
|
ActorSetPos(ActorSetPos),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server when they send a chat message
|
2025-03-16 17:43:29 -04:00
|
|
|
ServerChatMessage {
|
|
|
|
unk: u8, // channel?
|
|
|
|
#[brw(pad_after = 775)]
|
|
|
|
#[br(count = 775)]
|
|
|
|
#[br(map = read_string)]
|
|
|
|
#[bw(map = write_string)]
|
|
|
|
message: String,
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// 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
|
2025-03-16 17:43:29 -04:00
|
|
|
Unk11 {
|
|
|
|
timestamp: u32,
|
|
|
|
#[brw(pad_after = 24)] // empty bytes
|
|
|
|
unk: u32,
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// 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
|
2025-03-18 23:30:59 -04:00
|
|
|
ActorControl(ActorControl),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server
|
2025-03-16 17:43:29 -04:00
|
|
|
ActorMove {
|
|
|
|
#[brw(pad_after = 4)] // empty
|
|
|
|
pos: Position,
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server
|
|
|
|
Unk17 { unk: [u8; 104] },
|
|
|
|
/// Sent by the server in response to SocialListRequest
|
2025-03-16 17:43:29 -04:00
|
|
|
SocialList(SocialList),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server to spawn an NPC
|
2025-03-18 22:13:28 -04:00
|
|
|
NpcSpawn(NpcSpawn),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server to update an actor's status effect list
|
2025-03-18 23:30:59 -04:00
|
|
|
StatusEffectList(StatusEffectList),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the server when it's time to change the weather
|
2025-03-18 23:48:00 -04:00
|
|
|
WeatherChange(WeatherChange),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent to inform the client of an inventory item
|
2025-03-23 17:10:47 -04:00
|
|
|
ItemInfo(ItemInfo),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent to inform the client of container status
|
2025-03-23 17:10:47 -04:00
|
|
|
ContainerInfo(ContainerInfo),
|
2025-03-28 20:19:17 -04:00
|
|
|
/// Sent to tell the client to play a scene
|
|
|
|
EventPlay(EventPlay),
|
|
|
|
/// Sent to tell the client to load a scene, but not play it
|
|
|
|
EventStart(EventStart),
|
2025-03-16 17:43:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[binrw]
|
|
|
|
#[br(import(magic: &ClientZoneIpcType))]
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum ClientZoneIpcData {
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the client when they successfully initialize with the server, and they need several bits of information (e.g. what zone to load)
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::InitRequest))]
|
|
|
|
InitRequest {
|
|
|
|
// TODO: full of possibly interesting information
|
2025-03-25 18:15:41 -04:00
|
|
|
unk: [u8; 120],
|
2025-03-16 17:43:29 -04:00
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the client when they're done loading and they need to be spawned in
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::FinishLoading))]
|
|
|
|
FinishLoading {
|
|
|
|
// TODO: full of possibly interesting information
|
|
|
|
unk: [u8; 72],
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// FIXME: 32 bytes of something from the client, not sure what yet
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Unk1))]
|
|
|
|
Unk1 {
|
|
|
|
// TODO: full of possibly interesting information
|
|
|
|
unk: [u8; 32],
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// FIXME: 16 bytes of something from the client, not sure what yet
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Unk2))]
|
|
|
|
Unk2 {
|
|
|
|
// TODO: full of possibly interesting information
|
2025-03-25 18:15:41 -04:00
|
|
|
unk: [u8; 16],
|
2025-03-16 17:43:29 -04:00
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// FIXME: 8 bytes of something from the client, not sure what yet
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Unk3))]
|
|
|
|
Unk3 {
|
|
|
|
// TODO: full of possibly interesting information
|
|
|
|
unk: [u8; 8],
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// FIXME: 8 bytes of something from the client, not sure what yet
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Unk4))]
|
|
|
|
Unk4 {
|
|
|
|
// TODO: full of possibly interesting information
|
|
|
|
unk: [u8; 8],
|
|
|
|
},
|
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::SetSearchInfoHandler))]
|
|
|
|
SetSearchInfoHandler {
|
|
|
|
// TODO: full of possibly interesting information
|
|
|
|
unk: [u8; 8],
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// FIXME: 8 bytes of something from the client, not sure what yet
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Unk5))]
|
|
|
|
Unk5 {
|
|
|
|
// TODO: full of possibly interesting information
|
|
|
|
unk: [u8; 8],
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the client when it requests the friends list and other related info
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::SocialListRequest))]
|
|
|
|
SocialListRequest(SocialListRequest),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// FIXME: 32 bytes of something from the client, not sure what yet
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Unk7))]
|
|
|
|
Unk7 {
|
|
|
|
// TODO: full of possibly interesting information
|
|
|
|
timestamp: u32,
|
|
|
|
#[brw(pad_before = 8)] // empty bytes
|
|
|
|
#[brw(pad_after = 4)] // empty bytes
|
|
|
|
unk1: [u8; 16], // something
|
|
|
|
},
|
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::UpdatePositionHandler))]
|
|
|
|
UpdatePositionHandler {
|
|
|
|
// TODO: full of possibly interesting information
|
2025-03-23 10:33:49 -04:00
|
|
|
unk: [u8; 8], // not empty
|
|
|
|
#[brw(pad_after = 4)] // empty
|
|
|
|
position: Position,
|
2025-03-16 17:43:29 -04:00
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the client when the user requests to log out
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::LogOut))]
|
|
|
|
LogOut {
|
|
|
|
// TODO: full of possibly interesting information
|
|
|
|
unk: [u8; 8],
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the client when it's actually disconnecting
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Disconnected))]
|
|
|
|
Disconnected {
|
|
|
|
// TODO: full of possibly interesting information
|
|
|
|
unk: [u8; 8],
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the client when they send a chat message
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::ChatMessage))]
|
|
|
|
ChatMessage(ChatMessage),
|
2025-03-26 18:28:51 -04:00
|
|
|
/// 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.
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::GameMasterCommand))]
|
|
|
|
GameMasterCommand {
|
|
|
|
// TODO: incomplete
|
|
|
|
command: GameMasterCommandType,
|
|
|
|
#[br(pad_before = 3)] // idk, not empty though
|
|
|
|
arg: u32,
|
|
|
|
unk: [u8; 24],
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the client when the character walks into a zone transistion
|
2025-03-16 17:43:29 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::EnterZoneLine))]
|
|
|
|
EnterZoneLine {
|
|
|
|
exit_box_id: u32,
|
|
|
|
position: Position,
|
|
|
|
#[brw(pad_after = 4)] // empty
|
|
|
|
landset_index: i32,
|
|
|
|
},
|
2025-03-26 18:28:51 -04:00
|
|
|
/// Sent by the client when a character performs an action
|
2025-03-19 00:28:47 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::ActionRequest))]
|
|
|
|
ActionRequest(ActionRequest),
|
2025-03-26 16:58:55 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Unk16))]
|
|
|
|
Unk16 {
|
|
|
|
unk: [u8; 8], // TODO: unknown
|
|
|
|
},
|
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Unk17))]
|
|
|
|
Unk17 {
|
|
|
|
unk: [u8; 32], // TODO: unknown
|
|
|
|
},
|
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::Unk18))]
|
|
|
|
Unk18 {
|
|
|
|
unk: [u8; 8], // TODO: unknown
|
|
|
|
},
|
2025-03-28 20:19:17 -04:00
|
|
|
#[br(pre_assert(*magic == ClientZoneIpcType::EventRelatedUnk))]
|
|
|
|
EventRelatedUnk {
|
|
|
|
unk1: u32,
|
|
|
|
unk2: u32,
|
|
|
|
unk3: u32,
|
|
|
|
unk4: u32,
|
|
|
|
},
|
2025-03-16 17:43:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
use binrw::BinWrite;
|
|
|
|
|
2025-03-26 18:28:51 -04:00
|
|
|
use crate::opcodes::ServerZoneIpcType;
|
|
|
|
|
2025-03-16 17:43:29 -04:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
/// Ensure that the IPC data size as reported matches up with what we write
|
|
|
|
#[test]
|
|
|
|
fn world_ipc_sizes() {
|
|
|
|
let ipc_types = [
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::ActorControlSelf,
|
|
|
|
ServerZoneIpcData::ActorControlSelf(ActorControlSelf::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::InitializeChat,
|
|
|
|
ServerZoneIpcData::InitializeChat { unk: [0; 8] },
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::PlayerStats,
|
|
|
|
ServerZoneIpcData::PlayerStats(PlayerStats::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::PlayerSetup,
|
|
|
|
ServerZoneIpcData::PlayerSetup(PlayerSetup::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::UpdateClassInfo,
|
|
|
|
ServerZoneIpcData::UpdateClassInfo(UpdateClassInfo::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::PlayerSpawn,
|
|
|
|
ServerZoneIpcData::PlayerSpawn(PlayerSpawn::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::ActorSetPos,
|
|
|
|
ServerZoneIpcData::ActorSetPos(ActorSetPos::default()),
|
|
|
|
),
|
2025-03-18 22:13:28 -04:00
|
|
|
(
|
|
|
|
ServerZoneIpcType::NpcSpawn,
|
|
|
|
ServerZoneIpcData::NpcSpawn(NpcSpawn::default()),
|
|
|
|
),
|
2025-03-18 23:30:59 -04:00
|
|
|
(
|
|
|
|
ServerZoneIpcType::ActorControl,
|
|
|
|
ServerZoneIpcData::ActorControl(ActorControl::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::StatusEffectList,
|
|
|
|
ServerZoneIpcData::StatusEffectList(StatusEffectList::default()),
|
|
|
|
),
|
2025-03-18 23:48:00 -04:00
|
|
|
(
|
|
|
|
ServerZoneIpcType::WeatherChange,
|
|
|
|
ServerZoneIpcData::WeatherChange(WeatherChange::default()),
|
|
|
|
),
|
2025-03-23 15:28:53 -04:00
|
|
|
(
|
|
|
|
ServerZoneIpcType::ActorControl,
|
|
|
|
ServerZoneIpcData::ActorControl(ActorControl::default()),
|
|
|
|
),
|
2025-03-23 17:10:47 -04:00
|
|
|
(
|
|
|
|
ServerZoneIpcType::ItemInfo,
|
|
|
|
ServerZoneIpcData::ItemInfo(ItemInfo::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::ContainerInfo,
|
|
|
|
ServerZoneIpcData::ContainerInfo(ContainerInfo::default()),
|
|
|
|
),
|
2025-03-28 20:19:17 -04:00
|
|
|
(
|
|
|
|
ServerZoneIpcType::EventPlay,
|
|
|
|
ServerZoneIpcData::EventPlay(EventPlay::default()),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ServerZoneIpcType::EventStart,
|
|
|
|
ServerZoneIpcData::EventStart(EventStart::default()),
|
|
|
|
),
|
2025-03-16 17:43:29 -04:00
|
|
|
];
|
|
|
|
|
|
|
|
for (opcode, data) in &ipc_types {
|
|
|
|
let mut cursor = Cursor::new(Vec::new());
|
|
|
|
|
|
|
|
let ipc_segment = ServerZoneIpcSegment {
|
|
|
|
unk1: 0,
|
|
|
|
unk2: 0,
|
|
|
|
op_code: opcode.clone(), // doesn't matter for this test
|
|
|
|
server_id: 0,
|
|
|
|
timestamp: 0,
|
|
|
|
data: data.clone(),
|
|
|
|
};
|
|
|
|
ipc_segment.write_le(&mut cursor).unwrap();
|
|
|
|
|
|
|
|
let buffer = cursor.into_inner();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
buffer.len(),
|
|
|
|
ipc_segment.calc_size() as usize,
|
|
|
|
"{:#?} did not match size!",
|
|
|
|
opcode
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|