fix DC NTE lobby interactions
This commit is contained in:
+5
-1
@@ -95,8 +95,12 @@ struct Client : public std::enable_shared_from_this<Client> {
|
||||
|
||||
Config() = default;
|
||||
|
||||
[[nodiscard]] static inline bool check_flag(uint64_t enabled_flags, Flag flag) {
|
||||
return !!(enabled_flags & static_cast<uint64_t>(flag));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
||||
return !!(this->enabled_flags & static_cast<uint64_t>(flag));
|
||||
return this->check_flag(this->enabled_flags, flag);
|
||||
}
|
||||
inline void set_flag(Flag flag) {
|
||||
this->enabled_flags |= static_cast<uint64_t>(flag);
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
#include <phosg/Random.hh>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Client.hh"
|
||||
#include "ItemData.hh"
|
||||
#include "ItemParameterTable.hh"
|
||||
#include "Loggers.hh"
|
||||
#include "PSOEncryption.hh"
|
||||
#include "ServerState.hh"
|
||||
#include "StaticGameData.hh"
|
||||
#include "Text.hh"
|
||||
#include "Version.hh"
|
||||
@@ -33,7 +35,7 @@ void PlayerVisualConfig::compute_name_color_checksum() {
|
||||
this->name_color_checksum = this->compute_name_color_checksum(this->name_color);
|
||||
}
|
||||
|
||||
void PlayerDispDataDCPCV3::enforce_lobby_join_limits(GameVersion target_version) {
|
||||
void PlayerDispDataDCPCV3::enforce_lobby_join_limits_for_client(shared_ptr<Client> c) {
|
||||
struct ClassMaxes {
|
||||
uint16_t costume;
|
||||
uint16_t skin;
|
||||
@@ -79,7 +81,7 @@ void PlayerDispDataDCPCV3::enforce_lobby_join_limits(GameVersion target_version)
|
||||
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000}};
|
||||
|
||||
const ClassMaxes* maxes;
|
||||
if ((target_version == GameVersion::PC) || (target_version == GameVersion::DC)) {
|
||||
if ((c->version() == GameVersion::PC) || (c->version() == GameVersion::DC)) {
|
||||
// V1/V2 have fewer classes, so we'll substitute some here
|
||||
switch (this->visual.char_class) {
|
||||
case 0: // HUmar
|
||||
@@ -115,7 +117,7 @@ void PlayerDispDataDCPCV3::enforce_lobby_join_limits(GameVersion target_version)
|
||||
}
|
||||
|
||||
maxes = &v1_v2_class_maxes[this->visual.char_class];
|
||||
this->visual.version = 2;
|
||||
this->visual.version = c->config.check_flag(Client::Flag::IS_DC_V1) ? 1 : 2;
|
||||
|
||||
} else {
|
||||
if (this->visual.char_class >= 19) {
|
||||
@@ -139,8 +141,8 @@ void PlayerDispDataDCPCV3::enforce_lobby_join_limits(GameVersion target_version)
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerDispDataBB::enforce_lobby_join_limits(GameVersion version) {
|
||||
if (version != GameVersion::BB) {
|
||||
void PlayerDispDataBB::enforce_lobby_join_limits_for_client(shared_ptr<Client> c) {
|
||||
if (c->version() != GameVersion::BB) {
|
||||
throw logic_error("PlayerDispDataBB being sent to non-BB client");
|
||||
}
|
||||
this->play_time = 0;
|
||||
@@ -631,15 +633,26 @@ size_t PlayerInventory::remove_all_items_of_type(uint8_t data1_0, int16_t data1_
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PlayerInventory::decode_for_version(GameVersion version) {
|
||||
void PlayerInventory::decode_from_client(shared_ptr<Client> c) {
|
||||
for (size_t z = 0; z < this->items.size(); z++) {
|
||||
this->items[z].data.decode_for_version(version);
|
||||
this->items[z].data.decode_for_version(c->version());
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerInventory::encode_for_version(GameVersion version, shared_ptr<const ItemParameterTable> item_parameter_table) {
|
||||
void PlayerInventory::encode_for_client(shared_ptr<Client> c) {
|
||||
if (c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
// DC NTE has the item count as a 32-bit value here, whereas every other
|
||||
// version uses a single byte. To stop DC NTE from crashing by trying to
|
||||
// construct far more than 30 TItem objects, we clear the fields DC NTE
|
||||
// doesn't know about.
|
||||
this->hp_from_materials = 0;
|
||||
this->tp_from_materials = 0;
|
||||
this->language = 0;
|
||||
}
|
||||
|
||||
auto item_parameter_table = c->require_server_state()->item_parameter_table_for_version(c->version());
|
||||
for (size_t z = 0; z < this->items.size(); z++) {
|
||||
this->items[z].data.encode_for_version(version, item_parameter_table);
|
||||
this->items[z].data.encode_for_version(c->version(), item_parameter_table);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "Text.hh"
|
||||
#include "Version.hh"
|
||||
|
||||
struct Client;
|
||||
class ItemParameterTable;
|
||||
|
||||
// PSO V2 stored some extra data in the character structs in a format that I'm
|
||||
@@ -68,6 +69,8 @@ struct PlayerInventory {
|
||||
/* 0004 */ parray<PlayerInventoryItem, 30> items;
|
||||
/* 034C */
|
||||
|
||||
void clear_dc_nte_unused_fields();
|
||||
|
||||
size_t find_item(uint32_t item_id) const;
|
||||
size_t find_item_by_primary_identifier(uint32_t primary_identifier) const;
|
||||
|
||||
@@ -78,8 +81,8 @@ struct PlayerInventory {
|
||||
|
||||
size_t remove_all_items_of_type(uint8_t data0, int16_t data1 = -1);
|
||||
|
||||
void decode_for_version(GameVersion version);
|
||||
void encode_for_version(GameVersion version, std::shared_ptr<const ItemParameterTable> item_parameter_table);
|
||||
void decode_from_client(std::shared_ptr<Client> c);
|
||||
void encode_for_client(std::shared_ptr<Client> c);
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PlayerBank {
|
||||
@@ -142,7 +145,8 @@ struct PlayerDispDataDCPCV3 {
|
||||
/* 74 */ parray<uint8_t, 0x48> config;
|
||||
/* BC */ parray<uint8_t, 0x14> technique_levels_v1;
|
||||
/* D0 */
|
||||
void enforce_lobby_join_limits(GameVersion target_version);
|
||||
|
||||
void enforce_lobby_join_limits_for_client(std::shared_ptr<Client> c);
|
||||
PlayerDispDataBB to_bb(uint8_t to_language, uint8_t from_language) const;
|
||||
} __attribute__((packed));
|
||||
|
||||
@@ -168,7 +172,7 @@ struct PlayerDispDataBB {
|
||||
/* 017C */ parray<uint8_t, 0x14> technique_levels_v1;
|
||||
/* 0190 */
|
||||
|
||||
void enforce_lobby_join_limits(GameVersion target_version);
|
||||
void enforce_lobby_join_limits_for_client(std::shared_ptr<Client> c);
|
||||
PlayerDispDataDCPCV3 to_dcpcv3(uint8_t to_language, uint8_t from_language) const;
|
||||
PlayerDispDataBBPreview to_preview() const;
|
||||
void apply_preview(const PlayerDispDataBBPreview&);
|
||||
|
||||
@@ -2878,7 +2878,7 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
|
||||
default:
|
||||
throw logic_error("player data command not implemented for version");
|
||||
}
|
||||
player->inventory.decode_for_version(c->version());
|
||||
player->inventory.decode_from_client(c);
|
||||
c->channel.language = player->inventory.language;
|
||||
|
||||
string name_str = player->disp.name.decode(c->language());
|
||||
|
||||
+169
-48
@@ -23,6 +23,19 @@ using namespace std;
|
||||
// The functions in this file are called when a client sends a game command
|
||||
// (60, 62, 6C, 6D, C9, or CB).
|
||||
|
||||
// There are three different sets of subcommand numbers: the DC NTE set, the
|
||||
// November 2000 prototype set, and the set used by all other versions of the
|
||||
// game (starting from the December 2000 prototype, all the way through BB).
|
||||
// Currently we do not support the November 2000 prototype, but we do support
|
||||
// DC NTE. In general, DC NTE clients can only interact with non-NTE players in
|
||||
// very limited ways, since most subcommand-based actions take place in games,
|
||||
// and non-NTE players cannot join NTE games. Commands sent by DC NTE clients
|
||||
// are not handled by the functions defined in subcommand_handlers, but are
|
||||
// instead handled by handle_subcommand_dc_nte. This means we only have to
|
||||
// consider sending to DC NTE clients in a small subset of the command handlers
|
||||
// (those that can occur in the lobby), and we can skip sending most
|
||||
// subcommands to DC NTE by default.
|
||||
|
||||
bool command_is_private(uint8_t command) {
|
||||
return (command == 0x62) || (command == 0x6D);
|
||||
}
|
||||
@@ -39,7 +52,8 @@ static void forward_subcommand(
|
||||
uint8_t command,
|
||||
uint8_t flag,
|
||||
const void* data,
|
||||
size_t size) {
|
||||
size_t size,
|
||||
uint8_t dc_nte_subcommand = 0x00) {
|
||||
|
||||
// If the command is an Ep3-only command, make sure an Ep3 client sent it
|
||||
bool command_is_ep3 = (command & 0xF0) == 0xC0;
|
||||
@@ -56,7 +70,17 @@ static void forward_subcommand(
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
send_command(target, command, flag, data, size);
|
||||
if (target->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
if (dc_nte_subcommand) {
|
||||
string nte_data(reinterpret_cast<const char*>(data), size);
|
||||
nte_data[0] = dc_nte_subcommand;
|
||||
send_command(target, command, flag, nte_data);
|
||||
} else {
|
||||
c->log.warning("Attempted to send unsupported target command to DC NTE client; dropping command");
|
||||
}
|
||||
} else {
|
||||
send_command(target, command, flag, data, size);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (command_is_ep3) {
|
||||
@@ -66,8 +90,25 @@ static void forward_subcommand(
|
||||
}
|
||||
send_command(target, command, flag, data, size);
|
||||
}
|
||||
|
||||
} else {
|
||||
send_command_excluding_client(l, c, command, flag, data, size);
|
||||
string nte_data;
|
||||
for (auto& lc : l->clients) {
|
||||
if (!lc || (lc == c)) {
|
||||
continue;
|
||||
}
|
||||
if (lc->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
if (dc_nte_subcommand) {
|
||||
if (nte_data.empty()) {
|
||||
nte_data.assign(reinterpret_cast<const char*>(data), size);
|
||||
nte_data[0] = dc_nte_subcommand;
|
||||
}
|
||||
send_command(lc, command, flag, nte_data);
|
||||
}
|
||||
} else {
|
||||
send_command(lc, command, flag, data, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Before battle, forward only chat commands to watcher lobbies; during
|
||||
@@ -590,13 +631,21 @@ static void on_word_select(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
}
|
||||
}
|
||||
|
||||
static void on_set_player_visibility(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
static void on_set_player_invisible(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_SetPlayerVisibility_6x22_6x23>(data, size);
|
||||
if (cmd.header.client_id != c->lobby_client_id) {
|
||||
return;
|
||||
}
|
||||
forward_subcommand(c, command, flag, data, size, 0x1E);
|
||||
}
|
||||
|
||||
static void on_set_player_visible(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_SetPlayerVisibility_6x22_6x23>(data, size);
|
||||
|
||||
if (cmd.header.client_id == c->lobby_client_id) {
|
||||
auto l = c->require_lobby();
|
||||
forward_subcommand(c, command, flag, data, size, 0x1F);
|
||||
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
auto l = c->require_lobby();
|
||||
if (!l->is_game() && !c->config.check_flag(Client::Flag::IS_DC_V1)) {
|
||||
send_arrow_update(l);
|
||||
}
|
||||
@@ -610,13 +659,18 @@ static void on_set_player_visibility(shared_ptr<Client> c, uint8_t command, uint
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Game commands used by cheat mechanisms
|
||||
|
||||
template <typename CmdT>
|
||||
static void on_change_floor(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<CmdT>(data, size);
|
||||
static void on_change_floor_6x1F(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_SetPlayerArea_6x1F>(data, size);
|
||||
c->floor = cmd.floor;
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
|
||||
static void on_change_floor_6x21(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_InterLevelWarp_6x21>(data, size);
|
||||
c->floor = cmd.floor;
|
||||
forward_subcommand(c, command, flag, data, size, 0x1D);
|
||||
}
|
||||
|
||||
// When a player dies, decrease their mag's synchro
|
||||
static void on_player_died(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_ClientIDHeader>(data, size, 0xFFFF);
|
||||
@@ -722,7 +776,7 @@ static void on_switch_state_changed(shared_ptr<Client> c, uint8_t command, uint8
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename CmdT>
|
||||
template <typename CmdT, uint8_t DCNTESubcommand>
|
||||
void on_movement(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<CmdT>(data, size);
|
||||
if (cmd.header.client_id != c->lobby_client_id) {
|
||||
@@ -732,10 +786,10 @@ void on_movement(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void
|
||||
c->x = cmd.x;
|
||||
c->z = cmd.z;
|
||||
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
forward_subcommand(c, command, flag, data, size, DCNTESubcommand);
|
||||
}
|
||||
|
||||
template <typename CmdT>
|
||||
template <typename CmdT, uint8_t DCNTESubcommand>
|
||||
void on_movement_with_floor(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<CmdT>(data, size);
|
||||
if (cmd.header.client_id != c->lobby_client_id) {
|
||||
@@ -746,7 +800,12 @@ void on_movement_with_floor(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
c->z = cmd.z;
|
||||
c->floor = cmd.floor;
|
||||
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
forward_subcommand(c, command, flag, data, size, DCNTESubcommand);
|
||||
}
|
||||
|
||||
static void on_toggle_counter_interaction(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
check_size_t<G_ToggleCounterInteraction_6x52>(data, size, 0xFFFF);
|
||||
forward_subcommand(c, command, flag, data, size, 0x46);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -2544,6 +2603,69 @@ static void on_write_quest_global_flag_bb(shared_ptr<Client> c, uint8_t, uint8_t
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void handle_subcommand_dc_nte(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game()) {
|
||||
// In a game, assume all other clients are DC NTE as well and forward the
|
||||
// subcommand without any processing
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
|
||||
} else {
|
||||
// In a lobby, we have to deal with all other versions of the game having
|
||||
// different subcommand numbers than DC NTE. We'll forward the command
|
||||
// verbatim to other DC NTE clients, but will have to translate it for
|
||||
// non-NTE clients. Some subcommands may not map cleanly; for those, we
|
||||
// don't send anything at all to non-NTE clients.
|
||||
auto& header = check_size_t<G_UnusedHeader>(data, size, 0xFFFF);
|
||||
uint8_t nte_subcommand = header.subcommand;
|
||||
uint8_t non_nte_subcommand = 0x00;
|
||||
switch (nte_subcommand) {
|
||||
case 0x1D:
|
||||
non_nte_subcommand = 0x21;
|
||||
break;
|
||||
case 0x1E:
|
||||
non_nte_subcommand = 0x22;
|
||||
break;
|
||||
case 0x1F:
|
||||
non_nte_subcommand = 0x23;
|
||||
break;
|
||||
case 0x36:
|
||||
non_nte_subcommand = 0x3F;
|
||||
break;
|
||||
case 0x37:
|
||||
non_nte_subcommand = 0x40;
|
||||
break;
|
||||
case 0x39:
|
||||
non_nte_subcommand = 0x42;
|
||||
break;
|
||||
case 0x46:
|
||||
non_nte_subcommand = 0x52;
|
||||
break;
|
||||
default:
|
||||
non_nte_subcommand = 0x00;
|
||||
}
|
||||
|
||||
string non_nte_data;
|
||||
for (auto lc : l->clients) {
|
||||
if (!lc || (lc == c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lc->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
send_command(lc, command, flag, data, size);
|
||||
} else if (non_nte_subcommand != 0x00) {
|
||||
if (non_nte_data.empty()) {
|
||||
non_nte_data.assign(reinterpret_cast<const char*>(data), size);
|
||||
non_nte_data[0] = non_nte_subcommand;
|
||||
}
|
||||
send_command(lc, command, flag, non_nte_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef void (*subcommand_handler_t)(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size);
|
||||
|
||||
subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
@@ -2578,11 +2700,11 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
/* 6x1C */ on_forward_check_size_game,
|
||||
/* 6x1D */ nullptr,
|
||||
/* 6x1E */ nullptr,
|
||||
/* 6x1F */ on_change_floor<G_SetPlayerArea_6x1F>,
|
||||
/* 6x20 */ on_movement_with_floor<G_SetPosition_6x20>,
|
||||
/* 6x21 */ on_change_floor<G_InterLevelWarp_6x21>,
|
||||
/* 6x22 */ on_forward_check_size_client,
|
||||
/* 6x23 */ on_set_player_visibility,
|
||||
/* 6x1F */ on_change_floor_6x1F,
|
||||
/* 6x20 */ on_movement_with_floor<G_SetPosition_6x20, 0x00>,
|
||||
/* 6x21 */ on_change_floor_6x21,
|
||||
/* 6x22 */ on_set_player_invisible,
|
||||
/* 6x23 */ on_set_player_visible,
|
||||
/* 6x24 */ on_forward_check_size_game,
|
||||
/* 6x25 */ on_equip_item,
|
||||
/* 6x26 */ on_unequip_item,
|
||||
@@ -2609,11 +2731,11 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
/* 6x3B */ on_forward_check_size,
|
||||
/* 6x3C */ nullptr,
|
||||
/* 6x3D */ nullptr,
|
||||
/* 6x3E */ on_movement_with_floor<G_StopAtPosition_6x3E>,
|
||||
/* 6x3F */ on_movement_with_floor<G_SetPosition_6x3F>,
|
||||
/* 6x40 */ on_movement<G_WalkToPosition_6x40>,
|
||||
/* 6x3E */ on_movement_with_floor<G_StopAtPosition_6x3E, 0x00>,
|
||||
/* 6x3F */ on_movement_with_floor<G_SetPosition_6x3F, 0x36>,
|
||||
/* 6x40 */ on_movement<G_WalkToPosition_6x40, 0x37>,
|
||||
/* 6x41 */ nullptr,
|
||||
/* 6x42 */ on_movement<G_RunToPosition_6x42>,
|
||||
/* 6x42 */ on_movement<G_RunToPosition_6x42, 0x39>,
|
||||
/* 6x43 */ on_forward_check_size_client,
|
||||
/* 6x44 */ on_forward_check_size_client,
|
||||
/* 6x45 */ on_forward_check_size_client,
|
||||
@@ -2629,7 +2751,7 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
/* 6x4F */ on_forward_check_size_client,
|
||||
/* 6x50 */ on_forward_check_size_client,
|
||||
/* 6x51 */ nullptr,
|
||||
/* 6x52 */ on_forward_check_size,
|
||||
/* 6x52 */ on_toggle_counter_interaction,
|
||||
/* 6x53 */ on_forward_check_size_game,
|
||||
/* 6x54 */ nullptr,
|
||||
/* 6x55 */ on_forward_check_size_client,
|
||||
@@ -2809,32 +2931,31 @@ void on_subcommand_multi(shared_ptr<Client> c, uint8_t command, uint8_t flag, co
|
||||
if (data.empty()) {
|
||||
throw runtime_error("game command is empty");
|
||||
}
|
||||
if (c->version() == GameVersion::DC &&
|
||||
(c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION) || c->config.check_flag(Client::Flag::IS_DC_V1_PROTOTYPE))) {
|
||||
// TODO: We should convert these to non-trial formats and vice versa
|
||||
forward_subcommand(c, command, flag, data.data(), data.size());
|
||||
} else {
|
||||
StringReader r(data);
|
||||
while (!r.eof()) {
|
||||
size_t size;
|
||||
const auto& header = r.get<G_UnusedHeader>(false);
|
||||
if (header.size != 0) {
|
||||
size = header.size << 2;
|
||||
} else {
|
||||
const auto& ext_header = r.get<G_ExtendedHeader<G_UnusedHeader>>(false);
|
||||
size = ext_header.size;
|
||||
if (size < 8) {
|
||||
throw runtime_error("extended subcommand header has size < 8");
|
||||
}
|
||||
if (size & 3) {
|
||||
throw runtime_error("extended subcommand size is not a multiple of 4");
|
||||
}
|
||||
}
|
||||
if (size == 0) {
|
||||
throw runtime_error("invalid subcommand size");
|
||||
}
|
||||
const void* data = r.getv(size);
|
||||
|
||||
StringReader r(data);
|
||||
while (!r.eof()) {
|
||||
size_t size;
|
||||
const auto& header = r.get<G_UnusedHeader>(false);
|
||||
if (header.size != 0) {
|
||||
size = header.size << 2;
|
||||
} else {
|
||||
const auto& ext_header = r.get<G_ExtendedHeader<G_UnusedHeader>>(false);
|
||||
size = ext_header.size;
|
||||
if (size < 8) {
|
||||
throw runtime_error("extended subcommand header has size < 8");
|
||||
}
|
||||
if (size & 3) {
|
||||
throw runtime_error("extended subcommand size is not a multiple of 4");
|
||||
}
|
||||
}
|
||||
if (size == 0) {
|
||||
throw runtime_error("invalid subcommand size");
|
||||
}
|
||||
const void* data = r.getv(size);
|
||||
|
||||
if (c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
handle_subcommand_dc_nte(c, command, flag, data, size);
|
||||
} else {
|
||||
auto fn = subcommand_handlers[header.subcommand];
|
||||
if (fn) {
|
||||
fn(c, command, flag, data, size);
|
||||
|
||||
+13
-13
@@ -1473,9 +1473,9 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
|
||||
p.lobby_data.client_id = wc->lobby_client_id;
|
||||
p.lobby_data.name.encode(wc_p->disp.name.decode(wc_p->inventory.language), c->language());
|
||||
p.inventory = wc_p->inventory;
|
||||
p.inventory.encode_for_version(c->version(), s->item_parameter_table_for_version(c->version()));
|
||||
p.inventory.encode_for_client(c);
|
||||
p.disp = wc_p->disp.to_dcpcv3(c->language(), p.inventory.language);
|
||||
p.disp.enforce_lobby_join_limits(c->version());
|
||||
p.disp.enforce_lobby_join_limits_for_client(c);
|
||||
|
||||
auto& e = cmd.entries[z];
|
||||
e.player_tag = 0x00010000;
|
||||
@@ -1511,9 +1511,9 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
|
||||
auto& p = cmd.players[client_id];
|
||||
p.lobby_data = entry.lobby_data;
|
||||
p.inventory = entry.inventory;
|
||||
p.inventory.encode_for_version(c->version(), s->item_parameter_table_for_version(c->version()));
|
||||
p.inventory.encode_for_client(c);
|
||||
p.disp = entry.disp;
|
||||
p.disp.enforce_lobby_join_limits(c->version());
|
||||
p.disp.enforce_lobby_join_limits_for_client(c);
|
||||
|
||||
auto& e = cmd.entries[client_id];
|
||||
e.player_tag = 0x00010000;
|
||||
@@ -1542,7 +1542,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
|
||||
cmd_p.lobby_data.name.encode(other_p->disp.name.decode(other_p->inventory.language), c->language());
|
||||
cmd_p.inventory = other_p->inventory;
|
||||
cmd_p.disp = other_p->disp.to_dcpcv3(c->language(), cmd_p.inventory.language);
|
||||
cmd_p.disp.enforce_lobby_join_limits(c->version());
|
||||
cmd_p.disp.enforce_lobby_join_limits_for_client(c);
|
||||
|
||||
cmd_e.player_tag = 0x00010000;
|
||||
cmd_e.guild_card_number = other_c->license->serial_number;
|
||||
@@ -1648,9 +1648,9 @@ void send_join_game(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
if (l->clients[x]) {
|
||||
auto other_p = l->clients[x]->game_data.character();
|
||||
cmd.players_ep3[x].inventory = other_p->inventory;
|
||||
cmd.players_ep3[x].inventory.encode_for_version(c->version(), s->item_parameter_table_for_version(c->version()));
|
||||
cmd.players_ep3[x].inventory.encode_for_client(c);
|
||||
cmd.players_ep3[x].disp = convert_player_disp_data<PlayerDispDataDCPCV3>(other_p->disp, c->language(), other_p->inventory.language);
|
||||
cmd.players_ep3[x].disp.enforce_lobby_join_limits(c->version());
|
||||
cmd.players_ep3[x].disp.enforce_lobby_join_limits_for_client(c);
|
||||
}
|
||||
}
|
||||
send_command_t(c, 0x64, player_count, cmd);
|
||||
@@ -1778,9 +1778,9 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l, shared_ptr<Cli
|
||||
e.lobby_data.name.encode(lp->disp.name.decode(lp->inventory.language), c->language());
|
||||
}
|
||||
e.inventory = lp->inventory;
|
||||
e.inventory.encode_for_version(c->version(), s->item_parameter_table_for_version(c->version()));
|
||||
e.inventory.encode_for_client(c);
|
||||
e.disp = convert_player_disp_data<DispDataT>(lp->disp, c->language(), lp->inventory.language);
|
||||
e.disp.enforce_lobby_join_limits(c->version());
|
||||
e.disp.enforce_lobby_join_limits_for_client(c);
|
||||
}
|
||||
|
||||
send_command(c, command, used_entries, &cmd, cmd.size(used_entries));
|
||||
@@ -1851,9 +1851,9 @@ void send_join_lobby_xb(shared_ptr<Client> c, shared_ptr<Lobby> l, shared_ptr<Cl
|
||||
e.lobby_data.client_id = lc->lobby_client_id;
|
||||
e.lobby_data.name.encode(lp->disp.name.decode(lp->inventory.language), c->language());
|
||||
e.inventory = lp->inventory;
|
||||
e.inventory.encode_for_version(c->version(), s->item_parameter_table_for_version(c->version()));
|
||||
e.inventory.encode_for_client(c);
|
||||
e.disp = convert_player_disp_data<PlayerDispDataDCPCV3>(lp->disp, c->language(), lp->inventory.language);
|
||||
e.disp.enforce_lobby_join_limits(c->version());
|
||||
e.disp.enforce_lobby_join_limits_for_client(c);
|
||||
}
|
||||
|
||||
send_command(c, command, used_entries, &cmd, cmd.size(used_entries));
|
||||
@@ -1899,9 +1899,9 @@ void send_join_lobby_dc_nte(shared_ptr<Client> c, shared_ptr<Lobby> l,
|
||||
e.lobby_data.client_id = lc->lobby_client_id;
|
||||
e.lobby_data.name.encode(lp->disp.name.decode(lp->inventory.language), c->language());
|
||||
e.inventory = lp->inventory;
|
||||
e.inventory.encode_for_version(c->version(), s->item_parameter_table_for_version(c->version()));
|
||||
e.inventory.encode_for_client(c);
|
||||
e.disp = convert_player_disp_data<PlayerDispDataDCPCV3>(lp->disp, c->language(), lp->inventory.language);
|
||||
e.disp.enforce_lobby_join_limits(c->version());
|
||||
e.disp.enforce_lobby_join_limits_for_client(c);
|
||||
}
|
||||
|
||||
send_command(c, command, used_entries, &cmd, cmd.size(used_entries));
|
||||
|
||||
Reference in New Issue
Block a user