refine more commands

This commit is contained in:
Martin Michelsen
2025-01-22 23:22:31 -08:00
parent 7da0da66f1
commit 2e6e1adcf3
3 changed files with 60 additions and 58 deletions
+42 -49
View File
@@ -1297,7 +1297,7 @@ struct S_JoinGame_XB_64 : S_JoinGameT_DC_PC<PlayerLobbyDataXB> {
uint8_t episode = 0;
uint8_t xb_enable_voice_chat = 1;
parray<uint8_t, 2> unused;
S_UnknownXBVoiceChatConfig unknown_a1;
S_UnknownXBVoiceChatConfig voice_chat_config;
} __packed_ws__(S_JoinGame_XB_64, 0x1D8);
struct S_JoinGame_BB_64 : S_JoinGameT_DC_PC<PlayerLobbyDataBB> {
@@ -1371,7 +1371,7 @@ check_struct_size(S_JoinLobby_BB_65_67_68, 0x3D8C);
struct S_JoinLobby_XB_65_67_68 {
LobbyFlags lobby_flags;
S_UnknownXBVoiceChatConfig unknown_a4;
S_UnknownXBVoiceChatConfig voice_chat_config;
struct Entry {
PlayerLobbyDataXB lobby_data;
PlayerInventory inventory;
@@ -1998,8 +1998,10 @@ struct C_LoginExtended_GC_9E : C_Login_GC_9E {
} __packed_ws__(C_LoginExtended_GC_9E, 0x14C);
struct C_Login_XB_9E : C_Login_DC_PC_GC_9D {
/* 00C8 */ parray<uint8_t, 0x20> unused;
/* 00C8 */ parray<uint8_t, 0x20> unknown_a4;
/* 00E8 */ XBNetworkLocation netloc;
// By default, this array is initialized with [0x5A2773DD, 0xD82B2345,
// 0x4FF904D5] on 4OEU (US TU version).
/* 0118 */ parray<le_uint32_t, 3> unknown_a1a;
/* 0124 */ le_uint32_t xb_user_id_high = 0;
/* 0128 */ le_uint32_t xb_user_id_low = 0;
@@ -2412,12 +2414,11 @@ struct S_UpdateMediaHeader_Ep3_B9 {
// BA: Meseta transaction (Episode 3)
// This command is not valid on Episode 3 Trial Edition.
// header.flag specifies the transaction purpose. Specific known values:
// 00 = unknown
// 01 = Initialize Meseta subsystem (C->S; always has a value of 0)
// 02 = Spend meseta (at e.g. lobby jukebox or Pinz's shop) (C->S)
// 02 = Spend Meseta (at e.g. lobby jukebox or Pinz's shop) (C->S)
// 03 = Successful transaction (S->C; request_token must match the last token
// sent by client)
// 04 = Insufficient meseta (S->C; request_token must match the last token sent
// 04 = Insufficient Meseta (S->C; request_token must match the last token sent
// by client)
struct C_MesetaTransaction_Ep3_BA {
@@ -2432,33 +2433,36 @@ struct S_MesetaTransaction_Ep3_BA {
le_uint32_t request_token = 0; // Should match the token sent by the client
} __packed_ws__(S_MesetaTransaction_Ep3_BA, 0x0C);
// BB (S->C): Tournament match information (Episode 3)
// This command is not valid on Episode 3 Trial Edition. Because of this, it
// must have been added fairly late in development, but it seems to be unused,
// perhaps because the E1/E3 commands are generally more useful... but the E1/E3
// commands exist in Trial Edition! So why was this added? Was it just never
// finished? We may never know...
// header.flag is the number of valid match entries.
// BB (S->C): Tournament spectator team list (Episode 3)
// This command is not valid on Episode 3 Trial Edition.
// This is sent in response to an 09 command when viewing the tournament list
// for spectating. newserv doesn't implement this because tournaments work
// differently here - there is no fixed start time, and tournament spectator
// teams behave the same way as free-battle spectator teams.
// header.flag is the number of game entries.
struct S_TournamentMatchInformation_Ep3_BB {
pstring<TextEncoding::MARKED, 0x20> tournament_name;
struct TeamEntry {
le_uint16_t win_count = 0;
le_uint16_t is_active = 0;
pstring<TextEncoding::MARKED, 0x20> name;
} __packed_ws__(TeamEntry, 0x24);
parray<TeamEntry, 0x20> team_entries;
le_uint16_t num_teams = 0;
le_uint16_t unknown_a3 = 0; // Probably actually unused
struct MatchEntry {
struct Ep3TournamentBracketEntry { // Also used in commands CC and E3
le_uint16_t win_count = 0;
le_uint16_t is_active = 0;
pstring<TextEncoding::MARKED, 0x18> team_name;
parray<uint8_t, 8> unused;
} __packed_ws__(Ep3TournamentBracketEntry, 0x24);
struct S_TournamentSpectatorTeamList_Ep3_BB {
/* 0000 */ pstring<TextEncoding::MARKED, 0x20> tournament_name;
/* 0020 */ parray<Ep3TournamentBracketEntry, 0x20> bracket_entries;
/* 04A0 */ le_uint16_t num_teams = 0;
/* 04A2 */ le_uint16_t players_per_team = 0;
struct GameEntry {
pstring<TextEncoding::MARKED, 0x20> name;
uint8_t locked = 0;
uint8_t count = 0;
uint8_t max_count = 0;
uint8_t unused = 0;
} __packed_ws__(MatchEntry, 0x24);
parray<MatchEntry, 0x40> match_entries;
} __packed_ws__(S_TournamentMatchInformation_Ep3_BB, 0xDA4);
uint8_t unknown_a1 = 0; // Possibly unused
} __packed_ws__(GameEntry, 0x24);
/* 04A4 */ parray<GameEntry, 0x40> match_entries;
/* 0DA4 */
} __packed_ws__(S_TournamentSpectatorTeamList_Ep3_BB, 0xDA4);
// BC: Invalid command
// BD: Invalid command
@@ -2646,16 +2650,11 @@ struct S_ConfirmTournamentEntry_Ep3_CC {
pstring<TextEncoding::MARKED, 0x40> tournament_name;
le_uint16_t num_teams = 0;
le_uint16_t players_per_team = 0;
le_uint16_t unknown_a2 = 0;
le_uint16_t unknown_a3 = 0;
le_uint16_t unused1 = 0;
le_uint16_t unused2 = 0;
pstring<TextEncoding::MARKED, 0x20> server_name;
pstring<TextEncoding::MARKED, 0x20> start_time; // e.g. "15:09:30" or "13:03 PST"
struct TeamEntry {
le_uint16_t win_count = 0;
le_uint16_t is_active = 0;
pstring<TextEncoding::MARKED, 0x20> name;
} __packed_ws__(TeamEntry, 0x24);
parray<TeamEntry, 0x20> team_entries;
parray<Ep3TournamentBracketEntry, 0x20> bracket_entries;
} __packed_ws__(S_ConfirmTournamentEntry_Ep3_CC, 0x508);
// CD: Invalid command
@@ -2788,7 +2787,7 @@ struct C_VerifyAccount_V3_DB {
pstring<TextEncoding::ASCII, 0x10> v1_access_key; // Unused
pstring<TextEncoding::ASCII, 0x10> serial_number; // On XB, this is the XBL gamertag
pstring<TextEncoding::ASCII, 0x10> access_key; // On XB, this is the XBL user ID
pstring<TextEncoding::ASCII, 0x08> unused2;
be_uint64_t hardware_id = 0;
le_uint32_t sub_version = 0;
pstring<TextEncoding::ASCII, 0x30> serial_number2; // On XB, this is the XBL gamertag
pstring<TextEncoding::ASCII, 0x30> access_key2; // On XB, this is the XBL user ID
@@ -2799,10 +2798,10 @@ struct C_VerifyAccount_V3_DB {
// all during the data server phase). All current servers use 03/93 instead.
struct C_VerifyAccount_BB_DB {
// Note: These four fields are likely the same as those used in BB's 9E
pstring<TextEncoding::ASCII, 0x10> unknown_a3; // Always blank?
pstring<TextEncoding::ASCII, 0x10> unknown_a4; // == "?"
pstring<TextEncoding::ASCII, 0x10> unknown_a5; // Always blank?
pstring<TextEncoding::ASCII, 0x10> unknown_a6; // Always blank?
pstring<TextEncoding::ASCII, 0x10> v1_serial_number; // Always blank?
pstring<TextEncoding::ASCII, 0x10> v1_access_key; // == "?"
pstring<TextEncoding::ASCII, 0x10> serial_number; // Always blank?
pstring<TextEncoding::ASCII, 0x10> access_key; // Always blank?
le_uint32_t sub_version = 0;
pstring<TextEncoding::ASCII, 0x30> username;
pstring<TextEncoding::ASCII, 0x30> password;
@@ -3066,18 +3065,12 @@ struct S_SyncSystemFile_BB_E2 {
template <typename RulesT>
struct S_TournamentGameDetailsBaseT_Ep3_E3 {
// These fields are used only if the Rules pane is shown
/* 0000 */ pstring<TextEncoding::MARKED, 0x20> name;
/* 0000 */ pstring<TextEncoding::MARKED, 0x20> tournament_name;
/* 0020 */ pstring<TextEncoding::MARKED, 0x20> map_name;
/* 0040 */ RulesT rules;
// This field is used only if the bracket pane is shown
struct BracketEntry {
le_uint16_t win_count = 0;
le_uint16_t is_active = 0;
pstring<TextEncoding::MARKED, 0x18> team_name;
parray<uint8_t, 8> unused;
} __packed_ws__(BracketEntry, 0x24);
/* 0054 */ parray<BracketEntry, 0x20> bracket_entries;
/* 0054 */ parray<Ep3TournamentBracketEntry, 0x20> bracket_entries;
// This field is used only if the Opponents pane is shown. If players_per_team
// is 2, all fields are shown; if player_per_team is 1, team_name and
+6 -1
View File
@@ -1309,7 +1309,12 @@ static void on_BA_Ep3(shared_ptr<Client> c, uint16_t command, uint32_t, string&
total_meseta_earned = c->login->account->ep3_total_meseta_earned;
} else {
if (c->login->account->ep3_current_meseta < in_cmd.value) {
throw runtime_error("meseta overdraft not allowed");
S_MesetaTransaction_Ep3_BA out_cmd = {
c->login->account->ep3_current_meseta,
c->login->account->ep3_total_meseta_earned,
in_cmd.request_token};
send_command(c, command, 0x04, &out_cmd, sizeof(out_cmd));
return;
}
c->login->account->ep3_current_meseta -= in_cmd.value;
if (!s->is_replay) {
+12 -8
View File
@@ -3296,9 +3296,11 @@ void send_ep3_confirm_tournament_entry(
cmd.num_teams = min<size_t>(teams.size(), 0x20);
cmd.players_per_team = (tourn->get_flags() & Episode3::Tournament::Flag::IS_2V2) ? 2 : 1;
for (size_t z = 0; z < min<size_t>(teams.size(), 0x20); z++) {
cmd.team_entries[z].win_count = teams[z]->num_rounds_cleared;
cmd.team_entries[z].is_active = teams[z]->is_active;
cmd.team_entries[z].name.encode(teams[z]->name, c->language());
const auto& team = teams[z];
auto& entry = cmd.bracket_entries[z];
entry.win_count = team->num_rounds_cleared;
entry.is_active = team->is_active;
entry.team_name.encode(team->name, c->language());
}
}
send_command_t(c, 0xCC, tourn ? 0x01 : 0x00, cmd);
@@ -3389,14 +3391,16 @@ void send_ep3_tournament_details_t(
shared_ptr<const Episode3::Tournament> tourn) {
S_TournamentGameDetailsBaseT_Ep3_E3<RulesT> cmd;
auto vm = tourn->get_map()->version(c->language());
cmd.name.encode(tourn->get_name(), c->language());
cmd.tournament_name.encode(tourn->get_name(), c->language());
cmd.map_name.encode(vm->map->name.decode(vm->language), c->language());
cmd.rules = tourn->get_rules();
const auto& teams = tourn->all_teams();
for (size_t z = 0; z < min<size_t>(teams.size(), 0x20); z++) {
cmd.bracket_entries[z].win_count = teams[z]->num_rounds_cleared;
cmd.bracket_entries[z].is_active = teams[z]->is_active ? 1 : 0;
cmd.bracket_entries[z].team_name.encode(teams[z]->name, c->language());
const auto& team = teams[z];
auto& entry = cmd.bracket_entries[z];
entry.win_count = team->num_rounds_cleared;
entry.is_active = team->is_active ? 1 : 0;
entry.team_name.encode(team->name, c->language());
}
cmd.num_bracket_entries = teams.size();
cmd.players_per_team = (tourn->get_flags() & Episode3::Tournament::Flag::IS_2V2) ? 2 : 1;
@@ -3440,7 +3444,7 @@ void send_ep3_game_details_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
if (tourn) {
S_TournamentGameDetailsBaseT_Ep3_E3<RulesT> cmd;
cmd.name.encode(l->name, c->language());
cmd.tournament_name.encode(l->name, c->language());
auto vm = tourn->get_map()->version(c->language());
cmd.map_name.encode(vm->map->name.decode(vm->language), c->language());