implement E1/E3 commands on NTE
This commit is contained in:
+16
-4
@@ -2874,7 +2874,8 @@ struct S_TournamentList_Ep3_E0 {
|
||||
// used with the E3 command. See the E3 command for descriptions of what each
|
||||
// flag value means.
|
||||
|
||||
struct S_GameInformation_Ep3_E1 {
|
||||
template <typename RulesT>
|
||||
struct S_GameInformationBase_Ep3_E1 {
|
||||
/* 0004 */ pstring<TextEncoding::MARKED, 0x20> game_name;
|
||||
struct PlayerEntry {
|
||||
pstring<TextEncoding::ASCII, 0x10> name; // From disp.name
|
||||
@@ -2882,11 +2883,16 @@ struct S_GameInformation_Ep3_E1 {
|
||||
} __packed__;
|
||||
/* 0024 */ parray<PlayerEntry, 4> player_entries;
|
||||
/* 00E4 */ pstring<TextEncoding::MARKED, 0x20> map_name;
|
||||
/* 0104 */ Episode3::Rules rules;
|
||||
/* 0104 */ RulesT rules;
|
||||
/* 0118 */ parray<PlayerEntry, 8> spectator_entries;
|
||||
/* 0298 */
|
||||
} __packed__;
|
||||
|
||||
struct S_GameInformation_Ep3NTE_E1 : S_GameInformationBase_Ep3_E1<Episode3::RulesTrial> {
|
||||
} __packed__;
|
||||
struct S_GameInformation_Ep3_E1 : S_GameInformationBase_Ep3_E1<Episode3::Rules> {
|
||||
} __packed__;
|
||||
|
||||
// E1 (S->C): System file created (BB)
|
||||
// This seems to take the place of 00E2 in certain cases. Perhaps it was used
|
||||
// when a client hadn't logged in before and didn't have a system file, so the
|
||||
@@ -2973,11 +2979,12 @@ struct S_TournamentEntryList_Ep3_E2 {
|
||||
// The 00, 01, and 04 cases don't really make sense, because the E1 command is
|
||||
// more appropriate for inspecting non-tournament games.
|
||||
|
||||
struct S_TournamentGameDetails_Ep3_E3 {
|
||||
template <typename RulesT>
|
||||
struct S_TournamentGameDetailsBase_Ep3_E3 {
|
||||
// These fields are used only if the Rules pane is shown
|
||||
/* 0004 */ pstring<TextEncoding::MARKED, 0x20> name;
|
||||
/* 0024 */ pstring<TextEncoding::MARKED, 0x20> map_name;
|
||||
/* 0044 */ Episode3::Rules rules;
|
||||
/* 0044 */ RulesT rules;
|
||||
|
||||
// This field is used only if the bracket pane is shown
|
||||
struct BracketEntry {
|
||||
@@ -3009,6 +3016,11 @@ struct S_TournamentGameDetails_Ep3_E3 {
|
||||
/* 0740 */
|
||||
} __packed__;
|
||||
|
||||
struct S_TournamentGameDetails_Ep3NTE_E3 : S_TournamentGameDetailsBase_Ep3_E3<Episode3::RulesTrial> {
|
||||
} __packed__;
|
||||
struct S_TournamentGameDetails_Ep3_E3 : S_TournamentGameDetailsBase_Ep3_E3<Episode3::Rules> {
|
||||
} __packed__;
|
||||
|
||||
// E3 (C->S): Player preview request (BB)
|
||||
|
||||
struct C_PlayerPreviewRequest_BB_E3 {
|
||||
|
||||
@@ -999,7 +999,7 @@ struct RulesTrial {
|
||||
/* 0C */
|
||||
|
||||
RulesTrial() = default;
|
||||
explicit RulesTrial(const Rules&);
|
||||
RulesTrial(const Rules&);
|
||||
operator Rules() const;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
@@ -1332,12 +1332,19 @@ static bool start_ep3_battle_table_game_if_ready(shared_ptr<Lobby> l, int16_t ta
|
||||
// Figure out which clients are at this table. If any client has declined, we
|
||||
// never start a match, but we may start a match even if all clients have not
|
||||
// yet accepted (in case of a tournament match).
|
||||
Version base_version = Version::UNKNOWN;
|
||||
unordered_map<size_t, shared_ptr<Client>> table_clients;
|
||||
bool all_clients_accepted = true;
|
||||
for (const auto& c : l->clients) {
|
||||
if (!c || (c->card_battle_table_number != table_number)) {
|
||||
continue;
|
||||
}
|
||||
// Prevent match from starting unless all players are on the same version
|
||||
if (base_version == Version::UNKNOWN) {
|
||||
base_version = c->version();
|
||||
} else if (base_version != c->version()) {
|
||||
return false;
|
||||
}
|
||||
if (c->card_battle_table_seat_number >= 4) {
|
||||
throw runtime_error("invalid seat number");
|
||||
}
|
||||
|
||||
+41
-17
@@ -2775,14 +2775,11 @@ void send_ep3_rank_update(shared_ptr<Client> c) {
|
||||
}
|
||||
|
||||
void send_ep3_card_battle_table_state(shared_ptr<Lobby> l, uint16_t table_number) {
|
||||
S_CardBattleTableState_Ep3_E4 cmd;
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
cmd.entries[z].state = 0;
|
||||
cmd.entries[z].unknown_a1 = 0;
|
||||
cmd.entries[z].guild_card_number = 0;
|
||||
}
|
||||
S_CardBattleTableState_Ep3_E4 cmd_nte;
|
||||
S_CardBattleTableState_Ep3_E4 cmd_final;
|
||||
|
||||
set<shared_ptr<Client>> clients;
|
||||
set<shared_ptr<Client>> clients_nte;
|
||||
set<shared_ptr<Client>> clients_final;
|
||||
for (const auto& c : l->clients) {
|
||||
if (!c) {
|
||||
continue;
|
||||
@@ -2791,17 +2788,23 @@ void send_ep3_card_battle_table_state(shared_ptr<Lobby> l, uint16_t table_number
|
||||
if (c->card_battle_table_seat_number > 3) {
|
||||
throw runtime_error("invalid battle table seat number");
|
||||
}
|
||||
auto& e = cmd.entries[c->card_battle_table_seat_number];
|
||||
|
||||
bool is_trial = (c->version() == Version::GC_EP3_NTE);
|
||||
auto& e = is_trial ? cmd_nte.entries[c->card_battle_table_seat_number] : cmd_final.entries[c->card_battle_table_seat_number];
|
||||
if (e.state == 0) {
|
||||
e.state = c->card_battle_table_seat_state;
|
||||
e.guild_card_number = c->license->serial_number;
|
||||
auto& clients = is_trial ? clients_nte : clients_final;
|
||||
clients.emplace(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& c : clients) {
|
||||
send_command_t(c, 0xE4, table_number, cmd);
|
||||
for (const auto& c : clients_nte) {
|
||||
send_command_t(c, 0xE4, table_number, cmd_nte);
|
||||
}
|
||||
for (const auto& c : clients_final) {
|
||||
send_command_t(c, 0xE4, table_number, cmd_final);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2911,10 +2914,11 @@ void send_ep3_tournament_entry_list(
|
||||
send_command_t(c, is_for_spectator_team_create ? 0xE7 : 0xE2, z, cmd);
|
||||
}
|
||||
|
||||
void send_ep3_tournament_details(
|
||||
template <typename RulesT>
|
||||
void send_ep3_tournament_details_t(
|
||||
shared_ptr<Client> c,
|
||||
shared_ptr<const Episode3::Tournament> tourn) {
|
||||
S_TournamentGameDetails_Ep3_E3 cmd;
|
||||
S_TournamentGameDetailsBase_Ep3_E3<RulesT> cmd;
|
||||
auto vm = tourn->get_map()->version(c->language());
|
||||
cmd.name.encode(tourn->get_name(), c->language());
|
||||
cmd.map_name.encode(vm->map->name.decode(vm->language), c->language());
|
||||
@@ -2930,6 +2934,16 @@ void send_ep3_tournament_details(
|
||||
send_command_t(c, 0xE3, 0x02, cmd);
|
||||
}
|
||||
|
||||
void send_ep3_tournament_details(
|
||||
shared_ptr<Client> c,
|
||||
shared_ptr<const Episode3::Tournament> tourn) {
|
||||
if (c->version() == Version::GC_EP3_NTE) {
|
||||
send_ep3_tournament_details_t<Episode3::RulesTrial>(c, tourn);
|
||||
} else {
|
||||
send_ep3_tournament_details_t<Episode3::Rules>(c, tourn);
|
||||
}
|
||||
}
|
||||
|
||||
string ep3_description_for_client(shared_ptr<Client> c) {
|
||||
if (!is_ep3(c->version())) {
|
||||
throw runtime_error("client is not Episode 3");
|
||||
@@ -2942,7 +2956,8 @@ string ep3_description_for_client(shared_ptr<Client> c) {
|
||||
char_for_language_code(p->inventory.language));
|
||||
}
|
||||
|
||||
void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
template <typename RulesT>
|
||||
void send_ep3_game_details_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
|
||||
shared_ptr<Lobby> primary_lobby;
|
||||
if (l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
@@ -2955,7 +2970,7 @@ void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
auto tourn = tourn_match ? tourn_match->tournament.lock() : nullptr;
|
||||
|
||||
if (tourn) {
|
||||
S_TournamentGameDetails_Ep3_E3 cmd;
|
||||
S_TournamentGameDetailsBase_Ep3_E3<RulesT> cmd;
|
||||
cmd.name.encode(l->name, c->language());
|
||||
|
||||
auto vm = tourn->get_map()->version(c->language());
|
||||
@@ -2974,7 +2989,8 @@ void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
|
||||
if (primary_lobby) {
|
||||
auto serial_number_to_client = primary_lobby->clients_by_serial_number();
|
||||
auto describe_team = [&](S_TournamentGameDetails_Ep3_E3::TeamEntry& team_entry, shared_ptr<const Episode3::Tournament::Team> team) -> void {
|
||||
using TeamEntryT = typename S_TournamentGameDetailsBase_Ep3_E3<RulesT>::TeamEntry;
|
||||
auto describe_team = [&](TeamEntryT& team_entry, shared_ptr<const Episode3::Tournament::Team> team) -> void {
|
||||
team_entry.team_name.encode(team->name, c->language());
|
||||
for (size_t z = 0; z < team->players.size(); z++) {
|
||||
auto& entry = team_entry.players[z];
|
||||
@@ -3014,7 +3030,7 @@ void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
send_command_t(c, 0xE3, flag, cmd);
|
||||
|
||||
} else {
|
||||
S_GameInformation_Ep3_E1 cmd;
|
||||
S_GameInformationBase_Ep3_E1<RulesT> cmd;
|
||||
cmd.game_name.encode(l->name, c->language());
|
||||
if (primary_lobby) {
|
||||
size_t num_players = 0;
|
||||
@@ -3043,7 +3059,7 @@ void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
// spectator count in the info window object. To account for this, we send
|
||||
// a mostly-blank E3 to set the spectator count, followed by an E1 with
|
||||
// the correct data.
|
||||
S_TournamentGameDetails_Ep3_E3 cmd_E3;
|
||||
S_TournamentGameDetailsBase_Ep3_E3<RulesT> cmd_E3;
|
||||
cmd_E3.num_spectators = num_spectators;
|
||||
send_command_t(c, 0xE3, 0x04, cmd_E3);
|
||||
|
||||
@@ -3063,6 +3079,14 @@ void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
}
|
||||
}
|
||||
|
||||
void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
if (c->version() == Version::GC_EP3_NTE) {
|
||||
send_ep3_game_details_t<Episode3::RulesTrial>(c, l);
|
||||
} else {
|
||||
send_ep3_game_details_t<Episode3::Rules>(c, l);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CmdT>
|
||||
void send_ep3_set_tournament_player_decks_t(shared_ptr<Client> c) {
|
||||
auto s = c->require_server_state();
|
||||
|
||||
Reference in New Issue
Block a user