1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-25 16:27:46 +00:00

Send ActorControl and StatusEffectList packets in vain

This was hopefully supposed to show the player spawned by !spawnactor, but it
still doesn't work...
This commit is contained in:
Joshua Goins 2025-03-18 23:30:59 -04:00
parent da0860cdd7
commit 4927fa9119
8 changed files with 126 additions and 27 deletions

View file

@ -7,7 +7,7 @@ use kawari::world::ipc::{
use kawari::world::{ use kawari::world::{
ChatHandler, Zone, ZoneConnection, ChatHandler, Zone, ZoneConnection,
ipc::{ ipc::{
ActorControlSelf, ActorControlType, PlayerEntry, PlayerSetup, PlayerSpawn, PlayerStats, ActorControlCategory, ActorControlSelf, PlayerEntry, PlayerSetup, PlayerSpawn, PlayerStats,
Position, SocialList, Position, SocialList,
}, },
}; };
@ -171,7 +171,7 @@ async fn main() {
data: ServerZoneIpcData::ActorControlSelf( data: ServerZoneIpcData::ActorControlSelf(
ActorControlSelf { ActorControlSelf {
category: category:
ActorControlType::SetCharaGearParamUI, ActorControlCategory::SetCharaGearParamUI,
param1: 1, param1: 1,
param2: 1, param2: 1,
param3: 0, param3: 0,

View file

@ -1,10 +1,11 @@
use crate::{ use crate::{
CHAR_NAME, CUSTOMIZE_DATA, INVALID_OBJECT_ID, WORLD_ID, CHAR_NAME, CUSTOMIZE_DATA, INVALID_OBJECT_ID, WORLD_ID,
common::timestamp_secs, common::timestamp_secs,
packet::{PacketSegment, SegmentType}, packet::{PacketSegment, SegmentType},
world::ipc::{ world::ipc::{
CommonSpawn, NpcSpawn, ObjectKind, PlayerSpawn, ServerZoneIpcData, ServerZoneIpcSegment, ActorControl, ActorControlCategory, CommonSpawn, NpcSpawn, ObjectKind, PlayerSpawn,
ServerZoneIpcType, ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType, StatusEffectList,
}, },
}; };
@ -37,6 +38,35 @@ impl ChatHandler {
"!spawnactor" => { "!spawnactor" => {
tracing::info!("Spawning actor..."); tracing::info!("Spawning actor...");
// status effect
{
let ipc = ServerZoneIpcSegment {
unk1: 20,
unk2: 0,
op_code: ServerZoneIpcType::StatusEffectList,
server_id: 0,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::StatusEffectList(StatusEffectList {
classjob_id: 3,
level: 10,
unk1: 10,
curr_hp: 241,
max_hp: 241,
curr_mp: 10000,
max_mp: 10000,
..Default::default()
}),
};
connection
.send_segment(PacketSegment {
source_actor: 0x106ad804,
target_actor: connection.player_id,
segment_type: SegmentType::Ipc { data: ipc },
})
.await;
}
// send player spawn // send player spawn
{ {
let ipc = ServerZoneIpcSegment { let ipc = ServerZoneIpcSegment {
@ -63,6 +93,7 @@ impl ChatHandler {
spawn_index: connection.get_free_spawn_index(), spawn_index: connection.get_free_spawn_index(),
look: CUSTOMIZE_DATA, look: CUSTOMIZE_DATA,
fc_tag: "LOCAL".to_string(), fc_tag: "LOCAL".to_string(),
subtype: 4,
models: [ models: [
0, // head 0, // head
89, // body 89, // body
@ -90,6 +121,29 @@ impl ChatHandler {
}) })
.await; .await;
} }
// zone in
{
let ipc = ServerZoneIpcSegment {
unk1: 20,
unk2: 0,
op_code: ServerZoneIpcType::ActorControl,
server_id: 0,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::ActorControl(ActorControl {
category: ActorControlCategory::ZoneIn,
..Default::default()
}),
};
connection
.send_segment(PacketSegment {
source_actor: 0x106ad804,
target_actor: connection.player_id,
segment_type: SegmentType::Ipc { data: ipc },
})
.await;
}
} }
"!spawnnpc" => { "!spawnnpc" => {
// spawn another one of us // spawn another one of us

View file

@ -0,0 +1,23 @@
use binrw::binrw;
// See https://github.com/awgil/ffxiv_reverse/blob/f35b6226c1478234ca2b7149f82d251cffca2f56/vnetlog/vnetlog/ServerIPC.cs#L266 for a REALLY useful list of known values
#[binrw]
#[derive(Debug, Eq, PartialEq, Clone, Default)]
#[brw(repr = u16)]
pub enum ActorControlCategory {
#[default]
ZoneIn = 0xC8,
SetCharaGearParamUI = 0x260,
}
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct ActorControl {
#[brw(pad_after = 2)]
pub category: ActorControlCategory,
pub param1: u32,
pub param2: u32,
pub param3: u32,
#[brw(pad_after = 4)] // maybe not always empty?
pub param4: u32,
}

View file

@ -1,18 +1,12 @@
use binrw::binrw; use binrw::binrw;
#[binrw] use super::ActorControlCategory;
#[derive(Debug, Eq, PartialEq, Clone, Default)]
#[brw(repr = u16)]
pub enum ActorControlType {
#[default]
SetCharaGearParamUI = 0x260,
}
#[binrw] #[binrw]
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ActorControlSelf { pub struct ActorControlSelf {
#[brw(pad_after = 2)] #[brw(pad_after = 2)]
pub category: ActorControlType, pub category: ActorControlCategory,
pub param1: u32, pub param1: u32,
pub param2: u32, pub param2: u32,
pub param3: u32, pub param3: u32,

View file

@ -28,7 +28,9 @@ pub use player_stats::PlayerStats;
mod actor_control_self; mod actor_control_self;
pub use actor_control_self::ActorControlSelf; pub use actor_control_self::ActorControlSelf;
pub use actor_control_self::ActorControlType;
mod actor_control;
pub use actor_control::{ActorControl, ActorControlCategory};
mod init_zone; mod init_zone;
pub use init_zone::InitZone; pub use init_zone::InitZone;
@ -39,6 +41,9 @@ pub use npc_spawn::NpcSpawn;
mod common_spawn; mod common_spawn;
pub use common_spawn::{CharacterMode, CommonSpawn, ObjectKind}; pub use common_spawn::{CharacterMode, CommonSpawn, ObjectKind};
mod status_effect_list;
pub use status_effect_list::StatusEffectList;
use crate::common::read_string; use crate::common::read_string;
use crate::common::write_string; use crate::common::write_string;
use crate::packet::IpcSegment; use crate::packet::IpcSegment;
@ -92,6 +97,7 @@ impl ReadWriteIpcSegment for ServerZoneIpcSegment {
ServerZoneIpcType::SocialList => 1136, ServerZoneIpcType::SocialList => 1136,
ServerZoneIpcType::PrepareZoning => 16, ServerZoneIpcType::PrepareZoning => 16,
ServerZoneIpcType::NpcSpawn => 648, ServerZoneIpcType::NpcSpawn => 648,
ServerZoneIpcType::StatusEffectList => 384,
} }
} }
} }
@ -171,7 +177,7 @@ pub enum ServerZoneIpcType {
// Sent by the server before init zone??? // Sent by the server before init zone???
Unk16 = 0x3AB, Unk16 = 0x3AB,
// Sent by the server // Sent by the server
ActorControl = 0x1B9, ActorControl = 0x38E,
// Sent by the server // Sent by the server
ActorMove = 0x3D8, ActorMove = 0x3D8,
// Sent by the server // Sent by the server
@ -180,6 +186,8 @@ pub enum ServerZoneIpcType {
SocialList = 0x36C, SocialList = 0x36C,
// Sent by the server to spawn an NPC // Sent by the server to spawn an NPC
NpcSpawn = 0x100, NpcSpawn = 0x100,
// Sent by the server to update an actor's status effect list
StatusEffectList = 0xBB,
} }
#[binrw] #[binrw]
@ -283,10 +291,7 @@ pub enum ServerZoneIpcData {
Unk16 { Unk16 {
unk: [u8; 136], unk: [u8; 136],
}, },
ActorControl { ActorControl(ActorControl),
#[brw(pad_after = 20)] // empty
unk: u32,
},
ActorMove { ActorMove {
#[brw(pad_after = 4)] // empty #[brw(pad_after = 4)] // empty
pos: Position, pos: Position,
@ -296,6 +301,7 @@ pub enum ServerZoneIpcData {
}, },
SocialList(SocialList), SocialList(SocialList),
NpcSpawn(NpcSpawn), NpcSpawn(NpcSpawn),
StatusEffectList(StatusEffectList),
} }
#[binrw] #[binrw]
@ -442,6 +448,14 @@ mod tests {
ServerZoneIpcType::NpcSpawn, ServerZoneIpcType::NpcSpawn,
ServerZoneIpcData::NpcSpawn(NpcSpawn::default()), ServerZoneIpcData::NpcSpawn(NpcSpawn::default()),
), ),
(
ServerZoneIpcType::ActorControl,
ServerZoneIpcData::ActorControl(ActorControl::default()),
),
(
ServerZoneIpcType::StatusEffectList,
ServerZoneIpcData::StatusEffectList(StatusEffectList::default()),
),
]; ];
for (opcode, data) in &ipc_types { for (opcode, data) in &ipc_types {

View file

@ -1,10 +1,7 @@
use binrw::binrw; use binrw::binrw;
use crate::CHAR_NAME_MAX_LENGTH;
use crate::common::{CustomizeData, read_string, write_string};
use super::position::Position; use super::CommonSpawn;
use super::{CharacterMode, CommonSpawn, ObjectKind, StatusEffect};
#[binrw] #[binrw]
#[brw(little)] #[brw(little)]

View file

@ -1,11 +1,7 @@
use binrw::binrw; use binrw::binrw;
use crate::CHAR_NAME_MAX_LENGTH;
use crate::common::{CustomizeData, read_string, write_string};
use super::position::Position; use super::CommonSpawn;
use super::status_effect::StatusEffect;
use super::{CommonSpawn, ObjectKind};
#[binrw] #[binrw]
#[brw(little)] #[brw(little)]
@ -63,5 +59,6 @@ mod tests {
assert_eq!(player_spawn.common.subtype, 4); assert_eq!(player_spawn.common.subtype, 4);
assert_eq!(player_spawn.common.model_chara, 0); assert_eq!(player_spawn.common.model_chara, 0);
assert_eq!(player_spawn.common.object_kind, ObjectKind::Player); assert_eq!(player_spawn.common.object_kind, ObjectKind::Player);
assert_eq!(player_spawn.common.display_flags, 262144);
} }
} }

View file

@ -0,0 +1,20 @@
use binrw::binrw;
use super::StatusEffect;
#[binrw]
#[derive(Debug, Clone, Copy, Default)]
pub struct StatusEffectList {
pub classjob_id: u8,
pub level: u8,
pub unk1: u8,
pub unk2: u8,
pub curr_hp: u32,
pub max_hp: u32,
pub curr_mp: u16,
pub max_mp: u16,
pub shield: u16,
pub unk3: u16,
pub statues: [StatusEffect; 30],
pub unk4: u32,
}