implement hack to make tournament specatators work
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user