diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index fb2862cd..cdd17b5e 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -5615,15 +5615,25 @@ struct G_TournamentMatchResult_GC_Ep3_6xB4x51 { ptext meseta_reward_text; } __packed__; -// 6xB4x52: Unknown +// 6xB4x52: Set game metadata -struct G_Unknown_GC_Ep3_6xB4x52 { - G_CardBattleCommandHeader header = {0xB4, sizeof(G_Unknown_GC_Ep3_6xB4x52) / 4, 0, 0x52, 0, 0, 0}; +struct G_SetGameMetadata_GC_Ep3_6xB4x52 { + G_CardBattleCommandHeader header = {0xB4, sizeof(G_SetGameMetadata_GC_Ep3_6xB4x52) / 4, 0, 0x52, 0, 0, 0}; le_uint16_t unknown_a1 = 0; // Clamped to [0, 999] by the client - le_uint16_t unknown_a2 = 0; // Clamped to [0, 999] by the client + // If num_spectators is nonzero, an icon appears in the middle of the screen + // during battle when the details view is enabled (by pressing Z). However, + // the number of people visible in the icon doesn't match the actual number of + // spectators. Specifically: + // 0 = no icon + // 1 = icon with a single spectator (green) + // 2-4 = icon with 3 spectators (blue) + // 5-10 = icon with 5 spectators (yellow) + // 11-29 = icon with 8 spectators (purple) + // 30+ = icon with 12 spectators (red) + le_uint16_t num_spectators = 0; // Clamped to [0, 999] by the client le_uint16_t unused = 0; - le_uint16_t size = 0; // Number of used bytes in data (clamped to 0xFF) - parray data; + le_uint16_t size = 0; // Number of used bytes in unknown_a2 (clamped to 0xFF) + parray unknown_a2; } __packed__; // 6xB4x53: Reject battle start request diff --git a/src/Lobby.cc b/src/Lobby.cc index 7b034742..a8da82e2 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -132,6 +132,18 @@ void Lobby::add_client(shared_ptr c) { c->game_data.player()->inventory, c->game_data.player()->disp.to_dcpcv3()); } + + // Send spectator count notifications if needed + if (this->is_game() && (this->flags & Lobby::Flag::EPISODE_3_ONLY)) { + if (this->flags & Lobby::Flag::IS_SPECTATOR_TEAM) { + auto watched_l = this->watched_lobby.lock(); + if (watched_l) { + send_ep3_update_spectator_count(watched_l); + } + } else { + send_ep3_update_spectator_count(this->shared_from_this()); + } + } } void Lobby::remove_client(shared_ptr c) { @@ -154,10 +166,22 @@ void Lobby::remove_client(shared_ptr c) { this->reassign_leader_on_client_departure(c->lobby_client_id); - // If the lobby ios recording a battle record, add the player leave event + // If the lobby is recording a battle record, add the player leave event if (this->battle_record) { this->battle_record->delete_player(c->lobby_client_id); } + + // If the lobby is Episode 3, update the appropriate spectator counts + if (this->is_game() && (this->flags & Lobby::Flag::EPISODE_3_ONLY)) { + if (this->flags & Lobby::Flag::IS_SPECTATOR_TEAM) { + auto watched_l = this->watched_lobby.lock(); + if (watched_l) { + send_ep3_update_spectator_count(watched_l); + } + } else { + send_ep3_update_spectator_count(this->shared_from_this()); + } + } } void Lobby::move_client_to_lobby(shared_ptr dest_lobby, diff --git a/src/Lobby.hh b/src/Lobby.hh index 0051969c..d9b36786 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -20,7 +20,7 @@ #include "Episode3/BattleRecord.hh" #include "Episode3/Server.hh" -struct Lobby { +struct Lobby : public std::enable_shared_from_this { enum Flag { GAME = 0x00000001, EPISODE_3_ONLY = 0x00000002, diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 0f0528f2..f30a6ace 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -2284,6 +2284,25 @@ void send_ep3_tournament_match_result( // the player 1000000 and never charge for anything. cmd.meseta_amount = 100; cmd.meseta_reward_text = "You got %s meseta!"; + if (!(tourn->get_data_index()->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) { + uint8_t mask_key = (random_object() % 0xFF) + 1; + set_mask_for_ep3_game_command(&cmd, sizeof(cmd), mask_key); + } + send_command_t(l, 0xC9, 0x00, cmd); +} + +void send_ep3_update_spectator_count(shared_ptr l) { + G_SetGameMetadata_GC_Ep3_6xB4x52 cmd; + for (auto watcher_l : l->watcher_lobbies) { + for (auto c : watcher_l->clients) { + if (c) { + cmd.num_spectators++; + } + } + } + // TODO: Make s available here so we can apply masking if needed (perhaps by + // adding a weak_ptr in Lobby... it'd be dumb to require s to be passed in to + // this function just to check a behavior flag) send_command_t(l, 0xC9, 0x00, cmd); } diff --git a/src/SendCommands.hh b/src/SendCommands.hh index bdadaa32..54865eaa 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -339,6 +339,8 @@ void send_ep3_tournament_details( void send_ep3_game_details( std::shared_ptr c, std::shared_ptr l); +void send_ep3_update_spectator_count(std::shared_ptr l); + // Pass mask_key = 0 to unmask the command void set_mask_for_ep3_game_command(void* vdata, size_t size, uint8_t mask_key); diff --git a/tests/GC-Episode3GameSmokeTest.test.txt b/tests/GC-Episode3GameSmokeTest.test.txt index 271f8bc6..2191a588 100644 --- a/tests/GC-Episode3GameSmokeTest.test.txt +++ b/tests/GC-Episode3GameSmokeTest.test.txt @@ -2805,6 +2805,25 @@ I 13136 2022-11-27 21:06:44 - [Lobby/15] Created lobby [PlayerInventory] 3 (00010003): 030000 (Monomate x4) [PlayerInventory] 4 (00010004): 030100 (Monofluid x4) I 13136 2022-11-27 21:06:44 - [Commands] Sending to C-6 (Tali) (version=GC command=64 flag=01) +0000 | C9 00 14 01 B4 44 00 00 52 00 00 00 00 00 00 00 | D R +0010 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0020 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0040 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0050 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0060 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0070 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0080 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0090 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +00A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +00B0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +00F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0100 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0110 | 00 00 00 00 | +I 13136 2022-11-27 21:06:44 - [Commands] Sending to C-6 (Tali) (version=GC command=64 flag=01) 0000 | 64 01 84 11 00 00 00 00 00 00 00 00 00 00 00 00 | d 0010 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0020 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |