fix client crash when creating spectator team

This commit is contained in:
Martin Michelsen
2023-09-16 09:57:28 -07:00
parent 4ddc4fce1d
commit 4741091b9f
4 changed files with 72 additions and 59 deletions
+5 -1
View File
@@ -593,8 +593,12 @@ static void server_command_playrec(shared_ptr<ServerState> s, shared_ptr<Lobby>
shared_ptr<Episode3::BattleRecord> record(new Episode3::BattleRecord(data));
shared_ptr<Episode3::BattleRecordPlayer> battle_player(
new Episode3::BattleRecordPlayer(record, s->game_server->get_base()));
create_game_generic(s, c, args.c_str(), u"", Episode::EP3, GameMode::NORMAL,
auto game = create_game_generic(s, c, args.c_str(), u"", Episode::EP3, GameMode::NORMAL,
0, flags, nullptr, battle_player);
if (game) {
s->change_client_lobby(c, game);
c->flags |= Client::Flag::LOADING;
}
}
}
+37 -32
View File
@@ -3099,46 +3099,51 @@ struct SC_SyncCharacterSaveFile_BB_00E7 {
// E8 (S->C): Join spectator team (Episode 3)
// header.flag = player count (including spectators)
// The client will crash if leader_id == client_id. Presumably one of the
// primary game's players should be the leader (this is what newserv does).
struct S_JoinSpectatorTeam_GC_Ep3_E8 {
parray<le_uint32_t, 0x20> variations; // 04-84; unused
/* 0004 */ parray<le_uint32_t, 0x20> variations; // unused
struct PlayerEntry {
PlayerLobbyDataDCGC lobby_data; // 0x20 bytes
PlayerInventory inventory; // 0x34C bytes
PlayerDispDataDCPCV3 disp; // 0xD0 bytes
} __packed__; // 0x43C bytes
parray<PlayerEntry, 4> players; // 84-1174
uint8_t client_id = 0;
uint8_t leader_id = 0;
uint8_t disable_udp = 1;
uint8_t difficulty = 0;
uint8_t battle_mode = 0;
uint8_t event = 0;
uint8_t section_id = 0;
uint8_t challenge_mode = 0;
le_uint32_t rare_seed = 0;
uint8_t episode = 0;
uint8_t unused2 = 1;
uint8_t solo_mode = 0;
uint8_t unused3 = 0;
/* 0000 */ PlayerLobbyDataDCGC lobby_data;
/* 0020 */ PlayerInventory inventory;
/* 036C */ PlayerDispDataDCPCV3 disp;
/* 043C */
} __packed__;
/* 0084 */ parray<PlayerEntry, 4> players;
/* 1174 */ uint8_t client_id = 0;
/* 1175 */ uint8_t leader_id = 0;
/* 1176 */ uint8_t disable_udp = 1;
/* 1177 */ uint8_t difficulty = 0;
/* 1178 */ uint8_t battle_mode = 0;
/* 1179 */ uint8_t event = 0;
/* 117A */ uint8_t section_id = 0;
/* 117B */ uint8_t challenge_mode = 0;
/* 117C */ le_uint32_t rare_seed = 0;
/* 1180 */ uint8_t episode = 0;
/* 1181 */ uint8_t unused2 = 1;
/* 1182 */ uint8_t solo_mode = 0;
/* 1183 */ uint8_t unused3 = 0;
struct SpectatorEntry {
le_uint32_t player_tag = 0;
le_uint32_t guild_card_number = 0;
ptext<char, 0x20> name;
uint8_t present = 0;
uint8_t unknown_a3 = 0;
le_uint16_t level = 0;
parray<le_uint32_t, 2> unknown_a5;
parray<le_uint16_t, 2> unknown_a6;
} __packed__; // 0x38 bytes
/* 00 */ le_uint32_t player_tag = 0;
/* 04 */ le_uint32_t guild_card_number = 0;
/* 08 */ ptext<char, 0x20> name;
/* 28 */ uint8_t present = 0;
/* 29 */ uint8_t unknown_a3 = 0;
/* 2A */ le_uint16_t level = 0;
/* 2C */ parray<le_uint32_t, 2> unknown_a5;
/* 34 */ parray<le_uint16_t, 2> unknown_a6;
/* 38 */
} __packed__;
// Somewhat misleadingly, this array also includes the players actually in the
// battle - they appear in the first positions. Presumably the first 4 are
// always for battlers, and the last 8 are always for spectators.
parray<SpectatorEntry, 12> entries; // 1184-1424
ptext<char, 0x20> spectator_team_name;
/* 1184 */ parray<SpectatorEntry, 12> entries;
/* 1424 */ ptext<char, 0x20> spectator_team_name;
// This field doesn't appear to be actually used by the game, but some servers
// send it anyway (and the game presumably ignores it)
parray<PlayerEntry, 8> spectator_players;
// send it anyway (and the game ignores it)
/* 1444 */ parray<PlayerEntry, 8> spectator_players;
/* 3624 */
} __packed__;
// E8 (C->S): Guild card commands (BB)
+25 -25
View File
@@ -112,12 +112,12 @@ struct PlayerDispDataBB;
struct PlayerStats {
/* 00 */ CharacterStats char_stats;
/* 0E */ le_uint16_t unknown_a1;
/* 10 */ le_float unknown_a2;
/* 14 */ le_float unknown_a3;
/* 18 */ le_uint32_t level;
/* 1C */ le_uint32_t experience;
/* 20 */ le_uint32_t meseta;
/* 0E */ le_uint16_t unknown_a1 = 0;
/* 10 */ le_float unknown_a2 = 0.0;
/* 14 */ le_float unknown_a3 = 0.0;
/* 18 */ le_uint32_t level = 0;
/* 1C */ le_uint32_t experience = 0;
/* 20 */ le_uint32_t meseta = 0;
/* 24 */
PlayerStats() noexcept;
@@ -125,26 +125,26 @@ struct PlayerStats {
struct PlayerVisualConfig {
/* 00 */ ptext<char, 0x10> name;
/* 10 */ le_uint64_t unknown_a2; // Note: This is probably not actually a 64-bit int.
/* 18 */ le_uint32_t name_color; // RGBA
/* 1C */ uint8_t extra_model;
/* 10 */ le_uint64_t unknown_a2 = 0; // Note: This is probably not actually a 64-bit int.
/* 18 */ le_uint32_t name_color = 0xFFFFFFFF; // RGBA
/* 1C */ uint8_t extra_model = 0;
/* 1D */ parray<uint8_t, 0x0F> unused;
/* 2C */ le_uint32_t unknown_a3;
/* 30 */ uint8_t section_id;
/* 31 */ uint8_t char_class;
/* 32 */ uint8_t v2_flags;
/* 33 */ uint8_t version;
/* 34 */ le_uint32_t v1_flags;
/* 38 */ le_uint16_t costume;
/* 3A */ le_uint16_t skin;
/* 3C */ le_uint16_t face;
/* 3E */ le_uint16_t head;
/* 40 */ le_uint16_t hair;
/* 42 */ le_uint16_t hair_r;
/* 44 */ le_uint16_t hair_g;
/* 46 */ le_uint16_t hair_b;
/* 48 */ le_float proportion_x;
/* 4C */ le_float proportion_y;
/* 2C */ le_uint32_t unknown_a3 = 0;
/* 30 */ uint8_t section_id = 0;
/* 31 */ uint8_t char_class = 0;
/* 32 */ uint8_t v2_flags = 0;
/* 33 */ uint8_t version = 0;
/* 34 */ le_uint32_t v1_flags = 0;
/* 38 */ le_uint16_t costume = 0;
/* 3A */ le_uint16_t skin = 0;
/* 3C */ le_uint16_t face = 0;
/* 3E */ le_uint16_t head = 0;
/* 40 */ le_uint16_t hair = 0;
/* 42 */ le_uint16_t hair_r = 0;
/* 44 */ le_uint16_t hair_g = 0;
/* 46 */ le_uint16_t hair_b = 0;
/* 48 */ le_float proportion_x = 0.0;
/* 4C */ le_float proportion_y = 0.0;
/* 50 */
PlayerVisualConfig() noexcept;
+5 -1
View File
@@ -1419,7 +1419,6 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
cmd.variations.clear(0);
cmd.client_id = c->lobby_client_id;
cmd.leader_id = l->leader_id;
cmd.event = l->event;
cmd.section_id = l->section_id;
cmd.rare_seed = l->random_seed;
@@ -1428,6 +1427,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
uint8_t player_count = 0;
auto watched_lobby = l->watched_lobby.lock();
if (watched_lobby) {
cmd.leader_id = watched_lobby->leader_id;
// Live spectating
for (size_t z = 0; z < 4; z++) {
if (!watched_lobby->clients[z]) {
@@ -1466,6 +1466,10 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
if (ev->type != Episode3::BattleRecord::Event::Type::SET_INITIAL_PLAYERS) {
throw runtime_error("battle record does not begin with set players event");
}
if (ev->players.empty()) {
throw runtime_error("battle record contains no players");
}
cmd.leader_id = ev->players[0].lobby_data.client_id;
for (const auto& entry : ev->players) {
uint8_t client_id = entry.lobby_data.client_id;
if (client_id >= 4) {