implement shared bank
This commit is contained in:
@@ -276,6 +276,7 @@ Some commands only work on the game server and not on the proxy server. The chat
|
||||
* Blue Burst player commands (game server only)
|
||||
* `$bbchar <username> <password> <1-4>`: Use this command when playing on a non-BB version of PSO. If the username and password are correct, this command converts your current character to BB format and saves it on the server in the given slot. Any character already in that slot is overwritten.
|
||||
* `$edit <stat> <value>`: Modifies your character data. If the server does not allow cheat mode anywhere (that is, "CheatModeBehavior" is "Off" in config.json), this command does nothing.
|
||||
* `$bank [number]`: Switches your current bank, so you can access your other character's banks (if `number` is 1-4) or your shared account bank (if `number` is 0). If `number` is not given, switches back to your current character's bank.
|
||||
* `$save`: Saves your character, system, and Guild Card data immediately. (By default, your character is saved every 60 seconds while online, and your account and Guild Card data are saved whenever they change.)
|
||||
|
||||
* Game state commands (game server only)
|
||||
|
||||
+30
-19
@@ -980,11 +980,33 @@ static void server_command_edit(shared_ptr<Client> c, const std::string& args) {
|
||||
s->send_lobby_join_notifications(l, c);
|
||||
}
|
||||
|
||||
// TODO: implement this (and make sure the bank name is filesystem-safe)
|
||||
/* static void server_command_change_bank(shared_ptr<Client> c, const std::string&) {
|
||||
static void server_command_change_bank(shared_ptr<Client> c, const std::string& args) {
|
||||
check_version(c, Version::BB_V4);
|
||||
...
|
||||
} */
|
||||
|
||||
if (c->config.check_flag(Client::Flag::AT_BANK_COUNTER)) {
|
||||
throw runtime_error("cannot change banks while at the bank counter");
|
||||
}
|
||||
|
||||
ssize_t new_char_index = args.empty() ? (c->game_data.bb_character_index + 1) : stol(args, nullptr, 0);
|
||||
|
||||
if (new_char_index == 0) {
|
||||
if (c->game_data.use_shared_bank()) {
|
||||
send_text_message_printf(c, "$C6Using shared bank (0)");
|
||||
} else {
|
||||
send_text_message_printf(c, "$C6Created shared bank (0)");
|
||||
}
|
||||
} else if (new_char_index <= 4) {
|
||||
c->game_data.use_character_bank(new_char_index - 1);
|
||||
auto bp = c->game_data.current_bank_character();
|
||||
auto name = bp->disp.name.decode(c->language());
|
||||
send_text_message_printf(c, "$C6Using %s\'s bank (%zu)", name.c_str(), new_char_index);
|
||||
} else {
|
||||
throw runtime_error("invalid bank number");
|
||||
}
|
||||
|
||||
const auto& bank = c->game_data.current_bank();
|
||||
send_text_message_printf(c, "%" PRIu32 " items\n%" PRIu32 " Meseta", bank.num_items.load(), bank.meseta.load());
|
||||
}
|
||||
|
||||
// TODO: This can be implemented on the proxy server too.
|
||||
static void server_command_convert_char_to_bb(shared_ptr<Client> c, const std::string& args) {
|
||||
@@ -1023,22 +1045,10 @@ static void server_command_convert_char_to_bb(shared_ptr<Client> c, const std::s
|
||||
static void server_command_save(shared_ptr<Client> c, const std::string&) {
|
||||
check_version(c, Version::BB_V4);
|
||||
try {
|
||||
c->game_data.save_character_file();
|
||||
send_text_message(c, "Character data saved");
|
||||
c->game_data.save_all();
|
||||
send_text_message(c, "All data saved");
|
||||
} catch (const exception& e) {
|
||||
send_text_message_printf(c, "Can\'t save character:\n%s", e.what());
|
||||
}
|
||||
try {
|
||||
c->game_data.save_system_file();
|
||||
send_text_message(c, "System data saved");
|
||||
} catch (const exception& e) {
|
||||
send_text_message_printf(c, "Can\'t save system data:\n%s", e.what());
|
||||
}
|
||||
try {
|
||||
c->game_data.save_guild_card_file();
|
||||
send_text_message(c, "Guild Card data saved");
|
||||
} catch (const exception& e) {
|
||||
send_text_message_printf(c, "Can\'t save Guild Cards:\n%s", e.what());
|
||||
send_text_message_printf(c, "Can\'t save data:\n%s", e.what());
|
||||
}
|
||||
c->reschedule_save_game_data_event();
|
||||
}
|
||||
@@ -1677,6 +1687,7 @@ static const unordered_map<string, ChatCommandDefinition> chat_commands({
|
||||
{"$auction", {server_command_auction, proxy_command_auction}},
|
||||
{"$ax", {server_command_ax, nullptr}},
|
||||
{"$ban", {server_command_ban, nullptr}},
|
||||
{"$bank", {server_command_change_bank, nullptr}},
|
||||
{"$bbchar", {server_command_convert_char_to_bb, nullptr}},
|
||||
{"$cheat", {server_command_cheat, nullptr}},
|
||||
{"$debug", {server_command_debug, nullptr}},
|
||||
|
||||
+2
-2
@@ -226,7 +226,7 @@ void Client::set_license(shared_ptr<License> l) {
|
||||
this->license = l;
|
||||
this->game_data.guild_card_number = this->license->serial_number;
|
||||
if (this->version() == Version::BB_V4) {
|
||||
this->game_data.bb_username = this->license->bb_username;
|
||||
this->game_data.set_bb_username(this->license->bb_username);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ void Client::save_game_data() {
|
||||
throw logic_error("save_game_data called for non-BB client");
|
||||
}
|
||||
if (this->game_data.character(false)) {
|
||||
this->game_data.save_character_file();
|
||||
this->game_data.save_all();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+172
-27
@@ -66,19 +66,35 @@ std::shared_ptr<PSOBBGuildCardFile> PlayerFilesManager::get_guild_card(const std
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<PlayerBank> PlayerFilesManager::get_bank(const std::string& filename) {
|
||||
try {
|
||||
return this->loaded_bank_files.at(filename);
|
||||
} catch (const out_of_range&) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerFilesManager::set_system(const std::string& filename, std::shared_ptr<PSOBBBaseSystemFile> file) {
|
||||
if (!this->loaded_system_files.emplace(filename, file).second) {
|
||||
throw runtime_error("Guild Card file already loaded");
|
||||
throw runtime_error("Guild Card file already loaded: " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerFilesManager::set_character(const std::string& filename, std::shared_ptr<PSOBBCharacterFile> file) {
|
||||
if (!this->loaded_character_files.emplace(filename, file).second) {
|
||||
throw runtime_error("character file already loaded");
|
||||
throw runtime_error("character file already loaded: " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerFilesManager::set_guild_card(const std::string& filename, std::shared_ptr<PSOBBGuildCardFile> file) {
|
||||
if (!this->loaded_guild_card_files.emplace(filename, file).second) {
|
||||
throw runtime_error("Guild Card file already loaded");
|
||||
throw runtime_error("Guild Card file already loaded: " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerFilesManager::set_bank(const std::string& filename, std::shared_ptr<PlayerBank> file) {
|
||||
if (!this->loaded_bank_files.emplace(filename, file).second) {
|
||||
throw runtime_error("bank file already loaded: " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +112,10 @@ void PlayerFilesManager::clear_expired_files(evutil_socket_t, short, void* ctx)
|
||||
if (num_deleted) {
|
||||
player_data_log.info("Cleared %zu expired Guild Card file(s)", num_deleted);
|
||||
}
|
||||
num_deleted = erase_unused(self->loaded_bank_files);
|
||||
if (num_deleted) {
|
||||
player_data_log.info("Cleared %zu expired bank file(s)", num_deleted);
|
||||
}
|
||||
}
|
||||
|
||||
ClientGameData::ClientGameData(std::shared_ptr<PlayerFilesManager> files_manager)
|
||||
@@ -111,10 +131,24 @@ ClientGameData::ClientGameData(std::shared_ptr<PlayerFilesManager> files_manager
|
||||
|
||||
ClientGameData::~ClientGameData() {
|
||||
if (!this->bb_username.empty() && this->character_data.get()) {
|
||||
this->save_character_file();
|
||||
this->save_all();
|
||||
}
|
||||
}
|
||||
|
||||
const string& ClientGameData::get_bb_username() const {
|
||||
return this->bb_username;
|
||||
}
|
||||
|
||||
void ClientGameData::set_bb_username(const string& bb_username) {
|
||||
// Make sure bb_username is filename-safe
|
||||
for (char ch : bb_username) {
|
||||
if (!isalnum(ch) && (ch != '-') && (ch != '_')) {
|
||||
throw runtime_error("invalid characters in username");
|
||||
}
|
||||
}
|
||||
this->bb_username = bb_username;
|
||||
}
|
||||
|
||||
void ClientGameData::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));
|
||||
|
||||
@@ -268,14 +302,17 @@ string ClientGameData::system_filename() const {
|
||||
return string_printf("system/players/system_%s.psosys", this->bb_username.c_str());
|
||||
}
|
||||
|
||||
string ClientGameData::character_filename() const {
|
||||
string ClientGameData::character_filename(int8_t index) const {
|
||||
if (this->bb_username.empty()) {
|
||||
throw logic_error("non-BB players do not have character data");
|
||||
}
|
||||
if (this->bb_character_index < 0) {
|
||||
if (index < 0) {
|
||||
index = this->bb_character_index;
|
||||
}
|
||||
if (index < 0) {
|
||||
throw logic_error("character index is not set");
|
||||
}
|
||||
return string_printf("system/players/player_%s_%hhd.psochar", this->bb_username.c_str(), this->bb_character_index);
|
||||
return string_printf("system/players/player_%s_%hhd.psochar", this->bb_username.c_str(), index);
|
||||
}
|
||||
|
||||
string ClientGameData::guild_card_filename() const {
|
||||
@@ -285,6 +322,13 @@ string ClientGameData::guild_card_filename() const {
|
||||
return string_printf("system/players/guild_cards_%s.psocard", this->bb_username.c_str());
|
||||
}
|
||||
|
||||
string ClientGameData::shared_bank_filename() const {
|
||||
if (this->bb_username.empty()) {
|
||||
throw logic_error("non-BB players do not have shared bank files");
|
||||
}
|
||||
return string_printf("system/players/shared_bank_%s.psobank", this->bb_username.c_str());
|
||||
}
|
||||
|
||||
string ClientGameData::legacy_account_filename() const {
|
||||
if (this->bb_username.empty()) {
|
||||
throw logic_error("non-BB players do not have legacy account data");
|
||||
@@ -356,7 +400,7 @@ void ClientGameData::load_all_files() {
|
||||
throw runtime_error("incorrect flag in character file header");
|
||||
}
|
||||
this->character_data = make_shared<PSOBBCharacterFile>(freadx<PSOBBCharacterFile>(f.get()));
|
||||
this->files_manager->set_character(this->character_filename(), this->character_data);
|
||||
this->files_manager->set_character(char_filename, this->character_data);
|
||||
player_data_log.info("Loaded character data from %s", char_filename.c_str());
|
||||
|
||||
// If there was no .psosys file, load the system file from the .psochar
|
||||
@@ -473,6 +517,29 @@ void ClientGameData::load_all_files() {
|
||||
}
|
||||
}
|
||||
|
||||
void ClientGameData::save_all() {
|
||||
if (this->system_data) {
|
||||
this->save_system_file();
|
||||
}
|
||||
if (this->character_data) {
|
||||
this->save_character_file();
|
||||
}
|
||||
if (this->guild_card_data) {
|
||||
this->save_guild_card_file();
|
||||
}
|
||||
if (this->external_bank) {
|
||||
string filename = this->shared_bank_filename();
|
||||
save_object_file<PlayerBank>(filename, *this->external_bank);
|
||||
player_data_log.info("Saved shared bank file %s", filename.c_str());
|
||||
}
|
||||
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 ClientGameData::save_system_file() const {
|
||||
if (!this->system_data) {
|
||||
throw logic_error("no system file loaded");
|
||||
@@ -482,6 +549,30 @@ void ClientGameData::save_system_file() const {
|
||||
player_data_log.info("Saved system file %s", filename.c_str());
|
||||
}
|
||||
|
||||
void ClientGameData::save_character_file(
|
||||
const string& filename,
|
||||
shared_ptr<const PSOBBBaseSystemFile> system,
|
||||
shared_ptr<const PSOBBCharacterFile> character) {
|
||||
auto f = fopen_unique(filename, "wb");
|
||||
PSOCommandHeaderBB header = {sizeof(PSOCommandHeaderBB) + sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBTeamMembership), 0x00E7, 0x00000000};
|
||||
fwritex(f.get(), header);
|
||||
fwritex(f.get(), *character);
|
||||
fwritex(f.get(), *system);
|
||||
// TODO: Technically, we should write the actual team membership struct to the
|
||||
// file here, but that would cause ClientGameData to depend on License, which
|
||||
// it currently does not. This data doesn't matter at all for correctness
|
||||
// within newserv, since it ignores this data entirely and instead generates
|
||||
// the membership struct from the team ID in the License and the team's state.
|
||||
// So, writing correct data here would mostly be for compatibility with other
|
||||
// PSO servers. But if the other server is newserv, then this data would be
|
||||
// used anyway, and if it's not, then it would presumably have a different set
|
||||
// of teams with a different set of team IDs anyway, so the membership struct
|
||||
// here would be useless either way.
|
||||
static const PSOBBTeamMembership empty_membership;
|
||||
fwritex(f.get(), empty_membership);
|
||||
player_data_log.info("Saved character file %s", filename.c_str());
|
||||
}
|
||||
|
||||
void ClientGameData::save_character_file() {
|
||||
if (!this->system_data.get()) {
|
||||
throw logic_error("no system file loaded");
|
||||
@@ -500,25 +591,7 @@ void ClientGameData::save_character_file() {
|
||||
this->last_play_time_update = t;
|
||||
}
|
||||
|
||||
string filename = this->character_filename();
|
||||
auto f = fopen_unique(filename, "wb");
|
||||
PSOCommandHeaderBB header = {sizeof(PSOCommandHeaderBB) + sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBTeamMembership), 0x00E7, 0x00000000};
|
||||
fwritex(f.get(), header);
|
||||
fwritex(f.get(), *this->character_data);
|
||||
fwritex(f.get(), *this->system_data);
|
||||
// TODO: Technically, we should write the actual team membership struct to the
|
||||
// file here, but that would cause ClientGameData to depend on License, which
|
||||
// it currently does not. This data doesn't matter at all for correctness
|
||||
// within newserv, since it ignores this data entirely and instead generates
|
||||
// the membership struct from the team ID in the License and the team's state.
|
||||
// So, writing correct data here would mostly be for compatibility with other
|
||||
// PSO servers. But if the other server is newserv, then this data would be
|
||||
// used anyway, and if it's not, then it would presumably have a different set
|
||||
// of teams with a different set of team IDs anyway, so the membership struct
|
||||
// here would be useless either way.
|
||||
static const PSOBBTeamMembership empty_membership;
|
||||
fwritex(f.get(), empty_membership);
|
||||
player_data_log.info("Saved character file %s", filename.c_str());
|
||||
this->save_character_file(this->character_filename(), this->system_data, this->character_data);
|
||||
}
|
||||
|
||||
void ClientGameData::save_guild_card_file() const {
|
||||
@@ -529,3 +602,75 @@ void ClientGameData::save_guild_card_file() const {
|
||||
save_object_file(filename, *this->guild_card_data);
|
||||
player_data_log.info("Saved Guild Card file %s", filename.c_str());
|
||||
}
|
||||
|
||||
PlayerBank& ClientGameData::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;
|
||||
}
|
||||
|
||||
std::shared_ptr<PSOBBCharacterFile> ClientGameData::current_bank_character() {
|
||||
return this->external_bank_character ? this->external_bank_character : this->character();
|
||||
}
|
||||
|
||||
void ClientGameData::use_default_bank() {
|
||||
if (this->external_bank) {
|
||||
string filename = this->shared_bank_filename();
|
||||
save_object_file<PlayerBank>(filename, *this->external_bank);
|
||||
this->external_bank.reset();
|
||||
player_data_log.info("Detached shared bank %s", filename.c_str());
|
||||
}
|
||||
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("Detached character %s from bank", filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool ClientGameData::use_shared_bank() {
|
||||
this->use_default_bank();
|
||||
string filename = this->shared_bank_filename();
|
||||
if (isfile(filename)) {
|
||||
this->external_bank = make_shared<PlayerBank>(load_object_file<PlayerBank>(filename));
|
||||
player_data_log.info("Loaded shared bank %s", filename.c_str());
|
||||
return true;
|
||||
} else {
|
||||
this->external_bank = make_shared<PlayerBank>();
|
||||
player_data_log.info("Created shared bank for %s", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientGameData::use_character_bank(int8_t index) {
|
||||
this->use_default_bank();
|
||||
if (index != this->bb_character_index) {
|
||||
string filename = this->character_filename(index);
|
||||
this->external_bank_character = this->files_manager->get_character(filename);
|
||||
if (this->external_bank_character) {
|
||||
this->external_bank_character_index = index;
|
||||
player_data_log.info("Using loaded character file %s for external bank", filename.c_str());
|
||||
} else if (isfile(filename)) {
|
||||
auto f = fopen_unique(filename, "rb");
|
||||
auto header = freadx<PSOCommandHeaderBB>(f.get());
|
||||
if (header.size != 0x399C) {
|
||||
throw runtime_error("incorrect size in character file header");
|
||||
}
|
||||
if (header.command != 0x00E7) {
|
||||
throw runtime_error("incorrect command in character file header");
|
||||
}
|
||||
if (header.flag != 0x00000000) {
|
||||
throw runtime_error("incorrect flag in character file header");
|
||||
}
|
||||
this->external_bank_character = make_shared<PSOBBCharacterFile>(freadx<PSOBBCharacterFile>(f.get()));
|
||||
this->external_bank_character_index = index;
|
||||
this->files_manager->set_character(filename, this->external_bank_character);
|
||||
player_data_log.info("Loaded character data from %s for external bank", filename.c_str());
|
||||
} else {
|
||||
throw runtime_error("character does not exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-2
@@ -39,10 +39,12 @@ public:
|
||||
std::shared_ptr<PSOBBBaseSystemFile> get_system(const std::string& filename);
|
||||
std::shared_ptr<PSOBBCharacterFile> get_character(const std::string& filename);
|
||||
std::shared_ptr<PSOBBGuildCardFile> get_guild_card(const std::string& filename);
|
||||
std::shared_ptr<PlayerBank> get_bank(const std::string& filename);
|
||||
|
||||
void set_system(const std::string& filename, std::shared_ptr<PSOBBBaseSystemFile> file);
|
||||
void set_character(const std::string& filename, std::shared_ptr<PSOBBCharacterFile> file);
|
||||
void set_guild_card(const std::string& filename, std::shared_ptr<PSOBBGuildCardFile> file);
|
||||
void set_bank(const std::string& filename, std::shared_ptr<PlayerBank> file);
|
||||
|
||||
private:
|
||||
std::shared_ptr<struct event_base> base;
|
||||
@@ -51,6 +53,7 @@ private:
|
||||
std::unordered_map<std::string, std::shared_ptr<PSOBBBaseSystemFile>> loaded_system_files;
|
||||
std::unordered_map<std::string, std::shared_ptr<PSOBBCharacterFile>> loaded_character_files;
|
||||
std::unordered_map<std::string, std::shared_ptr<PSOBBGuildCardFile>> loaded_guild_card_files;
|
||||
std::unordered_map<std::string, std::shared_ptr<PlayerBank>> loaded_bank_files;
|
||||
|
||||
static void clear_expired_files(evutil_socket_t fd, short events, void* ctx);
|
||||
};
|
||||
@@ -75,7 +78,6 @@ public:
|
||||
std::shared_ptr<Episode3::PlayerConfig> ep3_config;
|
||||
|
||||
// These are only used if the client is BB
|
||||
std::string bb_username;
|
||||
int8_t bb_character_index;
|
||||
ItemData identify_result;
|
||||
std::array<std::vector<ItemData>, 3> shop_contents;
|
||||
@@ -83,6 +85,9 @@ public:
|
||||
explicit ClientGameData(std::shared_ptr<PlayerFilesManager> files_manager);
|
||||
~ClientGameData();
|
||||
|
||||
const std::string& get_bb_username() const;
|
||||
void set_bb_username(const std::string& bb_username);
|
||||
|
||||
void create_battle_overlay(std::shared_ptr<const BattleRules> rules, std::shared_ptr<const LevelTable> level_table);
|
||||
void create_challenge_overlay(Version version, size_t template_index, std::shared_ptr<const LevelTable> level_table);
|
||||
inline void delete_overlay() {
|
||||
@@ -107,12 +112,24 @@ public:
|
||||
const PlayerDispDataBBPreview& preview,
|
||||
std::shared_ptr<const LevelTable> level_table);
|
||||
|
||||
void save_all();
|
||||
void save_system_file() const;
|
||||
static void save_character_file(
|
||||
const std::string& filename,
|
||||
std::shared_ptr<const PSOBBBaseSystemFile> sys,
|
||||
std::shared_ptr<const PSOBBCharacterFile> character);
|
||||
// Note: This function is not const because it updates the player's play time.
|
||||
void save_character_file();
|
||||
void save_guild_card_file() const;
|
||||
|
||||
PlayerBank& current_bank();
|
||||
std::shared_ptr<PSOBBCharacterFile> current_bank_character();
|
||||
bool use_shared_bank(); // Returns true if the bank exists; false if it was created
|
||||
void use_character_bank(int8_t bb_character_index);
|
||||
void use_default_bank();
|
||||
|
||||
private:
|
||||
std::string bb_username;
|
||||
std::shared_ptr<PlayerFilesManager> files_manager;
|
||||
|
||||
// The overlay character data is used in battle and challenge modes, when
|
||||
@@ -122,13 +139,19 @@ private:
|
||||
std::shared_ptr<PSOBBCharacterFile> overlay_character_data;
|
||||
std::shared_ptr<PSOBBCharacterFile> character_data;
|
||||
std::shared_ptr<PSOBBGuildCardFile> guild_card_data;
|
||||
std::shared_ptr<PlayerBank> external_bank;
|
||||
std::shared_ptr<PSOBBCharacterFile> external_bank_character;
|
||||
int8_t external_bank_character_index;
|
||||
uint64_t last_play_time_update;
|
||||
|
||||
void save_and_clear_external_bank();
|
||||
|
||||
void load_all_files();
|
||||
|
||||
std::string system_filename() const;
|
||||
std::string character_filename() const;
|
||||
std::string character_filename(int8_t index = -1) const;
|
||||
std::string guild_card_filename() const;
|
||||
std::string shared_bank_filename() const;
|
||||
|
||||
std::string legacy_player_filename() const;
|
||||
std::string legacy_account_filename() const;
|
||||
|
||||
@@ -2974,10 +2974,10 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
|
||||
|
||||
} else if (command == 0x61) {
|
||||
if (!c->pending_bb_save_username.empty()) {
|
||||
string prev_bb_username = c->game_data.bb_username;
|
||||
string prev_bb_username = c->game_data.get_bb_username();
|
||||
int8_t prev_bb_character_index = c->game_data.bb_character_index;
|
||||
|
||||
c->game_data.bb_username = c->pending_bb_save_username;
|
||||
c->game_data.set_bb_username(c->pending_bb_save_username);
|
||||
c->game_data.bb_character_index = c->pending_bb_save_character_index;
|
||||
|
||||
// Update a few fields for BB
|
||||
@@ -3005,7 +3005,7 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
|
||||
c->pending_bb_save_username.c_str());
|
||||
}
|
||||
|
||||
c->game_data.bb_username = prev_bb_username;
|
||||
c->game_data.set_bb_username(prev_bb_username);
|
||||
c->game_data.bb_character_index = prev_bb_character_index;
|
||||
|
||||
c->pending_bb_save_username.clear();
|
||||
@@ -3117,7 +3117,7 @@ static void on_E3_BB(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
|
||||
ClientGameData temp_gd(s->player_files_manager);
|
||||
temp_gd.guild_card_number = c->license->serial_number;
|
||||
temp_gd.bb_username = c->license->bb_username;
|
||||
temp_gd.set_bb_username(c->license->bb_username);
|
||||
temp_gd.bb_character_index = cmd.character_index;
|
||||
|
||||
try {
|
||||
@@ -4789,7 +4789,7 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
|
||||
}
|
||||
}
|
||||
if (!reward.reward_item.empty()) {
|
||||
c->game_data.character()->bank.add_item(reward.reward_item);
|
||||
c->game_data.current_bank().add_item(reward.reward_item);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
+11
-11
@@ -1453,24 +1453,25 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
|
||||
}
|
||||
|
||||
auto p = c->game_data.character();
|
||||
auto& bank = c->game_data.current_bank();
|
||||
if (cmd.action == 0) { // Deposit
|
||||
if (cmd.item_id == 0xFFFFFFFF) { // Deposit Meseta
|
||||
if (cmd.meseta_amount > p->disp.stats.meseta) {
|
||||
l->log.info("Player %hu attempted to deposit %" PRIu32 " Meseta in the bank, but has only %" PRIu32 " Meseta on hand",
|
||||
c->lobby_client_id, cmd.meseta_amount.load(), p->disp.stats.meseta.load());
|
||||
} else if ((p->bank.meseta + cmd.meseta_amount) > 999999) {
|
||||
} else if ((bank.meseta + cmd.meseta_amount) > 999999) {
|
||||
l->log.info("Player %hu attempted to deposit %" PRIu32 " Meseta in the bank, but already has %" PRIu32 " Meseta in the bank",
|
||||
c->lobby_client_id, cmd.meseta_amount.load(), p->disp.stats.meseta.load());
|
||||
} else {
|
||||
p->bank.meseta += cmd.meseta_amount;
|
||||
bank.meseta += cmd.meseta_amount;
|
||||
p->disp.stats.meseta -= cmd.meseta_amount;
|
||||
l->log.info("Player %hu deposited %" PRIu32 " Meseta in the bank (bank now has %" PRIu32 "; inventory now has %" PRIu32 ")",
|
||||
c->lobby_client_id, cmd.meseta_amount.load(), p->bank.meseta.load(), p->disp.stats.meseta.load());
|
||||
c->lobby_client_id, cmd.meseta_amount.load(), bank.meseta.load(), p->disp.stats.meseta.load());
|
||||
}
|
||||
|
||||
} else { // Deposit item
|
||||
auto item = p->remove_item(cmd.item_id, cmd.item_amount, c->version() != Version::BB_V4);
|
||||
p->bank.add_item(item);
|
||||
bank.add_item(item);
|
||||
send_destroy_item(c, cmd.item_id, cmd.item_amount);
|
||||
|
||||
string name = s->item_name_index->describe_item(Version::BB_V4, item);
|
||||
@@ -1481,21 +1482,21 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
|
||||
|
||||
} else if (cmd.action == 1) { // Take
|
||||
if (cmd.item_index == 0xFFFF) { // Take Meseta
|
||||
if (cmd.meseta_amount > p->bank.meseta) {
|
||||
if (cmd.meseta_amount > bank.meseta) {
|
||||
l->log.info("Player %hu attempted to withdraw %" PRIu32 " Meseta from the bank, but has only %" PRIu32 " Meseta in the bank",
|
||||
c->lobby_client_id, cmd.meseta_amount.load(), p->bank.meseta.load());
|
||||
c->lobby_client_id, cmd.meseta_amount.load(), bank.meseta.load());
|
||||
} else if ((p->disp.stats.meseta + cmd.meseta_amount) > 999999) {
|
||||
l->log.info("Player %hu attempted to withdraw %" PRIu32 " Meseta from the bank, but already has %" PRIu32 " Meseta on hand",
|
||||
c->lobby_client_id, cmd.meseta_amount.load(), p->disp.stats.meseta.load());
|
||||
} else {
|
||||
p->bank.meseta -= cmd.meseta_amount;
|
||||
bank.meseta -= cmd.meseta_amount;
|
||||
p->disp.stats.meseta += cmd.meseta_amount;
|
||||
l->log.info("Player %hu withdrew %" PRIu32 " Meseta from the bank (bank now has %" PRIu32 "; inventory now has %" PRIu32 ")",
|
||||
c->lobby_client_id, cmd.meseta_amount.load(), p->bank.meseta.load(), p->disp.stats.meseta.load());
|
||||
c->lobby_client_id, cmd.meseta_amount.load(), bank.meseta.load(), p->disp.stats.meseta.load());
|
||||
}
|
||||
|
||||
} else { // Take item
|
||||
auto item = p->bank.remove_item_by_index(cmd.item_index, cmd.item_amount);
|
||||
auto item = bank.remove_item_by_index(cmd.item_index, cmd.item_amount);
|
||||
item.id = l->generate_item_id(c->lobby_client_id);
|
||||
p->add_item(item);
|
||||
send_create_inventory_item(c, item);
|
||||
@@ -2160,9 +2161,8 @@ void on_transfer_item_via_mail_message_bb(shared_ptr<Client> c, uint8_t command,
|
||||
(target_c->version() == Version::BB_V4) &&
|
||||
(target_c->game_data.character(false) != nullptr) &&
|
||||
!target_c->config.check_flag(Client::Flag::AT_BANK_COUNTER)) {
|
||||
auto target_p = target_c->game_data.character(false);
|
||||
try {
|
||||
target_p->bank.add_item(item);
|
||||
target_c->game_data.current_bank().add_item(item);
|
||||
item_sent = true;
|
||||
} catch (const runtime_error&) {
|
||||
}
|
||||
|
||||
+6
-5
@@ -2357,14 +2357,15 @@ void send_bank(shared_ptr<Client> c) {
|
||||
}
|
||||
|
||||
auto p = c->game_data.character();
|
||||
const auto* items_it = p->bank.items.data();
|
||||
vector<PlayerBankItem> items(items_it, items_it + p->bank.num_items);
|
||||
const auto& bank = c->game_data.current_bank();
|
||||
const auto* items_it = bank.items.data();
|
||||
vector<PlayerBankItem> items(items_it, items_it + bank.num_items);
|
||||
|
||||
G_BankContentsHeader_BB_6xBC cmd = {
|
||||
{{0xBC, 0, 0}, sizeof(G_BankContentsHeader_BB_6xBC) + items.size() * sizeof(PlayerBankItem)},
|
||||
random_object<uint32_t>(),
|
||||
p->bank.num_items,
|
||||
p->bank.meseta};
|
||||
bank.num_items,
|
||||
bank.meseta};
|
||||
|
||||
send_command_t_vt(c, 0x6C, 0x00, cmd, items);
|
||||
}
|
||||
@@ -3459,7 +3460,7 @@ void send_team_reward_list(std::shared_ptr<Client> c, bool show_purchased) {
|
||||
}
|
||||
auto s = c->require_server_state();
|
||||
|
||||
bool show_item_rewards = show_purchased || (c->game_data.character()->bank.num_items < 200);
|
||||
bool show_item_rewards = show_purchased || (c->game_data.current_bank().num_items < 200);
|
||||
|
||||
vector<S_TeamRewardList_BB_19EA_1AEA::Entry> entries;
|
||||
for (const auto& reward : s->team_index->reward_definitions()) {
|
||||
|
||||
Reference in New Issue
Block a user