1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-25 08:27:44 +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::{
ChatHandler, Zone, ZoneConnection,
ipc::{
ActorControlSelf, ActorControlType, PlayerEntry, PlayerSetup, PlayerSpawn, PlayerStats,
ActorControlCategory, ActorControlSelf, PlayerEntry, PlayerSetup, PlayerSpawn, PlayerStats,
Position, SocialList,
},
};
@ -171,7 +171,7 @@ async fn main() {
data: ServerZoneIpcData::ActorControlSelf(
ActorControlSelf {
category:
ActorControlType::SetCharaGearParamUI,
ActorControlCategory::SetCharaGearParamUI,
param1: 1,
param2: 1,
param3: 0,

View file

@ -1,10 +1,11 @@
use crate::{
CHAR_NAME, CUSTOMIZE_DATA, INVALID_OBJECT_ID, WORLD_ID,
common::timestamp_secs,
packet::{PacketSegment, SegmentType},
world::ipc::{
CommonSpawn, NpcSpawn, ObjectKind, PlayerSpawn, ServerZoneIpcData, ServerZoneIpcSegment,
ServerZoneIpcType,
ActorControl, ActorControlCategory, CommonSpawn, NpcSpawn, ObjectKind, PlayerSpawn,
ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType, StatusEffectList,
},
};
@ -37,6 +38,35 @@ impl ChatHandler {
"!spawnactor" => {
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
{
let ipc = ServerZoneIpcSegment {
@ -63,6 +93,7 @@ impl ChatHandler {
spawn_index: connection.get_free_spawn_index(),
look: CUSTOMIZE_DATA,
fc_tag: "LOCAL".to_string(),
subtype: 4,
models: [
0, // head
89, // body
@ -90,6 +121,29 @@ impl ChatHandler {
})
.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" => {
// 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;
#[binrw]
#[derive(Debug, Eq, PartialEq, Clone, Default)]
#[brw(repr = u16)]
pub enum ActorControlType {
#[default]
SetCharaGearParamUI = 0x260,
}
use super::ActorControlCategory;
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct ActorControlSelf {
#[brw(pad_after = 2)]
pub category: ActorControlType,
pub category: ActorControlCategory,
pub param1: u32,
pub param2: u32,
pub param3: u32,

View file

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

View file

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

View file

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