From 317c9fd616567f0028eefa6a066dfe64b7102a7a Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Thu, 7 Dec 2023 12:45:56 -0800 Subject: [PATCH] implement Simple Mail auto-reply when recipient is offline --- src/Client.cc | 15 +++++++++++---- src/Client.hh | 2 +- src/License.cc | 2 ++ src/License.hh | 1 + src/ReceiveCommands.cc | 42 +++++++++++++++++++++++++++++++++++++----- 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/Client.cc b/src/Client.cc index 1d95b43f..8f007015 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -759,6 +759,11 @@ void Client::load_all_files() { } } + if (this->character_data) { + this->license->auto_reply_message = this->character_data->auto_reply.decode(); + this->license->save(); + } + 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) { @@ -874,10 +879,12 @@ void Client::load_backup_character(uint32_t serial_number, size_t index) { this->v1_v2_last_reported_disp.reset(); } -void Client::unload_character() { - this->save_character_file(); - this->character_data.reset(); - this->log.info("Unloaded character"); +void Client::save_and_unload_character() { + if (this->character_data) { + this->save_character_file(); + this->character_data.reset(); + this->log.info("Unloaded character"); + } } PlayerBank& Client::current_bank() { diff --git a/src/Client.hh b/src/Client.hh index 61b10392..0dc2259b 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -319,7 +319,7 @@ public: void save_guild_card_file() const; void load_backup_character(uint32_t serial_number, size_t index); - void unload_character(); + void save_and_unload_character(); PlayerBank& current_bank(); std::shared_ptr current_bank_character(); diff --git a/src/License.cc b/src/License.cc index 11908e58..3a8a975a 100644 --- a/src/License.cc +++ b/src/License.cc @@ -26,6 +26,7 @@ License::License(const JSON& json) this->bb_password = json.get_string("BBPassword", ""); this->flags = json.get_int("Flags", 0); this->ban_end_time = json.get_int("BanEndTime", 0); + this->last_player_name = json.get_string("LastPlayerName", ""); this->auto_reply_message = json.get_string("AutoReplyMessage", ""); this->ep3_current_meseta = json.get_int("Ep3CurrentMeseta", 0); this->ep3_total_meseta_earned = json.get_int("Ep3TotalMesetaEarned", 0); @@ -44,6 +45,7 @@ JSON License::json() const { {"BBPassword", this->bb_password}, {"Flags", this->flags}, {"BanEndTime", this->ban_end_time}, + {"LastPlayerName", this->last_player_name}, {"AutoReplyMessage", this->auto_reply_message}, {"Ep3CurrentMeseta", this->ep3_current_meseta}, {"Ep3TotalMesetaEarned", this->ep3_total_meseta_earned}, diff --git a/src/License.hh b/src/License.hh index a23ff3e3..82412bf8 100644 --- a/src/License.hh +++ b/src/License.hh @@ -43,6 +43,7 @@ struct License { uint32_t flags = 0; uint64_t ban_end_time = 0; // 0 = not banned + std::string last_player_name; std::string auto_reply_message; uint32_t ep3_current_meseta = 0; diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index bb666d09..7572403c 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -2804,6 +2804,7 @@ static void on_61_98(shared_ptr c, uint16_t command, uint32_t flag, stri c->v1_v2_last_reported_disp = make_unique(cmd.disp); player->inventory = cmd.inventory; player->disp = cmd.disp.to_bb(player->inventory.language, player->inventory.language); + c->license->last_player_name = player->disp.name.decode(player->inventory.language); break; } case Version::DC_V2: { @@ -2814,6 +2815,7 @@ static void on_61_98(shared_ptr c, uint16_t command, uint32_t flag, stri player->battle_records = cmd.records.battle; player->challenge_records = cmd.records.challenge; player->choice_search_config = cmd.choice_search_config; + c->license->last_player_name = player->disp.name.decode(player->inventory.language); break; } case Version::PC_V2: { @@ -2832,9 +2834,12 @@ static void on_61_98(shared_ptr c, uint16_t command, uint32_t flag, stri auto_reply.push_back(0); } player->auto_reply.encode(tt_utf16_to_utf8(auto_reply), player->inventory.language); + c->license->auto_reply_message = auto_reply; } else { player->auto_reply.clear(); + c->license->auto_reply_message.clear(); } + c->license->last_player_name = player->disp.name.decode(player->inventory.language); break; } case Version::GC_NTE: { @@ -2853,9 +2858,12 @@ static void on_61_98(shared_ptr c, uint16_t command, uint32_t flag, stri strip_trailing_zeroes(auto_reply); string encoded = tt_decode_marked(auto_reply, player->inventory.language, false); player->auto_reply.encode(encoded, player->inventory.language); + c->license->auto_reply_message = auto_reply; } else { player->auto_reply.clear(); + c->license->auto_reply_message.clear(); } + c->license->last_player_name = player->disp.name.decode(player->inventory.language); break; } @@ -2926,9 +2934,12 @@ static void on_61_98(shared_ptr c, uint16_t command, uint32_t flag, stri strip_trailing_zeroes(auto_reply); string encoded = tt_decode_marked(auto_reply, player->inventory.language, false); player->auto_reply.encode(encoded, player->inventory.language); + c->license->auto_reply_message = auto_reply; } else { player->auto_reply.clear(); + c->license->auto_reply_message.clear(); } + c->license->last_player_name = player->disp.name.decode(player->inventory.language); break; } case Version::BB_V4: { @@ -2947,9 +2958,12 @@ static void on_61_98(shared_ptr c, uint16_t command, uint32_t flag, stri auto_reply.push_back(0); } player->auto_reply.encode(tt_utf16_to_utf8(auto_reply), player->inventory.language); + c->license->auto_reply_message = auto_reply; } else { player->auto_reply.clear(); + c->license->auto_reply_message.clear(); } + c->license->last_player_name = player->disp.name.decode(player->inventory.language); break; } default: @@ -2957,6 +2971,7 @@ static void on_61_98(shared_ptr c, uint16_t command, uint32_t flag, stri } player->inventory.decode_from_client(c); c->channel.language = player->inventory.language; + c->license->save(); string name_str = player->disp.name.decode(c->language()); if ((name_str.size() > 2) && (name_str[0] == '\t') && ((name_str[1] == 'E') || (name_str[1] == 'J'))) { @@ -3116,8 +3131,8 @@ static void on_E0_BB(shared_ptr c, uint16_t, uint32_t, string& data) { static void on_E3_BB(shared_ptr c, uint16_t, uint32_t, string& data) { const auto& cmd = check_size_t(data); + c->save_and_unload_character(); c->bb_character_index = cmd.character_index; - c->unload_character(); if (c->bb_connection_phase != 0x00) { send_approve_player_choice_bb(c); @@ -3653,16 +3668,27 @@ static void on_81(shared_ptr c, uint16_t, uint32_t, string& data) { throw logic_error("invalid game version"); } + auto s = c->require_server_state(); shared_ptr target; try { - target = c->require_server_state()->find_client(nullptr, to_guild_card_number); + target = s->find_client(nullptr, to_guild_card_number); } catch (const out_of_range&) { } if (!target) { // TODO: We should store pending messages for accounts somewhere, and send - // them when the player signs on again. We should also persist the player's - // autoreply setting when they're offline and use it if appropriate here. + // them when the player signs on again. + try { + auto target_license = s->license_index->get(to_guild_card_number); + if (!target_license->auto_reply_message.empty()) { + send_simple_mail( + c, + target_license->serial_number, + target_license->last_player_name, + target_license->auto_reply_message); + } + } catch (const LicenseIndex::missing_license&) { + } send_text_message(c, "$C6Player is offline"); } else { @@ -3711,12 +3737,18 @@ void on_C7(shared_ptr c, uint16_t, uint32_t, string& data) { if (is_w && (data.size() & 1)) { data.push_back(0); } - c->character(true, false)->auto_reply.encode(tt_decode_marked(data, c->language(), is_w), c->language()); + + string message = tt_decode_marked(data, c->language(), is_w); + c->character(true, false)->auto_reply.encode(message, c->language()); + c->license->auto_reply_message = message; + c->license->save(); } static void on_C8(shared_ptr c, uint16_t, uint32_t, string& data) { check_size_v(data.size(), 0); c->character(true, false)->auto_reply.clear(); + c->license->auto_reply_message.clear(); + c->license->save(); } static void on_C6(shared_ptr c, uint16_t, uint32_t, string& data) {