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:
parent
0b470dc28b
commit
5272439bca
5 changed files with 73 additions and 29 deletions
|
@ -3,8 +3,8 @@ use std::sync::{Arc, Mutex};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use kawari::RECEIVE_BUFFER_SIZE;
|
use kawari::RECEIVE_BUFFER_SIZE;
|
||||||
use kawari::common::Position;
|
|
||||||
use kawari::common::{GameData, TerritoryNameKind, timestamp_secs};
|
use kawari::common::{GameData, TerritoryNameKind, timestamp_secs};
|
||||||
|
use kawari::common::{Position, value_to_flag_byte_index_value};
|
||||||
use kawari::config::get_config;
|
use kawari::config::get_config;
|
||||||
use kawari::inventory::{Item, Storage};
|
use kawari::inventory::{Item, Storage};
|
||||||
use kawari::ipc::chat::{ServerChatIpcData, ServerChatIpcSegment};
|
use kawari::ipc::chat::{ServerChatIpcData, ServerChatIpcSegment};
|
||||||
|
@ -294,6 +294,7 @@ async fn client_loop(
|
||||||
current_job: connection.player_data.classjob_id,
|
current_job: connection.player_data.classjob_id,
|
||||||
levels: connection.player_data.classjob_levels.map(|x| x as u16),
|
levels: connection.player_data.classjob_levels.map(|x| x as u16),
|
||||||
unlocks: connection.player_data.unlocks.clone(),
|
unlocks: connection.player_data.unlocks.clone(),
|
||||||
|
aetherytes: connection.player_data.aetherytes.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -689,10 +690,16 @@ async fn client_loop(
|
||||||
// id == 0 means "all"
|
// id == 0 means "all"
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
for i in 1..239 {
|
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 {
|
connection.actor_control_self(ActorControlSelf {
|
||||||
category: ActorControlCategory::LearnTeleport { id: i, unlocked: on } }).await;
|
category: ActorControlCategory::LearnTeleport { id: i, unlocked: on } }).await;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let (value, index) = value_to_flag_byte_index_value(id);
|
||||||
|
connection.player_data.aetherytes[index as usize] |= value;
|
||||||
|
|
||||||
connection.actor_control_self(ActorControlSelf {
|
connection.actor_control_self(ActorControlSelf {
|
||||||
category: ActorControlCategory::LearnTeleport { id, unlocked: on } }).await;
|
category: ActorControlCategory::LearnTeleport { id, unlocked: on } }).await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,10 @@ pub struct PlayerStatus {
|
||||||
#[br(count = 64)]
|
#[br(count = 64)]
|
||||||
#[bw(pad_size_to = 64)]
|
#[bw(pad_size_to = 64)]
|
||||||
pub unlocks: Vec<u8>,
|
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 favorite_aetheryte_ids: [u16; 4],
|
||||||
pub free_aetheryte_id: u16,
|
pub free_aetheryte_id: u16,
|
||||||
pub ps_plus_free_aetheryte_id: u16,
|
pub ps_plus_free_aetheryte_id: u16,
|
||||||
|
|
|
@ -80,6 +80,7 @@ pub struct PlayerData {
|
||||||
pub gm_invisible: bool,
|
pub gm_invisible: bool,
|
||||||
|
|
||||||
pub unlocks: Vec<u8>,
|
pub unlocks: Vec<u8>,
|
||||||
|
pub aetherytes: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerData {
|
impl PlayerData {
|
||||||
|
@ -690,6 +691,36 @@ impl ZoneConnection {
|
||||||
})
|
})
|
||||||
.await;
|
.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();
|
player.queued_tasks.clear();
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl WorldDatabase {
|
||||||
|
|
||||||
// Create characters data table
|
// 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();
|
connection.execute(query, ()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ impl WorldDatabase {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
stmt = connection
|
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();
|
.unwrap();
|
||||||
let (
|
let (
|
||||||
pos_x,
|
pos_x,
|
||||||
|
@ -153,7 +153,20 @@ impl WorldDatabase {
|
||||||
classjob_id,
|
classjob_id,
|
||||||
classjob_levels,
|
classjob_levels,
|
||||||
unlocks,
|
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| {
|
.query_row((content_id,), |row| {
|
||||||
Ok((
|
Ok((
|
||||||
row.get(0)?,
|
row.get(0)?,
|
||||||
|
@ -166,6 +179,7 @@ impl WorldDatabase {
|
||||||
row.get(7)?,
|
row.get(7)?,
|
||||||
row.get(8)?,
|
row.get(8)?,
|
||||||
row.get(9)?,
|
row.get(9)?,
|
||||||
|
row.get(10)?,
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -188,6 +202,7 @@ impl WorldDatabase {
|
||||||
classjob_id: classjob_id as u8,
|
classjob_id: classjob_id as u8,
|
||||||
classjob_levels: serde_json::from_str(&classjob_levels).unwrap(),
|
classjob_levels: serde_json::from_str(&classjob_levels).unwrap(),
|
||||||
unlocks: serde_json::from_str(&unlocks).unwrap(),
|
unlocks: serde_json::from_str(&unlocks).unwrap(),
|
||||||
|
aetherytes: serde_json::from_str(&aetherytes).unwrap(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,7 +212,7 @@ impl WorldDatabase {
|
||||||
let connection = self.connection.lock().unwrap();
|
let connection = self.connection.lock().unwrap();
|
||||||
|
|
||||||
let mut stmt = connection
|
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();
|
.unwrap();
|
||||||
stmt.execute((
|
stmt.execute((
|
||||||
data.zone_id,
|
data.zone_id,
|
||||||
|
@ -209,6 +224,7 @@ impl WorldDatabase {
|
||||||
data.classjob_id,
|
data.classjob_id,
|
||||||
serde_json::to_string(&data.classjob_levels).unwrap(),
|
serde_json::to_string(&data.classjob_levels).unwrap(),
|
||||||
serde_json::to_string(&data.unlocks).unwrap(),
|
serde_json::to_string(&data.unlocks).unwrap(),
|
||||||
|
serde_json::to_string(&data.aetherytes).unwrap(),
|
||||||
data.content_id,
|
data.content_id,
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -370,6 +386,9 @@ impl WorldDatabase {
|
||||||
// fill out initial unlocks
|
// fill out initial unlocks
|
||||||
let unlocks = vec![0u8; 64];
|
let unlocks = vec![0u8; 64];
|
||||||
|
|
||||||
|
// fill out initial aetherytes
|
||||||
|
let aetherytes = vec![0u8; 29];
|
||||||
|
|
||||||
// insert ids
|
// insert ids
|
||||||
connection
|
connection
|
||||||
.execute(
|
.execute(
|
||||||
|
@ -381,7 +400,7 @@ impl WorldDatabase {
|
||||||
// insert char data
|
// insert char data
|
||||||
connection
|
connection
|
||||||
.execute(
|
.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,
|
content_id,
|
||||||
name,
|
name,
|
||||||
|
@ -392,6 +411,7 @@ impl WorldDatabase {
|
||||||
chara_make.classjob_id,
|
chara_make.classjob_id,
|
||||||
serde_json::to_string(&classjob_levels).unwrap(),
|
serde_json::to_string(&classjob_levels).unwrap(),
|
||||||
serde_json::to_string(&unlocks).unwrap(),
|
serde_json::to_string(&unlocks).unwrap(),
|
||||||
|
serde_json::to_string(&aetherytes).unwrap(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -28,6 +28,7 @@ pub enum Task {
|
||||||
ReloadScripts,
|
ReloadScripts,
|
||||||
ToggleInvisibility { invisible: bool },
|
ToggleInvisibility { invisible: bool },
|
||||||
Unlock { id: u32 },
|
Unlock { id: u32 },
|
||||||
|
UnlockAetheryte { id: u32, on: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -159,28 +160,10 @@ impl LuaPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unlock_aetheryte(&mut self, unlocked: u32, id: u32) {
|
fn unlock_aetheryte(&mut self, unlocked: u32, id: u32) {
|
||||||
let op_code = ServerZoneIpcType::ActorControlSelf;
|
self.queued_tasks.push(Task::UnlockAetheryte {
|
||||||
let on = unlocked == 0;
|
id,
|
||||||
if id == 0 {
|
on: unlocked == 1,
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_territory(&mut self, zone_id: u16) {
|
fn change_territory(&mut self, zone_id: u16) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue