1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-06-30 11:47:45 +00:00

Store aetherytes in the database

This requires yet another database wipe, but this is worth it as you no
longer have to aetheryte unlock spam on login.
This commit is contained in:
Joshua Goins 2025-06-27 23:24:30 -04:00
parent 0b470dc28b
commit 5272439bca
5 changed files with 73 additions and 29 deletions

View file

@ -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;
}

View file

@ -91,7 +91,10 @@ pub struct PlayerStatus {
#[br(count = 64)]
#[bw(pad_size_to = 64)]
pub unlocks: Vec<u8>,
pub aetheryte: [u8; 26],
pub unknown10e: [u8; 28],
#[br(count = 29)]
#[bw(pad_size_to = 29)]
pub aetherytes: Vec<u8>,
pub favorite_aetheryte_ids: [u16; 4],
pub free_aetheryte_id: u16,
pub ps_plus_free_aetheryte_id: u16,

View file

@ -80,6 +80,7 @@ pub struct PlayerData {
pub gm_invisible: bool,
pub unlocks: Vec<u8>,
pub aetherytes: Vec<u8>,
}
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();

View file

@ -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();

View file

@ -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,
},
self.queued_tasks.push(Task::UnlockAetheryte {
id,
on: unlocked == 1,
});
/* 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);
}
}
fn change_territory(&mut self, zone_id: u16) {