diff --git a/README.md b/README.md index 78e77f4f..ad80c9b8 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ With that said, I offer no guarantees on how or when this project will advance. Current known issues / missing features / things to do: - Support disconnect hooks to clean up state, like if a client disconnects during quest loading or during a trade window execution. - Episode 3 battles are implemented but are not well-tested. + - Fix behavior when joining a spectator team after the beginning of a battle. - PSOBB is not well-tested and likely will disconnect or misbehave when clients try to use unimplemented features. - Enemy indexes also desync slightly in most games, often in later areas, leading to incorrect EXP values being given for killed enemies. - Fix some edge cases on the BB proxy server (e.g. make sure Change Ship does the right thing, which is not the same as what it should do on V2/V3). @@ -78,7 +79,7 @@ The following Episode 3 features are well-tested and work normally: The following Episode 3 features are implemented, but only partially tested: * CARD battles. If you find a feature or card ability that doesn't work, please make a GitHub issue and describe the situation (including the attacking card(s), defending card(s), and ability that didn't work). -* Spectator teams are partially implemented, but are entirely untested. +* Spectator teams are partially implemented, but are not well-tested. There is a known issue that prevents viewing battles unless you're in the spectator team when the battle begins. * Battle replays sometimes cause the client to crash during the replay. Using the $playrec command is therefore not recommended. * Tournaments. diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index b2bc31bd..19094e1a 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -2396,11 +2396,11 @@ struct S_GameInformation_GC_Ep3_E1 { // E2 (C->S): Tournament control (Episode 3) // No arguments (in any of its forms) except header.flag, which determines ths // command's meaning. Specifically: -// header.flag = 00 => request tournament list (server responds with E0) -// header.flag = 01 => check tournament (server responds with E2) -// header.flag = 02 => cancel tournament entry (server responds with CC) -// header.flag = 03 => create tournament spectator team (get battle list) -// header.flag = 04 => join tournament spectator team (get team list) +// flag=00: request tournament list (server responds with E0) +// flag=01: check tournament (server responds with E2) +// flag=02: cancel tournament entry (server responds with CC) +// flag=03: create tournament spectator team (server responds with E6) +// flag=04: join tournament spectator team (server responds with E0) // In case 02, the resulting CC command is apparently completely blank (all 0), // and has header.flag = 0 to indicate the player isn't registered. // In cases 03 and 04, it's not clear what the server should respond with. diff --git a/src/Menu.hh b/src/Menu.hh index 3154af72..cfdb63b0 100644 --- a/src/Menu.hh +++ b/src/Menu.hh @@ -13,18 +13,19 @@ // casting values all over the place, so we can't use enum classes either. namespace MenuID { - constexpr uint32_t MAIN = 0x11000011; - constexpr uint32_t INFORMATION = 0x22000022; - constexpr uint32_t LOBBY = 0x33000033; - constexpr uint32_t GAME = 0x44000044; - constexpr uint32_t QUEST = 0x55000055; - constexpr uint32_t QUEST_FILTER = 0x66000066; - constexpr uint32_t PROXY_DESTINATIONS = 0x77000077; - constexpr uint32_t PROGRAMS = 0x88000088; - constexpr uint32_t PATCHES = 0x99000099; - constexpr uint32_t PROXY_OPTIONS = 0xAA0000AA; - constexpr uint32_t TOURNAMENTS = 0xBB0000BB; - constexpr uint32_t TOURNAMENT_ENTRIES = 0xCC0000CC; + constexpr uint32_t MAIN = 0x11000011; + constexpr uint32_t INFORMATION = 0x22000022; + constexpr uint32_t LOBBY = 0x33000033; + constexpr uint32_t GAME = 0x44000044; + constexpr uint32_t QUEST = 0x55000055; + constexpr uint32_t QUEST_FILTER = 0x66000066; + constexpr uint32_t PROXY_DESTINATIONS = 0x77000077; + constexpr uint32_t PROGRAMS = 0x88000088; + constexpr uint32_t PATCHES = 0x99000099; + constexpr uint32_t PROXY_OPTIONS = 0xAA0000AA; + constexpr uint32_t TOURNAMENTS = 0xBB0000BB; + constexpr uint32_t TOURNAMENTS_FOR_SPEC = 0xBB1111BB; + constexpr uint32_t TOURNAMENT_ENTRIES = 0xCC0000CC; } namespace MainMenuItemID { diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index b5935a1d..f6fda4b9 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1141,14 +1141,14 @@ static void on_ep3_tournament_control(shared_ptr s, shared_ptrep3_tournament_team.lock(); if (team) { auto tourn = team->tournament.lock(); if (tourn) { - send_ep3_tournament_entry_list(c, tourn); + send_ep3_tournament_entry_list(c, tourn, false); } else { send_lobby_message_box(c, u"$C6The tournament\nhas concluded."); } @@ -1175,10 +1175,8 @@ static void on_ep3_tournament_control(shared_ptr s, shared_ptr s, shared_ptrflags & Client::Flag::IS_EPISODE_3)) { send_ship_info(c, u"Incorrect menu ID"); @@ -1832,13 +1831,15 @@ static void on_menu_selection(shared_ptr s, shared_ptr c, } break; + case MenuID::TOURNAMENTS_FOR_SPEC: case MenuID::TOURNAMENTS: { if (!(c->flags & Client::Flag::IS_EPISODE_3)) { throw runtime_error("non-Episode 3 client attempted to join tournament"); } auto tourn = s->ep3_tournament_index->get_tournament(item_id); if (tourn) { - send_ep3_tournament_entry_list(c, tourn); + send_ep3_tournament_entry_list(c, tourn, + (menu_id == MenuID::TOURNAMENTS_FOR_SPEC)); } break; } diff --git a/src/SendCommands.cc b/src/SendCommands.cc index df9cc3f6..683ec862 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -1088,7 +1088,7 @@ void send_game_menu_t( shared_ptr c, shared_ptr s, bool is_spectator_team_list, - bool is_tournament_game_list) { + bool show_tournaments_only) { vector> entries; { auto& e = entries.emplace_back(); @@ -1118,8 +1118,7 @@ void send_game_menu_t( if (l_is_spectator_team != is_spectator_team_list) { continue; } - bool l_is_tournament_game = !!l->tournament_match; - if (l_is_tournament_game != is_tournament_game_list) { + if (show_tournaments_only && !l->tournament_match) { continue; } @@ -1157,13 +1156,13 @@ void send_game_menu( shared_ptr c, shared_ptr s, bool is_spectator_team_list, - bool is_tournament_game_list) { + bool show_tournaments_only) { if ((c->version() == GameVersion::DC) || (c->version() == GameVersion::GC) || (c->version() == GameVersion::XB)) { - send_game_menu_t(c, s, is_spectator_team_list, is_tournament_game_list); + send_game_menu_t(c, s, is_spectator_team_list, show_tournaments_only); } else { - send_game_menu_t(c, s, is_spectator_team_list, is_tournament_game_list); + send_game_menu_t(c, s, is_spectator_team_list, show_tournaments_only); } } @@ -1961,7 +1960,10 @@ void send_ep3_confirm_tournament_entry( send_command_t(c, 0xCC, tourn ? 0x01 : 0x00, cmd); } -void send_ep3_tournament_list(shared_ptr s, shared_ptr c) { +void send_ep3_tournament_list( + shared_ptr s, + shared_ptr c, + bool is_for_spectator_team_create) { S_TournamentList_GC_Ep3_E0 cmd; size_t z = 0; for (const auto& tourn : s->ep3_tournament_index->all_tournaments()) { @@ -1969,7 +1971,8 @@ void send_ep3_tournament_list(shared_ptr s, shared_ptr c) { throw logic_error("more than 32 tournaments exist"); } auto& entry = cmd.entries[z]; - entry.menu_id = MenuID::TOURNAMENTS; + entry.menu_id = is_for_spectator_team_create + ? MenuID::TOURNAMENTS_FOR_SPEC : MenuID::TOURNAMENTS; entry.item_id = tourn->get_number(); // TODO: What does it mean for a tournament to be locked? Should we support // that? @@ -1997,7 +2000,8 @@ void send_ep3_tournament_list(shared_ptr s, shared_ptr c) { void send_ep3_tournament_entry_list( shared_ptr c, - shared_ptr tourn) { + shared_ptr tourn, + bool is_for_spectator_team_create) { S_TournamentEntryList_GC_Ep3_E2 cmd; cmd.players_per_team = tourn->get_is_2v2() ? 2 : 1; size_t z = 0; @@ -2022,7 +2026,7 @@ void send_ep3_tournament_entry_list( entry.name = team->name; z++; } - send_command_t(c, 0xE2, z, cmd); + send_command_t(c, is_for_spectator_team_create ? 0xE7 : 0xE2, z, cmd); } void send_ep3_tournament_details( diff --git a/src/SendCommands.hh b/src/SendCommands.hh index d5ab7d9c..bdadaa32 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -315,10 +315,13 @@ void send_ep3_confirm_tournament_entry( std::shared_ptr c, std::shared_ptr t); void send_ep3_tournament_list( - std::shared_ptr s, std::shared_ptr c); + std::shared_ptr s, + std::shared_ptr c, + bool is_for_spectator_team_create); void send_ep3_tournament_entry_list( std::shared_ptr c, - std::shared_ptr t); + std::shared_ptr t, + bool is_for_spectator_team_create); void send_ep3_tournament_info( std::shared_ptr c, std::shared_ptr t);