implement hack to make tournament specatators work

This commit is contained in:
Martin Michelsen
2022-12-12 00:42:39 -08:00
parent ed2568fc7a
commit e858b2101d
6 changed files with 46 additions and 36 deletions
+2 -1
View File
@@ -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.
+5 -5
View File
@@ -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.
+13 -12
View File
@@ -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 {
+7 -6
View File
@@ -1141,14 +1141,14 @@ static void on_ep3_tournament_control(shared_ptr<ServerState> s, shared_ptr<Clie
uint16_t, uint32_t flag, const string&) { // E2
switch (flag) {
case 0x00: // Request tournament list
send_ep3_tournament_list(s, c);
send_ep3_tournament_list(s, c, false);
break;
case 0x01: { // Check tournament
auto team = c->ep3_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<ServerState> s, shared_ptr<Clie
break;
}
case 0x03: // Create tournament spectator team (get battle list)
send_game_menu(c, s, false, true);
break;
case 0x04: // Join tournament spectator team (get team list)
send_game_menu(c, s, true, true);
send_lobby_message_box(c, u"$C6Use View Regular\nBattle for this");
break;
default:
throw runtime_error("invalid tournament operation");
@@ -1380,6 +1378,7 @@ static void on_menu_item_info_request(shared_ptr<ServerState> s, shared_ptr<Clie
break;
}
case MenuID::TOURNAMENTS_FOR_SPEC:
case MenuID::TOURNAMENTS: {
if (!(c->flags & Client::Flag::IS_EPISODE_3)) {
send_ship_info(c, u"Incorrect menu ID");
@@ -1832,13 +1831,15 @@ static void on_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> 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;
}
+14 -10
View File
@@ -1088,7 +1088,7 @@ void send_game_menu_t(
shared_ptr<Client> c,
shared_ptr<ServerState> s,
bool is_spectator_team_list,
bool is_tournament_game_list) {
bool show_tournaments_only) {
vector<S_GameMenuEntry<CharT>> 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<Client> c,
shared_ptr<ServerState> 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<char>(c, s, is_spectator_team_list, is_tournament_game_list);
send_game_menu_t<char>(c, s, is_spectator_team_list, show_tournaments_only);
} else {
send_game_menu_t<char16_t>(c, s, is_spectator_team_list, is_tournament_game_list);
send_game_menu_t<char16_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<ServerState> s, shared_ptr<Client> c) {
void send_ep3_tournament_list(
shared_ptr<ServerState> s,
shared_ptr<Client> 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<ServerState> s, shared_ptr<Client> 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<ServerState> s, shared_ptr<Client> c) {
void send_ep3_tournament_entry_list(
shared_ptr<Client> c,
shared_ptr<const Episode3::Tournament> tourn) {
shared_ptr<const Episode3::Tournament> 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(
+5 -2
View File
@@ -315,10 +315,13 @@ void send_ep3_confirm_tournament_entry(
std::shared_ptr<Client> c,
std::shared_ptr<const Episode3::Tournament> t);
void send_ep3_tournament_list(
std::shared_ptr<ServerState> s, std::shared_ptr<Client> c);
std::shared_ptr<ServerState> s,
std::shared_ptr<Client> c,
bool is_for_spectator_team_create);
void send_ep3_tournament_entry_list(
std::shared_ptr<Client> c,
std::shared_ptr<const Episode3::Tournament> t);
std::shared_ptr<const Episode3::Tournament> t,
bool is_for_spectator_team_create);
void send_ep3_tournament_info(
std::shared_ptr<Client> c,
std::shared_ptr<const Episode3::Tournament> t);