From 7e9c6c185adb395c998f7be53c8e87c7890ab707 Mon Sep 17 00:00:00 2001 From: James Osborne Date: Mon, 25 May 2026 18:13:50 -0400 Subject: [PATCH 1/6] Add opt-in room crossplay compatibility --- src/ReceiveCommands.cc | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 129808f3..90767743 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -4920,6 +4920,31 @@ static asio::awaitable on_C9_XB(shared_ptr c, Channel::Message& ms co_return; } +static uint16_t safe_default_compatibility_group_for_version(Version v) { + static_assert(NUM_VERSIONS == 14, "Don't forget to update the safe default compatibility groups"); + static const array groups = {{ + 0x0000, // PC_PATCH + 0x0000, // BB_PATCH + 0x0004, // DC_NTE compatible only with itself + 0x0008, // DC_11_2000 compatible only with itself + 0x00B0, // DC_V1 compatible with DC_V1, DC_V2, and PC_V2 + 0x00B0, // DC_V2 compatible with DC_V1, DC_V2, and PC_V2 + 0x0040, // PC_NTE compatible only with itself + 0x00B0, // PC_V2 compatible with DC_V1, DC_V2, and PC_V2 + 0x0100, // GC_NTE compatible only with itself + 0x1200, // GC_V3 compatible with GC_V3 and XB_V3 + 0x0400, // GC_EP3_NTE compatible only with itself + 0x0800, // GC_EP3 compatible only with itself + 0x1200, // XB_V3 compatible with GC_V3 and XB_V3 + 0x2000, // BB_V4 compatible only with itself + }}; + return groups.at(static_cast(v)); +} + +static bool game_name_enables_full_crossplay(const string& name) { + return !name.empty() && ((name[0] == 'x') || (name[0] == 'X')); +} + shared_ptr create_game_generic( shared_ptr s, shared_ptr creator_c, @@ -4960,7 +4985,11 @@ shared_ptr create_game_generic( game->episode = episode; game->mode = mode; game->difficulty = difficulty; - game->allowed_versions = s->compatibility_groups.at(static_cast(creator_c->version())); + if (game_name_enables_full_crossplay(name)) { + game->allowed_versions = s->compatibility_groups.at(static_cast(creator_c->version())); + } else { + game->allowed_versions = safe_default_compatibility_group_for_version(creator_c->version()); + } static_assert(NUM_VERSIONS == 14, "Don't forget to update the group compatibility restrictions"); if (!allow_v1 || (difficulty == Difficulty::ULTIMATE) || (mode == GameMode::CHALLENGE) || (mode == GameMode::SOLO)) { game->forbid_version(Version::DC_NTE); From fa543b842ee7c6767e5458cd5774f4e08758f3d1 Mon Sep 17 00:00:00 2001 From: James Osborne Date: Mon, 25 May 2026 18:23:42 -0400 Subject: [PATCH 2/6] Log room crossplay opt-in decisions --- src/ReceiveCommands.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 90767743..657cf8bb 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -4985,11 +4985,19 @@ shared_ptr create_game_generic( game->episode = episode; game->mode = mode; game->difficulty = difficulty; - if (game_name_enables_full_crossplay(name)) { + bool full_crossplay_enabled = game_name_enables_full_crossplay(name); + if (full_crossplay_enabled) { game->allowed_versions = s->compatibility_groups.at(static_cast(creator_c->version())); } else { game->allowed_versions = safe_default_compatibility_group_for_version(creator_c->version()); } + + game->log.info_f( + "PSO Peeps crossplay opt-in: name=[{}] creator_version={} full_crossplay_enabled={} allowed_versions={:04X}", + name, + static_cast(creator_c->version()), + full_crossplay_enabled, + game->allowed_versions); static_assert(NUM_VERSIONS == 14, "Don't forget to update the group compatibility restrictions"); if (!allow_v1 || (difficulty == Difficulty::ULTIMATE) || (mode == GameMode::CHALLENGE) || (mode == GameMode::SOLO)) { game->forbid_version(Version::DC_NTE); From bf52bfb291b1618dba0bfe5f6ee2f99990a63bd6 Mon Sep 17 00:00:00 2001 From: James Osborne Date: Mon, 25 May 2026 18:37:30 -0400 Subject: [PATCH 3/6] Recognize BB x-prefixed crossplay rooms --- src/ReceiveCommands.cc | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 657cf8bb..889c6a23 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -4941,8 +4941,20 @@ static uint16_t safe_default_compatibility_group_for_version(Version v) { return groups.at(static_cast(v)); } -static bool game_name_enables_full_crossplay(const string& name) { - return !name.empty() && ((name[0] == 'x') || (name[0] == 'X')); +static bool game_name_enables_full_crossplay(const string& name, Version creator_version) { + if (!name.empty() && ((name[0] == 'x') || (name[0] == 'X'))) { + return true; + } + + // BB room names appear here with an 8-space + "E" prefix before the user-visible room name. + // Example: a BB room entered as "x asdf" is decoded here as " Ex asdf". + if ((creator_version == Version::BB_V4) && + (name.size() >= 10) && + (name.compare(0, 9, " E") == 0)) { + return (name[9] == 'x') || (name[9] == 'X'); + } + + return false; } shared_ptr create_game_generic( @@ -4985,7 +4997,7 @@ shared_ptr create_game_generic( game->episode = episode; game->mode = mode; game->difficulty = difficulty; - bool full_crossplay_enabled = game_name_enables_full_crossplay(name); + bool full_crossplay_enabled = game_name_enables_full_crossplay(name, creator_c->version()); if (full_crossplay_enabled) { game->allowed_versions = s->compatibility_groups.at(static_cast(creator_c->version())); } else { From 5ec302831638789e6b8c2847b4fdfbdab7cbdb94 Mon Sep 17 00:00:00 2001 From: James Osborne Date: Mon, 25 May 2026 18:45:40 -0400 Subject: [PATCH 4/6] Handle padded BB room names for crossplay opt-in --- src/ReceiveCommands.cc | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 889c6a23..07b55120 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -4946,12 +4946,26 @@ static bool game_name_enables_full_crossplay(const string& name, Version creator return true; } - // BB room names appear here with an 8-space + "E" prefix before the user-visible room name. - // Example: a BB room entered as "x asdf" is decoded here as " Ex asdf". - if ((creator_version == Version::BB_V4) && - (name.size() >= 10) && - (name.compare(0, 9, " E") == 0)) { - return (name[9] == 'x') || (name[9] == 'X'); + // BB room names appear here with leading padding and an episode marker before + // the user-visible room name. For example, a BB room entered as "x asdf" has + // been observed as " Ex asdf". Skip the padding and accept either: + // x... + // Ex... + if (creator_version == Version::BB_V4) { + size_t offset = 0; + while ((offset < name.size()) && (name[offset] == ' ')) { + offset++; + } + + if ((offset < name.size()) && ((name[offset] == 'x') || (name[offset] == 'X'))) { + return true; + } + + if ((offset + 1 < name.size()) && + ((name[offset] == 'E') || (name[offset] == 'e')) && + ((name[offset + 1] == 'x') || (name[offset + 1] == 'X'))) { + return true; + } } return false; From d40dbec9f0f752b7753430bdb1cccab6f61b3b8d Mon Sep 17 00:00:00 2001 From: James Osborne Date: Mon, 25 May 2026 18:49:45 -0400 Subject: [PATCH 5/6] Log room name bytes for crossplay opt-in --- src/ReceiveCommands.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 07b55120..2af0fa76 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -5018,9 +5018,18 @@ shared_ptr create_game_generic( game->allowed_versions = safe_default_compatibility_group_for_version(creator_c->version()); } + string name_bytes; + for (uint8_t ch : name) { + if (!name_bytes.empty()) { + name_bytes += ' '; + } + name_bytes += std::format("{:02X}", ch); + } + game->log.info_f( - "PSO Peeps crossplay opt-in: name=[{}] creator_version={} full_crossplay_enabled={} allowed_versions={:04X}", + "PSO Peeps crossplay opt-in: name=[{}] name_bytes=[{}] creator_version={} full_crossplay_enabled={} allowed_versions={:04X}", name, + name_bytes, static_cast(creator_c->version()), full_crossplay_enabled, game->allowed_versions); From 356abb6698af0b84e201b092a60f19cf3eb3631e Mon Sep 17 00:00:00 2001 From: James Osborne Date: Mon, 25 May 2026 18:57:00 -0400 Subject: [PATCH 6/6] Treat tab-padded BB room names as crossplay opt-in --- src/ReceiveCommands.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 2af0fa76..41281dce 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -4953,7 +4953,7 @@ static bool game_name_enables_full_crossplay(const string& name, Version creator // Ex... if (creator_version == Version::BB_V4) { size_t offset = 0; - while ((offset < name.size()) && (name[offset] == ' ')) { + while ((offset < name.size()) && ((name[offset] == ' ') || (name[offset] == '\t'))) { offset++; }