1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-24 08:07:45 +00:00

ActorControl and ActorMove packet fixes, add support for //gm invis

I don't think this is the right kind of invisibility, but it's more of a test of
ActorControlSelf really. I have also been trying (and failing) to make the
client show another player.
This commit is contained in:
Joshua Goins 2025-03-23 15:28:53 -04:00
parent d47779c8d6
commit 90a78fcaa8
6 changed files with 133 additions and 79 deletions

View file

@ -11,13 +11,15 @@ use kawari::packet::{
send_packet, send_packet,
}; };
use kawari::world::ipc::{ use kawari::world::ipc::{
ClientZoneIpcData, CommonSpawn, DisplayFlag, GameMasterCommandType, GameMasterRank, ObjectKind, OnlineStatus, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType, SocialListRequestType, StatusEffect ClientZoneIpcData, CommonSpawn, DisplayFlag, GameMasterCommandType, GameMasterRank, ObjectKind,
OnlineStatus, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType,
SocialListRequestType, StatusEffect,
}; };
use kawari::world::{ use kawari::world::{
ChatHandler, Zone, ZoneConnection, ChatHandler, Zone, ZoneConnection,
ipc::{ ipc::{
ActorControlCategory, ActorControlSelf, PlayerEntry, PlayerSetup, PlayerSpawn, PlayerStats, ActorControl, ActorControlCategory, ActorControlSelf, PlayerEntry, PlayerSetup,
SocialList, PlayerSpawn, PlayerStats, SocialList,
}, },
}; };
use kawari::world::{PlayerData, WorldDatabase}; use kawari::world::{PlayerData, WorldDatabase};
@ -194,13 +196,10 @@ async fn main() {
data: ServerZoneIpcData::ActorControlSelf( data: ServerZoneIpcData::ActorControlSelf(
ActorControlSelf { ActorControlSelf {
category: category:
ActorControlCategory::SetCharaGearParamUI, ActorControlCategory::SetCharaGearParamUI {
param1: 1, unk1: 1,
param2: 1, unk2: 1,
param3: 0, }
param4: 0,
param5: 0,
param6: 0,
}, },
), ),
..Default::default() ..Default::default()
@ -571,6 +570,38 @@ async fn main() {
GameMasterCommandType::ChangeTerritory => { GameMasterCommandType::ChangeTerritory => {
connection.change_zone(*arg as u16).await connection.change_zone(*arg as u16).await
} }
GameMasterCommandType::ToggleInvisibility => {
// Control Data
{
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::ActorControlSelf,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::ActorControlSelf(
ActorControlSelf {
category:
ActorControlCategory::ToggleInvisibility {
invisible: 1
},
},
),
..Default::default()
};
connection
.send_segment(PacketSegment {
source_actor: connection
.player_data
.actor_id,
target_actor: connection
.player_data
.actor_id,
segment_type: SegmentType::Ipc {
data: ipc,
},
})
.await;
}
}
} }
} }
ClientZoneIpcData::Unk12 { .. } => { ClientZoneIpcData::Unk12 { .. } => {

View file

@ -3,9 +3,9 @@ use crate::{
config::get_config, config::get_config,
packet::{PacketSegment, SegmentType}, packet::{PacketSegment, SegmentType},
world::ipc::{ world::ipc::{
ActorControl, ActorControlCategory, BattleNpcSubKind, CommonSpawn, NpcSpawn, ObjectKind, ActorControl, ActorControlCategory, BattleNpcSubKind, CommonSpawn, DisplayFlag, NpcSpawn,
PlayerSpawn, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, ServerZoneIpcType, ObjectKind, PlayerSpawn, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment,
StatusEffectList, ServerZoneIpcType, StatusEffectList,
}, },
}; };
@ -64,35 +64,6 @@ 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_data.actor_id,
segment_type: SegmentType::Ipc { data: ipc },
})
.await;
}
let config = get_config(); let config = get_config();
// send player spawn // send player spawn
@ -104,8 +75,8 @@ impl ChatHandler {
server_id: 0, server_id: 0,
timestamp: timestamp_secs(), timestamp: timestamp_secs(),
data: ServerZoneIpcData::PlayerSpawn(PlayerSpawn { data: ServerZoneIpcData::PlayerSpawn(PlayerSpawn {
account_id: 1, account_id: 1000000,
content_id: 1, content_id: 1000000,
current_world_id: config.world.world_id, current_world_id: config.world.world_id,
home_world_id: config.world.world_id, home_world_id: config.world.world_id,
common: CommonSpawn { common: CommonSpawn {
@ -120,6 +91,9 @@ 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(),
display_flags: DisplayFlag::INVISIBLE
| DisplayFlag::HIDE_HEAD
| DisplayFlag::UNK,
models: [ models: [
0, // head 0, // head
89, // body 89, // body
@ -157,7 +131,10 @@ impl ChatHandler {
server_id: 0, server_id: 0,
timestamp: timestamp_secs(), timestamp: timestamp_secs(),
data: ServerZoneIpcData::ActorControl(ActorControl { data: ServerZoneIpcData::ActorControl(ActorControl {
category: ActorControlCategory::ZoneIn, category: ActorControlCategory::ZoneIn {
warp_finish_anim: 0x0,
raise_anim: 0x0,
},
..Default::default() ..Default::default()
}), }),
}; };
@ -170,6 +147,32 @@ impl ChatHandler {
}) })
.await; .await;
} }
// move
{
let ipc = ServerZoneIpcSegment {
unk1: 20,
unk2: 0,
op_code: ServerZoneIpcType::ActorMove,
server_id: 0,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::ActorMove {
pos: Position {
x: 1.0,
y: 0.0,
z: 1.0,
},
},
};
connection
.send_segment(PacketSegment {
source_actor: 0x106ad804,
target_actor: connection.player_data.actor_id,
segment_type: SegmentType::Ipc { data: ipc },
})
.await;
}
} }
"!spawnnpc" => { "!spawnnpc" => {
// spawn another one of us // spawn another one of us

View file

@ -2,22 +2,56 @@ 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 // See https://github.com/awgil/ffxiv_reverse/blob/f35b6226c1478234ca2b7149f82d251cffca2f56/vnetlog/vnetlog/ServerIPC.cs#L266 for a REALLY useful list of known values
#[binrw] #[binrw]
#[derive(Debug, Eq, PartialEq, Clone, Default)] #[derive(Debug, Eq, PartialEq, Clone)]
#[brw(repr = u16)]
pub enum ActorControlCategory { pub enum ActorControlCategory {
#[default] #[brw(magic = 0x26u16)]
ZoneIn = 0xC8, ToggleInvisibility {
SetCharaGearParamUI = 0x260, #[brw(pad_before = 2)]
invisible: u32, // FIXME: change to bool
},
#[brw(magic = 0xC8u16)]
ZoneIn {
#[brw(pad_before = 2)]
warp_finish_anim: u32,
raise_anim: u32,
},
#[brw(magic = 0x260u16)]
SetCharaGearParamUI {
#[brw(pad_before = 2)]
unk1: u32,
unk2: u32,
},
} }
#[binrw] #[binrw]
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone)]
pub struct ActorControl { pub struct ActorControl {
#[brw(pad_after = 2)] #[brw(pad_after = 4)]
#[brw(pad_size_to = 20)] // take into account categories without params
pub category: ActorControlCategory, pub category: ActorControlCategory,
pub param1: u32, }
pub param2: u32,
pub param3: u32, impl Default for ActorControl {
#[brw(pad_after = 4)] // maybe not always empty? fn default() -> Self {
pub param4: u32, Self {
category: ActorControlCategory::ToggleInvisibility { invisible: 1 },
}
}
}
// Has more padding than ActorControl?
#[binrw]
#[derive(Debug, Clone)]
pub struct ActorControlSelf {
#[brw(pad_after = 12)]
#[brw(pad_size_to = 20)] // take into account categories without params
pub category: ActorControlCategory,
}
impl Default for ActorControlSelf {
fn default() -> Self {
Self {
category: ActorControlCategory::ToggleInvisibility { invisible: 1 },
}
}
} }

View file

@ -1,17 +0,0 @@
use binrw::binrw;
use super::ActorControlCategory;
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct ActorControlSelf {
#[brw(pad_after = 2)]
pub category: ActorControlCategory,
pub param1: u32,
pub param2: u32,
pub param3: u32,
pub param4: u32,
pub param5: u32,
#[brw(pad_after = 4)]
pub param6: u32,
}

View file

@ -93,6 +93,7 @@ pub enum OnlineStatus {
GameMaster = 2, GameMaster = 2,
GameMasterBlue = 3, GameMasterBlue = 3,
EventParticipant = 4, EventParticipant = 4,
NewAdventurer = 32, // TODO: This is actually a flag!
#[default] #[default]
Online = 47, Online = 47,
} }

View file

@ -23,11 +23,8 @@ pub use player_setup::PlayerSetup;
mod player_stats; mod player_stats;
pub use player_stats::PlayerStats; pub use player_stats::PlayerStats;
mod actor_control_self;
pub use actor_control_self::ActorControlSelf;
mod actor_control; mod actor_control;
pub use actor_control::{ActorControl, ActorControlCategory}; pub use actor_control::{ActorControl, ActorControlCategory, ActorControlSelf};
mod init_zone; mod init_zone;
pub use init_zone::InitZone; pub use init_zone::InitZone;
@ -138,6 +135,7 @@ pub struct ActorSetPos {
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum GameMasterCommandType { pub enum GameMasterCommandType {
ChangeWeather = 0x6, ChangeWeather = 0x6,
ToggleInvisibility = 0xD,
ChangeTerritory = 0x58, ChangeTerritory = 0x58,
} }
@ -184,7 +182,7 @@ pub enum ServerZoneIpcType {
// Sent by the server // Sent by the server
ActorControl = 0x38E, ActorControl = 0x38E,
// Sent by the server // Sent by the server
ActorMove = 0x3D8, ActorMove = 0x31C,
// Sent by the server // Sent by the server
Unk17 = 0x2A1, Unk17 = 0x2A1,
// Sent by the server in response to SocialListRequest // Sent by the server in response to SocialListRequest
@ -475,6 +473,10 @@ mod tests {
ServerZoneIpcType::WeatherChange, ServerZoneIpcType::WeatherChange,
ServerZoneIpcData::WeatherChange(WeatherChange::default()), ServerZoneIpcData::WeatherChange(WeatherChange::default()),
), ),
(
ServerZoneIpcType::ActorControl,
ServerZoneIpcData::ActorControl(ActorControl::default()),
),
]; ];
for (opcode, data) in &ipc_types { for (opcode, data) in &ipc_types {