rewrite $bbchar implementation
This commit is contained in:
@@ -274,7 +274,7 @@ Some commands only work on the game server and not on the proxy server. The chat
|
||||
* `$patch <name>`: Run a patch on your client. `<name>` must exactly match the name of a patch on the server.
|
||||
|
||||
* 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.
|
||||
* `$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. Note that the character's chat data, quick menu config, and bank contents are not copied, since there is no way for the server to request those types of data.
|
||||
* `$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.)
|
||||
|
||||
+1
-3
@@ -1029,14 +1029,12 @@ static void server_command_convert_char_to_bb(shared_ptr<Client> c, const std::s
|
||||
}
|
||||
|
||||
try {
|
||||
s->license_index->verify_bb(tokens[0].c_str(), tokens[1].c_str());
|
||||
c->pending_bb_save_license = s->license_index->verify_bb(tokens[0].c_str(), tokens[1].c_str());
|
||||
} catch (const exception& e) {
|
||||
send_text_message_printf(c, "$C6Login failed: %s", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
c->pending_bb_save_username = tokens[0];
|
||||
|
||||
// Request the player data. The client will respond with a 61, and the handler
|
||||
// for that command will execute the conversion
|
||||
send_get_player_info(c);
|
||||
|
||||
+1
-1
@@ -201,7 +201,7 @@ struct Client : public std::enable_shared_from_this<Client> {
|
||||
uint32_t next_exp_value; // next EXP value to give
|
||||
G_SwitchStateChanged_6x05 last_switch_enabled_command;
|
||||
bool can_chat;
|
||||
std::string pending_bb_save_username;
|
||||
std::shared_ptr<License> pending_bb_save_license;
|
||||
uint8_t pending_bb_save_character_index;
|
||||
std::deque<std::function<void(uint32_t, uint32_t)>> function_call_response_queue;
|
||||
|
||||
|
||||
+11
-1
@@ -302,6 +302,16 @@ string ClientGameData::system_filename() const {
|
||||
return string_printf("system/players/system_%s.psosys", this->bb_username.c_str());
|
||||
}
|
||||
|
||||
string ClientGameData::character_filename(const std::string& bb_username, int8_t index) {
|
||||
if (bb_username.empty()) {
|
||||
throw logic_error("non-BB players do not have character data");
|
||||
}
|
||||
if (index < 0) {
|
||||
throw logic_error("character index is not set");
|
||||
}
|
||||
return string_printf("system/players/player_%s_%hhd.psochar", bb_username.c_str(), index);
|
||||
}
|
||||
|
||||
string ClientGameData::character_filename(int8_t index) const {
|
||||
if (this->bb_username.empty()) {
|
||||
throw logic_error("non-BB players do not have character data");
|
||||
@@ -312,7 +322,7 @@ string ClientGameData::character_filename(int8_t index) const {
|
||||
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(), index);
|
||||
return this->character_filename(this->bb_username, index);
|
||||
}
|
||||
|
||||
string ClientGameData::guild_card_filename() const {
|
||||
|
||||
+9
-8
@@ -112,6 +112,15 @@ public:
|
||||
const PlayerDispDataBBPreview& preview,
|
||||
std::shared_ptr<const LevelTable> level_table);
|
||||
|
||||
std::string system_filename() const;
|
||||
static std::string character_filename(const std::string& bb_username, int8_t index);
|
||||
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;
|
||||
|
||||
void save_all();
|
||||
void save_system_file() const;
|
||||
static void save_character_file(
|
||||
@@ -147,12 +156,4 @@ private:
|
||||
void save_and_clear_external_bank();
|
||||
|
||||
void load_all_files();
|
||||
|
||||
std::string system_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;
|
||||
};
|
||||
|
||||
@@ -194,13 +194,6 @@ PlayerDispDataBBPreview PlayerDispDataBB::to_preview() const {
|
||||
return pre;
|
||||
}
|
||||
|
||||
void PlayerDispDataBB::apply_preview(const PlayerDispDataBBPreview& pre) {
|
||||
this->stats.level = pre.level;
|
||||
this->stats.experience = pre.experience;
|
||||
this->visual = pre.visual;
|
||||
this->name = pre.name;
|
||||
}
|
||||
|
||||
void PlayerDispDataBB::apply_dressing_room(const PlayerDispDataBBPreview& pre) {
|
||||
this->visual.name_color = pre.visual.name_color;
|
||||
this->visual.extra_model = pre.visual.extra_model;
|
||||
|
||||
@@ -112,7 +112,7 @@ struct PlayerDispDataBB;
|
||||
struct PlayerVisualConfig {
|
||||
/* 00 */ pstring<TextEncoding::ASCII, 0x10> name;
|
||||
/* 10 */ parray<uint8_t, 8> unknown_a2;
|
||||
/* 18 */ le_uint32_t name_color = 0x00000000; // ARGB
|
||||
/* 18 */ le_uint32_t name_color = 0xFFFFFFFF; // ARGB
|
||||
/* 1C */ uint8_t extra_model = 0;
|
||||
/* 1D */ parray<uint8_t, 0x0F> unused;
|
||||
// See compute_name_color_checksum for details on how this is computed. This
|
||||
|
||||
+33
-33
@@ -2973,42 +2973,42 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
|
||||
s->remove_client_from_lobby(c);
|
||||
|
||||
} else if (command == 0x61) {
|
||||
if (!c->pending_bb_save_username.empty()) {
|
||||
string prev_bb_username = c->game_data.get_bb_username();
|
||||
int8_t prev_bb_character_index = c->game_data.bb_character_index;
|
||||
if (c->pending_bb_save_license) {
|
||||
shared_ptr<License> dest_license = c->pending_bb_save_license;
|
||||
c->pending_bb_save_license.reset();
|
||||
|
||||
c->game_data.set_bb_username(c->pending_bb_save_username);
|
||||
c->game_data.bb_character_index = c->pending_bb_save_character_index;
|
||||
string filename = ClientGameData::character_filename(dest_license->bb_username, c->pending_bb_save_character_index);
|
||||
if (s->player_files_manager->get_character(filename)) {
|
||||
send_text_message(c, "$C6The target player\nis currently loaded.\nSign off in Blue\nBurst and try again.");
|
||||
|
||||
// Update a few fields for BB
|
||||
const auto& p = c->game_data.character();
|
||||
uint8_t prev_version = p->disp.visual.version;
|
||||
p->disp.visual.version = 4;
|
||||
uint32_t prev_name_color_checksum = p->disp.visual.name_color_checksum;
|
||||
p->disp.visual.name_color_checksum = 0x00000000;
|
||||
|
||||
bool failure = false;
|
||||
try {
|
||||
c->game_data.save_character_file();
|
||||
} catch (const exception& e) {
|
||||
send_text_message_printf(c, "$C6PSOBB player data could\nnot be saved:\n%s", e.what());
|
||||
failure = true;
|
||||
} else {
|
||||
auto bb_player = PSOBBCharacterFile::create_from_config(
|
||||
dest_license->serial_number,
|
||||
c->language(),
|
||||
player->disp.visual,
|
||||
player->disp.name.decode(c->language()),
|
||||
s->level_table);
|
||||
bb_player->disp.visual.version = 4;
|
||||
bb_player->disp.visual.name_color_checksum = 0x00000000;
|
||||
bb_player->inventory = player->inventory;
|
||||
bb_player->disp.stats.advance_to_level(bb_player->disp.visual.char_class, player->disp.stats.level, s->level_table);
|
||||
bb_player->disp.stats.experience = player->disp.stats.experience;
|
||||
bb_player->disp.technique_levels_v1 = player->disp.technique_levels_v1;
|
||||
bb_player->auto_reply = player->auto_reply;
|
||||
bb_player->info_board = player->info_board;
|
||||
bb_player->battle_records = player->battle_records;
|
||||
bb_player->challenge_records = player->challenge_records;
|
||||
bb_player->choice_search_config = player->choice_search_config;
|
||||
try {
|
||||
ClientGameData::save_character_file(filename, c->game_data.system(), bb_player);
|
||||
send_text_message_printf(c,
|
||||
"$C6BB player data saved\nas player %hhu for user\n%s",
|
||||
static_cast<uint8_t>(c->pending_bb_save_character_index + 1),
|
||||
dest_license->bb_username.c_str());
|
||||
} catch (const exception& e) {
|
||||
send_text_message_printf(c, "$C6PSOBB player data could\nnot be saved:\n%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
p->disp.visual.version = prev_version;
|
||||
p->disp.visual.name_color_checksum = prev_name_color_checksum;
|
||||
|
||||
if (!failure) {
|
||||
send_text_message_printf(c,
|
||||
"$C6BB player data saved\nas player %hhu for user\n%s",
|
||||
static_cast<uint8_t>(c->pending_bb_save_character_index + 1),
|
||||
c->pending_bb_save_username.c_str());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// We use 61 during the lobby server init sequence to trigger joining an
|
||||
|
||||
+17
-6
@@ -208,10 +208,11 @@ PSOBBBaseSystemFile::PSOBBBaseSystemFile() {
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
const PlayerDispDataBBPreview& preview,
|
||||
const PlayerVisualConfig& visual,
|
||||
const std::string& name,
|
||||
shared_ptr<const LevelTable> level_table) {
|
||||
static const array<array<PlayerInventoryItem, 5>, 12> initial_inventory{{
|
||||
{
|
||||
@@ -300,7 +301,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
},
|
||||
}};
|
||||
|
||||
array<uint8_t, 0xE8> config_hunter_ranger{
|
||||
static const array<uint8_t, 0xE8> config_hunter_ranger{
|
||||
{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
@@ -316,7 +317,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
array<uint8_t, 0xE8> config_force{
|
||||
static const array<uint8_t, 0xE8> config_force{
|
||||
{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
@@ -334,8 +335,10 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||
|
||||
auto ret = make_shared<PSOBBCharacterFile>();
|
||||
ret->disp.visual = visual;
|
||||
ret->disp.name.encode(name, language);
|
||||
|
||||
const auto& initial_items = initial_inventory.at(preview.visual.char_class);
|
||||
const auto& initial_items = initial_inventory.at(visual.char_class);
|
||||
ret->inventory.num_items = initial_items.size();
|
||||
for (size_t z = 0; z < initial_items.size(); z++) {
|
||||
ret->inventory.items[z] = initial_items[z];
|
||||
@@ -347,7 +350,6 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
ret->disp.config[z] = config[z];
|
||||
}
|
||||
|
||||
ret->disp.apply_preview(preview);
|
||||
ret->disp.stats.reset_to_base(ret->disp.visual.char_class, level_table);
|
||||
ret->disp.technique_levels_v1.clear(0xFF);
|
||||
if (ret->disp.visual.class_flags & 0x80) {
|
||||
@@ -369,6 +371,15 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
return ret;
|
||||
}
|
||||
|
||||
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
const PlayerDispDataBBPreview& preview,
|
||||
shared_ptr<const LevelTable> level_table) {
|
||||
return PSOBBCharacterFile::create_from_config(
|
||||
guild_card_number, language, preview.visual, preview.name.decode(language), level_table);
|
||||
}
|
||||
|
||||
PSOBBCharacterFile::SymbolChatEntry PSOBBCharacterFile::DefaultSymbolChatEntry::to_entry() const {
|
||||
SymbolChatEntry ret;
|
||||
ret.present = 1;
|
||||
|
||||
@@ -219,6 +219,12 @@ struct PSOBBCharacterFile {
|
||||
|
||||
PSOBBCharacterFile() = default;
|
||||
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_config(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
const PlayerVisualConfig& visual,
|
||||
const std::string& name,
|
||||
std::shared_ptr<const LevelTable> level_table);
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_preview(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
|
||||
Reference in New Issue
Block a user