From 6291e42ba990a925623415b70e82c92485c73b12 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Fri, 5 Dec 2025 19:53:09 -0800 Subject: [PATCH] make 04E8 handler match the client's logic --- src/Client.cc | 1 + src/ReceiveCommands.cc | 57 +++++++++++++++++++++--------------------- src/SaveFileFormats.cc | 36 ++++++++++++++++++++++++++ src/SaveFileFormats.hh | 1 + 4 files changed, 66 insertions(+), 29 deletions(-) diff --git a/src/Client.cc b/src/Client.cc index cd8d9e23..1d4838a0 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -913,6 +913,7 @@ void Client::load_all_files() { string card_filename = this->guild_card_filename(); if (std::filesystem::is_regular_file(card_filename)) { this->guild_card_data = make_shared(phosg::load_object_file(card_filename)); + this->guild_card_data->delete_duplicates(); this->log.info_f("Loaded Guild Card data from {}", card_filename); } else { this->log.info_f("Guild Card file is missing: {}", card_filename); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index b2b1d22d..3bdc8bab 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -3809,10 +3809,8 @@ static asio::awaitable on_E8_BB(shared_ptr c, Channel::Message& ms case 0x01E8: { // Check guild card file checksum const auto& cmd = check_size_t(msg.data); uint32_t checksum = gcf->checksum(); - c->log.info_f("(Guild card file) Server checksum = {:08X}, client checksum = {:08X}", - checksum, cmd.checksum); - S_GuildCardChecksumResponse_BB_02E8 response = { - (cmd.checksum != checksum), 0}; + c->log.info_f("(Guild card file) Server checksum = {:08X}, client checksum = {:08X}", checksum, cmd.checksum); + S_GuildCardChecksumResponse_BB_02E8 response = {(cmd.checksum != checksum), 0}; send_command_t(c, 0x02E8, 0x00000000, response); break; } @@ -3820,13 +3818,14 @@ static asio::awaitable on_E8_BB(shared_ptr c, Channel::Message& ms check_size_v(msg.data.size(), 0); send_guild_card_header_bb(c); break; - case 0x04E8: { // Add guild card + case 0x04E8: { // Add or replace guild card auto& new_gc = check_size_t(msg.data); for (size_t z = 0; z < max_count; z++) { - if (!gcf->entries[z].data.present) { - gcf->entries[z].data = new_gc; - gcf->entries[z].unknown_a1.clear(0); - c->log.info_f("Added guild card {} at position {}", new_gc.guild_card_number, z); + auto& gcf_entry = gcf->entries[z]; + if (!gcf_entry.data.present || (gcf_entry.data.guild_card_number == new_gc.guild_card_number)) { + gcf_entry.data = new_gc; + gcf_entry.unknown_a1.clear(0); + c->log.info_f("Added or replaced guild card {} at position {}", new_gc.guild_card_number, z); should_save = true; break; } @@ -3836,10 +3835,11 @@ static asio::awaitable on_E8_BB(shared_ptr c, Channel::Message& ms case 0x05E8: { // Delete guild card auto& cmd = check_size_t(msg.data); for (size_t z = 0; z < max_count; z++) { - if (gcf->entries[z].data.guild_card_number == cmd.guild_card_number) { + auto& gcf_entry = gcf->entries[z]; + if (gcf_entry.data.guild_card_number == cmd.guild_card_number) { c->log.info_f("Deleted guild card {} at position {}", cmd.guild_card_number, z); for (z = 0; z < max_count - 1; z++) { - gcf->entries[z] = gcf->entries[z + 1]; + gcf_entry = gcf->entries[z + 1]; } gcf->entries[max_count - 1].clear(); should_save = true; @@ -3851,8 +3851,9 @@ static asio::awaitable on_E8_BB(shared_ptr c, Channel::Message& ms case 0x06E8: { // Update guild card auto& new_gc = check_size_t(msg.data); for (size_t z = 0; z < max_count; z++) { - if (gcf->entries[z].data.guild_card_number == new_gc.guild_card_number) { - gcf->entries[z].data = new_gc; + auto& gcf_entry = gcf->entries[z]; + if (gcf_entry.data.guild_card_number == new_gc.guild_card_number) { + gcf_entry.data = new_gc; c->log.info_f("Updated guild card {} at position {}", new_gc.guild_card_number, z); should_save = true; } @@ -3866,11 +3867,10 @@ static asio::awaitable on_E8_BB(shared_ptr c, Channel::Message& ms case 0x07E8: { // Add blocked user auto& new_gc = check_size_t(msg.data); for (size_t z = 0; z < max_blocked; z++) { - if (!gcf->blocked[z].present) { - gcf->blocked[z] = new_gc; + auto& gcf_blocked = gcf->blocked[z]; + if (!gcf_blocked.present) { + gcf_blocked = new_gc; c->log.info_f("Added blocked guild card {} at position {}", new_gc.guild_card_number, z); - // Note: The client also sends a C6 command, so we don't have to - // manually sync the actual blocked senders list here should_save = true; break; } @@ -3880,15 +3880,13 @@ static asio::awaitable on_E8_BB(shared_ptr c, Channel::Message& ms case 0x08E8: { // Delete blocked user auto& cmd = check_size_t(msg.data); for (size_t z = 0; z < max_blocked; z++) { - if (gcf->blocked[z].guild_card_number == cmd.guild_card_number) { - c->log.info_f("Deleted blocked guild card {} at position {}", - cmd.guild_card_number, z); + auto& gcf_blocked = gcf->blocked[z]; + if (gcf_blocked.guild_card_number == cmd.guild_card_number) { + c->log.info_f("Deleted blocked guild card {} at position {}", cmd.guild_card_number, z); for (z = 0; z < max_blocked - 1; z++) { - gcf->blocked[z] = gcf->blocked[z + 1]; + gcf_blocked = gcf->blocked[z + 1]; } gcf->blocked[max_blocked - 1].clear(); - // Note: The client also sends a C6 command, so we don't have to - // manually sync the actual blocked senders list here should_save = true; break; } @@ -3898,8 +3896,9 @@ static asio::awaitable on_E8_BB(shared_ptr c, Channel::Message& ms case 0x09E8: { // Write comment auto& cmd = check_size_t(msg.data); for (size_t z = 0; z < max_count; z++) { - if (gcf->entries[z].data.guild_card_number == cmd.guild_card_number) { - gcf->entries[z].comment = cmd.comment; + auto& gcf_entry = gcf->entries[z]; + if (gcf_entry.data.guild_card_number == cmd.guild_card_number) { + gcf_entry.comment = cmd.comment; c->log.info_f("Updated comment on guild card {} at position {}", cmd.guild_card_number, z); should_save = true; break; @@ -3912,14 +3911,15 @@ static asio::awaitable on_E8_BB(shared_ptr c, Channel::Message& ms size_t index1 = max_count; size_t index2 = max_count; for (size_t z = 0; z < max_count; z++) { - if (gcf->entries[z].data.guild_card_number == cmd.guild_card_number1) { + auto& gcf_entry = gcf->entries[z]; + if (gcf_entry.data.guild_card_number == cmd.guild_card_number1) { if (index1 >= max_count) { index1 = z; } else { throw runtime_error("guild card 1 appears multiple times in file"); } } - if (gcf->entries[z].data.guild_card_number == cmd.guild_card_number2) { + if (gcf_entry.data.guild_card_number == cmd.guild_card_number2) { if (index2 >= max_count) { index2 = z; } else { @@ -3935,8 +3935,7 @@ static asio::awaitable on_E8_BB(shared_ptr c, Channel::Message& ms PSOBBGuildCardFile::Entry displaced_entry = gcf->entries[index1]; gcf->entries[index1] = gcf->entries[index2]; gcf->entries[index2] = displaced_entry; - c->log.info_f("Swapped positions of guild cards {} and {}", - cmd.guild_card_number1, cmd.guild_card_number2); + c->log.info_f("Swapped positions of guild cards {} and {}", cmd.guild_card_number1, cmd.guild_card_number2); should_save = true; } break; diff --git a/src/SaveFileFormats.cc b/src/SaveFileFormats.cc index 67553ea0..ea6c493e 100644 --- a/src/SaveFileFormats.cc +++ b/src/SaveFileFormats.cc @@ -327,6 +327,42 @@ uint32_t PSOBBGuildCardFile::checksum() const { return phosg::crc32(this, sizeof(*this)); } +void PSOBBGuildCardFile::delete_duplicates() { + { + unordered_set seen; + size_t read_index = 0, write_index = 0; + for (read_index = 0; read_index < this->blocked.size(); read_index++) { + const auto& read_blocked = this->blocked[read_index]; + if (seen.emplace(read_blocked.guild_card_number).second) { + if (write_index != read_index) { + this->blocked[write_index] = read_blocked; + } + write_index++; + } + } + for (; write_index < this->blocked.size(); write_index++) { + this->blocked[write_index].clear(); + } + } + + { + unordered_set seen; + size_t read_index = 0, write_index = 0; + for (read_index = 0; read_index < this->entries.size(); read_index++) { + const auto& read_entry = this->entries[read_index]; + if (seen.emplace(read_entry.data.guild_card_number).second) { + if (write_index != read_index) { + this->entries[write_index] = read_entry; + } + write_index++; + } + } + for (; write_index < this->entries.size(); write_index++) { + this->entries[write_index].clear(); + } + } +} + PSOBBBaseSystemFile::PSOBBBaseSystemFile() { // This field is based on 1/1/2000, not 1/1/1970, so adjust appropriately this->creation_timestamp = (phosg::now() - 946684800000000ULL) / 1000000; diff --git a/src/SaveFileFormats.hh b/src/SaveFileFormats.hh index ca5482b6..2417efbf 100644 --- a/src/SaveFileFormats.hh +++ b/src/SaveFileFormats.hh @@ -1045,6 +1045,7 @@ struct PSOBBGuildCardFile { PSOBBGuildCardFile() = default; uint32_t checksum() const; + void delete_duplicates(); } __packed_ws__(PSOBBGuildCardFile, 0xD590); ////////////////////////////////////////////////////////////////////////////////