mirror of
https://github.com/redstrate/Kawari.git
synced 2025-06-30 03:37: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
|
||||
command_sender = "[unlockaction] "
|
||||
command_sender = "[unlock] "
|
||||
|
||||
function onCommand(args, player)
|
||||
local parts = split(args)
|
||||
|
@ -14,18 +14,18 @@ function onCommand(args, player)
|
|||
|
||||
if parts[1] == "all" then
|
||||
for i = 0, 1000, 1 do
|
||||
player:unlock_action(i)
|
||||
player:unlock(i)
|
||||
end
|
||||
printf(player, "Everything is unlocked!", id)
|
||||
else
|
||||
local id = tonumber(parts[1])
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
player:unlock_action(id)
|
||||
printf(player, "Action %s unlocked!", id)
|
||||
player:unlock(id)
|
||||
printf(player, "%s unlocked!", id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -293,6 +293,7 @@ async fn client_loop(
|
|||
current_class: connection.player_data.classjob_id,
|
||||
current_job: connection.player_data.classjob_id,
|
||||
levels: connection.player_data.classjob_levels.map(|x| x as u16),
|
||||
unlocks: connection.player_data.unlocks.clone(),
|
||||
..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 strength: u32,
|
||||
pub dexterity: u32,
|
||||
|
|
|
@ -79,19 +79,18 @@ pub struct PlayerStatus {
|
|||
#[bw(pad_size_to = 33)]
|
||||
pub mount_guide_mask: Vec<u8>,
|
||||
pub ornament_mask: [u8; 4],
|
||||
#[br(count = 85)]
|
||||
#[bw(pad_size_to = 85)]
|
||||
#[br(count = 95)]
|
||||
#[bw(pad_size_to = 95)]
|
||||
pub unknown281: Vec<u8>,
|
||||
#[br(count = CHAR_NAME_MAX_LENGTH)]
|
||||
#[bw(pad_size_to = CHAR_NAME_MAX_LENGTH)]
|
||||
#[br(map = read_string)]
|
||||
#[bw(map = write_string)]
|
||||
pub name: String,
|
||||
pub unknown293: [u8; 16],
|
||||
pub unknown2a3: u8,
|
||||
pub unknown293: [u8; 32],
|
||||
#[br(count = 64)]
|
||||
#[bw(pad_size_to = 64)]
|
||||
pub unlock_bitmask: Vec<u8>,
|
||||
pub unlocks: Vec<u8>,
|
||||
pub aetheryte: [u8; 26],
|
||||
pub favorite_aetheryte_ids: [u16; 4],
|
||||
pub free_aetheryte_id: u16,
|
||||
|
@ -159,8 +158,8 @@ pub struct PlayerStatus {
|
|||
pub cleared_pvp: [u8; 5],
|
||||
|
||||
// meh, this is where i put all of the new data
|
||||
#[br(count = 216)]
|
||||
#[bw(pad_size_to = 216)]
|
||||
#[br(count = 191)]
|
||||
#[bw(pad_size_to = 191)]
|
||||
pub unknown948: Vec<u8>,
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@ use tokio::net::TcpStream;
|
|||
|
||||
use crate::{
|
||||
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},
|
||||
inventory::{ContainerType, Inventory, Item},
|
||||
ipc::{
|
||||
|
@ -76,6 +78,8 @@ pub struct PlayerData {
|
|||
pub teleport_query: TeleportQuery,
|
||||
pub gm_rank: GameMasterRank,
|
||||
pub gm_invisible: bool,
|
||||
|
||||
pub unlocks: Vec<u8>,
|
||||
}
|
||||
|
||||
impl PlayerData {
|
||||
|
@ -674,6 +678,18 @@ impl ZoneConnection {
|
|||
Task::ToggleInvisibility { invisible } => {
|
||||
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();
|
||||
|
|
|
@ -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);";
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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 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();
|
||||
let (
|
||||
pos_x,
|
||||
|
@ -152,7 +152,8 @@ impl WorldDatabase {
|
|||
gm_rank,
|
||||
classjob_id,
|
||||
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| {
|
||||
Ok((
|
||||
row.get(0)?,
|
||||
|
@ -164,6 +165,7 @@ impl WorldDatabase {
|
|||
row.get(6)?,
|
||||
row.get(7)?,
|
||||
row.get(8)?,
|
||||
row.get(9)?,
|
||||
))
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -185,6 +187,7 @@ impl WorldDatabase {
|
|||
gm_rank: GameMasterRank::try_from(gm_rank).unwrap(),
|
||||
classjob_id: classjob_id as u8,
|
||||
classjob_levels: serde_json::from_str(&classjob_levels).unwrap(),
|
||||
unlocks: serde_json::from_str(&unlocks).unwrap(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +197,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 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();
|
||||
stmt.execute((
|
||||
data.zone_id,
|
||||
|
@ -205,6 +208,7 @@ impl WorldDatabase {
|
|||
serde_json::to_string(&data.inventory).unwrap(),
|
||||
data.classjob_id,
|
||||
serde_json::to_string(&data.classjob_levels).unwrap(),
|
||||
serde_json::to_string(&data.unlocks).unwrap(),
|
||||
data.content_id,
|
||||
))
|
||||
.unwrap();
|
||||
|
@ -363,6 +367,9 @@ impl WorldDatabase {
|
|||
let mut classjob_levels = [0i32; 32];
|
||||
classjob_levels[chara_make.classjob_id as usize] = 1; // inital level
|
||||
|
||||
// fill out initial unlocks
|
||||
let unlocks = vec![0u8; 64];
|
||||
|
||||
// insert ids
|
||||
connection
|
||||
.execute(
|
||||
|
@ -374,7 +381,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);",
|
||||
"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,
|
||||
name,
|
||||
|
@ -384,6 +391,7 @@ impl WorldDatabase {
|
|||
serde_json::to_string(&inventory).unwrap(),
|
||||
chara_make.classjob_id,
|
||||
serde_json::to_string(&classjob_levels).unwrap(),
|
||||
serde_json::to_string(&unlocks).unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
|
@ -27,6 +27,7 @@ pub enum Task {
|
|||
WarpAetheryte { aetheryte_id: u32 },
|
||||
ReloadScripts,
|
||||
ToggleInvisibility { invisible: bool },
|
||||
Unlock { id: u32 },
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -135,13 +136,8 @@ impl LuaPlayer {
|
|||
self.create_segment_self(op_code, data);
|
||||
}
|
||||
|
||||
fn unlock_action(&mut self, id: u32) {
|
||||
let op_code = ServerZoneIpcType::ActorControlSelf;
|
||||
let data = ServerZoneIpcData::ActorControlSelf(ActorControlSelf {
|
||||
category: ActorControlCategory::ToggleUnlock { id, unlocked: true },
|
||||
});
|
||||
|
||||
self.create_segment_self(op_code, data);
|
||||
fn unlock(&mut self, id: u32) {
|
||||
self.queued_tasks.push(Task::Unlock { id });
|
||||
}
|
||||
|
||||
fn set_speed(&mut self, speed: u16) {
|
||||
|
@ -289,8 +285,8 @@ impl UserData for LuaPlayer {
|
|||
this.unlock_aetheryte(unlock, id);
|
||||
Ok(())
|
||||
});
|
||||
methods.add_method_mut("unlock_action", |_, this, action_id: u32| {
|
||||
this.unlock_action(action_id);
|
||||
methods.add_method_mut("unlock", |_, this, action_id: u32| {
|
||||
this.unlock(action_id);
|
||||
Ok(())
|
||||
});
|
||||
methods.add_method_mut("set_speed", |_, this, speed: u16| {
|
||||
|
|
Loading…
Add table
Reference in a new issue