add safeties for 6xBB and 6xBC commands
This commit is contained in:
+3
-2
@@ -35,7 +35,7 @@ public:
|
|||||||
// TODO: It'd be nice to use a pattern here (e.g. all server-side flags are
|
// TODO: It'd be nice to use a pattern here (e.g. all server-side flags are
|
||||||
// in the high bits) but that would require re-recording or manually
|
// in the high bits) but that would require re-recording or manually
|
||||||
// rewriting all the tests
|
// rewriting all the tests
|
||||||
CLIENT_SIDE_MASK = 0xF73CFFFF7C0BFFFB,
|
CLIENT_SIDE_MASK = 0xE73CFFFF7C0BFFFB,
|
||||||
|
|
||||||
// Version-related flags
|
// Version-related flags
|
||||||
CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002,
|
CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002,
|
||||||
@@ -73,6 +73,7 @@ public:
|
|||||||
SHOULD_SEND_ENABLE_SAVE = 0x0004000000000000,
|
SHOULD_SEND_ENABLE_SAVE = 0x0004000000000000,
|
||||||
SWITCH_ASSIST_ENABLED = 0x0000000100000000,
|
SWITCH_ASSIST_ENABLED = 0x0000000100000000,
|
||||||
IS_CLIENT_CUSTOMIZATION = 0x0100000000000000,
|
IS_CLIENT_CUSTOMIZATION = 0x0100000000000000,
|
||||||
|
EP3_ALLOW_6xBC = 0x1000000000000000, // Server-side only
|
||||||
|
|
||||||
// Cheat mode and option flags
|
// Cheat mode and option flags
|
||||||
INFINITE_HP_ENABLED = 0x0000000200000000,
|
INFINITE_HP_ENABLED = 0x0000000200000000,
|
||||||
@@ -80,7 +81,7 @@ public:
|
|||||||
DEBUG_ENABLED = 0x0000000800000000,
|
DEBUG_ENABLED = 0x0000000800000000,
|
||||||
ITEM_DROP_NOTIFICATIONS_1 = 0x0010000000000000,
|
ITEM_DROP_NOTIFICATIONS_1 = 0x0010000000000000,
|
||||||
ITEM_DROP_NOTIFICATIONS_2 = 0x0020000000000000,
|
ITEM_DROP_NOTIFICATIONS_2 = 0x0020000000000000,
|
||||||
FORCE_BATTLE_MODE_GAME = 0x0800000000000000,
|
FORCE_BATTLE_MODE_GAME = 0x0800000000000000, // Server-side only
|
||||||
|
|
||||||
// Proxy option flags
|
// Proxy option flags
|
||||||
PROXY_SAVE_FILES = 0x0000001000000000,
|
PROXY_SAVE_FILES = 0x0000001000000000,
|
||||||
|
|||||||
+40
-22
@@ -5831,14 +5831,25 @@ struct G_IdentifyResult_BB_6xB9 {
|
|||||||
} __packed_ws__(G_IdentifyResult_BB_6xB9, 0x18);
|
} __packed_ws__(G_IdentifyResult_BB_6xB9, 0x18);
|
||||||
|
|
||||||
// 6xBA: Sync card trade state (Episode 3)
|
// 6xBA: Sync card trade state (Episode 3)
|
||||||
// This command calls various member functions in TCardTradeServer.
|
// This command calls various member functions in TCardTrade. This is used
|
||||||
|
// after both players are standing at the respective kiosks and are ready to
|
||||||
|
// trade cards.
|
||||||
|
|
||||||
struct G_SyncCardTradeState_Ep3_6xBA {
|
struct G_SyncCardTradeState_Ep3_6xBA {
|
||||||
G_ClientIDHeader header;
|
G_ClientIDHeader header;
|
||||||
le_uint16_t what = 0; // Low byte must be < 9; this indexes into a handler table
|
// Values for what:
|
||||||
le_uint16_t unknown_a2 = 0;
|
// 1 = add card to trade (card_id and count used)
|
||||||
le_uint32_t unknown_a3 = 0;
|
// 2 = remove card from trade (card_id and count used)
|
||||||
le_uint32_t unknown_a4 = 0;
|
// 3 = first confirmation
|
||||||
|
// 4 = cancel first confirmation
|
||||||
|
// 5 = second confirmation
|
||||||
|
// 6 = cancel second confirmation
|
||||||
|
// 7 = leave trade window
|
||||||
|
// Anything else = does nothing
|
||||||
|
le_uint16_t what = 0;
|
||||||
|
le_uint16_t unused = 0;
|
||||||
|
le_uint32_t card_id = 0; // Only used when what = 1 or 2
|
||||||
|
le_uint32_t count = 0; // Only used when what = 1 or 2
|
||||||
} __packed_ws__(G_SyncCardTradeState_Ep3_6xBA, 0x10);
|
} __packed_ws__(G_SyncCardTradeState_Ep3_6xBA, 0x10);
|
||||||
|
|
||||||
// 6xBA: BB accept tekker result (handled by the server)
|
// 6xBA: BB accept tekker result (handled by the server)
|
||||||
@@ -5848,28 +5859,35 @@ struct G_AcceptItemIdentification_BB_6xBA {
|
|||||||
le_uint32_t item_id = 0;
|
le_uint32_t item_id = 0;
|
||||||
} __packed_ws__(G_AcceptItemIdentification_BB_6xBA, 8);
|
} __packed_ws__(G_AcceptItemIdentification_BB_6xBA, 8);
|
||||||
|
|
||||||
// 6xBB: Sync card trade state (Episode 3)
|
// 6xBB: Sync card trade server state (Episode 3)
|
||||||
// This command calls various member functions in TCardTradeServer.
|
// This command calls various member functions in TCardTradeServer. This is
|
||||||
// TODO: Certain invalid values for slot/args in this command can crash the
|
// used before both players have entered the card trade sequence (as opposed to
|
||||||
// client (what is properly bounds-checked). Find out the actual limits for
|
// 6xBA, which is used during that sequence).
|
||||||
// slot/args and make newserv enforce them.
|
|
||||||
|
|
||||||
struct G_SyncCardTradeState_Ep3_6xBB {
|
struct G_SyncCardTradeServerState_Ep3_6xBB {
|
||||||
G_ClientIDHeader header;
|
G_ClientIDHeader header;
|
||||||
le_uint16_t what = 0; // Must be < 5; this indexes into a jump table
|
// Values for what:
|
||||||
le_uint16_t slot = 0;
|
// 0 = request slot (leader sends accept message with what=1)
|
||||||
|
// 1 = accept slot (args[0] is the accepted client ID)
|
||||||
|
// 2 = cancel all slot requests
|
||||||
|
// 3 = replace all slots (args[0, 1] are the two client IDs to accept into
|
||||||
|
// the two slots)
|
||||||
|
// 4 = relinquish all slots
|
||||||
|
// Anything else = does nothing
|
||||||
|
le_uint16_t what = 0;
|
||||||
|
le_uint16_t slot = 0; // Must be 0 or 1 (not bounds checked!)
|
||||||
parray<le_uint32_t, 4> args;
|
parray<le_uint32_t, 4> args;
|
||||||
} __packed_ws__(G_SyncCardTradeState_Ep3_6xBB, 0x18);
|
} __packed_ws__(G_SyncCardTradeServerState_Ep3_6xBB, 0x18);
|
||||||
|
|
||||||
// 6xBB: BB bank request (handled by the server)
|
// 6xBB: BB bank request (handled by the server)
|
||||||
|
|
||||||
// 6xBC: Card counts (Episode 3)
|
// 6xBC: Card counts (Episode 3)
|
||||||
// This is sent by the client in response to a 6xB5x38 command.
|
// This is sent by the client in response to a 6xB5x38 command. This is used
|
||||||
// It's possible that this is an early, now-unused implementation of the CAx49
|
// along with 6xB5x38 so clients can see each other's card counts. Curiously,
|
||||||
// command. When the client receives this command, it copies the data into a
|
// this command is smaller than 0x400 bytes (even on NTE) but uses the extended
|
||||||
// globally-allocated array, but nothing reads from this array. Curiously, this
|
// subcommand format anyway.
|
||||||
// command is smaller than 0x400 bytes, but uses the extended subcommand format
|
// An Episode 3 client will crash if it receives this command when the card
|
||||||
// anyway (and uses the 6D command rather than 62).
|
// trade window is not active.
|
||||||
|
|
||||||
struct G_CardCounts_Ep3NTE_6xBC {
|
struct G_CardCounts_Ep3NTE_6xBC {
|
||||||
G_ExtendedHeaderT<G_UnusedHeader> header;
|
G_ExtendedHeaderT<G_UnusedHeader> header;
|
||||||
@@ -6855,8 +6873,8 @@ struct G_AdvanceFromStartingRollsPhase_Ep3_CAx37 {
|
|||||||
// 6xB5x38: Card counts request
|
// 6xB5x38: Card counts request
|
||||||
// This command causes the client identified by requested_client_id to send a
|
// This command causes the client identified by requested_client_id to send a
|
||||||
// 6xBC command to the client identified by reply_to_client_id (privately, via
|
// 6xBC command to the client identified by reply_to_client_id (privately, via
|
||||||
// the 6D command). This appears to be unused; it is likely superseded by the
|
// the 6D command). This is sent at the beginning of the card trade window
|
||||||
// CAx49 command.
|
// sequence.
|
||||||
|
|
||||||
struct G_CardCountsRequest_Ep3_6xB5x38 {
|
struct G_CardCountsRequest_Ep3_6xB5x38 {
|
||||||
G_CardBattleCommandHeader header = {0xB5, sizeof(G_CardCountsRequest_Ep3_6xB5x38) / 4, 0, 0x38, 0, 0, 0};
|
G_CardBattleCommandHeader header = {0xB5, sizeof(G_CardCountsRequest_Ep3_6xB5x38) / 4, 0, 0x38, 0, 0, 0};
|
||||||
|
|||||||
+1
-1
@@ -212,7 +212,7 @@ ItemCreator::DropResult ItemCreator::on_box_item_drop_with_area_norm(uint8_t are
|
|||||||
}
|
}
|
||||||
|
|
||||||
ItemCreator::DropResult ItemCreator::on_monster_item_drop_with_area_norm(uint32_t enemy_type, uint8_t area_norm) {
|
ItemCreator::DropResult ItemCreator::on_monster_item_drop_with_area_norm(uint32_t enemy_type, uint8_t area_norm) {
|
||||||
// Note: THe original GC implementation uses (enemy_type > 0x58) here; we
|
// Note: The original GC implementation uses (enemy_type > 0x58) here; we
|
||||||
// extend it to the full array size for BB
|
// extend it to the full array size for BB
|
||||||
if (enemy_type >= 0x64) {
|
if (enemy_type >= 0x64) {
|
||||||
this->log.warning("Invalid enemy type: %" PRIX32, enemy_type);
|
this->log.warning("Invalid enemy type: %" PRIX32, enemy_type);
|
||||||
|
|||||||
+27
-13
@@ -1185,6 +1185,15 @@ static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if ((static_cast<uint8_t>(data[0]) == 0xBB) && is_ep3(ses->version())) {
|
||||||
|
if (!validate_6xBB(check_size_t<G_SyncCardTradeServerState_Ep3_6xBB>(data))) {
|
||||||
|
return HandlerResult::Type::SUPPRESS;
|
||||||
|
}
|
||||||
|
return HandlerResult::Type::MODIFIED;
|
||||||
|
|
||||||
|
} else if ((static_cast<uint8_t>(data[0]) == 0xBC) && !ses->config.check_flag(Client::Flag::EP3_ALLOW_6xBC)) {
|
||||||
|
return HandlerResult::Type::SUPPRESS;
|
||||||
|
|
||||||
} else if ((static_cast<uint8_t>(data[0]) == 0xBD) &&
|
} else if ((static_cast<uint8_t>(data[0]) == 0xBD) &&
|
||||||
ses->config.check_flag(Client::Flag::PROXY_EP3_UNMASK_WHISPERS) &&
|
ses->config.check_flag(Client::Flag::PROXY_EP3_UNMASK_WHISPERS) &&
|
||||||
is_ep3(ses->version())) {
|
is_ep3(ses->version())) {
|
||||||
@@ -1228,15 +1237,15 @@ static HandlerResult C_GXB_61(shared_ptr<ProxyServer::LinkedSession> ses, uint16
|
|||||||
C_CharacterData_V3_61_98* pd;
|
C_CharacterData_V3_61_98* pd;
|
||||||
if (flag == 4) { // Episode 3
|
if (flag == 4) { // Episode 3
|
||||||
auto& ep3_pd = check_size_t<C_CharacterData_Ep3_61_98>(data);
|
auto& ep3_pd = check_size_t<C_CharacterData_Ep3_61_98>(data);
|
||||||
if (ep3_pd.ep3_config.is_encrypted) {
|
// if (ep3_pd.ep3_config.is_encrypted) {
|
||||||
decrypt_trivial_gci_data(
|
// decrypt_trivial_gci_data(
|
||||||
&ep3_pd.ep3_config.card_counts,
|
// &ep3_pd.ep3_config.card_counts,
|
||||||
offsetof(Episode3::PlayerConfig, decks) - offsetof(Episode3::PlayerConfig, card_counts),
|
// offsetof(Episode3::PlayerConfig, decks) - offsetof(Episode3::PlayerConfig, card_counts),
|
||||||
ep3_pd.ep3_config.basis);
|
// ep3_pd.ep3_config.basis);
|
||||||
ep3_pd.ep3_config.is_encrypted = 0;
|
// ep3_pd.ep3_config.is_encrypted = 0;
|
||||||
ep3_pd.ep3_config.basis = 0;
|
// ep3_pd.ep3_config.basis = 0;
|
||||||
modified = true;
|
// modified = true;
|
||||||
}
|
// }
|
||||||
pd = reinterpret_cast<C_CharacterData_V3_61_98*>(&ep3_pd);
|
pd = reinterpret_cast<C_CharacterData_V3_61_98*>(&ep3_pd);
|
||||||
} else {
|
} else {
|
||||||
if (is_ep3(ses->version()) && (ses->version() != Version::GC_EP3_NTE)) {
|
if (is_ep3(ses->version()) && (ses->version() != Version::GC_EP3_NTE)) {
|
||||||
@@ -2056,10 +2065,8 @@ HandlerResult C_6x<void>(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, u
|
|||||||
|
|
||||||
} else if (data[0] == 0x48) {
|
} else if (data[0] == 0x48) {
|
||||||
if (ses->config.check_flag(Client::Flag::INFINITE_TP_ENABLED)) {
|
if (ses->config.check_flag(Client::Flag::INFINITE_TP_ENABLED)) {
|
||||||
send_player_stats_change(ses->client_channel,
|
send_player_stats_change(ses->client_channel, ses->lobby_client_id, PlayerStatsChange::ADD_TP, 255);
|
||||||
ses->lobby_client_id, PlayerStatsChange::ADD_TP, 255);
|
send_player_stats_change(ses->server_channel, ses->lobby_client_id, PlayerStatsChange::ADD_TP, 255);
|
||||||
send_player_stats_change(ses->server_channel,
|
|
||||||
ses->lobby_client_id, PlayerStatsChange::ADD_TP, 255);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (data[0] == 0x5F) {
|
} else if (data[0] == 0x5F) {
|
||||||
@@ -2068,6 +2075,13 @@ HandlerResult C_6x<void>(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, u
|
|||||||
|
|
||||||
} else if (data[0] == 0x60 || static_cast<uint8_t>(data[0]) == 0xA2) {
|
} else if (data[0] == 0x60 || static_cast<uint8_t>(data[0]) == 0xA2) {
|
||||||
return SC_6x60_6xA2(ses, data);
|
return SC_6x60_6xA2(ses, data);
|
||||||
|
|
||||||
|
} else if (is_ep3(ses->version()) && (data.size() > 4) && (static_cast<uint8_t>(data[0]) == 0xB5)) {
|
||||||
|
if (data[4] == 0x38) {
|
||||||
|
ses->config.set_flag(Client::Flag::EP3_ALLOW_6xBC);
|
||||||
|
} else if (data[4] == 0x3C) {
|
||||||
|
ses->config.clear_flag(Client::Flag::EP3_ALLOW_6xBC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+62
-10
@@ -1164,14 +1164,6 @@ static void on_forward_check_ep3_lobby(shared_ptr<Client> c, uint8_t command, ui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_forward_check_ep3_game(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
|
||||||
check_size_t<G_UnusedHeader>(data, size, 0xFFFF);
|
|
||||||
auto l = c->require_lobby();
|
|
||||||
if (l->is_game() && l->is_ep3()) {
|
|
||||||
forward_subcommand(c, command, flag, data, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Ep3 subcommands
|
// Ep3 subcommands
|
||||||
|
|
||||||
@@ -1199,6 +1191,10 @@ static void on_ep3_battle_subs(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
|||||||
if (l->is_game() && (cmd.client_id >= 4)) {
|
if (l->is_game() && (cmd.client_id >= 4)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (header.subsubcommand == 0x38) {
|
||||||
|
c->config.set_flag(Client::Flag::EP3_ALLOW_6xBC);
|
||||||
|
} else if (header.subsubcommand == 0x3C) {
|
||||||
|
c->config.clear_flag(Client::Flag::EP3_ALLOW_6xBC);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING) && (c->version() != Version::GC_EP3_NTE)) {
|
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING) && (c->version() != Version::GC_EP3_NTE)) {
|
||||||
@@ -1208,6 +1204,28 @@ static void on_ep3_battle_subs(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
|||||||
forward_subcommand(c, command, flag, data.data(), data.size());
|
forward_subcommand(c, command, flag, data.data(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_ep3_trade_card_counts(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||||
|
if (c->version() == Version::GC_EP3_NTE) {
|
||||||
|
check_size_t<G_CardCounts_Ep3NTE_6xBC>(data, size, 0xFFFF);
|
||||||
|
} else {
|
||||||
|
check_size_t<G_CardCounts_Ep3_6xBC>(data, size, 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!command_is_private(command)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto l = c->require_lobby();
|
||||||
|
if (!l->is_game() || !l->is_ep3()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto target = l->clients.at(flag);
|
||||||
|
if (!target || !target->config.check_flag(Client::Flag::EP3_ALLOW_6xBC)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_subcommand(c, command, flag, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Chat commands and the like
|
// Chat commands and the like
|
||||||
|
|
||||||
@@ -2335,12 +2353,46 @@ static void on_open_shop_bb_or_ep3_battle_subs(shared_ptr<Client> c, uint8_t com
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool validate_6xBB(G_SyncCardTradeServerState_Ep3_6xBB& cmd) {
|
||||||
|
if ((cmd.header.client_id >= 4) || (cmd.slot > 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TTradeCardServer uses 4 to indicate the slot is empty, so we allow 4 in
|
||||||
|
// the client ID checks below
|
||||||
|
switch (cmd.what) {
|
||||||
|
case 1:
|
||||||
|
if (cmd.args[0] >= 5) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cmd.args[1] = 0;
|
||||||
|
cmd.args[2] = 0;
|
||||||
|
cmd.args[3] = 0;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
cmd.args.clear(0);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (cmd.args[0] >= 5 || cmd.args[1] >= 5) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cmd.args[2] = 0;
|
||||||
|
cmd.args[3] = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void on_open_bank_bb_or_card_trade_counter_ep3(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
static void on_open_bank_bb_or_card_trade_counter_ep3(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||||
auto l = c->require_lobby();
|
auto l = c->require_lobby();
|
||||||
if ((l->base_version == Version::BB_V4) && l->is_game()) {
|
if ((l->base_version == Version::BB_V4) && l->is_game()) {
|
||||||
c->config.set_flag(Client::Flag::AT_BANK_COUNTER);
|
c->config.set_flag(Client::Flag::AT_BANK_COUNTER);
|
||||||
send_bank(c);
|
send_bank(c);
|
||||||
} else if (l->is_ep3()) {
|
} else if (l->is_ep3() && validate_6xBB(check_size_t<G_SyncCardTradeServerState_Ep3_6xBB>(data, size))) {
|
||||||
forward_subcommand(c, command, flag, data, size);
|
forward_subcommand(c, command, flag, data, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4827,7 +4879,7 @@ const SubcommandDefinition subcommand_definitions[0x100] = {
|
|||||||
/* 6xB9 */ {NONE, NONE, 0xB9, on_invalid},
|
/* 6xB9 */ {NONE, NONE, 0xB9, on_invalid},
|
||||||
/* 6xBA */ {NONE, NONE, 0xBA, on_accept_identify_item_bb},
|
/* 6xBA */ {NONE, NONE, 0xBA, on_accept_identify_item_bb},
|
||||||
/* 6xBB */ {NONE, NONE, 0xBB, on_open_bank_bb_or_card_trade_counter_ep3},
|
/* 6xBB */ {NONE, NONE, 0xBB, on_open_bank_bb_or_card_trade_counter_ep3},
|
||||||
/* 6xBC */ {NONE, NONE, 0xBC, on_forward_check_ep3_game},
|
/* 6xBC */ {NONE, NONE, 0xBC, on_ep3_trade_card_counts},
|
||||||
/* 6xBD */ {NONE, NONE, 0xBD, on_ep3_private_word_select_bb_bank_action, SDF::ALWAYS_FORWARD_TO_WATCHERS},
|
/* 6xBD */ {NONE, NONE, 0xBD, on_ep3_private_word_select_bb_bank_action, SDF::ALWAYS_FORWARD_TO_WATCHERS},
|
||||||
/* 6xBE */ {NONE, NONE, 0xBE, forward_subcommand_m, SDF::ALWAYS_FORWARD_TO_WATCHERS | SDF::ALLOW_FORWARD_TO_WATCHED_LOBBY},
|
/* 6xBE */ {NONE, NONE, 0xBE, forward_subcommand_m, SDF::ALWAYS_FORWARD_TO_WATCHERS | SDF::ALLOW_FORWARD_TO_WATCHED_LOBBY},
|
||||||
/* 6xBF */ {NONE, NONE, 0xBF, on_forward_check_ep3_lobby},
|
/* 6xBF */ {NONE, NONE, 0xBF, on_forward_check_ep3_lobby},
|
||||||
|
|||||||
@@ -122,3 +122,5 @@ protected:
|
|||||||
bool from_client_customization);
|
bool from_client_customization);
|
||||||
G_SyncPlayerDispAndInventory_BaseV1 base_v1() const;
|
G_SyncPlayerDispAndInventory_BaseV1 base_v1() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool validate_6xBB(G_SyncCardTradeServerState_Ep3_6xBB& cmd);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user