fix tournament registration bug after disconnect
This commit is contained in:
@@ -5334,7 +5334,9 @@ struct G_CardAuctionResults_GC_Ep3_6xB5x45 {
|
|||||||
// client just copies the included strings to global buffers and then ignores
|
// client just copies the included strings to global buffers and then ignores
|
||||||
// them. Sega's servers sent this twice for each battle, however: once after the
|
// them. Sega's servers sent this twice for each battle, however: once after the
|
||||||
// initial setup phase (before starter rolls) and once when the results screen
|
// initial setup phase (before starter rolls) and once when the results screen
|
||||||
// appeared.
|
// appeared. The second instance of this command appears to be caused by them
|
||||||
|
// recreating the TCardServer object (implemented here as Episode3::Server) in
|
||||||
|
// order to support sequential multiple battles in the same team.
|
||||||
|
|
||||||
struct G_ServerVersionStrings_GC_Ep3_6xB4x46 {
|
struct G_ServerVersionStrings_GC_Ep3_6xB4x46 {
|
||||||
G_CardBattleCommandHeader header = {0xB4, sizeof(G_ServerVersionStrings_GC_Ep3_6xB4x46) / 4, 0, 0x46, 0, 0, 0};
|
G_CardBattleCommandHeader header = {0xB4, sizeof(G_ServerVersionStrings_GC_Ep3_6xB4x46) / 4, 0, 0x46, 0, 0, 0};
|
||||||
|
|||||||
@@ -345,6 +345,25 @@ shared_ptr<Tournament::Match> Tournament::get_final_match() const {
|
|||||||
return this->final_match;
|
return this->final_match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shared_ptr<Tournament::Team> Tournament::team_for_serial_number(
|
||||||
|
uint32_t serial_number) const {
|
||||||
|
if (!this->all_player_serial_numbers.count(serial_number)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto team : this->teams) {
|
||||||
|
if (!team->player_serial_numbers.count(serial_number)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!team->is_active) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return team;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw logic_error("serial number registered in tournament but not in any team");
|
||||||
|
}
|
||||||
|
|
||||||
void Tournament::start() {
|
void Tournament::start() {
|
||||||
if (this->current_state != State::REGISTRATION) {
|
if (this->current_state != State::REGISTRATION) {
|
||||||
throw runtime_error("tournament has already started");
|
throw runtime_error("tournament has already started");
|
||||||
@@ -470,6 +489,20 @@ shared_ptr<Tournament> TournamentIndex::get_tournament(const string& name) const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shared_ptr<Tournament::Team> TournamentIndex::team_for_serial_number(
|
||||||
|
uint32_t serial_number) const {
|
||||||
|
for (size_t z = 0; z < 0x20; z++) {
|
||||||
|
if (!this->tournaments[z]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto team = this->tournaments[z]->team_for_serial_number(serial_number);
|
||||||
|
if (team) {
|
||||||
|
return team;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Episode3
|
} // namespace Episode3
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ public:
|
|||||||
std::shared_ptr<Team> get_winner_team() const;
|
std::shared_ptr<Team> get_winner_team() const;
|
||||||
std::shared_ptr<Match> next_match_for_team(std::shared_ptr<Team> team) const;
|
std::shared_ptr<Match> next_match_for_team(std::shared_ptr<Team> team) const;
|
||||||
std::shared_ptr<Match> get_final_match() const;
|
std::shared_ptr<Match> get_final_match() const;
|
||||||
|
std::shared_ptr<Team> team_for_serial_number(uint32_t serial_number) const;
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
void print_bracket(FILE* stream) const;
|
void print_bracket(FILE* stream) const;
|
||||||
@@ -155,6 +157,9 @@ public:
|
|||||||
std::shared_ptr<Tournament> get_tournament(uint8_t number) const;
|
std::shared_ptr<Tournament> get_tournament(uint8_t number) const;
|
||||||
std::shared_ptr<Tournament> get_tournament(const std::string& name) const;
|
std::shared_ptr<Tournament> get_tournament(const std::string& name) const;
|
||||||
|
|
||||||
|
std::shared_ptr<Tournament::Team> team_for_serial_number(
|
||||||
|
uint32_t serial_number) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Tournament> tournaments[0x20];
|
std::shared_ptr<Tournament> tournaments[0x20];
|
||||||
};
|
};
|
||||||
|
|||||||
+42
-20
@@ -214,6 +214,11 @@ void on_connect(std::shared_ptr<ServerState> s, std::shared_ptr<Client> c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void on_login_complete(shared_ptr<ServerState> s, shared_ptr<Client> c) {
|
void on_login_complete(shared_ptr<ServerState> s, shared_ptr<Client> c) {
|
||||||
|
if (c->flags & Client::Flag::IS_EPISODE_3) {
|
||||||
|
c->ep3_tournament_team = s->ep3_tournament_index->team_for_serial_number(
|
||||||
|
c->license->serial_number);
|
||||||
|
}
|
||||||
|
|
||||||
// On the BB data server, this function is called only on the last connection
|
// On the BB data server, this function is called only on the last connection
|
||||||
// (when we should send the ship select menu).
|
// (when we should send the ship select menu).
|
||||||
if ((c->server_behavior == ServerBehavior::LOGIN_SERVER) ||
|
if ((c->server_behavior == ServerBehavior::LOGIN_SERVER) ||
|
||||||
@@ -863,24 +868,24 @@ static void on_ep3_meseta_transaction(shared_ptr<ServerState>,
|
|||||||
send_command(c, command, 0x03, &out_cmd, sizeof(out_cmd));
|
send_command(c, command, 0x03, &out_cmd, sizeof(out_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_ep3_battle_table_state(shared_ptr<ServerState> s,
|
static bool start_ep3_tournament_match_if_pending(
|
||||||
shared_ptr<Client> c, uint16_t, uint32_t flag, const string& data) { // E4
|
shared_ptr<ServerState> s,
|
||||||
const auto& cmd = check_size_t<C_CardBattleTableState_GC_Ep3_E4>(data);
|
shared_ptr<Lobby> l,
|
||||||
auto l = s->find_lobby(c->lobby_id);
|
shared_ptr<Client> c,
|
||||||
|
int16_t table_number) {
|
||||||
if (flag) {
|
|
||||||
if (l->is_game() || !(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
|
||||||
throw runtime_error("battle table join command sent in non-CARD lobby");
|
|
||||||
}
|
|
||||||
c->card_battle_table_number = cmd.table_number;
|
|
||||||
c->card_battle_table_seat_number = cmd.seat_number;
|
|
||||||
|
|
||||||
auto team = c->ep3_tournament_team.lock();
|
auto team = c->ep3_tournament_team.lock();
|
||||||
if (team) {
|
if (!team) {
|
||||||
|
return false; // Client is not registered in a tournament
|
||||||
|
}
|
||||||
auto tourn = team->tournament.lock();
|
auto tourn = team->tournament.lock();
|
||||||
if (tourn) {
|
if (!tourn) {
|
||||||
|
return false; // The tournament has been canceled
|
||||||
|
}
|
||||||
auto match = tourn->next_match_for_team(team);
|
auto match = tourn->next_match_for_team(team);
|
||||||
if (match) {
|
if (!match) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto other_team = match->opponent_team_for_team(team);
|
auto other_team = match->opponent_team_for_team(team);
|
||||||
unordered_set<uint32_t> required_serial_numbers;
|
unordered_set<uint32_t> required_serial_numbers;
|
||||||
for (uint32_t serial_number : team->player_serial_numbers) {
|
for (uint32_t serial_number : team->player_serial_numbers) {
|
||||||
@@ -894,14 +899,20 @@ static void on_ep3_battle_table_state(shared_ptr<ServerState> s,
|
|||||||
if (!other_c) {
|
if (!other_c) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((other_c->card_battle_table_number == cmd.table_number) &&
|
if ((other_c->card_battle_table_number == table_number) &&
|
||||||
required_serial_numbers.erase(other_c->license->serial_number)) {
|
required_serial_numbers.erase(other_c->license->serial_number)) {
|
||||||
game_clients.emplace(other_c);
|
game_clients.emplace(other_c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (required_serial_numbers.empty()) {
|
if (!required_serial_numbers.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we've checked all the necessary conditions for a tournament
|
||||||
|
// match to begin.
|
||||||
|
|
||||||
for (const auto& other_c : l->clients) {
|
for (const auto& other_c : l->clients) {
|
||||||
if (other_c && (other_c->card_battle_table_number == cmd.table_number)) {
|
if (other_c && (other_c->card_battle_table_number == table_number)) {
|
||||||
other_c->card_battle_table_number = -1;
|
other_c->card_battle_table_number = -1;
|
||||||
other_c->card_battle_table_seat_number = 0;
|
other_c->card_battle_table_seat_number = 0;
|
||||||
}
|
}
|
||||||
@@ -961,10 +972,21 @@ static void on_ep3_battle_table_state(shared_ptr<ServerState> s,
|
|||||||
send_join_lobby(game_c, game);
|
send_join_lobby(game_c, game);
|
||||||
game_c->flags |= Client::Flag::LOADING;
|
game_c->flags |= Client::Flag::LOADING;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_ep3_battle_table_state(shared_ptr<ServerState> s,
|
||||||
|
shared_ptr<Client> c, uint16_t, uint32_t flag, const string& data) { // E4
|
||||||
|
const auto& cmd = check_size_t<C_CardBattleTableState_GC_Ep3_E4>(data);
|
||||||
|
auto l = s->find_lobby(c->lobby_id);
|
||||||
|
|
||||||
|
if (flag) {
|
||||||
|
if (l->is_game() || !(l->flags & Lobby::Flag::EPISODE_3_ONLY)) {
|
||||||
|
throw runtime_error("battle table join command sent in non-CARD lobby");
|
||||||
}
|
}
|
||||||
}
|
c->card_battle_table_number = cmd.table_number;
|
||||||
}
|
c->card_battle_table_seat_number = cmd.seat_number;
|
||||||
|
start_ep3_tournament_match_if_pending(s, l, c, cmd.table_number);
|
||||||
|
|
||||||
} else { // Leaving battle table
|
} else { // Leaving battle table
|
||||||
c->card_battle_table_number = -1;
|
c->card_battle_table_number = -1;
|
||||||
|
|||||||
Reference in New Issue
Block a user