mirror of
https://github.com/redstrate/Kawari.git
synced 2025-06-30 11:47:45 +00:00
Make unlocks persistent, fix player name in chat
The PlayerStatus struct shifted around recently, so I fixed the offsets yet again. Unlocks should be persistent now, but this as usual requires a database wipe, sorry! I also included some refactors of the !unlock debug command that still had references to the old unlock_action API.
This commit is contained in:
parent
1fa861a35a
commit
0b470dc28b
7 changed files with 52 additions and 27 deletions
|
@ -1,5 +1,5 @@
|
||||||
required_rank = GM_RANK_DEBUG
|
required_rank = GM_RANK_DEBUG
|
||||||
command_sender = "[unlockaction] "
|
command_sender = "[unlock] "
|
||||||
|
|
||||||
function onCommand(args, player)
|
function onCommand(args, player)
|
||||||
local parts = split(args)
|
local parts = split(args)
|
||||||
|
@ -14,18 +14,18 @@ function onCommand(args, player)
|
||||||
|
|
||||||
if parts[1] == "all" then
|
if parts[1] == "all" then
|
||||||
for i = 0, 1000, 1 do
|
for i = 0, 1000, 1 do
|
||||||
player:unlock_action(i)
|
player:unlock(i)
|
||||||
end
|
end
|
||||||
printf(player, "Everything is unlocked!", id)
|
printf(player, "Everything is unlocked!", id)
|
||||||
else
|
else
|
||||||
local id = tonumber(parts[1])
|
local id = tonumber(parts[1])
|
||||||
|
|
||||||
if not id then
|
if not id then
|
||||||
printf(player, "Error parsing action id! Make sure the id is an integer."..usage)
|
printf(player, "Error parsing unlock id! Make sure the id is an integer."..usage)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
player:unlock_action(id)
|
player:unlock(id)
|
||||||
printf(player, "Action %s unlocked!", id)
|
printf(player, "%s unlocked!", id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -293,6 +293,7 @@ async fn client_loop(
|
||||||
current_class: connection.player_data.classjob_id,
|
current_class: connection.player_data.classjob_id,
|
||||||
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(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -147,6 +147,11 @@ pub fn determine_initial_starting_zone(citystate_id: u8) -> u16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn value_to_flag_byte_index_value(in_value: u32) -> (u8, u16) {
|
||||||
|
let bit_index = in_value % 8;
|
||||||
|
(1 << bit_index, (in_value / 8) as u16)
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Attributes {
|
pub struct Attributes {
|
||||||
pub strength: u32,
|
pub strength: u32,
|
||||||
pub dexterity: u32,
|
pub dexterity: u32,
|
||||||
|
|
|
@ -79,19 +79,18 @@ pub struct PlayerStatus {
|
||||||
#[bw(pad_size_to = 33)]
|
#[bw(pad_size_to = 33)]
|
||||||
pub mount_guide_mask: Vec<u8>,
|
pub mount_guide_mask: Vec<u8>,
|
||||||
pub ornament_mask: [u8; 4],
|
pub ornament_mask: [u8; 4],
|
||||||
#[br(count = 85)]
|
#[br(count = 95)]
|
||||||
#[bw(pad_size_to = 85)]
|
#[bw(pad_size_to = 95)]
|
||||||
pub unknown281: Vec<u8>,
|
pub unknown281: Vec<u8>,
|
||||||
#[br(count = CHAR_NAME_MAX_LENGTH)]
|
#[br(count = CHAR_NAME_MAX_LENGTH)]
|
||||||
#[bw(pad_size_to = CHAR_NAME_MAX_LENGTH)]
|
#[bw(pad_size_to = CHAR_NAME_MAX_LENGTH)]
|
||||||
#[br(map = read_string)]
|
#[br(map = read_string)]
|
||||||
#[bw(map = write_string)]
|
#[bw(map = write_string)]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub unknown293: [u8; 16],
|
pub unknown293: [u8; 32],
|
||||||
pub unknown2a3: u8,
|
|
||||||
#[br(count = 64)]
|
#[br(count = 64)]
|
||||||
#[bw(pad_size_to = 64)]
|
#[bw(pad_size_to = 64)]
|
||||||
pub unlock_bitmask: Vec<u8>,
|
pub unlocks: Vec<u8>,
|
||||||
pub aetheryte: [u8; 26],
|
pub aetheryte: [u8; 26],
|
||||||
pub favorite_aetheryte_ids: [u16; 4],
|
pub favorite_aetheryte_ids: [u16; 4],
|
||||||
pub free_aetheryte_id: u16,
|
pub free_aetheryte_id: u16,
|
||||||
|
@ -159,8 +158,8 @@ pub struct PlayerStatus {
|
||||||
pub cleared_pvp: [u8; 5],
|
pub cleared_pvp: [u8; 5],
|
||||||
|
|
||||||
// meh, this is where i put all of the new data
|
// meh, this is where i put all of the new data
|
||||||
#[br(count = 216)]
|
#[br(count = 191)]
|
||||||
#[bw(pad_size_to = 216)]
|
#[bw(pad_size_to = 191)]
|
||||||
pub unknown948: Vec<u8>,
|
pub unknown948: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,9 @@ use tokio::net::TcpStream;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
OBFUSCATION_ENABLED_MODE,
|
OBFUSCATION_ENABLED_MODE,
|
||||||
common::{GameData, ObjectId, ObjectTypeId, Position, timestamp_secs},
|
common::{
|
||||||
|
GameData, ObjectId, ObjectTypeId, Position, timestamp_secs, value_to_flag_byte_index_value,
|
||||||
|
},
|
||||||
config::{WorldConfig, get_config},
|
config::{WorldConfig, get_config},
|
||||||
inventory::{ContainerType, Inventory, Item},
|
inventory::{ContainerType, Inventory, Item},
|
||||||
ipc::{
|
ipc::{
|
||||||
|
@ -76,6 +78,8 @@ pub struct PlayerData {
|
||||||
pub teleport_query: TeleportQuery,
|
pub teleport_query: TeleportQuery,
|
||||||
pub gm_rank: GameMasterRank,
|
pub gm_rank: GameMasterRank,
|
||||||
pub gm_invisible: bool,
|
pub gm_invisible: bool,
|
||||||
|
|
||||||
|
pub unlocks: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerData {
|
impl PlayerData {
|
||||||
|
@ -674,6 +678,18 @@ impl ZoneConnection {
|
||||||
Task::ToggleInvisibility { invisible } => {
|
Task::ToggleInvisibility { invisible } => {
|
||||||
self.toggle_invisibility(*invisible).await;
|
self.toggle_invisibility(*invisible).await;
|
||||||
}
|
}
|
||||||
|
Task::Unlock { id } => {
|
||||||
|
let (value, index) = value_to_flag_byte_index_value(*id);
|
||||||
|
self.player_data.unlocks[index as usize] |= value;
|
||||||
|
|
||||||
|
self.actor_control_self(ActorControlSelf {
|
||||||
|
category: ActorControlCategory::ToggleUnlock {
|
||||||
|
id: *id,
|
||||||
|
unlocked: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.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);";
|
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);";
|
||||||
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 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 FROM character_data WHERE content_id = ?1")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (
|
let (
|
||||||
pos_x,
|
pos_x,
|
||||||
|
@ -152,7 +152,8 @@ impl WorldDatabase {
|
||||||
gm_rank,
|
gm_rank,
|
||||||
classjob_id,
|
classjob_id,
|
||||||
classjob_levels,
|
classjob_levels,
|
||||||
): (f32, f32, f32, f32, u16, String, u8, i32, String) = stmt
|
unlocks,
|
||||||
|
): (f32, f32, f32, f32, u16, String, u8, i32, String, String) = stmt
|
||||||
.query_row((content_id,), |row| {
|
.query_row((content_id,), |row| {
|
||||||
Ok((
|
Ok((
|
||||||
row.get(0)?,
|
row.get(0)?,
|
||||||
|
@ -164,6 +165,7 @@ impl WorldDatabase {
|
||||||
row.get(6)?,
|
row.get(6)?,
|
||||||
row.get(7)?,
|
row.get(7)?,
|
||||||
row.get(8)?,
|
row.get(8)?,
|
||||||
|
row.get(9)?,
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -185,6 +187,7 @@ impl WorldDatabase {
|
||||||
gm_rank: GameMasterRank::try_from(gm_rank).unwrap(),
|
gm_rank: GameMasterRank::try_from(gm_rank).unwrap(),
|
||||||
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(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +197,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 WHERE content_id = ?9")
|
.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")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
stmt.execute((
|
stmt.execute((
|
||||||
data.zone_id,
|
data.zone_id,
|
||||||
|
@ -205,6 +208,7 @@ impl WorldDatabase {
|
||||||
serde_json::to_string(&data.inventory).unwrap(),
|
serde_json::to_string(&data.inventory).unwrap(),
|
||||||
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(),
|
||||||
data.content_id,
|
data.content_id,
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -363,6 +367,9 @@ impl WorldDatabase {
|
||||||
let mut classjob_levels = [0i32; 32];
|
let mut classjob_levels = [0i32; 32];
|
||||||
classjob_levels[chara_make.classjob_id as usize] = 1; // inital level
|
classjob_levels[chara_make.classjob_id as usize] = 1; // inital level
|
||||||
|
|
||||||
|
// fill out initial unlocks
|
||||||
|
let unlocks = vec![0u8; 64];
|
||||||
|
|
||||||
// insert ids
|
// insert ids
|
||||||
connection
|
connection
|
||||||
.execute(
|
.execute(
|
||||||
|
@ -374,7 +381,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);",
|
"INSERT INTO character_data VALUES (?1, ?2, ?3, ?4, ?5, 0.0, 0.0, 0.0, 0.0, ?6, 0, 90, ?7, ?8, ?9);",
|
||||||
(
|
(
|
||||||
content_id,
|
content_id,
|
||||||
name,
|
name,
|
||||||
|
@ -384,6 +391,7 @@ impl WorldDatabase {
|
||||||
serde_json::to_string(&inventory).unwrap(),
|
serde_json::to_string(&inventory).unwrap(),
|
||||||
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(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub enum Task {
|
||||||
WarpAetheryte { aetheryte_id: u32 },
|
WarpAetheryte { aetheryte_id: u32 },
|
||||||
ReloadScripts,
|
ReloadScripts,
|
||||||
ToggleInvisibility { invisible: bool },
|
ToggleInvisibility { invisible: bool },
|
||||||
|
Unlock { id: u32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -135,13 +136,8 @@ impl LuaPlayer {
|
||||||
self.create_segment_self(op_code, data);
|
self.create_segment_self(op_code, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unlock_action(&mut self, id: u32) {
|
fn unlock(&mut self, id: u32) {
|
||||||
let op_code = ServerZoneIpcType::ActorControlSelf;
|
self.queued_tasks.push(Task::Unlock { id });
|
||||||
let data = ServerZoneIpcData::ActorControlSelf(ActorControlSelf {
|
|
||||||
category: ActorControlCategory::ToggleUnlock { id, unlocked: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
self.create_segment_self(op_code, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_speed(&mut self, speed: u16) {
|
fn set_speed(&mut self, speed: u16) {
|
||||||
|
@ -289,8 +285,8 @@ impl UserData for LuaPlayer {
|
||||||
this.unlock_aetheryte(unlock, id);
|
this.unlock_aetheryte(unlock, id);
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
methods.add_method_mut("unlock_action", |_, this, action_id: u32| {
|
methods.add_method_mut("unlock", |_, this, action_id: u32| {
|
||||||
this.unlock_action(action_id);
|
this.unlock(action_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
methods.add_method_mut("set_speed", |_, this, speed: u16| {
|
methods.add_method_mut("set_speed", |_, this, speed: u16| {
|
||||||
|
|
Loading…
Add table
Reference in a new issue