1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-22 07:27:44 +00:00

Add !setpos debug command to forcefully move the player

This is useful in some territories where you might not spawn in the correct
position. It currently locks up movement afterwards, but it's still useful.
This commit is contained in:
Joshua Goins 2025-03-13 19:52:01 -04:00
parent 74f7554aa8
commit 1141f6fb35
2 changed files with 64 additions and 2 deletions

View file

@ -1,14 +1,14 @@
use std::time::{SystemTime, UNIX_EPOCH};
use kawari::client_select_data::ClientCustomizeData;
use kawari::ipc::{GameMasterCommandType, IPCOpCode, IPCSegment, IPCStructData};
use kawari::ipc::{ActorSetPos, GameMasterCommandType, IPCOpCode, IPCSegment, IPCStructData};
use kawari::oodle::FFXIVOodle;
use kawari::packet::{
CompressionType, PacketSegment, SegmentType, State, parse_packet, send_keep_alive, send_packet,
};
use kawari::world::{
ActorControlSelf, ActorControlType, CustomizeData, InitZone, PlayerSetup, PlayerSpawn,
PlayerStats, UpdateClassInfo,
PlayerStats, Position, UpdateClassInfo,
};
use kawari::{CONTENT_ID, WORLD_ID, ZONE_ID};
use tokio::io::AsyncReadExt;
@ -483,6 +483,53 @@ async fn main() {
}
IPCStructData::ChatMessage { message, .. } => {
tracing::info!("Client sent chat message: {message}!");
let parts: Vec<&str> = message.split(' ').collect();
match parts[0] {
"!setpos" => {
let pos_x = parts[1].parse::<f32>().unwrap();
let pos_y = parts[2].parse::<f32>().unwrap();
let pos_z = parts[3].parse::<f32>().unwrap();
// set pos
{
let ipc = IPCSegment {
unk1: 14,
unk2: 0,
op_code: IPCOpCode::ActorSetPos,
server_id: WORLD_ID,
timestamp: timestamp_secs(),
data: IPCStructData::ActorSetPos(
ActorSetPos {
unk: 0x020fa3b8,
position: Position {
x: pos_x,
y: pos_y,
z: pos_z,
},
..Default::default()
},
),
};
let response_packet = PacketSegment {
source_actor: state.player_id.unwrap(),
target_actor: state.player_id.unwrap(),
segment_type: SegmentType::Ipc {
data: ipc,
},
};
send_packet(
&mut write,
&[response_packet],
&mut state,
CompressionType::Oodle,
)
.await;
}
}
_ => tracing::info!("Unrecognized debug command!"),
}
}
IPCStructData::GameMasterCommand { command, arg, .. } => {
tracing::info!("Got a game master command!");

View file

@ -81,6 +81,8 @@ pub enum IPCOpCode {
ChatMessage = 0xCA,
// 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.
GameMasterCommand = 0x3B3,
// Sent by the server to modify the client's position
ActorSetPos = 0x223,
}
#[binrw]
@ -112,6 +114,15 @@ pub struct Server {
pub name: String,
}
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct ActorSetPos {
pub unk: u32,
pub layer_id: u32,
pub position: Position,
pub unk3: u32,
}
#[binrw]
#[derive(Debug, Clone, Default)]
pub struct CharacterDetails {
@ -417,6 +428,8 @@ pub enum IPCStructData {
// TODO: guessed
unk: [u8; 8],
},
#[br(pre_assert(false))]
ActorSetPos(ActorSetPos),
}
#[binrw]
@ -474,6 +487,7 @@ impl IPCSegment {
IPCStructData::Disconnected { .. } => todo!(),
IPCStructData::ChatMessage { .. } => 1056,
IPCStructData::GameMasterCommand { .. } => todo!(),
IPCStructData::ActorSetPos { .. } => 24,
}
}
}
@ -523,6 +537,7 @@ mod tests {
IPCStructData::PlayerSetup(PlayerSetup::default()),
IPCStructData::UpdateClassInfo(UpdateClassInfo::default()),
IPCStructData::PlayerSpawn(PlayerSpawn::default()),
IPCStructData::ActorSetPos(ActorSetPos::default()),
];
for ipc in &ipc_types {