mirror of
https://github.com/redstrate/Kawari.git
synced 2025-06-30 11:47:45 +00:00
Add various things useful for using Kawari downstream
Such as better unknown packet parsing, IPC opcode names and more stuff exposed as public API.
This commit is contained in:
parent
b560642978
commit
db3166d8b3
9 changed files with 105 additions and 11 deletions
30
build.rs
30
build.rs
|
@ -24,7 +24,6 @@ fn main() {
|
||||||
if !opcodes.is_empty() {
|
if !opcodes.is_empty() {
|
||||||
// beginning
|
// beginning
|
||||||
output_str.push_str("#[binrw]\n");
|
output_str.push_str("#[binrw]\n");
|
||||||
output_str.push_str("#[brw(repr = u16)]\n");
|
|
||||||
output_str.push_str("#[derive(Clone, PartialEq, Debug)]\n");
|
output_str.push_str("#[derive(Clone, PartialEq, Debug)]\n");
|
||||||
output_str.push_str(&format!("pub enum {key} {{\n"));
|
output_str.push_str(&format!("pub enum {key} {{\n"));
|
||||||
|
|
||||||
|
@ -33,14 +32,18 @@ fn main() {
|
||||||
let name = opcode.get("name").unwrap().as_str().unwrap();
|
let name = opcode.get("name").unwrap().as_str().unwrap();
|
||||||
let opcode = opcode.get("opcode").unwrap().as_number().unwrap();
|
let opcode = opcode.get("opcode").unwrap().as_number().unwrap();
|
||||||
|
|
||||||
output_str.push_str(&format!("{name} = {opcode},\n"));
|
output_str.push_str(&format!("#[brw(magic = {opcode}u16)]\n"));
|
||||||
|
output_str.push_str(&format!("{name},\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output_str.push_str(&format!("Unknown,\n"));
|
||||||
|
|
||||||
// end
|
// end
|
||||||
output_str.push_str("}\n\n");
|
output_str.push_str("}\n\n");
|
||||||
|
|
||||||
// sizes
|
|
||||||
output_str.push_str(&format!("impl {key} {{\n"));
|
output_str.push_str(&format!("impl {key} {{\n"));
|
||||||
|
|
||||||
|
// sizes
|
||||||
output_str.push_str("/// Returns the expected size of the data segment of this IPC opcode, _without_ any headers.\n");
|
output_str.push_str("/// Returns the expected size of the data segment of this IPC opcode, _without_ any headers.\n");
|
||||||
output_str.push_str("pub fn calc_size(&self) -> u32 {\n");
|
output_str.push_str("pub fn calc_size(&self) -> u32 {\n");
|
||||||
output_str.push_str("match self {\n");
|
output_str.push_str("match self {\n");
|
||||||
|
@ -53,8 +56,29 @@ fn main() {
|
||||||
output_str.push_str(&format!("{key}::{name} => {size},\n"));
|
output_str.push_str(&format!("{key}::{name} => {size},\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output_str.push_str(&format!("{key}::Unknown => 0,\n"));
|
||||||
|
|
||||||
output_str.push_str("}\n\n");
|
output_str.push_str("}\n\n");
|
||||||
output_str.push_str("}\n\n");
|
output_str.push_str("}\n\n");
|
||||||
|
|
||||||
|
// names
|
||||||
|
output_str.push_str("/// Returns a human-readable name of the opcode.\n");
|
||||||
|
output_str.push_str("pub fn get_name(&self) -> &'static str {\n");
|
||||||
|
output_str.push_str("match self {\n");
|
||||||
|
|
||||||
|
for opcode in opcodes {
|
||||||
|
let opcode = opcode.as_object().unwrap();
|
||||||
|
let name = opcode.get("name").unwrap().as_str().unwrap();
|
||||||
|
|
||||||
|
output_str.push_str(&format!("{key}::{name} => \"{name}\",\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
output_str.push_str(&format!("{key}::Unknown => \"Unknown\",\n"));
|
||||||
|
|
||||||
|
output_str.push_str("}\n\n");
|
||||||
|
output_str.push_str("}\n\n");
|
||||||
|
|
||||||
|
// end impl
|
||||||
output_str.push_str("}\n\n");
|
output_str.push_str("}\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,12 @@ pub(crate) fn write_bool_as<T: std::convert::From<u8>>(x: &bool) -> T {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_string(byte_stream: Vec<u8>) -> String {
|
pub(crate) fn read_string(byte_stream: Vec<u8>) -> String {
|
||||||
let str = String::from_utf8(byte_stream).unwrap();
|
// TODO: better error handling here
|
||||||
str.trim_matches(char::from(0)).to_string() // trim \0 from the end of strings
|
if let Ok(str) = String::from_utf8(byte_stream) {
|
||||||
|
str.trim_matches(char::from(0)).to_string() // trim \0 from the end of strings
|
||||||
|
} else {
|
||||||
|
String::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn write_string(str: &String) -> Vec<u8> {
|
pub(crate) fn write_string(str: &String) -> Vec<u8> {
|
||||||
|
|
|
@ -12,6 +12,10 @@ impl ReadWriteIpcSegment for ServerChatIpcSegment {
|
||||||
// 16 is the size of the IPC header
|
// 16 is the size of the IPC header
|
||||||
16 + self.op_code.calc_size()
|
16 + self.op_code.calc_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> &'static str {
|
||||||
|
self.op_code.get_name()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make generic
|
// TODO: make generic
|
||||||
|
|
|
@ -27,6 +27,10 @@ impl ReadWriteIpcSegment for CustomIpcSegment {
|
||||||
CustomIpcType::CharacterRemade => 8,
|
CustomIpcType::CharacterRemade => 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> &'static str {
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CustomIpcSegment {
|
impl Default for CustomIpcSegment {
|
||||||
|
|
|
@ -25,6 +25,10 @@ impl ReadWriteIpcSegment for ClientLobbyIpcSegment {
|
||||||
// 16 is the size of the IPC header
|
// 16 is the size of the IPC header
|
||||||
16 + self.op_code.calc_size()
|
16 + self.op_code.calc_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> &'static str {
|
||||||
|
self.op_code.get_name()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make generic
|
// TODO: make generic
|
||||||
|
@ -52,6 +56,10 @@ impl ReadWriteIpcSegment for ServerLobbyIpcSegment {
|
||||||
// 16 is the size of the IPC header
|
// 16 is the size of the IPC header
|
||||||
16 + self.op_code.calc_size()
|
16 + self.op_code.calc_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> &'static str {
|
||||||
|
self.op_code.get_name()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make generic
|
// TODO: make generic
|
||||||
|
|
|
@ -115,6 +115,7 @@ pub enum ActorControlCategory {
|
||||||
actor_id: u32,
|
actor_id: u32,
|
||||||
unk1: u32,
|
unk1: u32,
|
||||||
},
|
},
|
||||||
|
Unknown {},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
|
|
@ -101,6 +101,10 @@ impl ReadWriteIpcSegment for ClientZoneIpcSegment {
|
||||||
// 16 is the size of the IPC header
|
// 16 is the size of the IPC header
|
||||||
16 + self.op_code.calc_size()
|
16 + self.op_code.calc_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> &'static str {
|
||||||
|
self.op_code.get_name()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make generic
|
// TODO: make generic
|
||||||
|
@ -124,6 +128,10 @@ impl ReadWriteIpcSegment for ServerZoneIpcSegment {
|
||||||
// 16 is the size of the IPC header
|
// 16 is the size of the IPC header
|
||||||
16 + self.op_code.calc_size()
|
16 + self.op_code.calc_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> &'static str {
|
||||||
|
self.op_code.get_name()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make generic
|
// TODO: make generic
|
||||||
|
@ -159,35 +167,45 @@ pub enum GameMasterCommandType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[br(import(_magic: &ServerZoneIpcType))]
|
#[br(import(magic: &ServerZoneIpcType))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ServerZoneIpcData {
|
pub enum ServerZoneIpcData {
|
||||||
/// Sent by the server as response to ZoneInitRequest.
|
/// Sent by the server as response to ZoneInitRequest.
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::InitResponse))]
|
||||||
InitResponse {
|
InitResponse {
|
||||||
unk1: u64,
|
unk1: u64,
|
||||||
character_id: u32,
|
character_id: u32,
|
||||||
unk2: u32,
|
unk2: u32,
|
||||||
},
|
},
|
||||||
/// Sent by the server that tells the client which zone to load
|
/// Sent by the server that tells the client which zone to load
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::InitZone))]
|
||||||
InitZone(InitZone),
|
InitZone(InitZone),
|
||||||
/// Sent by the server for... something
|
/// Sent by the server for... something
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::ActorControlSelf))]
|
||||||
ActorControlSelf(ActorControlSelf),
|
ActorControlSelf(ActorControlSelf),
|
||||||
/// Sent by the server containing character stats
|
/// Sent by the server containing character stats
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::PlayerStats))]
|
||||||
PlayerStats(PlayerStats),
|
PlayerStats(PlayerStats),
|
||||||
/// Sent by the server to setup the player on the client
|
/// Sent by the server to setup the player on the client
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::PlayerStatus))]
|
||||||
PlayerStatus(PlayerStatus),
|
PlayerStatus(PlayerStatus),
|
||||||
/// Sent by the server to setup class info
|
/// Sent by the server to setup class info
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::UpdateClassInfo))]
|
||||||
UpdateClassInfo(UpdateClassInfo),
|
UpdateClassInfo(UpdateClassInfo),
|
||||||
/// Sent by the server to spawn the player in
|
/// Sent by the server to spawn the player in
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::PlayerSpawn))]
|
||||||
PlayerSpawn(PlayerSpawn),
|
PlayerSpawn(PlayerSpawn),
|
||||||
/// Sent by the server to indicate the log out is complete
|
/// Sent by the server to indicate the log out is complete
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::LogOutComplete))]
|
||||||
LogOutComplete {
|
LogOutComplete {
|
||||||
// TODO: guessed
|
// TODO: guessed
|
||||||
unk: [u8; 8],
|
unk: [u8; 8],
|
||||||
},
|
},
|
||||||
/// Sent by the server to modify the client's position
|
/// Sent by the server to modify the client's position
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::Warp))]
|
||||||
Warp(Warp),
|
Warp(Warp),
|
||||||
/// Sent by the server when they send a chat message
|
/// Sent by the server when they send a chat message
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::ServerChatMessage))]
|
||||||
ServerChatMessage {
|
ServerChatMessage {
|
||||||
/*
|
/*
|
||||||
* bits (properties will apply when set, but a final base 10 value of zero defaults to chat log only):
|
* bits (properties will apply when set, but a final base 10 value of zero defaults to chat log only):
|
||||||
|
@ -210,46 +228,67 @@ pub enum ServerZoneIpcData {
|
||||||
message: String,
|
message: String,
|
||||||
},
|
},
|
||||||
/// Unknown, but seems to contain information on cross-world linkshells
|
/// Unknown, but seems to contain information on cross-world linkshells
|
||||||
LinkShellInformation { unk: [u8; 456] },
|
#[br(pre_assert(*magic == ServerZoneIpcType::LinkShellInformation))]
|
||||||
|
LinkShellInformation {
|
||||||
|
unk: [u8; 456],
|
||||||
|
},
|
||||||
/// Sent by the server when it wants the client to... prepare to zone?
|
/// Sent by the server when it wants the client to... prepare to zone?
|
||||||
PrepareZoning { unk: [u32; 4] },
|
#[br(pre_assert(*magic == ServerZoneIpcType::PrepareZoning))]
|
||||||
|
PrepareZoning {
|
||||||
|
unk: [u32; 4],
|
||||||
|
},
|
||||||
/// Sent by the server
|
/// Sent by the server
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::ActorControl))]
|
||||||
ActorControl(ActorControl),
|
ActorControl(ActorControl),
|
||||||
/// Sent by the server
|
/// Sent by the server
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::Move))]
|
||||||
Move(Move),
|
Move(Move),
|
||||||
/// Sent by the server in response to SocialListRequest
|
/// Sent by the server in response to SocialListRequest
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::SocialList))]
|
||||||
SocialList(SocialList),
|
SocialList(SocialList),
|
||||||
/// Sent by the server to spawn an NPC
|
/// Sent by the server to spawn an NPC
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::NpcSpawn))]
|
||||||
NpcSpawn(NpcSpawn),
|
NpcSpawn(NpcSpawn),
|
||||||
/// Sent by the server to update an actor's status effect list
|
/// Sent by the server to update an actor's status effect list
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::StatusEffectList))]
|
||||||
StatusEffectList(StatusEffectList),
|
StatusEffectList(StatusEffectList),
|
||||||
/// Sent by the server when it's time to change the weather
|
/// Sent by the server when it's time to change the weather
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::WeatherId))]
|
||||||
WeatherId(WeatherChange),
|
WeatherId(WeatherChange),
|
||||||
/// Sent to inform the client of an inventory item
|
/// Sent to inform the client of an inventory item
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::UpdateItem))]
|
||||||
UpdateItem(ItemInfo),
|
UpdateItem(ItemInfo),
|
||||||
/// Sent to inform the client of container status
|
/// Sent to inform the client of container status
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::ContainerInfo))]
|
||||||
ContainerInfo(ContainerInfo),
|
ContainerInfo(ContainerInfo),
|
||||||
/// Sent to tell the client to play a scene
|
/// Sent to tell the client to play a scene
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::EventScene))]
|
||||||
EventScene(EventScene),
|
EventScene(EventScene),
|
||||||
/// Sent to tell the client to load a scene, but not play it
|
/// Sent to tell the client to load a scene, but not play it
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::EventStart))]
|
||||||
EventStart(EventStart),
|
EventStart(EventStart),
|
||||||
/// Sent to update an actor's hp & mp values
|
/// Sent to update an actor's hp & mp values
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::UpdateHpMpTp))]
|
||||||
UpdateHpMpTp {
|
UpdateHpMpTp {
|
||||||
hp: u32,
|
hp: u32,
|
||||||
mp: u16,
|
mp: u16,
|
||||||
unk: u16, // it's filled with... something
|
unk: u16, // it's filled with... something
|
||||||
},
|
},
|
||||||
/// Sent to inform the client the consequences of their actions
|
/// Sent to inform the client the consequences of their actions
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::ActionResult))]
|
||||||
ActionResult(ActionResult),
|
ActionResult(ActionResult),
|
||||||
/// Sent to to the client to update their appearance
|
/// Sent to to the client to update their appearance
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::Equip))]
|
||||||
Equip(Equip),
|
Equip(Equip),
|
||||||
/// Sent to the client to free up a spawn index
|
/// Sent to the client to free up a spawn index
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::Delete))]
|
||||||
Delete {
|
Delete {
|
||||||
spawn_index: u8,
|
spawn_index: u8,
|
||||||
#[brw(pad_before = 3)] // padding
|
#[brw(pad_before = 3)] // padding
|
||||||
actor_id: u32,
|
actor_id: u32,
|
||||||
},
|
},
|
||||||
/// Sent to the client to stop their currently playing event.
|
/// Sent to the client to stop their currently playing event.
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::EventFinish))]
|
||||||
EventFinish {
|
EventFinish {
|
||||||
handler_id: u32,
|
handler_id: u32,
|
||||||
event: u8,
|
event: u8,
|
||||||
|
@ -259,16 +298,21 @@ pub enum ServerZoneIpcData {
|
||||||
arg: u32,
|
arg: u32,
|
||||||
},
|
},
|
||||||
/// Sent after EventFinish? it un-occupies the character lol
|
/// Sent after EventFinish? it un-occupies the character lol
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::Unk18))]
|
||||||
Unk18 {
|
Unk18 {
|
||||||
unk: [u8; 16], // all zero...
|
unk: [u8; 16], // all zero...
|
||||||
},
|
},
|
||||||
/// Used to control target information
|
/// Used to control target information
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::ActorControlTarget))]
|
||||||
ActorControlTarget(ActorControlTarget),
|
ActorControlTarget(ActorControlTarget),
|
||||||
/// Used to update the player's currencies
|
/// Used to update the player's currencies
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::CurrencyCrystalInfo))]
|
||||||
CurrencyCrystalInfo(CurrencyInfo),
|
CurrencyCrystalInfo(CurrencyInfo),
|
||||||
/// Used to update an actor's equip display flags
|
/// Used to update an actor's equip display flags
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::Config))]
|
||||||
Config(Config),
|
Config(Config),
|
||||||
/// Unknown, seen in haircut event
|
/// Unknown, seen in haircut event
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::EventUnkReply))]
|
||||||
EventUnkReply {
|
EventUnkReply {
|
||||||
event_id: u32,
|
event_id: u32,
|
||||||
unk1: u16,
|
unk1: u16,
|
||||||
|
@ -276,11 +320,13 @@ pub enum ServerZoneIpcData {
|
||||||
#[brw(pad_after = 8)]
|
#[brw(pad_after = 8)]
|
||||||
unk3: u8,
|
unk3: u8,
|
||||||
},
|
},
|
||||||
|
#[br(pre_assert(*magic == ServerZoneIpcType::UnkCall))]
|
||||||
UnkCall {
|
UnkCall {
|
||||||
unk1: u32,
|
unk1: u32,
|
||||||
#[brw(pad_after = 26)]
|
#[brw(pad_after = 26)]
|
||||||
unk2: u16,
|
unk2: u16,
|
||||||
},
|
},
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -430,6 +476,7 @@ pub enum ClientZoneIpcData {
|
||||||
#[brw(pad_after = 8)]
|
#[brw(pad_after = 8)]
|
||||||
unk3: u8,
|
unk3: u8,
|
||||||
},
|
},
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -7,6 +7,9 @@ pub trait ReadWriteIpcSegment:
|
||||||
/// Calculate the size of this Ipc segment *including* the 16 byte header.
|
/// Calculate the size of this Ipc segment *including* the 16 byte header.
|
||||||
/// When implementing this, please use the size seen in retail instead of guessing.
|
/// When implementing this, please use the size seen in retail instead of guessing.
|
||||||
fn calc_size(&self) -> u32;
|
fn calc_size(&self) -> u32;
|
||||||
|
|
||||||
|
/// Returns a human-readable name of the opcode.
|
||||||
|
fn get_name(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An IPC packet segment.
|
/// An IPC packet segment.
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
mod parsing;
|
mod parsing;
|
||||||
use parsing::PacketHeader;
|
|
||||||
pub use parsing::{
|
pub use parsing::{
|
||||||
ConnectionType, PacketSegment, PacketState, SegmentData, SegmentType, parse_packet,
|
ConnectionType, PacketHeader, PacketSegment, PacketState, SegmentData, SegmentType,
|
||||||
send_keep_alive, send_packet,
|
parse_packet, send_keep_alive, send_packet,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod compression;
|
mod compression;
|
||||||
|
|
Loading…
Add table
Reference in a new issue