diff --git a/USAGE.md b/USAGE.md index ad180d4..02992eb 100644 --- a/USAGE.md +++ b/USAGE.md @@ -102,3 +102,4 @@ These GM commands are implemented in the FFXIV protocol, but only some of them a * `//gm weather `: Changes the weather * `//gm wireframe`: Toggle wireframe rendering for the environment * `//gm item `: Gives yourself an item. This can only place a single item in the first page of your inventory currently. +* `//gm lv `: Sets your current level diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 98c85a4..e5e16e8 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -588,6 +588,10 @@ async fn client_loop( tracing::info!("Got a game master command!"); match &command { + GameMasterCommandType::SetLevel => { + connection.player_data.level = *arg as u8; + connection.update_class_info().await; + } GameMasterCommandType::ChangeWeather => { connection.change_weather(*arg as u16).await } diff --git a/src/world/connection.rs b/src/world/connection.rs index d8e95ad..1b8c818 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -329,6 +329,28 @@ impl ZoneConnection { .await; } + pub async fn update_class_info(&mut self) { + let ipc = ServerZoneIpcSegment { + op_code: ServerZoneIpcType::UpdateClassInfo, + timestamp: timestamp_secs(), + data: ServerZoneIpcData::UpdateClassInfo(UpdateClassInfo { + class_id: self.player_data.classjob_id as u16, + unknown: 1, + synced_level: self.player_data.level as u16, + class_level: self.player_data.level as u16, + ..Default::default() + }), + ..Default::default() + }; + + self.send_segment(PacketSegment { + source_actor: self.player_data.actor_id, + target_actor: self.player_data.actor_id, + segment_type: SegmentType::Ipc { data: ipc }, + }) + .await; + } + pub async fn change_zone(&mut self, new_zone_id: u16) { { let mut game_data = self.gamedata.lock().unwrap(); @@ -337,27 +359,7 @@ impl ZoneConnection { self.player_data.zone_id = new_zone_id; // Player Class Info - { - let ipc = ServerZoneIpcSegment { - op_code: ServerZoneIpcType::UpdateClassInfo, - timestamp: timestamp_secs(), - data: ServerZoneIpcData::UpdateClassInfo(UpdateClassInfo { - class_id: self.player_data.classjob_id as u16, - unknown: 1, - synced_level: self.player_data.level as u16, - class_level: self.player_data.level as u16, - ..Default::default() - }), - ..Default::default() - }; - - self.send_segment(PacketSegment { - source_actor: self.player_data.actor_id, - target_actor: self.player_data.actor_id, - segment_type: SegmentType::Ipc { data: ipc }, - }) - .await; - } + self.update_class_info().await; // link shell information /*{ diff --git a/src/world/ipc/mod.rs b/src/world/ipc/mod.rs index 74575b9..538f34a 100644 --- a/src/world/ipc/mod.rs +++ b/src/world/ipc/mod.rs @@ -132,6 +132,7 @@ impl Default for ServerZoneIpcSegment { #[brw(repr = u8)] #[derive(Clone, PartialEq, Debug)] pub enum GameMasterCommandType { + SetLevel = 0x1, ChangeWeather = 0x6, ToggleInvisibility = 0xD, ToggleWireframe = 0x26,