add BB BankSize patch

This commit is contained in:
Martin Michelsen
2025-08-22 22:39:32 -07:00
parent 45824b46fe
commit 0b4d5b2f89
23 changed files with 814 additions and 805 deletions
+307 -295
View File
@@ -244,12 +244,11 @@ Client::~Client() {
void Client::update_channel_name() {
string default_name = this->channel->default_name();
auto player = this->character(false, false);
auto player = this->character_file(false, false);
if (player) {
string name_str = player->disp.name.decode(this->language());
size_t level = player->disp.stats.level + 1;
this->channel->name = std::format("C-{:X} ({} Lv.{}) @ {}",
this->id, name_str, level, default_name);
this->channel->name = std::format("C-{:X} ({} Lv.{}) @ {}", this->id, name_str, level, default_name);
} else {
this->channel->name = std::format("C-{:X} @ {}", this->id, default_name);
}
@@ -263,7 +262,7 @@ void Client::reschedule_save_game_data_timer() {
this->save_game_data_timer.expires_after(std::chrono::seconds(60));
this->save_game_data_timer.async_wait([this](std::error_code ec) {
if (!ec) {
if (this->character(false)) {
if (this->character_file(false)) {
this->save_all();
}
this->reschedule_save_game_data_timer();
@@ -336,7 +335,7 @@ shared_ptr<const TeamIndex::Team> Client::team() const {
return nullptr;
}
auto p = this->character(false);
auto p = this->character_file(false);
auto s = this->require_server_state();
auto team = s->team_index->get_by_id(this->login->account->bb_team_id);
if (!team) {
@@ -384,7 +383,7 @@ bool Client::evaluate_quest_availability_expression(
if (game && !game->quest_flag_values) {
throw logic_error("quest flags are missing from game");
}
auto p = this->character();
auto p = this->character_file();
IntegralExpression::Env env = {
.flags = &p->quest_flags.data.at(difficulty),
.challenge_records = &p->challenge_records,
@@ -448,8 +447,184 @@ void Client::set_login(shared_ptr<Login> login) {
}
}
// System file
string Client::system_filename(const string& bb_username) {
return std::format("system/players/system_{}.psosys", bb_username);
}
string Client::system_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have system data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return this->system_filename(this->login->bb_license->username);
}
shared_ptr<PSOBBBaseSystemFile> Client::system_file(bool allow_load) {
if (!this->system_data && allow_load) {
this->load_all_files();
}
return this->system_data;
}
shared_ptr<const PSOBBBaseSystemFile> Client::system_file(bool throw_if_missing) const {
if (!this->system_data.get() && throw_if_missing) {
throw runtime_error("system file is not loaded");
}
return this->system_data;
}
void Client::save_system_file() const {
if (!this->system_data) {
throw logic_error("no system file loaded");
}
string filename = this->system_filename();
phosg::save_object_file(filename, *this->system_data);
this->log.info_f("Saved system file {}", filename);
}
// Guild Card file
string Client::guild_card_filename(const string& bb_username) {
return std::format("system/players/guild_cards_{}.psocard", bb_username);
}
string Client::guild_card_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have saved character data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return this->guild_card_filename(this->login->bb_license->username);
}
shared_ptr<PSOBBGuildCardFile> Client::guild_card_file(bool allow_load) {
if (!this->guild_card_data && allow_load) {
this->load_all_files();
}
return this->guild_card_data;
}
shared_ptr<const PSOBBGuildCardFile> Client::guild_card_file(bool allow_load) const {
if (!this->guild_card_data && allow_load) {
throw runtime_error("account data is not loaded");
}
return this->guild_card_data;
}
void Client::save_guild_card_file() const {
if (!this->guild_card_data.get()) {
throw logic_error("no Guild Card file loaded");
}
string filename = this->guild_card_filename();
phosg::save_object_file(filename, *this->guild_card_data);
this->log.info_f("Saved Guild Card file {}", filename);
}
// Character file
string Client::character_filename(const std::string& bb_username, ssize_t index) {
if (bb_username.empty()) {
throw logic_error("non-BB players do not have saved character data");
}
if (index < 0) {
throw logic_error("character index is not set");
}
return std::format("system/players/player_{}_{}.psochar", bb_username, index);
}
string Client::backup_character_filename(uint32_t account_id, size_t index, bool is_ep3) {
return std::format("system/players/backup_player_{}_{}.{}",
account_id, index, is_ep3 ? "pso3char" : "psochar");
}
string Client::character_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have saved character data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return this->character_filename(this->login->bb_license->username, this->bb_character_index);
}
shared_ptr<PSOBBCharacterFile> Client::character_file(bool allow_load, bool allow_overlay) {
if (this->overlay_character_data && allow_overlay) {
return this->overlay_character_data;
}
if (!this->character_data && allow_load) {
if ((this->version() == Version::BB_V4) && (this->bb_character_index < 0)) {
throw runtime_error("character index not specified");
}
this->load_all_files();
}
return this->character_data;
}
shared_ptr<const PSOBBCharacterFile> Client::character_file(bool throw_if_missing, bool allow_overlay) const {
if (allow_overlay && this->overlay_character_data) {
return this->overlay_character_data;
}
if (!this->character_data && throw_if_missing) {
throw runtime_error("character data is not loaded");
}
return this->character_data;
}
void Client::save_character_file(
const string& filename,
shared_ptr<const PSOBBBaseSystemFile> system,
shared_ptr<const PSOBBCharacterFile> character) {
PSOCHARFile::save(filename, system, character);
}
void Client::save_ep3_character_file(
const string& filename,
const PSOGCEp3CharacterFile::Character& character) {
phosg::save_file(filename, &character, sizeof(character));
}
void Client::save_character_file() {
if (!this->system_data.get()) {
throw logic_error("no system file loaded");
}
if (!this->character_data.get()) {
throw logic_error("no character file loaded");
}
if (this->should_update_play_time) {
// This is slightly inaccurate, since fractions of a second are truncated
// off each time we save. I'm lazy, so insert shrug emoji here.
uint64_t t = phosg::now();
uint64_t seconds = (t - this->last_play_time_update) / 1000000;
this->character_data->play_time_seconds += seconds;
this->log.info_f("Added {} seconds to play time", seconds);
this->last_play_time_update = t;
if (this->bank_data && (this->bb_bank_character_index == this->bb_character_index)) {
this->character_data->bank = *this->bank_data;
this->log.info_f("Committed bank data back to character file");
}
}
auto filename = this->character_filename();
this->save_character_file(filename, this->system_data, this->character_data);
this->log.info_f("Saved character file {}", filename);
}
void Client::create_character_file(
uint32_t guild_card_number,
uint8_t language,
const PlayerDispDataBBPreview& preview,
shared_ptr<const LevelTable> level_table) {
this->character_data = PSOBBCharacterFile::create_from_preview(guild_card_number, language, preview, level_table);
this->save_character_file();
}
void Client::create_battle_overlay(shared_ptr<const BattleRules> rules, shared_ptr<const LevelTable> level_table) {
this->overlay_character_data = make_shared<PSOBBCharacterFile>(*this->character(true, false));
this->overlay_character_data = make_shared<PSOBBCharacterFile>(*this->character_file(true, false));
if (rules->weapon_and_armor_mode != BattleRules::WeaponAndArmorMode::ALLOW) {
this->overlay_character_data->inventory.remove_all_items_of_type(0);
@@ -499,7 +674,7 @@ void Client::create_battle_overlay(shared_ptr<const BattleRules> rules, shared_p
}
void Client::create_challenge_overlay(Version version, size_t template_index, shared_ptr<const LevelTable> level_table) {
auto p = this->character(true, false);
auto p = this->character_file(true, false);
const auto& tpl = get_challenge_template_definition(version, p->disp.visual.class_flags, template_index);
this->overlay_character_data = make_shared<PSOBBCharacterFile>(*p);
@@ -543,124 +718,109 @@ void Client::create_challenge_overlay(Version version, size_t template_index, sh
}
}
void Client::import_blocked_senders(const parray<le_uint32_t, 30>& blocked_senders) {
this->blocked_senders.clear();
for (size_t z = 0; z < blocked_senders.size(); z++) {
if (blocked_senders[z]) {
this->blocked_senders.emplace(blocked_senders[z]);
}
}
}
// Bank file
shared_ptr<PSOBBBaseSystemFile> Client::system_file(bool allow_load) {
if (!this->system_data && allow_load) {
this->load_all_files();
}
return this->system_data;
}
shared_ptr<const PSOBBBaseSystemFile> Client::system_file(bool allow_load) const {
if (!this->system_data.get() && allow_load) {
throw runtime_error("system data is not loaded");
}
return this->system_data;
}
shared_ptr<PSOBBCharacterFile> Client::character(bool allow_load, bool allow_overlay) {
if (this->overlay_character_data && allow_overlay) {
return this->overlay_character_data;
}
if (!this->character_data && allow_load) {
if ((this->version() == Version::BB_V4) && (this->bb_character_index < 0)) {
throw runtime_error("character index not specified");
}
this->load_all_files();
}
return this->character_data;
}
shared_ptr<const PSOBBCharacterFile> Client::character(bool allow_load, bool allow_overlay) const {
if (allow_overlay && this->overlay_character_data) {
return this->overlay_character_data;
}
if (!this->character_data && allow_load) {
throw runtime_error("character data is not loaded");
}
return this->character_data;
}
shared_ptr<PSOBBGuildCardFile> Client::guild_card_file(bool allow_load) {
if (!this->guild_card_data && allow_load) {
this->load_all_files();
}
return this->guild_card_data;
}
shared_ptr<const PSOBBGuildCardFile> Client::guild_card_file(bool allow_load) const {
if (!this->guild_card_data && allow_load) {
throw runtime_error("account data is not loaded");
}
return this->guild_card_data;
}
string Client::system_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have system data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return std::format("system/players/system_{}.psosys", this->login->bb_license->username);
}
string Client::character_filename(const std::string& bb_username, ssize_t index) {
string Client::bank_filename(const std::string& bb_username, ssize_t index) {
if (bb_username.empty()) {
throw logic_error("non-BB players do not have character data");
throw logic_error("non-BB players do not have saved character data");
}
if (index < 0) {
throw logic_error("character index is not set");
return std::format("system/players/shared_bank_{}.psobank", bb_username);
} else {
return std::format("system/players/player_{}_{}.psobank", bb_username, index);
}
return std::format("system/players/player_{}_{}.psochar", bb_username, index);
}
string Client::backup_character_filename(uint32_t account_id, size_t index, bool is_ep3) {
return std::format("system/players/backup_player_{}_{}.{}",
account_id, index, is_ep3 ? "pso3char" : "psochar");
}
string Client::character_filename(ssize_t index) const {
string Client::bank_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
throw logic_error("non-BB players do not have saved character data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return this->character_filename(this->login->bb_license->username, (index < 0) ? this->bb_character_index : index);
return this->bank_filename(this->login->bb_license->username, this->bb_bank_character_index);
}
string Client::guild_card_filename() const {
std::shared_ptr<PlayerBank> Client::bank_file(bool allow_load) {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
throw logic_error("non-BB players do not have saved character data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
if (this->has_overlay()) {
throw std::runtime_error("bank is inaccessible when overlay is present");
}
return std::format("system/players/guild_cards_{}.psocard", this->login->bb_license->username);
if (!this->bank_data && allow_load) {
try {
// If there's a psobank file, load it and ignore the character file bank
auto filename = this->bank_filename();
auto f = phosg::fopen_unique(filename, "rb");
this->bank_data = make_shared<PlayerBank>();
this->bank_data->load(f.get());
this->log.info_f("Loaded bank data from {}", filename);
} catch (const phosg::cannot_open_file&) {
// If there isn't a psobank file, use the loaded character data if the
// bank character index matches the current character index (that is, we
// should use the current character's bank); otherwise, load the
// corresponding character and parse the bank from that character file
if (this->bb_bank_character_index == this->bb_character_index) {
this->bank_data = std::make_shared<PlayerBank>(this->character_file(false, false)->bank);
this->log.info_f("Using bank data from loaded character");
} else {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
string filename = this->character_filename(this->login->bb_license->username, this->bb_bank_character_index);
auto character = PSOCHARFile::load_shared(filename, false).character_file;
this->bank_data = std::make_shared<PlayerBank>(character->bank);
this->log.info_f("Using bank data from {}", filename);
}
}
auto s = this->require_server_state();
this->bank_data->max_items = s->bb_max_bank_items;
this->bank_data->max_meseta = s->bb_max_bank_meseta;
}
return this->bank_data;
}
string Client::shared_bank_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
std::shared_ptr<const PlayerBank> Client::bank_file(bool throw_if_missing) const {
if (!this->bank_data && throw_if_missing) {
throw std::runtime_error("bank is not loaded");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return std::format("system/players/shared_bank_{}.psobank", this->login->bb_license->username);
return this->bank_data;
}
void Client::save_bank_file(const string& filename, const PlayerBank& bank) {
auto f = phosg::fopen_unique(filename, "wb");
bank.save(f.get());
}
void Client::save_bank_file() const {
if (!this->bank_data) {
throw logic_error("no bank file loaded");
}
auto filename = this->bank_filename();
this->save_bank_file(filename, *this->bank_data);
this->log.info_f("Saved bank file {}", filename);
}
void Client::change_bank(ssize_t index) {
if (this->bank_data) {
this->save_bank_file();
this->bank_data.reset();
if (this->bb_bank_character_index < 0) {
this->log.info_f("Unloaded shared bank");
} else {
this->log.info_f("Unloaded bank from character {}", this->bb_bank_character_index);
}
}
this->bb_bank_character_index = index;
}
// Legacy files
string Client::legacy_account_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
throw logic_error("non-BB players do not have saved character data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
@@ -670,7 +830,7 @@ string Client::legacy_account_filename() const {
string Client::legacy_player_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
throw logic_error("non-BB players do not have saved character data");
}
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
@@ -684,13 +844,13 @@ string Client::legacy_player_filename() const {
static_cast<ssize_t>(this->bb_character_index + 1));
}
void Client::create_character_file(
uint32_t guild_card_number,
uint8_t language,
const PlayerDispDataBBPreview& preview,
shared_ptr<const LevelTable> level_table) {
this->character_data = PSOBBCharacterFile::create_from_preview(guild_card_number, language, preview, level_table);
this->save_character_file();
void Client::import_blocked_senders(const parray<le_uint32_t, 30>& blocked_senders) {
this->blocked_senders.clear();
for (size_t z = 0; z < blocked_senders.size(); z++) {
if (blocked_senders[z]) {
this->blocked_senders.emplace(blocked_senders[z]);
}
}
}
void Client::load_all_files() {
@@ -698,6 +858,7 @@ void Client::load_all_files() {
this->system_data = make_shared<PSOBBBaseSystemFile>();
this->character_data = make_shared<PSOBBCharacterFile>();
this->guild_card_data = make_shared<PSOBBGuildCardFile>();
this->bank_data = make_shared<PlayerBank>();
return;
}
if (!this->login || !this->login->bb_license) {
@@ -707,31 +868,22 @@ void Client::load_all_files() {
this->system_data.reset();
this->character_data.reset();
this->guild_card_data.reset();
auto files_manager = this->require_server_state()->player_files_manager;
this->bank_data.reset();
string sys_filename = this->system_filename();
this->system_data = files_manager->get_system(sys_filename);
if (this->system_data) {
player_data_log.info_f("Using loaded system file {}", sys_filename);
} else if (std::filesystem::is_regular_file(sys_filename)) {
if (std::filesystem::is_regular_file(sys_filename)) {
this->system_data = make_shared<PSOBBBaseSystemFile>(phosg::load_object_file<PSOBBBaseSystemFile>(sys_filename, true));
files_manager->set_system(sys_filename, this->system_data);
player_data_log.info_f("Loaded system data from {}", sys_filename);
this->log.info_f("Loaded system data from {}", sys_filename);
} else {
player_data_log.info_f("System file is missing: {}", sys_filename);
this->log.info_f("System file is missing: {}", sys_filename);
}
if (this->bb_character_index >= 0) {
string char_filename = this->character_filename();
this->character_data = files_manager->get_character(char_filename);
if (this->character_data) {
player_data_log.info_f("Using loaded character file {}", char_filename);
} else if (std::filesystem::is_regular_file(char_filename)) {
if (std::filesystem::is_regular_file(char_filename)) {
auto psochar = PSOCHARFile::load_shared(char_filename, !this->system_data);
this->character_data = psochar.character_file;
files_manager->set_character(char_filename, this->character_data);
player_data_log.info_f("Loaded character data from {}", char_filename);
this->log.info_f("Loaded character data from {}", char_filename);
// If there was no .psosys file, use the system file from the .psochar
// file instead
@@ -740,28 +892,23 @@ void Client::load_all_files() {
throw logic_error("account system data not present, and also not loaded from psochar file");
}
this->system_data = psochar.system_file;
files_manager->set_system(sys_filename, this->system_data);
player_data_log.info_f("Loaded system data from {}", char_filename);
this->log.info_f("Loaded system data from {}", char_filename);
}
this->update_character_data_after_load(this->character_data);
this->system_data->language = this->language();
} else {
player_data_log.info_f("Character file is missing: {}", char_filename);
this->log.info_f("Character file is missing: {}", char_filename);
}
}
string card_filename = this->guild_card_filename();
this->guild_card_data = files_manager->get_guild_card(card_filename);
if (this->guild_card_data) {
player_data_log.info_f("Using loaded Guild Card file {}", card_filename);
} else if (std::filesystem::is_regular_file(card_filename)) {
if (std::filesystem::is_regular_file(card_filename)) {
this->guild_card_data = make_shared<PSOBBGuildCardFile>(phosg::load_object_file<PSOBBGuildCardFile>(card_filename));
files_manager->set_guild_card(card_filename, this->guild_card_data);
player_data_log.info_f("Loaded Guild Card data from {}", card_filename);
this->log.info_f("Loaded Guild Card data from {}", card_filename);
} else {
player_data_log.info_f("Guild Card file is missing: {}", card_filename);
this->log.info_f("Guild Card file is missing: {}", card_filename);
}
// If any of the above files were missing, try to load from .nsa/.nsc files instead
@@ -775,13 +922,11 @@ void Client::load_all_files() {
}
if (!this->system_data) {
this->system_data = make_shared<PSOBBBaseSystemFile>(nsa_data->system_file);
files_manager->set_system(sys_filename, this->system_data);
player_data_log.info_f("Loaded legacy system data from {}", nsa_filename);
this->log.info_f("Loaded legacy system data from {}", nsa_filename);
}
if (!this->guild_card_data) {
this->guild_card_data = make_shared<PSOBBGuildCardFile>(nsa_data->guild_card_file);
files_manager->set_guild_card(card_filename, this->guild_card_data);
player_data_log.info_f("Loaded legacy Guild Card data from {}", nsa_filename);
this->log.info_f("Loaded legacy Guild Card data from {}", nsa_filename);
}
}
@@ -794,13 +939,11 @@ void Client::load_all_files() {
if (s->bb_default_joystick_config) {
this->system_data->joystick_config = *s->bb_default_joystick_config;
}
files_manager->set_system(sys_filename, this->system_data);
player_data_log.info_f("Created new system data");
this->log.info_f("Created new system data");
}
if (!this->guild_card_data) {
this->guild_card_data = make_shared<PSOBBGuildCardFile>();
files_manager->set_guild_card(card_filename, this->guild_card_data);
player_data_log.info_f("Created new Guild Card data");
this->log.info_f("Created new Guild Card data");
}
if (!this->character_data && (this->bb_character_index >= 0)) {
@@ -817,7 +960,6 @@ void Client::load_all_files() {
}
this->character_data = make_shared<PSOBBCharacterFile>();
files_manager->set_character(this->character_filename(), this->character_data);
this->character_data->inventory = nsc_data.inventory;
this->character_data->disp = nsc_data.disp;
this->character_data->play_time_seconds = 0;
@@ -841,14 +983,20 @@ void Client::load_all_files() {
this->character_data->option_flags = nsa_data->option_flags;
this->character_data->symbol_chats = nsa_data->symbol_chats;
this->character_data->shortcuts = nsa_data->shortcuts;
player_data_log.info_f("Loaded legacy player data from {} and {}", nsa_filename, nsc_filename);
this->log.info_f("Loaded legacy player data from {} and {}", nsa_filename, nsc_filename);
} else {
player_data_log.info_f("Loaded legacy player data from {}", nsc_filename);
this->log.info_f("Loaded legacy player data from {}", nsc_filename);
}
this->update_character_data_after_load(this->character_data);
}
}
auto s = this->require_server_state();
auto stack_limits = s->item_stack_limits(this->version());
// bank_file() loads the bank data
this->bank_file()->enforce_stack_limits(stack_limits);
this->blocked_senders.clear();
for (size_t z = 0; z < this->guild_card_data->blocked.size(); z++) {
if (this->guild_card_data->blocked[z].present) {
@@ -860,11 +1008,7 @@ void Client::load_all_files() {
// Clear legacy play_time field
this->character_data->disp.name.clear_after_bytes(0x18);
// Enforce item stack limits, in case they've changed
auto s = this->require_server_state();
auto stack_limits = s->item_stack_limits(this->version());
this->character_data->inventory.enforce_stack_limits(stack_limits);
this->character_data->bank.enforce_stack_limits(stack_limits);
this->login->account->auto_reply_message = this->character_data->auto_reply.decode();
this->login->account->save();
@@ -876,7 +1020,7 @@ void Client::update_character_data_after_load(shared_ptr<PSOBBCharacterFile> cha
charfile->import_tethealla_material_usage(this->require_server_state()->level_table(this->version()));
uint8_t lang = this->language();
player_data_log.info_f("Overriding language fields in save files with {:02X} ({})", lang, char_for_language_code(lang));
this->log.info_f("Overriding language fields in save files with {:02X} ({})", lang, char_for_language_code(lang));
charfile->inventory.language = lang;
charfile->guild_card.language = lang;
}
@@ -891,70 +1035,9 @@ void Client::save_all() {
if (this->guild_card_data) {
this->save_guild_card_file();
}
if (this->external_bank) {
string filename = this->shared_bank_filename();
phosg::save_object_file<PlayerBank200>(filename, *this->external_bank);
player_data_log.info_f("Saved shared bank file {}", filename);
if (this->bank_data) {
this->save_bank_file();
}
if (this->external_bank_character) {
this->save_character_file(
this->character_filename(this->external_bank_character_index),
this->system_data,
this->external_bank_character);
}
}
void Client::save_system_file() const {
if (!this->system_data) {
throw logic_error("no system file loaded");
}
string filename = this->system_filename();
phosg::save_object_file(filename, *this->system_data);
player_data_log.info_f("Saved system file {}", filename);
}
void Client::save_character_file(
const string& filename,
shared_ptr<const PSOBBBaseSystemFile> system,
shared_ptr<const PSOBBCharacterFile> character) {
PSOCHARFile::save(filename, system, character);
player_data_log.info_f("Saved character file {}", filename);
}
void Client::save_ep3_character_file(
const string& filename,
const PSOGCEp3CharacterFile::Character& character) {
phosg::save_file(filename, &character, sizeof(character));
player_data_log.info_f("Saved Episode 3 character file {}", filename);
}
void Client::save_character_file() {
if (!this->system_data.get()) {
throw logic_error("no system file loaded");
}
if (!this->character_data.get()) {
throw logic_error("no character file loaded");
}
if (this->should_update_play_time) {
// This is slightly inaccurate, since fractions of a second are truncated
// off each time we save. I'm lazy, so insert shrug emoji here.
uint64_t t = phosg::now();
uint64_t seconds = (t - this->last_play_time_update) / 1000000;
this->character_data->play_time_seconds += seconds;
player_data_log.info_f("Added {} seconds to play time", seconds);
this->last_play_time_update = t;
}
this->save_character_file(this->character_filename(), this->system_data, this->character_data);
}
void Client::save_guild_card_file() const {
if (!this->guild_card_data.get()) {
throw logic_error("no Guild Card file loaded");
}
string filename = this->guild_card_filename();
phosg::save_object_file(filename, *this->guild_card_data);
player_data_log.info_f("Saved Guild Card file {}", filename);
}
void Client::load_backup_character(uint32_t account_id, size_t index) {
@@ -979,88 +1062,17 @@ void Client::save_and_unload_character() {
this->save_character_file();
this->character_data.reset();
this->log.info_f("Unloaded character");
}
}
PlayerBank200& Client::current_bank() {
if (this->external_bank) {
return *this->external_bank;
} else if (this->external_bank_character) {
return this->external_bank_character->bank;
}
return this->character()->bank;
}
const PlayerBank200& Client::current_bank() const {
return const_cast<Client*>(this)->current_bank();
}
std::shared_ptr<PSOBBCharacterFile> Client::current_bank_character() {
return this->external_bank_character ? this->external_bank_character : this->character();
}
void Client::use_default_bank() {
if (this->external_bank) {
string filename = this->shared_bank_filename();
phosg::save_object_file<PlayerBank200>(filename, *this->external_bank);
this->external_bank.reset();
player_data_log.info_f("Detached shared bank {}", filename);
}
if (this->external_bank_character) {
string filename = this->character_filename(this->external_bank_character_index);
this->save_character_file(filename, this->system_data, this->external_bank_character);
this->external_bank_character.reset();
player_data_log.info_f("Detached character {} from bank", filename);
}
}
bool Client::use_shared_bank() {
this->use_default_bank();
string filename = this->shared_bank_filename();
auto files_manager = this->require_server_state()->player_files_manager;
this->external_bank = files_manager->get_bank(filename);
if (this->external_bank) {
player_data_log.info_f("Using loaded shared bank {}", filename);
return true;
} else if (std::filesystem::is_regular_file(filename)) {
this->external_bank = make_shared<PlayerBank200>(phosg::load_object_file<PlayerBank200>(filename));
files_manager->set_bank(filename, this->external_bank);
player_data_log.info_f("Loaded shared bank {}", filename);
return true;
} else {
this->external_bank = make_shared<PlayerBank200>();
files_manager->set_bank(filename, this->external_bank);
player_data_log.info_f("Created shared bank for {}", filename);
return false;
}
}
void Client::use_character_bank(ssize_t index) {
this->use_default_bank();
if (index != this->bb_character_index) {
auto files_manager = this->require_server_state()->player_files_manager;
string filename = this->character_filename(index);
this->external_bank_character = files_manager->get_character(filename);
if (this->external_bank_character) {
this->external_bank_character_index = index;
player_data_log.info_f("Using loaded character file {} for external bank", filename);
} else if (std::filesystem::is_regular_file(filename)) {
this->external_bank_character = PSOCHARFile::load_shared(filename, false).character_file;
this->update_character_data_after_load(this->external_bank_character);
this->external_bank_character_index = index;
files_manager->set_character(filename, this->external_bank_character);
player_data_log.info_f("Loaded character data from {} for external bank", filename);
} else {
throw runtime_error("character does not exist");
if (this->bank_data) {
this->save_bank_file();
this->bank_data.reset();
this->log.info_f("Unloaded bank");
}
}
}
void Client::print_inventory() const {
auto s = this->require_server_state();
auto p = this->character();
auto p = this->character_file();
this->log.info_f("[PlayerInventory] Meseta: {}", p->disp.stats.meseta);
this->log.info_f("[PlayerInventory] {} items", p->inventory.num_items);
for (size_t x = 0; x < p->inventory.num_items; x++) {
@@ -1073,11 +1085,11 @@ void Client::print_inventory() const {
void Client::print_bank() const {
auto s = this->require_server_state();
auto bank = this->current_bank();
this->log.info_f("[PlayerBank] Meseta: {}", bank.meseta);
this->log.info_f("[PlayerBank] {} items", bank.num_items);
for (size_t x = 0; x < bank.num_items; x++) {
const auto& item = bank.items[x];
auto bank = this->bank_file();
this->log.info_f("[PlayerBank] Meseta: {}", bank->meseta);
this->log.info_f("[PlayerBank] {} items", bank->items.size());
for (size_t x = 0; x < bank->items.size(); x++) {
const auto& item = bank->items[x];
const char* present_token = item.present ? "" : " (missing present flag)";
auto hex = item.data.hex();
auto name = s->describe_item(this->version(), item.data);