diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index ef04d39..0d29a23 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -3,8 +3,8 @@ use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; use kawari::RECEIVE_BUFFER_SIZE; -use kawari::common::Position; use kawari::common::{GameData, TerritoryNameKind, timestamp_secs}; +use kawari::common::{Position, value_to_flag_byte_index_value}; use kawari::config::get_config; use kawari::inventory::{Item, Storage}; use kawari::ipc::chat::{ServerChatIpcData, ServerChatIpcSegment}; @@ -294,6 +294,7 @@ async fn client_loop( current_job: connection.player_data.classjob_id, levels: connection.player_data.classjob_levels.map(|x| x as u16), unlocks: connection.player_data.unlocks.clone(), + aetherytes: connection.player_data.aetherytes.clone(), ..Default::default() }), ..Default::default() @@ -689,10 +690,16 @@ async fn client_loop( // id == 0 means "all" if id == 0 { for i in 1..239 { + let (value, index) = value_to_flag_byte_index_value(id); + connection.player_data.aetherytes[index as usize] |= value; + connection.actor_control_self(ActorControlSelf { category: ActorControlCategory::LearnTeleport { id: i, unlocked: on } }).await; } } else { + let (value, index) = value_to_flag_byte_index_value(id); + connection.player_data.aetherytes[index as usize] |= value; + connection.actor_control_self(ActorControlSelf { category: ActorControlCategory::LearnTeleport { id, unlocked: on } }).await; } diff --git a/src/ipc/zone/player_setup.rs b/src/ipc/zone/player_setup.rs index 5b2b324..528d6a5 100644 --- a/src/ipc/zone/player_setup.rs +++ b/src/ipc/zone/player_setup.rs @@ -91,7 +91,10 @@ pub struct PlayerStatus { #[br(count = 64)] #[bw(pad_size_to = 64)] pub unlocks: Vec, - pub aetheryte: [u8; 26], + pub unknown10e: [u8; 28], + #[br(count = 29)] + #[bw(pad_size_to = 29)] + pub aetherytes: Vec, pub favorite_aetheryte_ids: [u16; 4], pub free_aetheryte_id: u16, pub ps_plus_free_aetheryte_id: u16, diff --git a/src/world/connection.rs b/src/world/connection.rs index 32cc870..ec79bb1 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -80,6 +80,7 @@ pub struct PlayerData { pub gm_invisible: bool, pub unlocks: Vec, + pub aetherytes: Vec, } impl PlayerData { @@ -690,6 +691,36 @@ impl ZoneConnection { }) .await; } + Task::UnlockAetheryte { id, on } => { + let unlock_all = *id == 0; + if unlock_all { + for i in 1..239 { + let (value, index) = value_to_flag_byte_index_value(*id); + self.player_data.aetherytes[index as usize] |= value; + + /* Unknown if this will make the server panic from a flood of packets. + * Needs testing once toggling aetherytes actually works. */ + self.actor_control_self(ActorControlSelf { + category: ActorControlCategory::LearnTeleport { + id: i, + unlocked: *on, + }, + }) + .await; + } + } else { + let (value, index) = value_to_flag_byte_index_value(*id); + self.player_data.aetherytes[index as usize] |= value; + + self.actor_control_self(ActorControlSelf { + category: ActorControlCategory::LearnTeleport { + id: *id, + unlocked: *on, + }, + }) + .await; + } + } } } player.queued_tasks.clear(); diff --git a/src/world/database.rs b/src/world/database.rs index ae33fc9..e325b2e 100644 --- a/src/world/database.rs +++ b/src/world/database.rs @@ -47,7 +47,7 @@ impl WorldDatabase { // Create characters data table { - let query = "CREATE TABLE IF NOT EXISTS character_data (content_id INTEGER PRIMARY KEY, name STRING, chara_make STRING, city_state INTEGER, zone_id INTEGER, pos_x REAL, pos_y REAL, pos_z REAL, rotation REAL, inventory STRING, remake_mode INTEGER, gm_rank INTEGER, classjob_id INTEGER, classjob_levels STRING, unlocks STRING);"; + let query = "CREATE TABLE IF NOT EXISTS character_data (content_id INTEGER PRIMARY KEY, name STRING, chara_make STRING, city_state INTEGER, zone_id INTEGER, pos_x REAL, pos_y REAL, pos_z REAL, rotation REAL, inventory STRING, remake_mode INTEGER, gm_rank INTEGER, classjob_id INTEGER, classjob_levels STRING, unlocks STRING, aetherytes STRING);"; connection.execute(query, ()).unwrap(); } @@ -140,7 +140,7 @@ impl WorldDatabase { .unwrap(); stmt = connection - .prepare("SELECT pos_x, pos_y, pos_z, rotation, zone_id, inventory, gm_rank, classjob_id, classjob_levels, unlocks FROM character_data WHERE content_id = ?1") + .prepare("SELECT pos_x, pos_y, pos_z, rotation, zone_id, inventory, gm_rank, classjob_id, classjob_levels, unlocks, aetherytes FROM character_data WHERE content_id = ?1") .unwrap(); let ( pos_x, @@ -153,7 +153,20 @@ impl WorldDatabase { classjob_id, classjob_levels, unlocks, - ): (f32, f32, f32, f32, u16, String, u8, i32, String, String) = stmt + aetherytes, + ): ( + f32, + f32, + f32, + f32, + u16, + String, + u8, + i32, + String, + String, + String, + ) = stmt .query_row((content_id,), |row| { Ok(( row.get(0)?, @@ -166,6 +179,7 @@ impl WorldDatabase { row.get(7)?, row.get(8)?, row.get(9)?, + row.get(10)?, )) }) .unwrap(); @@ -188,6 +202,7 @@ impl WorldDatabase { classjob_id: classjob_id as u8, classjob_levels: serde_json::from_str(&classjob_levels).unwrap(), unlocks: serde_json::from_str(&unlocks).unwrap(), + aetherytes: serde_json::from_str(&aetherytes).unwrap(), ..Default::default() } } @@ -197,7 +212,7 @@ impl WorldDatabase { let connection = self.connection.lock().unwrap(); let mut stmt = connection - .prepare("UPDATE character_data SET zone_id=?1, pos_x=?2, pos_y=?3, pos_z=?4, rotation=?5, inventory=?6, classjob_id=?7, classjob_levels=?8, unlocks=?9 WHERE content_id = ?10") + .prepare("UPDATE character_data SET zone_id=?1, pos_x=?2, pos_y=?3, pos_z=?4, rotation=?5, inventory=?6, classjob_id=?7, classjob_levels=?8, unlocks=?9, aetherytes=?10 WHERE content_id = ?11") .unwrap(); stmt.execute(( data.zone_id, @@ -209,6 +224,7 @@ impl WorldDatabase { data.classjob_id, serde_json::to_string(&data.classjob_levels).unwrap(), serde_json::to_string(&data.unlocks).unwrap(), + serde_json::to_string(&data.aetherytes).unwrap(), data.content_id, )) .unwrap(); @@ -370,6 +386,9 @@ impl WorldDatabase { // fill out initial unlocks let unlocks = vec![0u8; 64]; + // fill out initial aetherytes + let aetherytes = vec![0u8; 29]; + // insert ids connection .execute( @@ -381,7 +400,7 @@ impl WorldDatabase { // insert char data connection .execute( - "INSERT INTO character_data VALUES (?1, ?2, ?3, ?4, ?5, 0.0, 0.0, 0.0, 0.0, ?6, 0, 90, ?7, ?8, ?9);", + "INSERT INTO character_data VALUES (?1, ?2, ?3, ?4, ?5, 0.0, 0.0, 0.0, 0.0, ?6, 0, 90, ?7, ?8, ?9, ?10);", ( content_id, name, @@ -392,6 +411,7 @@ impl WorldDatabase { chara_make.classjob_id, serde_json::to_string(&classjob_levels).unwrap(), serde_json::to_string(&unlocks).unwrap(), + serde_json::to_string(&aetherytes).unwrap(), ), ) .unwrap(); diff --git a/src/world/lua.rs b/src/world/lua.rs index c73dfa3..e5b3bca 100644 --- a/src/world/lua.rs +++ b/src/world/lua.rs @@ -28,6 +28,7 @@ pub enum Task { ReloadScripts, ToggleInvisibility { invisible: bool }, Unlock { id: u32 }, + UnlockAetheryte { id: u32, on: bool }, } #[derive(Default)] @@ -159,28 +160,10 @@ impl LuaPlayer { } fn unlock_aetheryte(&mut self, unlocked: u32, id: u32) { - let op_code = ServerZoneIpcType::ActorControlSelf; - let on = unlocked == 0; - if id == 0 { - for i in 1..239 { - let data = ServerZoneIpcData::ActorControlSelf(ActorControlSelf { - category: ActorControlCategory::LearnTeleport { - id: i, - unlocked: on, - }, - }); - - /* Unknown if this will make the server panic from a flood of packets. - * Needs testing once toggling aetherytes actually works. */ - self.create_segment_self(op_code.clone(), data); - } - } else { - let data = ServerZoneIpcData::ActorControlSelf(ActorControlSelf { - category: ActorControlCategory::LearnTeleport { id, unlocked: on }, - }); - - self.create_segment_self(op_code, data); - } + self.queued_tasks.push(Task::UnlockAetheryte { + id, + on: unlocked == 1, + }); } fn change_territory(&mut self, zone_id: u16) {