complete implementation of $checkchar and make slot count configurable; closes #645
This commit is contained in:
+84
-30
@@ -24,6 +24,41 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Tools
|
||||
|
||||
string str_for_flag_ranges(const vector<bool>& flags) {
|
||||
string ret;
|
||||
auto add_result = [&](size_t start, size_t end) {
|
||||
if (!ret.empty()) {
|
||||
ret.push_back(',');
|
||||
}
|
||||
if (start == end) {
|
||||
ret += std::format("{}", start);
|
||||
} else if (start == end - 1) {
|
||||
ret += std::format("{},{}", start, end);
|
||||
} else {
|
||||
ret += std::format("{}-{}", start, end);
|
||||
}
|
||||
};
|
||||
|
||||
size_t range_start = 0;
|
||||
bool in_range = false;
|
||||
for (size_t z = 0; z < flags.size(); z++) {
|
||||
if (flags[z] && !in_range) {
|
||||
in_range = true;
|
||||
range_start = z;
|
||||
} else if (!flags[z] && in_range) {
|
||||
in_range = false;
|
||||
add_result(range_start, z - 1);
|
||||
}
|
||||
}
|
||||
if (in_range) {
|
||||
add_result(range_start, flags.size() - 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Checks
|
||||
|
||||
@@ -421,7 +456,7 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
|
||||
|
||||
shared_ptr<Account> dest_account;
|
||||
shared_ptr<BBLicense> dest_bb_license;
|
||||
ssize_t dest_character_index = 0;
|
||||
size_t dest_character_index = 0;
|
||||
if (is_bb_conversion) {
|
||||
vector<string> tokens = phosg::split(a.text, ' ');
|
||||
if (tokens.size() != 3) {
|
||||
@@ -430,8 +465,8 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
|
||||
|
||||
// username/password are tokens[0] and [1]
|
||||
dest_character_index = stoll(tokens[2]) - 1;
|
||||
if ((dest_character_index > 3) || (dest_character_index < 0)) {
|
||||
throw precondition_failed("$C6Player index must\nbe in range 1-4");
|
||||
if ((dest_character_index >= 127) || (dest_character_index < 0)) {
|
||||
throw precondition_failed("$C6Player index must\nbe in range 1-127");
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -444,8 +479,8 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
|
||||
|
||||
} else {
|
||||
dest_character_index = stoll(a.text) - 1;
|
||||
if ((dest_character_index > 15) || (dest_character_index < 0)) {
|
||||
throw precondition_failed("$C6Player index must\nbe in range 1-16");
|
||||
if ((dest_character_index >= s->num_backup_character_slots) || (dest_character_index < 0)) {
|
||||
throw precondition_failed("$C6Player index must\nbe in range 1-{}", s->num_backup_character_slots);
|
||||
}
|
||||
dest_account = a.c->login->account;
|
||||
}
|
||||
@@ -574,30 +609,49 @@ ChatCommandDefinition cc_checkchar(
|
||||
throw precondition_failed("$C7This command cannot\nbe used on a shared\naccount");
|
||||
}
|
||||
|
||||
size_t index = stoull(a.text, nullptr, 0) - 1;
|
||||
if (index > 15) {
|
||||
throw precondition_failed("$C6Player index must\nbe in range 1-16");
|
||||
}
|
||||
auto s = a.c->require_server_state();
|
||||
|
||||
try {
|
||||
if (is_ep3(a.c->version())) {
|
||||
string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, true);
|
||||
auto ch = phosg::load_object_file<PSOGCEp3CharacterFile::Character>(filename);
|
||||
send_text_message_fmt(a.c, "Slot {}: $C6{}$C7\n{} {}\nCLv: on {}.{}, off {}.{}",
|
||||
index + 1, ch.disp.visual.name.decode(),
|
||||
name_for_section_id(ch.disp.visual.section_id), name_for_char_class(ch.disp.visual.char_class),
|
||||
(ch.ep3_config.online_clv_exp / 100) + 1, ch.ep3_config.online_clv_exp % 100,
|
||||
(ch.ep3_config.offline_clv_exp / 100) + 1, ch.ep3_config.offline_clv_exp % 100);
|
||||
} else {
|
||||
string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, false);
|
||||
auto ch = PSOCHARFile::load_shared(filename, false).character_file;
|
||||
send_text_message_fmt(a.c, "Slot {}: $C6{}$C7\n{} {}\nLevel {}",
|
||||
index + 1, ch->disp.name.decode(),
|
||||
name_for_section_id(ch->disp.visual.section_id), name_for_char_class(ch->disp.visual.char_class),
|
||||
ch->disp.stats.level + 1);
|
||||
if (a.text.empty()) {
|
||||
bool is_ep3 = ::is_ep3(a.c->version());
|
||||
|
||||
vector<bool> flags;
|
||||
flags.emplace_back(false);
|
||||
for (size_t z = 0; z < s->num_backup_character_slots; z++) {
|
||||
string filename = a.c->backup_character_filename(a.c->login->account->account_id, z, is_ep3);
|
||||
flags.emplace_back(std::filesystem::is_regular_file(filename));
|
||||
}
|
||||
string used_str = str_for_flag_ranges(flags);
|
||||
flags.flip();
|
||||
flags[0] = false;
|
||||
string free_str = str_for_flag_ranges(flags);
|
||||
send_text_message_fmt(a.c, "Used: {}\nFree: {}", used_str, free_str);
|
||||
|
||||
} else {
|
||||
size_t index = stoull(a.text, nullptr, 0) - 1;
|
||||
if (index >= s->num_backup_character_slots) {
|
||||
throw precondition_failed("$C6Player index must\nbe in range 1-{}", s->num_backup_character_slots);
|
||||
}
|
||||
|
||||
try {
|
||||
if (is_ep3(a.c->version())) {
|
||||
string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, true);
|
||||
auto ch = phosg::load_object_file<PSOGCEp3CharacterFile::Character>(filename);
|
||||
send_text_message_fmt(a.c, "Slot {}: $C6{}$C7\n{} {}\nCLv: on {}.{}, off {}.{}",
|
||||
index + 1, ch.disp.visual.name.decode(),
|
||||
name_for_section_id(ch.disp.visual.section_id), name_for_char_class(ch.disp.visual.char_class),
|
||||
(ch.ep3_config.online_clv_exp / 100) + 1, ch.ep3_config.online_clv_exp % 100,
|
||||
(ch.ep3_config.offline_clv_exp / 100) + 1, ch.ep3_config.offline_clv_exp % 100);
|
||||
} else {
|
||||
string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, false);
|
||||
auto ch = PSOCHARFile::load_shared(filename, false).character_file;
|
||||
send_text_message_fmt(a.c, "Slot {}: $C6{}$C7\n{} {}\nLevel {}",
|
||||
index + 1, ch->disp.name.decode(),
|
||||
name_for_section_id(ch->disp.visual.section_id), name_for_char_class(ch->disp.visual.char_class),
|
||||
ch->disp.stats.level + 1);
|
||||
}
|
||||
} catch (const phosg::cannot_open_file&) {
|
||||
send_text_message_fmt(a.c, "No character in\nslot {}", index + 1);
|
||||
}
|
||||
} catch (const phosg::cannot_open_file&) {
|
||||
send_text_message_fmt(a.c, "No character in\nslot {}", index + 1);
|
||||
}
|
||||
|
||||
co_return;
|
||||
@@ -1508,11 +1562,12 @@ ChatCommandDefinition cc_loadchar(
|
||||
throw precondition_failed("$C7This command cannot\nbe used on a shared\naccount");
|
||||
}
|
||||
|
||||
auto s = a.c->require_server_state();
|
||||
auto l = a.c->require_lobby();
|
||||
|
||||
size_t index = stoull(a.text, nullptr, 0) - 1;
|
||||
if (index > 15) {
|
||||
throw precondition_failed("$C6Player index must\nbe in range 1-16");
|
||||
if (index >= s->num_backup_character_slots) {
|
||||
throw precondition_failed("$C6Player index must\nbe in range 1-{}", s->num_backup_character_slots);
|
||||
}
|
||||
|
||||
shared_ptr<PSOGCEp3CharacterFile::Character> ep3_char;
|
||||
@@ -1524,7 +1579,6 @@ ChatCommandDefinition cc_loadchar(
|
||||
|
||||
if (a.c->version() == Version::BB_V4) {
|
||||
// On BB, it suffices to simply send the character file again
|
||||
auto s = a.c->require_server_state();
|
||||
send_complete_player_bb(a.c);
|
||||
send_player_leave_notification(l, a.c->lobby_client_id);
|
||||
s->send_lobby_join_notifications(l, a.c);
|
||||
|
||||
@@ -1095,6 +1095,7 @@ void ServerState::load_config_early() {
|
||||
}
|
||||
|
||||
this->enable_chat_commands = this->config_json->get_bool("EnableChatCommands", true);
|
||||
this->num_backup_character_slots = this->config_json->get_int("BackupCharacterSlots", 16);
|
||||
|
||||
this->version_name_colors.reset();
|
||||
this->client_customization_name_color = 0;
|
||||
|
||||
@@ -115,6 +115,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
bool use_temp_accounts_for_prototypes = true;
|
||||
std::array<uint16_t, NUM_VERSIONS> compatibility_groups = {};
|
||||
bool enable_chat_commands = true;
|
||||
size_t num_backup_character_slots = 16;
|
||||
std::unique_ptr<std::array<uint32_t, NUM_NON_PATCH_VERSIONS>> version_name_colors;
|
||||
uint32_t client_customization_name_color = 0x00000000;
|
||||
uint8_t allowed_drop_modes_v1_v2_normal = 0x1F;
|
||||
|
||||
Reference in New Issue
Block a user