allow multiple licenses per account

This commit is contained in:
Martin Michelsen
2024-04-12 18:35:48 -07:00
parent 40d5c6ee64
commit 34751f99e9
35 changed files with 2351 additions and 1723 deletions
+46 -63
View File
@@ -262,33 +262,16 @@ void Client::reschedule_ping_and_timeout_events() {
event_add(this->idle_timeout_event.get(), &idle_tv);
}
void Client::set_license(shared_ptr<License> l) {
if (this->version() == Version::BB_V4) {
// Make sure bb_username is filename-safe
for (char ch : l->bb_username) {
if (!isalnum(ch) && (ch != '-') && (ch != '_') && (ch != '@')) {
throw runtime_error("invalid characters in username");
}
}
}
this->license = l;
}
void Client::convert_license_to_temporary_if_nte() {
// If the session is a prototype version and the license was created and we
// should use a temporary license instead, delete the permanent license and
// replace it with a temporary license.
void Client::convert_account_to_temporary_if_nte() {
// If the session is a prototype version and the account was created and we
// should use a temporary account instead, delete the permanent account and
// replace it with a temporary account.
auto s = this->require_server_state();
if (s->use_temp_licenses_for_prototypes &&
this->config.check_flag(Client::Flag::LICENSE_WAS_CREATED) &&
is_any_nte(this->version())) {
this->log.info("Client is a prototype version and the license was created during this session; converting permanent license to temporary license");
s->license_index->remove(this->license->serial_number);
auto new_l = s->license_index->create_temporary_license();
this->license->delete_file();
*new_l = std::move(*this->license);
this->set_license(new_l);
this->config.clear_flag(Client::Flag::LICENSE_WAS_CREATED);
if (s->use_temp_accounts_for_prototypes && this->login->account_was_created && is_any_nte(this->version())) {
this->log.info("Client is a prototype version and the account was created during this session; converting permanent account to temporary account");
this->login->account->is_temporary = true;
this->login->account->delete_file();
this->login->account_was_created = false;
}
}
@@ -309,29 +292,29 @@ shared_ptr<Lobby> Client::require_lobby() const {
}
shared_ptr<const TeamIndex::Team> Client::team() const {
if (!this->license) {
throw logic_error("Client::team called on client with no license");
if (!this->login) {
throw logic_error("Client::team called on client with no account");
}
if (this->license->bb_team_id == 0) {
if (this->login->account->bb_team_id == 0) {
return nullptr;
}
auto p = this->character(false);
auto s = this->require_server_state();
auto team = s->team_index->get_by_id(this->license->bb_team_id);
auto team = s->team_index->get_by_id(this->login->account->bb_team_id);
if (!team) {
this->log.info("License contains a team ID, but the team does not exist; clearing team ID from license");
this->license->bb_team_id = 0;
this->license->save();
this->log.info("Account contains a team ID, but the team does not exist; clearing team ID from account");
this->login->account->bb_team_id = 0;
this->login->account->save();
return nullptr;
}
auto member_it = team->members.find(this->license->serial_number);
auto member_it = team->members.find(this->login->account->account_id);
if (member_it == team->members.end()) {
this->log.info("License contains a team ID, but the team does not contain this member; clearing team ID from license");
this->license->bb_team_id = 0;
this->license->save();
this->log.info("Account contains a team ID, but the team does not contain this member; clearing team ID from account");
this->login->account->bb_team_id = 0;
this->login->account->save();
return nullptr;
}
@@ -342,7 +325,7 @@ shared_ptr<const TeamIndex::Team> Client::team() const {
string name = p->disp.name.decode(this->language());
if (m.name != name) {
this->log.info("Updating player name in team config");
s->team_index->update_member_name(this->license->serial_number, name);
s->team_index->update_member_name(this->login->account->account_id, name);
}
}
@@ -356,7 +339,7 @@ bool Client::evaluate_quest_availability_expression(
uint8_t difficulty,
size_t num_players,
bool v1_present) const {
if (this->license && this->license->check_flag(License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
if (this->login && this->login->account->check_flag(Account::Flag::DISABLE_QUEST_REQUIREMENTS)) {
return true;
}
if (!expr) {
@@ -403,10 +386,10 @@ bool Client::can_play_quest(
}
bool Client::can_use_chat_commands() const {
if (!this->license) {
if (!this->login) {
return false;
}
if (this->license->check_flag(License::Flag::ALWAYS_ENABLE_CHAT_COMMANDS)) {
if (this->login->account->check_flag(Account::Flag::ALWAYS_ENABLE_CHAT_COMMANDS)) {
return true;
}
return this->require_server_state()->enable_chat_commands;
@@ -622,10 +605,10 @@ string Client::system_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have system data");
}
if (!this->license) {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return string_printf("system/players/system_%s.psosys", this->license->bb_username.c_str());
return string_printf("system/players/system_%s.psosys", this->login->bb_license->username.c_str());
}
string Client::character_filename(const std::string& bb_username, int8_t index) {
@@ -638,55 +621,55 @@ string Client::character_filename(const std::string& bb_username, int8_t index)
return string_printf("system/players/player_%s_%hhd.psochar", bb_username.c_str(), index);
}
string Client::backup_character_filename(uint32_t serial_number, size_t index) {
return string_printf("system/players/backup_player_%" PRIu32 "_%zu.psochar", serial_number, index);
string Client::backup_character_filename(uint32_t account_id, size_t index) {
return string_printf("system/players/backup_player_%" PRIu32 "_%zu.psochar", account_id, index);
}
string Client::character_filename(int8_t index) const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
}
if (!this->license) {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return this->character_filename(this->license->bb_username, (index < 0) ? this->bb_character_index : index);
return this->character_filename(this->login->bb_license->username, (index < 0) ? this->bb_character_index : index);
}
string Client::guild_card_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
}
if (!this->license) {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return string_printf("system/players/guild_cards_%s.psocard", this->license->bb_username.c_str());
return string_printf("system/players/guild_cards_%s.psocard", this->login->bb_license->username.c_str());
}
string Client::shared_bank_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
}
if (!this->license) {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return string_printf("system/players/shared_bank_%s.psobank", this->license->bb_username.c_str());
return string_printf("system/players/shared_bank_%s.psobank", this->login->bb_license->username.c_str());
}
string Client::legacy_account_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
}
if (!this->license) {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return string_printf("system/players/account_%s.nsa", this->license->bb_username.c_str());
return string_printf("system/players/account_%s.nsa", this->login->bb_license->username.c_str());
}
string Client::legacy_player_filename() const {
if (this->version() != Version::BB_V4) {
throw logic_error("non-BB players do not have character data");
}
if (!this->license) {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
if (this->bb_character_index < 0) {
@@ -694,7 +677,7 @@ string Client::legacy_player_filename() const {
}
return string_printf(
"system/players/player_%s_%hhd.nsc",
this->license->bb_username.c_str(),
this->login->bb_license->username.c_str(),
static_cast<int8_t>(this->bb_character_index + 1));
}
@@ -714,7 +697,7 @@ void Client::load_all_files() {
this->guild_card_data = make_shared<PSOBBGuildCardFile>();
return;
}
if (!this->license) {
if (!this->login || !this->login->bb_license) {
throw logic_error("cannot load BB player data until client is logged in");
}
@@ -835,7 +818,7 @@ void Client::load_all_files() {
this->character_data->quest_flags = nsc_data.quest_flags;
this->character_data->death_count = nsc_data.death_count;
this->character_data->bank = nsc_data.bank;
this->character_data->guild_card.guild_card_number = this->license->serial_number;
this->character_data->guild_card.guild_card_number = this->login->account->account_id;
this->character_data->guild_card.name = nsc_data.disp.name;
this->character_data->guild_card.description = nsc_data.guild_card_description;
this->character_data->guild_card.present = 1;
@@ -869,8 +852,8 @@ void Client::load_all_files() {
if (this->character_data) {
// Clear legacy play_time field
this->character_data->disp.name.clear_after_bytes(0x18);
this->license->auto_reply_message = this->character_data->auto_reply.decode();
this->license->save();
this->login->account->auto_reply_message = this->character_data->auto_reply.decode();
this->login->account->save();
this->last_play_time_update = now();
}
}
@@ -917,10 +900,10 @@ void Client::save_character_file(
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 Client to depend on License, which
// file here, but that would cause Client to depend on Account, 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.
// the membership struct from the team ID in the Account 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
@@ -960,8 +943,8 @@ void Client::save_guild_card_file() const {
player_data_log.info("Saved Guild Card file %s", filename.c_str());
}
void Client::load_backup_character(uint32_t serial_number, size_t index) {
string filename = this->backup_character_filename(serial_number, index);
void Client::load_backup_character(uint32_t account_id, size_t index) {
string filename = this->backup_character_filename(account_id, index);
auto f = fopen_unique(filename, "rb");
auto header = freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {