diff --git a/README.md b/README.md index ebe59b27..006bf50e 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,7 @@ Some commands only work on the game server and not on the proxy server. The chat * You'll see in-game messages from the server when you take certain actions, like killing an enemy in BB. * You'll see the rare seed value and floor variations when you join a game. * You'll be placed into the highest available slot in lobbies and games instead of the lowest, unless you're joining a BB solo-mode game. + * You'll be able to join games with any PSO version, not only those for which crossplay is normally supported. Be prepared for client crashes and other client-side brokenness if you do this. Please do not submit any issues for broken behaviors in crossplay, unless the situation is explicitly supported (see the "Cross-version play" section above). * The rest of the commands in this section are enabled on the game server. (They are always enabled on the proxy server.) * `$quest ` (game server only): Load a quest by quest number. Can be used to load battle or challenge quests with only one player present. * `$qcall `: Call a quest function on your client. diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index cd53199f..0b0b0808 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -590,7 +590,7 @@ static void server_command_persist(shared_ptr c, const std::string&) { auto l = c->require_lobby(); if (l->check_flag(Lobby::Flag::DEFAULT)) { send_text_message(c, "$C6Default lobbies\ncannot be marked\ntemporary"); - } else if (!l->check_flag(Lobby::Flag::GAME)) { + } else if (!l->is_game()) { send_text_message(c, "$C6Private lobbies\ncannot be marked\npersistent"); } else if (l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS) || l->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) { send_text_message(c, "$C6Games cannot be\npersistent if a\nquest has already\nbegun"); @@ -937,7 +937,7 @@ static void server_command_password(shared_ptr c, const std::string& arg check_is_leader(l, c); if (!args[0]) { - l->password[0] = 0; + l->password.clear(); send_text_message(l, "$C6Game unlocked"); } else { diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index aa071f12..3733fe3e 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -4581,7 +4581,7 @@ struct G_CreateTelepipe_6x68 { struct G_NPCControl_6x69 { G_UnusedHeader header; - le_uint16_t param1; // Commands 0/3: state; commands 1/2: npc_entity_id + le_uint16_t param1; // Commands 0/3: state; command 1: npc_entity_id; command 2: unknown le_uint16_t param2; // Commands 0/3: npc_entity_id; commands 1/2: unused le_uint16_t command = 0; // 0 = create follower NPC, 1 = stop acting, 2 = start acting, 3 = create attacker NPC le_uint16_t param3; // Commands 0/3: npc_template_index; commands 1/2: unused @@ -4698,46 +4698,61 @@ struct G_SetQuestFlags_6x6F { // and instead rearranged a bunch of things. struct Telepipe { - /* 00 */ le_uint16_t client_id; - /* 02 */ le_uint16_t unknown_a1; - /* 04 */ le_uint32_t unknown_a2; - /* 08 */ le_float x; - /* 0C */ le_float y; - /* 10 */ le_float z; - /* 14 */ le_uint32_t unknown_a3; - /* 18 */ parray unknown_a4; + /* 00 */ le_uint16_t client_id = 0xFFFF; + /* 02 */ le_uint16_t unknown_a1 = 0; + /* 04 */ le_uint32_t unknown_a2 = 0; + /* 08 */ le_float x = 0.0f; + /* 0C */ le_float y = 0.0f; + /* 10 */ le_float z = 0.0f; + /* 14 */ le_uint32_t unknown_a3 = 0; + /* 18 */ le_uint32_t unknown_a4 = 0x0000FFFF; /* 1C */ } __packed__; -struct G_Unknown_6x70_Sub { +struct G_Unknown_6x70_SubA1 { // This is used in all versions of this command except DCNTE and 11/2000. - /* 00 */ le_uint16_t unknown_a1; - /* 02 */ le_uint16_t unknown_a2; - /* 04 */ le_uint32_t unknown_a3; - /* 08 */ le_uint32_t unknown_a4; - /* 0C */ le_uint32_t unknown_a5; - /* 10 */ le_uint32_t unknown_a6; + /* 00 */ le_uint16_t unknown_a1 = 0; + /* 02 */ le_uint16_t unknown_a2 = 0; + /* 04 */ le_uint32_t unknown_a3 = 0; + /* 08 */ le_float unknown_a4 = 0.0f; + /* 0C */ le_uint32_t unknown_a5 = 0; + /* 10 */ le_uint32_t unknown_a6 = 0; /* 14 */ } __packed__; +struct G_Unknown_6x70_SubA2 { + // This is used in all versions of this command except DCNTE and 11/2000. + /* 00 */ le_uint32_t unknown_a1 = 0; + /* 04 */ le_float unknown_a2 = 0.0f; + /* 08 */ le_uint32_t unknown_a3 = 0; + /* 0C */ +} __packed__; + +struct G_SyncPlayerDispAndInventory_BaseDCNTE { + /* 0000 */ le_uint16_t client_id = 0; + /* 0002 */ le_uint16_t unknown_a1 = 0; + /* 0004 */ le_uint32_t flags1 = 0; + /* 0008 */ le_float x = 0.0f; + /* 000C */ le_float y = 0.0f; + /* 0010 */ le_float z = 0.0f; + /* 0014 */ le_uint32_t angle_x = 0; + /* 0018 */ le_uint32_t angle_y = 0; + /* 001C */ le_uint32_t angle_z = 0; + /* 0020 */ le_uint16_t unknown_a3a = 0; + /* 0022 */ le_uint16_t current_hp = 0; +} __packed__; + struct G_SyncPlayerDispAndInventory_DCNTE_6x70 { // Offsets in this struct are relative to the overall command header - /* 0004 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DCNTE_6x70)}; - /* 000C */ le_uint16_t client_id = 0; - /* 000E */ le_uint16_t unknown_a1 = 0; - /* 0010 */ le_uint32_t flags1; - /* 0014 */ le_float x; - /* 0018 */ le_float y; - /* 001C */ le_float z; - /* 0020 */ le_uint32_t angle_x; - /* 0024 */ le_uint32_t angle_y; - /* 0028 */ le_uint32_t angle_z; - /* 002C */ le_uint16_t unknown_a3a; - /* 002E */ le_uint16_t current_hp; - /* 0030 */ le_uint32_t unknown_a5; - /* 0034 */ le_uint32_t unknown_a6; + /* 0004 */ G_ExtendedHeader header = {{0x60, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DCNTE_6x70)}; + /* 000C */ G_SyncPlayerDispAndInventory_BaseDCNTE base; + // The following two fields appear to contain uninitialized data + /* 0030 */ le_uint32_t unknown_a5 = 0; + /* 0034 */ le_uint32_t unknown_a6 = 0; /* 0038 */ Telepipe telepipe; - /* 0054 */ parray unknown_a7; + /* 0054 */ le_uint32_t unknown_a8 = 0; + /* 0058 */ parray unknown_a9; + /* 0068 */ le_uint32_t area = 0; /* 006C */ le_uint32_t flags2 = 0; /* 0070 */ PlayerVisualConfig visual; /* 00C0 */ PlayerStats stats; @@ -4748,23 +4763,15 @@ struct G_SyncPlayerDispAndInventory_DCNTE_6x70 { struct G_SyncPlayerDispAndInventory_DC112000_6x70 { // Offsets in this struct are relative to the overall command header - /* 0004 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC112000_6x70)}; - /* 000C */ le_uint16_t client_id = 0; - /* 000E */ le_uint16_t unknown_a1 = 0; - /* 0010 */ le_uint32_t flags1; - /* 0014 */ le_float x; - /* 0018 */ le_float y; - /* 001C */ le_float z; - /* 0020 */ le_uint32_t angle_x; - /* 0024 */ le_uint32_t angle_y; - /* 0028 */ le_uint32_t angle_z; - /* 002C */ le_uint16_t unknown_a3a; - /* 002E */ le_uint16_t current_hp; - /* 0030 */ le_uint16_t bonus_hp_from_materials; - /* 0032 */ le_uint16_t bonus_tp_from_materials; + /* 0004 */ G_ExtendedHeader header = {{0x67, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC112000_6x70)}; + /* 000C */ G_SyncPlayerDispAndInventory_BaseDCNTE base; + /* 0030 */ le_uint16_t bonus_hp_from_materials = 0; + /* 0032 */ le_uint16_t bonus_tp_from_materials = 0; /* 0034 */ parray unknown_a5; /* 0044 */ Telepipe telepipe; - /* 0060 */ parray unknown_a6; + /* 0060 */ le_uint32_t unknown_a8 = 0; + /* 0064 */ parray unknown_a9; + /* 0074 */ le_uint32_t area = 0; /* 0078 */ le_uint32_t flags2 = 0; /* 007C */ PlayerVisualConfig visual; /* 00CC */ PlayerStats stats; @@ -4773,40 +4780,30 @@ struct G_SyncPlayerDispAndInventory_DC112000_6x70 { /* 043C */ } __packed__; -struct G_SyncPlayerDispAndInventory_Base { - /* 0000 */ le_uint16_t client_id = 0; - /* 0002 */ le_uint16_t unknown_a1 = 0; - /* 0004 */ le_uint32_t flags1; - /* 0008 */ le_float x; - /* 000C */ le_float y; - /* 0010 */ le_float z; - /* 0014 */ le_uint32_t angle_x; - /* 0018 */ le_uint32_t angle_y; - /* 001C */ le_uint32_t angle_z; - /* 0020 */ le_uint16_t unknown_a3a; - /* 0022 */ le_uint16_t current_hp; - /* 0024 */ le_uint16_t bonus_hp_from_materials; - /* 0026 */ le_uint16_t bonus_tp_from_materials; - /* 0028 */ parray, 5> unknown_a4; +struct G_SyncPlayerDispAndInventory_BaseV1 { + /* 0000 */ G_SyncPlayerDispAndInventory_BaseDCNTE base; + /* 0024 */ le_uint16_t bonus_hp_from_materials = 0; + /* 0026 */ le_uint16_t bonus_tp_from_materials = 0; + /* 0028 */ parray unknown_a4; /* 0064 */ le_uint32_t language = 0; /* 0068 */ le_uint32_t player_tag = 0; /* 006C */ le_uint32_t guild_card_number = 0; - /* 0070 */ le_uint32_t unknown_a6; - /* 0074 */ le_uint32_t battle_team_number; + /* 0070 */ le_uint32_t unknown_a6 = 0; + /* 0074 */ le_uint32_t battle_team_number = 0; /* 0078 */ Telepipe telepipe; /* 0094 */ le_uint32_t unknown_a8 = 0; - /* 0098 */ G_Unknown_6x70_Sub unknown_a9; + /* 0098 */ G_Unknown_6x70_SubA1 unknown_a9; /* 00AC */ le_uint32_t area = 0; /* 00B0 */ le_uint32_t flags2 = 0; - /* 00B4 */ parray technique_levels_v1; // Last byte is uninitialized + /* 00B4 */ parray technique_levels_v1 = 0xFF; // Last byte is uninitialized /* 00C8 */ PlayerVisualConfig visual; /* 0118 */ } __packed__; struct G_SyncPlayerDispAndInventory_DC_PC_6x70 { // Offsets in this struct are relative to the overall command header - /* 0004 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC_PC_6x70)}; - /* 000C */ G_SyncPlayerDispAndInventory_Base base; + /* 0004 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC_PC_6x70)}; + /* 000C */ G_SyncPlayerDispAndInventory_BaseV1 base; /* 0124 */ PlayerStats stats; /* 0148 */ le_uint32_t num_items = 0; /* 014C */ parray items; @@ -4816,8 +4813,8 @@ struct G_SyncPlayerDispAndInventory_DC_PC_6x70 { // GC NTE also uses this format. struct G_SyncPlayerDispAndInventory_GC_6x70 { // Offsets in this struct are relative to the overall command header - /* 0004 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_GC_6x70)}; - /* 000C */ G_SyncPlayerDispAndInventory_Base base; + /* 0004 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_GC_6x70)}; + /* 000C */ G_SyncPlayerDispAndInventory_BaseV1 base; /* 0124 */ PlayerStats stats; /* 0148 */ le_uint32_t num_items = 0; /* 014C */ parray items; @@ -4827,8 +4824,8 @@ struct G_SyncPlayerDispAndInventory_GC_6x70 { struct G_SyncPlayerDispAndInventory_XB_6x70 { // Offsets in this struct are relative to the overall command header - /* 0004 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_XB_6x70)}; - /* 000C */ G_SyncPlayerDispAndInventory_Base base; + /* 0004 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_XB_6x70)}; + /* 000C */ G_SyncPlayerDispAndInventory_BaseV1 base; /* 0124 */ PlayerStats stats; /* 0148 */ le_uint32_t num_items = 0; /* 014C */ parray items; @@ -4841,8 +4838,8 @@ struct G_SyncPlayerDispAndInventory_XB_6x70 { struct G_SyncPlayerDispAndInventory_BB_6x70 { // Offsets in this struct are relative to the overall command header - /* 0008 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_BB_6x70)}; - /* 0010 */ G_SyncPlayerDispAndInventory_Base base; + /* 0008 */ G_ExtendedHeader header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_BB_6x70)}; + /* 0010 */ G_SyncPlayerDispAndInventory_BaseV1 base; /* 0128 */ pstring name; /* 0148 */ PlayerStats stats; /* 016C */ le_uint32_t num_items = 0; diff --git a/src/ItemData.cc b/src/ItemData.cc index a0ff5d53..d21a76d4 100644 --- a/src/ItemData.cc +++ b/src/ItemData.cc @@ -647,6 +647,9 @@ bool ItemData::compare_for_sort(const ItemData& a, const ItemData& b) { } ItemData ItemData::from_data(const string& data) { + if (data.size() < 2) { + throw runtime_error("data is too short"); + } if (data.size() > 0x10) { throw runtime_error("data is too long"); } @@ -658,6 +661,9 @@ ItemData ItemData::from_data(const string& data) { for (size_t z = 12; z < min(data.size(), 16); z++) { ret.data2[z - 12] = data[z]; } + if (ret.data1[0] > 4) { + throw runtime_error("invalid item class"); + } return ret; } diff --git a/src/ItemNameIndex.cc b/src/ItemNameIndex.cc index bd98a053..cfce7f0e 100644 --- a/src/ItemNameIndex.cc +++ b/src/ItemNameIndex.cc @@ -371,23 +371,7 @@ ItemData ItemNameIndex::parse_item_description(Version version, const std::strin ret = this->parse_item_description_phase(version, desc, true); } catch (const exception& e2) { try { - string data = parse_data_string(desc); - if (data.size() < 2) { - throw runtime_error("item code too short"); - } - if (data[0] > 4) { - throw runtime_error("invalid item class"); - } - if (data.size() > 16) { - throw runtime_error("item code too long"); - } - - if (data.size() <= 12) { - memcpy(ret.data1.data(), data.data(), data.size()); - } else { - memcpy(ret.data1.data(), data.data(), 12); - memcpy(ret.data2.data(), data.data() + 12, data.size() - 12); - } + ret = ItemData::from_data(parse_data_string(desc)); } catch (const exception& ed) { if (strcmp(e1.what(), e2.what())) { throw runtime_error(string_printf("cannot parse item description \"%s\" in %s (as text 1: %s) (as text 2: %s) (as data: %s)", diff --git a/src/Lobby.cc b/src/Lobby.cc index f2a976e5..7e2abc77 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -135,9 +135,9 @@ uint32_t Lobby::FloorItemManager::reassign_all_item_ids(uint32_t next_item_id) { return next_item_id; } -Lobby::Lobby(shared_ptr s, uint32_t id) +Lobby::Lobby(shared_ptr s, uint32_t id, bool is_game) : server_state(s), - log(string_printf("[Lobby:%" PRIX32 "] ", id), lobby_log.min_level), + log(string_printf("[%s:%" PRIX32 "] ", is_game ? "Game" : "Lobby", id), lobby_log.min_level), lobby_id(id), min_level(0), max_level(0xFFFFFFFF), @@ -162,15 +162,24 @@ Lobby::Lobby(shared_ptr s, uint32_t id) event_new(s->base.get(), -1, EV_TIMEOUT | EV_PERSIST, &Lobby::dispatch_on_idle_timeout, this), event_free) { this->log.info("Created"); - for (size_t x = 0; x < 12; x++) { - this->next_item_id_for_client[x] = 0x00010000 + 0x00200000 * x; + if (is_game) { + this->set_flag(Flag::GAME); } + this->reset_next_item_ids(); } Lobby::~Lobby() { this->log.info("Deleted"); } +void Lobby::reset_next_item_ids() { + uint32_t base_item_id = this->is_game() ? 0x00010000 : 0x10010000; + for (size_t x = 0; x < 12; x++) { + this->next_item_id_for_client[x] = base_item_id + 0x00200000 * x; + } + this->next_game_item_id = 0xCC000000; +} + shared_ptr Lobby::require_server_state() const { auto s = this->server_state.lock(); if (!s) { @@ -468,10 +477,7 @@ void Lobby::add_client(shared_ptr c, ssize_t required_client_id) { // If this is a lobby or no one was here before this, reassign all the floor // item IDs and reset the next item IDs if (!this->is_game() || (leader_index >= this->max_clients)) { - for (size_t x = 0; x < 12; x++) { - this->next_item_id_for_client[x] = 0x00010000 + 0x00200000 * x; - } - this->next_game_item_id = 0xCC000000; + this->reset_next_item_ids(); for (auto& m : this->floor_item_managers) { this->next_game_item_id = m.reassign_all_item_ids(this->next_game_item_id); } diff --git a/src/Lobby.hh b/src/Lobby.hh index e68f7d10..32ebf296 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -169,13 +169,15 @@ struct Lobby : public std::enable_shared_from_this { uint64_t idle_timeout_usecs; std::unique_ptr idle_timeout_event; - Lobby(std::shared_ptr s, uint32_t id); + Lobby(std::shared_ptr s, uint32_t id, bool is_game); Lobby(const Lobby&) = delete; Lobby(Lobby&&) = delete; ~Lobby(); Lobby& operator=(const Lobby&) = delete; Lobby& operator=(Lobby&&) = delete; + void reset_next_item_ids(); + [[nodiscard]] inline bool check_flag(Flag flag) const { return !!(this->enabled_flags & static_cast(flag)); } diff --git a/src/PlayerSubordinates.cc b/src/PlayerSubordinates.cc index 82d6ae9a..6e05b94e 100644 --- a/src/PlayerSubordinates.cc +++ b/src/PlayerSubordinates.cc @@ -44,7 +44,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_for_client(shared_ptr c) { +void PlayerVisualConfig::enforce_lobby_join_limits_for_version(Version v) { struct ClassMaxes { uint16_t costume; uint16_t skin; @@ -90,9 +90,9 @@ void PlayerDispDataDCPCV3::enforce_lobby_join_limits_for_client(shared_ptrversion())) { + if (is_v1_or_v2(v)) { // V1/V2 have fewer classes, so we'll substitute some here - switch (this->visual.char_class) { + switch (this->char_class) { case 0: // HUmar case 1: // HUnewearl case 2: // HUcast @@ -106,53 +106,60 @@ void PlayerDispDataDCPCV3::enforce_lobby_join_limits_for_client(shared_ptrvisual.char_class = 5; // HUcaseal -> RAcaseal + this->char_class = 5; // HUcaseal -> RAcaseal break; case 10: // FOmar - this->visual.char_class = 0; // FOmar -> HUmar + this->char_class = 0; // FOmar -> HUmar break; case 11: // RAmarl - this->visual.char_class = 1; // RAmarl -> HUnewearl + this->char_class = 1; // RAmarl -> HUnewearl break; case 14: // V2 custom 1 / V3 custom 3 case 15: // V2 custom 2 / V3 custom 4 case 16: // V2 custom 3 / V3 custom 5 case 17: // V2 custom 4 / V3 custom 6 case 18: // V2 custom 5 / V3 custom 7 - this->visual.char_class -= 5; + this->char_class -= 5; break; default: - this->visual.char_class = 0; // Invalid classes -> HUmar + this->char_class = 0; // Invalid classes -> HUmar } - this->visual.version = min(this->visual.version, is_v1(c->version()) ? 0 : 2); - maxes = &v1_v2_class_maxes[this->visual.char_class]; + this->version = min(this->version, is_v1(v) ? 0 : 2); + maxes = &v1_v2_class_maxes[this->char_class]; } else { - if (this->visual.char_class >= 19) { - this->visual.char_class = 0; // Invalid classes -> HUmar + if (this->char_class >= 19) { + this->char_class = 0; // Invalid classes -> HUmar } - this->visual.version = min(this->visual.version, 3); - maxes = &v3_v4_class_maxes[this->visual.char_class]; + this->version = min(this->version, 3); + maxes = &v3_v4_class_maxes[this->char_class]; } // V1/V2 has fewer costumes and android skins, so substitute them here - this->visual.costume = maxes->costume ? (this->visual.costume % maxes->costume) : 0; - this->visual.skin = maxes->skin ? (this->visual.skin % maxes->skin) : 0; - this->visual.face = maxes->face ? (this->visual.face % maxes->face) : 0; - this->visual.head = maxes->head ? (this->visual.head % maxes->head) : 0; - this->visual.hair = maxes->hair ? (this->visual.hair % maxes->hair) : 0; + this->costume = maxes->costume ? (this->costume % maxes->costume) : 0; + this->skin = maxes->skin ? (this->skin % maxes->skin) : 0; + this->face = maxes->face ? (this->face % maxes->face) : 0; + this->head = maxes->head ? (this->head % maxes->head) : 0; + this->hair = maxes->hair ? (this->hair % maxes->hair) : 0; - this->visual.compute_name_color_checksum(); - this->visual.class_flags = class_flags_for_class(this->visual.char_class); + if (is_v1_or_v2(v)) { + this->compute_name_color_checksum(); + } + this->class_flags = class_flags_for_class(this->char_class); - if (this->visual.name.at(0) == '\t' && (this->visual.name.at(1) == 'J' || this->visual.name.at(1) == 'E')) { - this->visual.name.encode(this->visual.name.decode().substr(2)); + if (is_v4(v) && (this->name.at(0) == '\t') && (this->name.at(1) == 'J' || this->name.at(1) == 'E')) { + this->name.encode(this->name.decode().substr(2)); } } -void PlayerDispDataBB::enforce_lobby_join_limits_for_client(shared_ptr c) { - if (!is_v4(c->version())) { +void PlayerDispDataDCPCV3::enforce_lobby_join_limits_for_version(Version v) { + this->visual.enforce_lobby_join_limits_for_version(v); +} + +void PlayerDispDataBB::enforce_lobby_join_limits_for_version(Version v) { + this->visual.enforce_lobby_join_limits_for_version(v); + if (!is_v4(v)) { throw logic_error("PlayerDispDataBB being sent to non-BB client"); } this->play_time = 0; diff --git a/src/PlayerSubordinates.hh b/src/PlayerSubordinates.hh index 2f8db744..fb8d4fd4 100644 --- a/src/PlayerSubordinates.hh +++ b/src/PlayerSubordinates.hh @@ -152,6 +152,8 @@ struct PlayerVisualConfig { static uint32_t compute_name_color_checksum(uint32_t name_color); void compute_name_color_checksum(); + + void enforce_lobby_join_limits_for_version(Version v); } __attribute__((packed)); struct PlayerDispDataDCPCV3 { @@ -161,7 +163,7 @@ struct PlayerDispDataDCPCV3 { /* BC */ parray technique_levels_v1; /* D0 */ - void enforce_lobby_join_limits_for_client(std::shared_ptr c); + void enforce_lobby_join_limits_for_version(Version v); PlayerDispDataBB to_bb(uint8_t to_language, uint8_t from_language) const; } __attribute__((packed)); @@ -187,7 +189,7 @@ struct PlayerDispDataBB { /* 017C */ parray technique_levels_v1; /* 0190 */ - void enforce_lobby_join_limits_for_client(std::shared_ptr c); + void enforce_lobby_join_limits_for_version(Version v); PlayerDispDataDCPCV3 to_dcpcv3(uint8_t to_language, uint8_t from_language) const; PlayerDispDataBBPreview to_preview() const; void apply_preview(const PlayerDispDataBBPreview&); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 5bbca1f9..92d7719c 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1831,32 +1831,68 @@ static void on_09(shared_ptr c, uint16_t, uint32_t, string& data) { abbreviation_for_episode(game->episode), abbreviation_for_difficulty(game->difficulty), abbreviation_for_mode(game->mode), - name_for_section_id(game->section_id)); + abbreviation_for_section_id(game->section_id)); - bool cheats_enabled = game->check_flag(Lobby::Flag::CHEATS_ENABLED); - bool locked = !game->password.empty(); - if (cheats_enabled && locked) { - info += "$C4Locked$C7, $C6cheats on$C7\n"; - } else if (cheats_enabled) { - info += "$C6Cheats on$C7\n"; - } else if (locked) { - info += "$C4Locked$C7\n"; - } + if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { + vector flags_tokens; + string quest_name; + if (game->check_flag(Lobby::Flag::CHEATS_ENABLED)) { + flags_tokens.emplace_back("$C6C$C7"); + } + if (game->check_flag(Lobby::Flag::PERSISTENT)) { + flags_tokens.emplace_back("$C6P$C7"); + } + if (!game->password.empty()) { + flags_tokens.emplace_back("$C4L$C7"); + } + if (game->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) { + flags_tokens.emplace_back("$C8ST$C7"); + } + if (game->check_flag(Lobby::Flag::SPECTATORS_FORBIDDEN)) { + flags_tokens.emplace_back("$C8NS$C7"); + } + if (game->quest) { + flags_tokens.emplace_back(game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS) ? "$C3JQ$C7" : "$C3Q$C7"); + quest_name = remove_color(game->quest->name); + } else if (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) { + flags_tokens.emplace_back("$C3JQ$C7"); + } else if (game->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { + flags_tokens.emplace_back("$C3Q$C7"); + } else if (game->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS)) { + flags_tokens.emplace_back("$C3B$C7"); + } + info += ("Flags: " + join(flags_tokens, ",") + "\n"); + if (!quest_name.empty()) { + info += ("Q: $C6" + quest_name + "$C7\n"); + } + info += string_printf("Version: %s\n", name_for_enum(game->base_version)); - if (game->quest) { - info += (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) ? "$C6Quest: " : "$C4Quest: "; - info += remove_color(game->quest->name); - info += "\n"; - } else if (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) { - info += "$C6Quest in progress\n"; - } else if (game->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { - info += "$C4Quest in progress\n"; - } else if (game->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS)) { - info += "$C4Battle in progress\n"; - } + } else { + bool cheats_enabled = game->check_flag(Lobby::Flag::CHEATS_ENABLED); + bool locked = !game->password.empty(); + if (cheats_enabled && locked) { + info += "$C4Locked$C7, $C6cheats on$C7\n"; + } else if (cheats_enabled) { + info += "$C6Cheats on$C7\n"; + } else if (locked) { + info += "$C4Locked$C7\n"; + } - if (game->check_flag(Lobby::Flag::SPECTATORS_FORBIDDEN)) { - info += "$C4View Battle forbidden\n"; + if (game->quest) { + info += (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) ? "$C6Quest: " : "$C4Quest: "; + info += remove_color(game->quest->name); + info += "\n"; + } else if (game->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) { + info += "$C6Quest in progress\n"; + } else if (game->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { + info += "$C4Quest in progress\n"; + } else if (game->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS)) { + info += "$C4Battle in progress\n"; + } + + if (game->check_flag(Lobby::Flag::SPECTATORS_FORBIDDEN)) { + info += "$C4View Battle forbidden\n"; + } } strip_trailing_whitespace(info); @@ -2349,7 +2385,7 @@ static void on_10(shared_ptr c, uint16_t, uint32_t, string& data) { send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\nfull."); break; } - if (!game->version_is_allowed(c->version())) { + if (!game->version_is_allowed(c->version()) && !c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO."); break; } @@ -3955,10 +3991,8 @@ shared_ptr create_game_generic( return nullptr; } - shared_ptr game = s->create_lobby(); + shared_ptr game = s->create_lobby(true); game->name = name; - game->set_flag(Lobby::Flag::GAME); - game->base_version = c->version(); game->allowed_versions = 0; switch (game->base_version) { diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 1a59b6cd..a16be1c2 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include "Client.hh" #include "Compression.hh" @@ -127,7 +128,7 @@ static void forward_subcommand(shared_ptr c, uint8_t command, uint8_t fl Version c_version = c->version(); auto send_to_client = [&](shared_ptr lc) -> void { Version lc_version = lc->version(); - if (l->is_game() || (!is_pre_v1(lc_version) && !is_pre_v1(c_version)) || (lc_version == c_version)) { + if ((!is_pre_v1(lc_version) && !is_pre_v1(c_version)) || (lc_version == c_version)) { send_command(lc, command, flag, data, size); } else if (lc->version() == Version::DC_NTE) { if (def->nte_subcommand) { @@ -205,7 +206,7 @@ static void forward_subcommand(shared_ptr c, uint8_t command, uint8_t fl for (const auto& watcher_lobby : l->watcher_lobbies) { for (auto& target : watcher_lobby->clients) { if (target && is_ep3(target->version())) { - send_command(target, command, flag, data, size); + send_to_client(target); } } } @@ -402,6 +403,320 @@ static void on_sync_joining_player_item_state(shared_ptr c, uint8_t comm send_game_item_state(target); } +template +void clear_dc_protos_unused_6x70_item_fields(CmdT& cmd) { + for (size_t z = 0; z < min(cmd.num_items, 30); z++) { + auto& item = cmd.items[z]; + item.unknown_a1 = 0; + item.extension_data1 = 0; + item.extension_data2 = 0; + } +} + +class Parsed6x70Data { +public: + G_SyncPlayerDispAndInventory_BaseDCNTE base; + uint32_t unknown_a5_nte = 0; + uint32_t unknown_a6_nte = 0; + uint16_t bonus_hp_from_materials = 0; + uint16_t bonus_tp_from_materials = 0; + parray unknown_a5_112000; + parray unknown_a4_final; + uint32_t language = 0; + uint32_t player_tag = 0; + uint32_t guild_card_number = 0; + uint32_t unknown_a6 = 0; + uint32_t battle_team_number = 0; + Telepipe telepipe; + uint32_t unknown_a8 = 0; + parray unknown_a9_nte_112000; + G_Unknown_6x70_SubA1 unknown_a9_final; + uint32_t area = 0; + uint32_t flags2 = 0; + parray technique_levels_v1 = 0xFF; + PlayerVisualConfig visual; + std::string name; + PlayerStats stats; + uint32_t num_items = 0; + parray items; + uint32_t floor = 0; + uint64_t xb_user_id = 0; + uint32_t xb_unknown_a16 = 0; + + Parsed6x70Data(const G_SyncPlayerDispAndInventory_DCNTE_6x70& cmd, uint32_t guild_card_number) + : base(cmd.base), + unknown_a5_nte(cmd.unknown_a5), + unknown_a6_nte(cmd.unknown_a6), + bonus_hp_from_materials(0), + bonus_tp_from_materials(0), + language(0), + player_tag(0x00010000), + guild_card_number(guild_card_number), + unknown_a6(0), + battle_team_number(0), + telepipe(cmd.telepipe), + unknown_a8(cmd.unknown_a8), + unknown_a9_nte_112000(cmd.unknown_a9), + area(cmd.area), + flags2(cmd.flags2), + visual(cmd.visual), + stats(cmd.stats), + num_items(cmd.num_items), + items(cmd.items), + floor(cmd.area), + xb_user_id(this->default_xb_user_id()), + xb_unknown_a16(0) { + this->name = this->visual.name.decode(this->language); + } + + Parsed6x70Data(const G_SyncPlayerDispAndInventory_DC112000_6x70& cmd, uint32_t guild_card_number, uint8_t language) + : base(cmd.base), + unknown_a5_nte(0), + unknown_a6_nte(0), + bonus_hp_from_materials(cmd.bonus_hp_from_materials), + bonus_tp_from_materials(cmd.bonus_tp_from_materials), + unknown_a5_112000(cmd.unknown_a5), + language(language), + player_tag(0x00010000), + guild_card_number(guild_card_number), + unknown_a6(0), + battle_team_number(0), + telepipe(cmd.telepipe), + unknown_a8(cmd.unknown_a8), + unknown_a9_nte_112000(cmd.unknown_a9), + area(cmd.area), + flags2(cmd.flags2), + visual(cmd.visual), + stats(cmd.stats), + num_items(cmd.num_items), + items(cmd.items), + floor(cmd.area), + xb_user_id(this->default_xb_user_id()), + xb_unknown_a16(0) { + this->name = this->visual.name.decode(this->language); + } + + Parsed6x70Data(const G_SyncPlayerDispAndInventory_DC_PC_6x70& cmd, uint32_t guild_card_number) + : Parsed6x70Data(cmd.base, guild_card_number) { + this->stats = cmd.stats; + this->num_items = cmd.num_items; + this->items = cmd.items; + this->floor = cmd.base.area; + this->xb_user_id = this->default_xb_user_id(); + this->xb_unknown_a16 = 0; + this->name = this->visual.name.decode(this->language); + } + + Parsed6x70Data(const G_SyncPlayerDispAndInventory_GC_6x70& cmd, uint32_t guild_card_number) + : Parsed6x70Data(cmd.base, guild_card_number) { + this->stats = cmd.stats; + this->num_items = cmd.num_items; + this->items = cmd.items; + this->floor = cmd.floor; + this->xb_user_id = this->default_xb_user_id(); + this->xb_unknown_a16 = 0; + this->name = this->visual.name.decode(this->language); + } + + Parsed6x70Data(const G_SyncPlayerDispAndInventory_XB_6x70& cmd, uint32_t guild_card_number) + : Parsed6x70Data(cmd.base, guild_card_number) { + this->stats = cmd.stats; + this->num_items = cmd.num_items; + this->items = cmd.items; + this->floor = cmd.floor; + this->xb_user_id = (static_cast(cmd.xb_user_id_high) << 32) | cmd.xb_user_id_low; + this->xb_unknown_a16 = cmd.unknown_a16; + this->name = this->visual.name.decode(this->language); + } + + Parsed6x70Data(const G_SyncPlayerDispAndInventory_BB_6x70& cmd, uint32_t guild_card_number) + : Parsed6x70Data(cmd.base, guild_card_number) { + this->stats = cmd.stats; + this->num_items = cmd.num_items; + this->items = cmd.items; + this->floor = cmd.floor; + this->xb_user_id = this->default_xb_user_id(); + this->xb_unknown_a16 = cmd.unknown_a16; + this->name = cmd.name.decode(cmd.base.language); + if ((this->name.size() > 2) && (this->name[0] == '\t') && ((this->name[1] == 'E') || (this->name[1] == 'J'))) { + this->name = this->name.substr(2); + } + this->visual.name.encode(this->name, cmd.base.language); + } + + G_SyncPlayerDispAndInventory_DCNTE_6x70 as_dc_nte() const { + G_SyncPlayerDispAndInventory_DCNTE_6x70 ret; + ret.base = this->base; + ret.unknown_a5 = this->unknown_a5_nte; + ret.unknown_a6 = this->unknown_a6; + ret.telepipe = this->telepipe; + ret.unknown_a8 = this->unknown_a8; + ret.unknown_a9 = this->unknown_a9_nte_112000; + ret.area = this->area; + ret.flags2 = this->flags2; + ret.visual = this->visual; + ret.stats = this->stats; + ret.num_items = this->num_items; + ret.items = this->items; + return ret; + } + + G_SyncPlayerDispAndInventory_DC112000_6x70 as_dc_112000() const { + G_SyncPlayerDispAndInventory_DC112000_6x70 ret; + ret.base = this->base; + ret.bonus_hp_from_materials = this->bonus_hp_from_materials; + ret.bonus_tp_from_materials = this->bonus_tp_from_materials; + ret.unknown_a5 = this->unknown_a5_112000; + ret.telepipe = this->telepipe; + ret.unknown_a8 = this->unknown_a8; + ret.unknown_a9 = this->unknown_a9_nte_112000; + ret.area = this->area; + ret.flags2 = this->flags2; + ret.visual = this->visual; + ret.stats = this->stats; + ret.num_items = this->num_items; + ret.items = this->items; + return ret; + } + + G_SyncPlayerDispAndInventory_DC_PC_6x70 as_dc_pc() const { + G_SyncPlayerDispAndInventory_DC_PC_6x70 ret; + ret.base = this->base_v1(); + ret.stats = this->stats; + ret.num_items = this->num_items; + ret.items = this->items; + return ret; + } + + G_SyncPlayerDispAndInventory_GC_6x70 as_gc() const { + G_SyncPlayerDispAndInventory_GC_6x70 ret; + ret.base = this->base_v1(); + ret.stats = this->stats; + ret.num_items = this->num_items; + ret.items = this->items; + ret.floor = this->floor; + return ret; + } + + G_SyncPlayerDispAndInventory_XB_6x70 as_xb() const { + G_SyncPlayerDispAndInventory_XB_6x70 ret; + ret.base = this->base_v1(); + ret.stats = this->stats; + ret.num_items = this->num_items; + ret.items = this->items; + ret.floor = this->floor; + ret.xb_user_id_high = this->xb_user_id >> 32; + ret.xb_user_id_low = this->xb_user_id; + ret.unknown_a16 = this->xb_unknown_a16; + return ret; + } + + G_SyncPlayerDispAndInventory_BB_6x70 as_bb(uint8_t language) const { + G_SyncPlayerDispAndInventory_BB_6x70 ret; + ret.base = this->base_v1(); + ret.name.encode("\tJ" + this->name, language); + ret.base.visual.name.encode(string_printf("%10" PRId32, this->guild_card_number), language); + ret.stats = this->stats; + ret.num_items = this->num_items; + ret.items = this->items; + ret.floor = this->floor; + ret.xb_user_id_high = this->xb_user_id >> 32; + ret.xb_user_id_low = this->xb_user_id; + ret.unknown_a16 = this->xb_unknown_a16; + return ret; + } + + uint64_t default_xb_user_id() const { + return (0xAE00000000000000 | this->guild_card_number); + } + + void clear_dc_protos_unused_item_fields() { + for (size_t z = 0; z < min(this->num_items, 30); z++) { + auto& item = this->items[z]; + item.unknown_a1 = 0; + item.extension_data1 = 0; + item.extension_data2 = 0; + } + } + + void transcode_inventory_items( + Version from_version, + Version to_version, + shared_ptr to_item_parameter_table) { + if (this->num_items > 30) { + throw runtime_error("invalid inventory item count"); + } + if (from_version != to_version) { + for (size_t z = 0; z < this->num_items; z++) { + this->items[z].data.decode_for_version(from_version); + this->items[z].data.encode_for_version(to_version, to_item_parameter_table); + } + } + for (size_t z = this->num_items; z < 30; z++) { + auto& item = this->items[z]; + item.present = 0; + item.unknown_a1 = 0; + item.flags = 0; + item.data.clear(); + } + if (is_v1(to_version)) { + for (size_t z = 0; z < 30; z++) { + auto& item = this->items[z]; + item.extension_data1 = 0x00; + item.extension_data2 = 0x00; + } + } else { + for (size_t z = 20; z < 30; z++) { + this->items[z].extension_data1 = 0x00; + } + for (size_t z = 16; z < 30; z++) { + this->items[z].extension_data2 = 0x00; + } + } + } + +protected: + Parsed6x70Data(const G_SyncPlayerDispAndInventory_BaseV1& base, uint32_t guild_card_number) { + this->base = base.base; + this->bonus_hp_from_materials = base.bonus_hp_from_materials; + this->bonus_tp_from_materials = base.bonus_tp_from_materials; + this->unknown_a4_final = base.unknown_a4; + this->language = base.language; + this->player_tag = base.player_tag; + this->guild_card_number = guild_card_number; // Ignore the client's GC# + this->unknown_a6 = base.unknown_a6; + this->battle_team_number = base.battle_team_number; + this->telepipe = base.telepipe; + this->unknown_a8 = base.unknown_a8; + this->unknown_a9_final = base.unknown_a9; + this->area = base.area; + this->flags2 = base.flags2; + this->technique_levels_v1 = base.technique_levels_v1; + this->visual = base.visual; + } + + G_SyncPlayerDispAndInventory_BaseV1 base_v1() const { + G_SyncPlayerDispAndInventory_BaseV1 ret; + ret.base = this->base; + ret.bonus_hp_from_materials = this->bonus_hp_from_materials; + ret.bonus_tp_from_materials = this->bonus_tp_from_materials; + ret.unknown_a4 = this->unknown_a4_final; + ret.language = this->language; + ret.player_tag = this->player_tag; + ret.guild_card_number = this->guild_card_number; + ret.unknown_a6 = this->unknown_a6; + ret.battle_team_number = this->battle_team_number; + ret.telepipe = this->telepipe; + ret.unknown_a8 = this->unknown_a8; + ret.unknown_a9 = this->unknown_a9_final; + ret.area = this->area; + ret.flags2 = this->flags2; + ret.technique_levels_v1 = this->technique_levels_v1; + ret.visual = this->visual; + return ret; + } +}; + static void on_sync_joining_player_disp_and_inventory( shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { auto s = c->require_server_state(); @@ -426,220 +741,80 @@ static void on_sync_joining_player_disp_and_inventory( return; } - // Exactly one of the versioned pointers will be valid, and base will be valid - // if and only if neither dc_nte_cmd nor dc_112000_cmd are valid. - G_SyncPlayerDispAndInventory_Base* base = nullptr; - G_SyncPlayerDispAndInventory_DCNTE_6x70* dc_nte_cmd = nullptr; - G_SyncPlayerDispAndInventory_DC112000_6x70* dc_112000_cmd = nullptr; - G_SyncPlayerDispAndInventory_DC_PC_6x70* v2_cmd = nullptr; - G_SyncPlayerDispAndInventory_GC_6x70* gc_cmd = nullptr; - G_SyncPlayerDispAndInventory_XB_6x70* xb_cmd = nullptr; - G_SyncPlayerDispAndInventory_BB_6x70* bb_cmd = nullptr; + unique_ptr parsed; + switch (c->version()) { case Version::DC_NTE: - dc_nte_cmd = &check_size_t(data, size); + parsed = make_unique( + check_size_t(data, size), + c->license->serial_number); + parsed->clear_dc_protos_unused_item_fields(); break; case Version::DC_V1_11_2000_PROTOTYPE: - dc_112000_cmd = &check_size_t(data, size); + parsed = make_unique( + check_size_t(data, size), + c->license->serial_number, + c->language()); + parsed->clear_dc_protos_unused_item_fields(); break; case Version::DC_V1: case Version::DC_V2: case Version::PC_NTE: case Version::PC_V2: - v2_cmd = &check_size_t(data, size); - base = &v2_cmd->base; + parsed = make_unique( + check_size_t(data, size), + c->license->serial_number); break; case Version::GC_NTE: case Version::GC_V3: case Version::GC_EP3_NTE: case Version::GC_EP3: - gc_cmd = &check_size_t(data, size); - base = &gc_cmd->base; + parsed = make_unique( + check_size_t(data, size), + c->license->serial_number); break; case Version::XB_V3: - xb_cmd = &check_size_t(data, size); - base = &xb_cmd->base; + parsed = make_unique( + check_size_t(data, size), + c->license->serial_number); break; case Version::BB_V4: - bb_cmd = &check_size_t(data, size); - base = &bb_cmd->base; + parsed = make_unique( + check_size_t(data, size), + c->license->serial_number); break; default: throw logic_error("6x70 command from unknown game version"); } - auto transcode_inventory_items = [&](CmdT* cmd) -> void { - if (cmd->num_items > 30) { - throw runtime_error("invalid inventory item count"); - } - if (c->version() != target->version()) { - auto item_parameter_table = s->item_parameter_table_for_version(target->version()); - for (size_t z = 0; z < cmd->num_items; z++) { - cmd->items[z].data.decode_for_version(c->version()); - cmd->items[z].data.encode_for_version(c->version(), item_parameter_table); - } - } - for (size_t z = cmd->num_items; z < 30; z++) { - auto& item = cmd->items[z]; - item.present = 0; - item.unknown_a1 = 0; - item.flags = 0; - item.data.clear(); - } - for (size_t z = 20; z < 30; z++) { - cmd->items[z].extension_data1 = 0x00; - } - for (size_t z = 16; z < 30; z++) { - cmd->items[z].extension_data2 = 0x00; - } - }; + parsed->transcode_inventory_items(c->version(), target->version(), s->item_parameter_table_for_version(target->version())); + parsed->visual.enforce_lobby_join_limits_for_version(target->version()); switch (target->version()) { case Version::DC_NTE: - if (!dc_nte_cmd) { - throw runtime_error("target player is not the same version as sender"); - } - transcode_inventory_items(dc_nte_cmd); - send_or_enqueue_joining_player_command(target, command, flag, *dc_nte_cmd); + send_or_enqueue_joining_player_command(target, command, flag, parsed->as_dc_nte()); break; - case Version::DC_V1_11_2000_PROTOTYPE: - if (!dc_112000_cmd) { - throw runtime_error("target player is not the same version as sender"); - } - transcode_inventory_items(dc_112000_cmd); - send_or_enqueue_joining_player_command(target, command, flag, *dc_112000_cmd); + send_or_enqueue_joining_player_command(target, command, flag, parsed->as_dc_112000()); break; - case Version::DC_V1: case Version::DC_V2: case Version::PC_NTE: case Version::PC_V2: - if (v2_cmd) { - transcode_inventory_items(v2_cmd); - send_or_enqueue_joining_player_command(target, command, flag, *v2_cmd); - } else { - G_SyncPlayerDispAndInventory_DC_PC_6x70 out_cmd; - out_cmd.base = *base; - if (gc_cmd) { - out_cmd.stats = gc_cmd->stats; - out_cmd.num_items = gc_cmd->num_items; - out_cmd.items = gc_cmd->items; - } else if (xb_cmd) { - out_cmd.stats = xb_cmd->stats; - out_cmd.num_items = xb_cmd->num_items; - out_cmd.items = xb_cmd->items; - } else if (bb_cmd) { - out_cmd.stats = bb_cmd->stats; - out_cmd.num_items = bb_cmd->num_items; - out_cmd.items = bb_cmd->items; - } else { - throw logic_error("no source data can be converted to the required format"); - } - transcode_inventory_items(&out_cmd); - send_or_enqueue_joining_player_command(target, command, flag, out_cmd); - } + send_or_enqueue_joining_player_command(target, command, flag, parsed->as_dc_pc()); break; - case Version::GC_NTE: case Version::GC_V3: case Version::GC_EP3_NTE: case Version::GC_EP3: - if (gc_cmd) { - transcode_inventory_items(gc_cmd); - send_or_enqueue_joining_player_command(target, command, flag, *gc_cmd); - } else { - G_SyncPlayerDispAndInventory_GC_6x70 out_cmd; - out_cmd.base = *base; - if (v2_cmd) { - out_cmd.stats = v2_cmd->stats; - out_cmd.num_items = v2_cmd->num_items; - out_cmd.items = v2_cmd->items; - out_cmd.floor = c->floor; - } else if (xb_cmd) { - out_cmd.stats = xb_cmd->stats; - out_cmd.num_items = xb_cmd->num_items; - out_cmd.items = xb_cmd->items; - out_cmd.floor = xb_cmd->floor; - } else if (bb_cmd) { - out_cmd.stats = bb_cmd->stats; - out_cmd.num_items = bb_cmd->num_items; - out_cmd.items = bb_cmd->items; - out_cmd.floor = bb_cmd->floor; - } else { - throw logic_error("no source data can be converted to the required format"); - } - transcode_inventory_items(&out_cmd); - send_or_enqueue_joining_player_command(target, command, flag, out_cmd); - } + send_or_enqueue_joining_player_command(target, command, flag, parsed->as_gc()); break; - case Version::XB_V3: - if (xb_cmd) { - transcode_inventory_items(xb_cmd); - send_or_enqueue_joining_player_command(target, command, flag, *xb_cmd); - } else { - uint64_t xb_user_id = (c->license->xb_user_id) - ? c->license->xb_user_id - : (0xAE00000000000000 | c->license->serial_number); - G_SyncPlayerDispAndInventory_XB_6x70 out_cmd; - out_cmd.base = *base; - out_cmd.xb_user_id_high = (xb_user_id >> 32) & 0xFFFFFFFF; - out_cmd.xb_user_id_low = xb_user_id & 0xFFFFFFFF; - if (v2_cmd) { - out_cmd.stats = v2_cmd->stats; - out_cmd.num_items = v2_cmd->num_items; - out_cmd.items = v2_cmd->items; - out_cmd.floor = c->floor; - } else if (gc_cmd) { - out_cmd.stats = gc_cmd->stats; - out_cmd.num_items = gc_cmd->num_items; - out_cmd.items = gc_cmd->items; - out_cmd.floor = gc_cmd->floor; - } else if (bb_cmd) { - out_cmd.stats = bb_cmd->stats; - out_cmd.num_items = bb_cmd->num_items; - out_cmd.items = bb_cmd->items; - out_cmd.floor = bb_cmd->floor; - } else { - throw logic_error("no source data can be converted to the required format"); - } - transcode_inventory_items(&out_cmd); - send_or_enqueue_joining_player_command(target, command, flag, out_cmd); - } + send_or_enqueue_joining_player_command(target, command, flag, parsed->as_xb()); break; - case Version::BB_V4: - if (bb_cmd) { - transcode_inventory_items(bb_cmd); - send_or_enqueue_joining_player_command(target, command, flag, *bb_cmd); - } else { - G_SyncPlayerDispAndInventory_BB_6x70 out_cmd; - out_cmd.base = *base; - out_cmd.name.encode(base->visual.name.decode(c->language()), target->language()); - if (v2_cmd) { - out_cmd.stats = v2_cmd->stats; - out_cmd.num_items = v2_cmd->num_items; - out_cmd.items = v2_cmd->items; - out_cmd.floor = c->floor; - } else if (gc_cmd) { - out_cmd.stats = gc_cmd->stats; - out_cmd.num_items = gc_cmd->num_items; - out_cmd.items = gc_cmd->items; - out_cmd.floor = gc_cmd->floor; - } else if (xb_cmd) { - out_cmd.stats = xb_cmd->stats; - out_cmd.num_items = xb_cmd->num_items; - out_cmd.items = xb_cmd->items; - out_cmd.floor = xb_cmd->floor; - } else { - throw logic_error("no source data can be converted to the required format"); - } - transcode_inventory_items(&out_cmd); - send_or_enqueue_joining_player_command(target, command, flag, out_cmd); - } + send_or_enqueue_joining_player_command(target, command, flag, parsed->as_bb(target->language())); break; - default: throw logic_error("6x70 command from unknown game version"); } @@ -659,6 +834,13 @@ static void on_forward_check_game(shared_ptr c, uint8_t command, uint8_t } } +static void on_forward_check_lobby(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { + auto l = c->require_lobby(); + if (!l->is_game()) { + forward_subcommand(c, command, flag, data, size); + } +} + static void on_forward_check_lobby_client(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { const auto& cmd = check_size_t(data, size, 0xFFFF); auto l = c->require_lobby(); @@ -908,6 +1090,7 @@ static void on_change_floor_6x1F(shared_ptr c, uint8_t command, uint8_t // the loading flag here instead. if (c->config.check_flag(Client::Flag::LOADING)) { c->config.clear_flag(Client::Flag::LOADING); + send_resume_game(c->require_lobby(), c); } } else { @@ -1028,20 +1211,21 @@ static void on_npc_control(shared_ptr c, uint8_t command, uint8_t flag, throw runtime_error("cannot create or modify NPC in the lobby"); } - uint16_t npc_entity_id = 0xFFFF; + int32_t npc_entity_id = -1; switch (cmd.command) { case 0: case 3: npc_entity_id = cmd.param2; break; case 1: - case 2: npc_entity_id = cmd.param1; break; + case 2: + break; default: throw runtime_error("invalid 6x69 command"); } - if ((npc_entity_id < 4) && l->clients[npc_entity_id]) { + if ((npc_entity_id >= 0) && (npc_entity_id < 4) && l->clients[npc_entity_id]) { throw runtime_error("cannot create or modify NPC in existing player slot"); } @@ -3228,7 +3412,7 @@ const SubcommandDefinition subcommand_definitions[0x100] = { /* 6x76 */ {0x00, 0x00, 0x76, on_forward_check_game}, /* 6x77 */ {0x00, 0x00, 0x77, on_forward_check_game}, /* 6x78 */ {0x00, 0x00, 0x78, nullptr}, - /* 6x79 */ {0x00, 0x00, 0x79, on_forward_check_lobby_client}, + /* 6x79 */ {0x00, 0x00, 0x79, on_forward_check_lobby}, /* 6x7A */ {0x00, 0x00, 0x7A, nullptr}, /* 6x7B */ {0x00, 0x00, 0x7B, nullptr}, /* 6x7C */ {0x00, 0x00, 0x7C, on_forward_check_game}, diff --git a/src/SendCommands.cc b/src/SendCommands.cc index abc19729..d5e54ee6 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -1354,9 +1354,10 @@ void send_game_menu_t( } set, bool (*)(const shared_ptr&, const shared_ptr&)> games(Lobby::compare_shared); + bool client_has_debug = c->config.check_flag(Client::Flag::DEBUG_ENABLED); for (shared_ptr l : s->all_lobbies()) { if (l->is_game() && - l->version_is_allowed(c->version()) && + (client_has_debug || l->version_is_allowed(c->version())) && (l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM) == is_spectator_team_list) && (!show_tournaments_only || l->tournament_match)) { games.emplace(l); @@ -1700,7 +1701,7 @@ static void send_join_spectator_team(shared_ptr c, shared_ptr l) p.inventory = wc_p->inventory; p.inventory.encode_for_client(c); p.disp = wc_p->disp.to_dcpcv3(c->language(), p.inventory.language); - p.disp.enforce_lobby_join_limits_for_client(c); + p.disp.enforce_lobby_join_limits_for_version(c->version()); auto& e = cmd.entries[z]; e.player_tag = 0x00010000; @@ -1738,7 +1739,7 @@ static void send_join_spectator_team(shared_ptr c, shared_ptr l) p.inventory = entry.inventory; p.inventory.encode_for_client(c); p.disp = entry.disp; - p.disp.enforce_lobby_join_limits_for_client(c); + p.disp.enforce_lobby_join_limits_for_version(c->version()); auto& e = cmd.entries[client_id]; e.player_tag = 0x00010000; @@ -1764,7 +1765,7 @@ static void send_join_spectator_team(shared_ptr c, shared_ptr l) populate_lobby_data_for_client(cmd_p.lobby_data, other_c, c); 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_for_client(c); + cmd_p.disp.enforce_lobby_join_limits_for_version(c->version()); cmd_e.player_tag = 0x00010000; cmd_e.guild_card_number = other_c->license->serial_number; @@ -1879,7 +1880,7 @@ void send_join_game(shared_ptr c, shared_ptr l) { cmd.players_ep3[x].inventory = other_p->inventory; cmd.players_ep3[x].inventory.encode_for_client(c); cmd.players_ep3[x].disp = convert_player_disp_data(other_p->disp, c->language(), other_p->inventory.language); - cmd.players_ep3[x].disp.enforce_lobby_join_limits_for_client(c); + cmd.players_ep3[x].disp.enforce_lobby_join_limits_for_version(c->version()); } } send_command_t(c, 0x64, player_count, cmd); @@ -1990,7 +1991,7 @@ void send_join_lobby_t(shared_ptr c, shared_ptr l, shared_ptr(*lc->v1_v2_last_reported_disp, c->language(), lp->inventory.language); } else { e.disp = convert_player_disp_data(lp->disp, c->language(), lp->inventory.language); - e.disp.enforce_lobby_join_limits_for_client(c); + e.disp.enforce_lobby_join_limits_for_version(c->version()); } } @@ -2056,7 +2057,7 @@ void send_join_lobby_xb(shared_ptr c, shared_ptr l, shared_ptrinventory; e.inventory.encode_for_client(c); e.disp = convert_player_disp_data(lp->disp, c->language(), lp->inventory.language); - e.disp.enforce_lobby_join_limits_for_client(c); + e.disp.enforce_lobby_join_limits_for_version(c->version()); } send_command(c, command, used_entries, &cmd, cmd.size(used_entries)); @@ -2101,7 +2102,7 @@ void send_join_lobby_dc_nte(shared_ptr c, shared_ptr l, e.inventory = lp->inventory; e.inventory.encode_for_client(c); e.disp = convert_player_disp_data(lp->disp, c->language(), lp->inventory.language); - e.disp.enforce_lobby_join_limits_for_client(c); + e.disp.enforce_lobby_join_limits_for_version(c->version()); } send_command(c, command, used_entries, &cmd, cmd.size(used_entries)); @@ -2287,8 +2288,12 @@ void send_arrow_update(shared_ptr l) { // tells the player that the joining player is done joining, and the game can resume void send_resume_game(shared_ptr l, shared_ptr ready_client) { - static const be_uint32_t data = 0x72010000; - send_command_excluding_client(l, ready_client, 0x60, 0x00, &data, sizeof(be_uint32_t)); + for (auto lc : l->clients) { + if (lc && (lc != ready_client) && !is_pre_v1(lc->version())) { + static const be_uint32_t data = 0x72010000; + send_command(lc, 0x60, 0x00, &data, sizeof(be_uint32_t)); + } + } } //////////////////////////////////////////////////////////////////////////////// @@ -2667,14 +2672,15 @@ void send_give_experience(shared_ptr c, uint32_t amount) { } void send_set_exp_multiplier(shared_ptr l) { - if (l->base_version != Version::BB_V4) { - throw logic_error("6xDD can only be sent to BB clients"); - } if (!l->is_game()) { throw logic_error("6xDD can only be sent in games (not in lobbies)"); } G_SetEXPMultiplier_BB_6xDD cmd = {{0xDD, sizeof(G_SetEXPMultiplier_BB_6xDD) / 4, (l->mode == GameMode::CHALLENGE) ? 1 : l->base_exp_multiplier}}; - send_command_t(l, 0x60, 0x00, cmd); + for (auto lc : l->clients) { + if (lc && (lc->version() == Version::BB_V4)) { + send_command_t(l, 0x60, 0x00, cmd); + } + } } void send_rare_enemy_index_list(shared_ptr c, const vector& indexes) { diff --git a/src/ServerState.cc b/src/ServerState.cc index 74d5be76..2a4dc44f 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -93,7 +93,7 @@ void ServerState::init() { bool allow_v1 = (x <= 9); bool allow_non_ep3 = (x <= 14); - shared_ptr l = this->create_lobby(); + shared_ptr l = this->create_lobby(false); l->set_flag(Lobby::Flag::PUBLIC); l->set_flag(Lobby::Flag::DEFAULT); l->set_flag(Lobby::Flag::PERSISTENT); @@ -190,7 +190,7 @@ void ServerState::add_client_to_available_lobby(shared_ptr c) { } if (!added_to_lobby) { - added_to_lobby = this->create_lobby(); + added_to_lobby = this->create_lobby(false); added_to_lobby->set_flag(Lobby::Flag::PUBLIC); added_to_lobby->set_flag(Lobby::Flag::IS_OVERFLOW); added_to_lobby->block = 100; @@ -278,11 +278,11 @@ vector> ServerState::all_lobbies() { return ret; } -shared_ptr ServerState::create_lobby() { +shared_ptr ServerState::create_lobby(bool is_game) { while (this->id_to_lobby.count(this->next_lobby_id)) { this->next_lobby_id++; } - auto l = make_shared(this->shared_from_this(), this->next_lobby_id++); + auto l = make_shared(this->shared_from_this(), this->next_lobby_id++, is_game); this->id_to_lobby.emplace(l->lobby_id, l); l->idle_timeout_usecs = this->persistent_game_idle_timeout_usecs; return l; diff --git a/src/ServerState.hh b/src/ServerState.hh index fdfc7e79..18642aa3 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -245,7 +245,7 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr find_lobby(uint32_t lobby_id); std::vector> all_lobbies(); - std::shared_ptr create_lobby(); + std::shared_ptr create_lobby(bool is_game); void remove_lobby(std::shared_ptr l); void on_player_left_lobby(std::shared_ptr l, uint8_t leaving_client_id); diff --git a/src/StaticGameData.cc b/src/StaticGameData.cc index 2b8fe7f3..e0895e2e 100644 --- a/src/StaticGameData.cc +++ b/src/StaticGameData.cc @@ -109,6 +109,9 @@ static const array section_id_to_name = { "Viridia", "Greennill", "Skyly", "Bluefull", "Purplenum", "Pinkal", "Redria", "Oran", "Yellowboze", "Whitill"}; +static const array section_id_to_abbreviation = { + "Vir", "Grn", "Sky", "Blu", "Prp", "Pnk", "Red", "Orn", "Ylw", "Wht"}; + const unordered_map name_to_section_id({ {"viridia", 0}, {"greennill", 1}, @@ -221,6 +224,14 @@ const vector npc_id_to_name({"ninja", "rico", "sonic", "knuckles", "tail const unordered_map name_to_npc_id = { {"ninja", 0}, {"rico", 1}, {"sonic", 2}, {"knuckles", 3}, {"tails", 4}, {"flowen", 5}, {"elly", 6}}; +const char* abbreviation_for_section_id(uint8_t section_id) { + if (section_id < section_id_to_abbreviation.size()) { + return section_id_to_abbreviation[section_id]; + } else { + return ""; + } +} + const char* name_for_section_id(uint8_t section_id) { if (section_id < section_id_to_name.size()) { return section_id_to_name[section_id]; diff --git a/src/StaticGameData.hh b/src/StaticGameData.hh index 5181e6c9..87861eaf 100644 --- a/src/StaticGameData.hh +++ b/src/StaticGameData.hh @@ -40,6 +40,7 @@ extern const std::unordered_map name_to_tech_id; const std::string& name_for_technique(uint8_t tech); uint8_t technique_for_name(const std::string& name); +const char* abbreviation_for_section_id(uint8_t section_id); const char* name_for_section_id(uint8_t section_id); uint8_t section_id_for_name(const std::string& name); diff --git a/tests/DCNTE-GameSmokeTest.test.txt b/tests/DCNTE-GameSmokeTest.test.txt index 36853d93..56eaf7fe 100644 --- a/tests/DCNTE-GameSmokeTest.test.txt +++ b/tests/DCNTE-GameSmokeTest.test.txt @@ -198,14 +198,14 @@ I 25793 2023-11-24 23:06:59 - [Commands] Sending to C-8 (ABCDEFGHIJKL) (version= 0010 | 7F 00 00 01 00 00 00 00 41 42 43 44 45 46 47 48 | ABCDEFGH 0020 | 49 4A 4B 4C 00 00 00 00 05 00 00 00 02 00 00 00 | IJKL 0030 | 0C 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 | -0040 | 05 00 01 00 00 00 00 00 02 00 00 00 0C 00 00 00 | -0050 | 01 01 00 00 00 00 00 00 00 00 00 00 06 00 01 00 | +0040 | 00 00 01 10 00 00 00 00 02 00 00 00 0C 00 00 00 | +0050 | 01 01 00 00 00 00 00 00 00 00 00 00 01 00 01 10 | 0060 | 00 00 00 00 02 00 00 00 0C 00 00 00 02 00 05 00 | -0070 | F4 01 00 00 00 00 00 00 07 00 01 00 00 00 28 00 | ( +0070 | F4 01 00 00 00 00 00 00 02 00 01 10 00 00 28 00 | ( 0080 | 01 00 00 00 10 00 00 00 03 00 00 00 00 01 00 00 | -0090 | 00 00 00 00 08 00 01 00 00 00 00 00 01 00 00 00 | +0090 | 00 00 00 00 03 00 01 10 00 00 00 00 01 00 00 00 | 00A0 | 10 00 00 00 03 01 00 00 00 01 00 00 00 00 00 00 | -00B0 | 09 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | +00B0 | 04 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 | 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -325,14 +325,14 @@ I 25793 2023-11-24 23:07:09 - [Commands] Sending to C-8 (ABCDEFGHIJKL) (version= 0010 | 7F 00 00 01 00 00 00 00 41 42 43 44 45 46 47 48 | ABCDEFGH 0020 | 49 4A 4B 4C 00 00 00 00 05 00 00 00 02 00 00 00 | IJKL 0030 | 0C 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 | -0040 | 05 00 01 00 00 00 00 00 02 00 00 00 0C 00 00 00 | -0050 | 01 01 00 00 00 00 00 00 00 00 00 00 06 00 01 00 | +0040 | 00 00 01 10 00 00 00 00 02 00 00 00 0C 00 00 00 | +0050 | 01 01 00 00 00 00 00 00 00 00 00 00 01 00 01 10 | 0060 | 00 00 00 00 02 00 00 00 0C 00 00 00 02 00 05 00 | -0070 | F4 01 00 00 00 00 00 00 07 00 01 00 00 00 28 00 | ( +0070 | F4 01 00 00 00 00 00 00 02 00 01 10 00 00 28 00 | ( 0080 | 01 00 00 00 10 00 00 00 03 00 00 00 00 01 00 00 | -0090 | 00 00 00 00 08 00 01 00 00 00 00 00 01 00 00 00 | +0090 | 00 00 00 00 03 00 01 10 00 00 00 00 01 00 00 00 | 00A0 | 10 00 00 00 03 01 00 00 00 01 00 00 00 00 00 00 | -00B0 | 09 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | +00B0 | 04 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 | 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -745,14 +745,14 @@ I 25793 2023-11-24 23:07:53 - [Commands] Sending to C-A (ABCDEFGHIJKL) (version= 0010 | 7F 00 00 01 00 00 00 00 41 42 43 44 45 46 47 48 | ABCDEFGH 0020 | 49 4A 4B 4C 00 00 00 00 05 00 00 00 02 00 00 00 | IJKL 0030 | 0C 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 | -0040 | 00 00 01 00 00 00 00 00 02 00 00 00 0C 00 00 00 | -0050 | 01 01 00 00 00 00 00 00 00 00 00 00 01 00 01 00 | +0040 | 00 00 01 10 00 00 00 00 02 00 00 00 0C 00 00 00 | +0050 | 01 01 00 00 00 00 00 00 00 00 00 00 01 00 01 10 | 0060 | 00 00 00 00 02 00 00 00 0C 00 00 00 02 00 05 00 | -0070 | F4 01 00 00 00 00 00 00 02 00 01 00 00 00 28 00 | ( +0070 | F4 01 00 00 00 00 00 00 02 00 01 10 00 00 28 00 | ( 0080 | 01 00 00 00 10 00 00 00 03 00 00 00 00 01 00 00 | -0090 | 00 00 00 00 03 00 01 00 00 00 00 00 01 00 00 00 | +0090 | 00 00 00 00 03 00 01 10 00 00 00 00 01 00 00 00 | 00A0 | 10 00 00 00 03 01 00 00 00 01 00 00 00 00 00 00 | -00B0 | 04 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | +00B0 | 04 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 | 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | diff --git a/tests/DCv1-GameSmokeTest.test.txt b/tests/DCv1-GameSmokeTest.test.txt index 3c082a6f..577617c6 100644 --- a/tests/DCv1-GameSmokeTest.test.txt +++ b/tests/DCv1-GameSmokeTest.test.txt @@ -236,14 +236,14 @@ I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (Tali) (version=DC comma 0010 | 00 00 01 00 77 77 77 77 7F 00 00 01 00 00 00 00 | wwww 0020 | 54 61 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 | Tali 0030 | 05 00 00 01 02 00 00 00 0C 00 00 00 00 06 00 00 | -0040 | 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 | +0040 | 00 00 00 00 00 00 00 00 00 00 01 10 00 00 00 00 | 0050 | 02 00 00 00 0C 00 00 00 01 01 00 00 00 00 00 00 | -0060 | 00 00 00 00 01 00 01 00 00 00 00 00 02 00 00 00 | +0060 | 00 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0070 | 0C 00 00 00 02 00 05 00 F4 01 01 00 00 00 00 00 | -0080 | 02 00 01 00 00 00 23 00 01 00 00 00 10 00 00 00 | # -0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 00 | +0080 | 02 00 01 10 00 00 23 00 01 00 00 00 10 00 00 00 | # +0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 10 | 00A0 | 00 00 00 00 01 00 00 00 10 00 00 00 03 01 00 00 | -00B0 | 00 04 00 00 00 00 00 00 04 00 01 00 00 00 00 00 | +00B0 | 00 04 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -393,14 +393,14 @@ I 40469 2023-05-26 10:41:20 - [Commands] Sending to C-2 (Tali) (version=DC comma 0010 | 00 00 01 00 77 77 77 77 7F 00 00 01 00 00 00 00 | wwww 0020 | 54 61 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 | Tali 0030 | 05 00 00 01 02 00 00 00 0C 00 00 00 00 06 00 00 | -0040 | 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 | +0040 | 00 00 00 00 00 00 00 00 00 00 01 10 00 00 00 00 | 0050 | 02 00 00 00 0C 00 00 00 01 01 00 00 00 00 00 00 | -0060 | 00 00 00 00 01 00 01 00 00 00 00 00 02 00 00 00 | +0060 | 00 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0070 | 0C 00 00 00 02 00 05 00 F4 01 01 00 00 00 00 00 | -0080 | 02 00 01 00 00 00 23 00 01 00 00 00 10 00 00 00 | # -0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 00 | +0080 | 02 00 01 10 00 00 23 00 01 00 00 00 10 00 00 00 | # +0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 10 | 00A0 | 00 00 00 00 01 00 00 00 10 00 00 00 03 01 00 00 | -00B0 | 00 04 00 00 00 00 00 00 04 00 01 00 00 00 00 00 | +00B0 | 00 04 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -1116,16 +1116,16 @@ I 40469 2023-05-26 10:42:43 - [Commands] Sending to C-2 (Tali) (version=DC comma 0010 | 00 00 01 00 77 77 77 77 7F 00 00 01 00 00 00 00 | wwww 0020 | 54 61 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 | Tali 0030 | 06 00 00 01 02 00 00 00 4C 00 00 00 00 06 00 00 | L -0040 | 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 | +0040 | 00 00 00 00 00 00 00 00 00 00 01 10 00 00 00 00 | 0050 | 02 00 00 00 4C 00 00 00 01 01 00 00 00 00 00 00 | L -0060 | 00 00 00 00 01 00 01 00 00 00 00 00 02 00 00 00 | +0060 | 00 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0070 | 4C 00 00 00 02 00 05 00 F4 01 01 00 00 00 00 00 | L -0080 | 02 00 01 00 00 00 1E 00 01 00 00 00 10 00 00 00 | -0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 00 | +0080 | 02 00 01 10 00 00 1E 00 01 00 00 00 10 00 00 00 | +0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 10 | 00A0 | 00 00 00 00 01 00 00 00 10 00 00 00 03 01 00 00 | -00B0 | 00 04 00 00 00 00 00 00 04 00 01 00 00 00 00 00 | +00B0 | 00 04 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 01 00 00 00 04 00 00 00 00 06 00 00 00 00 00 00 | -00D0 | 00 00 00 00 7E 01 01 06 00 00 00 00 00 00 00 00 | ~ +00D0 | 00 00 00 00 05 00 01 10 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0100 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | diff --git a/tests/DCv2-GameSmokeTest.test.txt b/tests/DCv2-GameSmokeTest.test.txt index 214b6bb6..b3d1b7b0 100644 --- a/tests/DCv2-GameSmokeTest.test.txt +++ b/tests/DCv2-GameSmokeTest.test.txt @@ -240,16 +240,16 @@ I 40992 2023-05-26 10:52:57 - [Commands] Sending to C-2 (Tali) (version=DC comma 0010 | 00 00 01 00 77 77 77 77 7F 00 00 01 00 00 00 00 | wwww 0020 | 54 61 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 | Tali 0030 | 06 00 00 01 02 00 00 00 4C 00 00 00 00 06 00 00 | L -0040 | 00 00 00 00 00 00 00 00 06 00 01 10 00 00 00 00 | +0040 | 00 00 00 00 00 00 00 00 00 00 01 10 00 00 00 00 | 0050 | 02 00 00 00 4C 00 00 00 01 01 00 00 00 00 00 00 | L -0060 | 00 00 00 00 07 00 01 10 00 00 00 00 02 00 00 00 | +0060 | 00 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0070 | 4C 00 00 00 02 00 05 00 F4 01 01 00 00 00 00 00 | L -0080 | 08 00 01 10 00 00 19 00 01 00 00 00 10 00 00 00 | -0090 | 03 00 00 00 00 04 00 00 00 00 00 00 09 00 01 10 | +0080 | 02 00 01 10 00 00 19 00 01 00 00 00 10 00 00 00 | +0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 10 | 00A0 | 00 00 00 00 01 00 00 00 10 00 00 00 03 01 00 00 | -00B0 | 00 04 00 00 00 00 00 00 0A 00 01 10 00 00 00 00 | +00B0 | 00 04 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 01 00 00 00 04 00 00 00 00 06 00 00 00 00 00 00 | -00D0 | 00 00 00 00 0B 00 01 10 00 00 00 00 00 FF 00 00 | +00D0 | 00 00 00 00 05 00 01 10 00 00 00 00 00 FF 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00F0 | FF FF FF FF 00 00 00 00 00 FF 00 00 00 00 00 00 | 0100 | 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF | @@ -387,16 +387,16 @@ I 40992 2023-05-26 10:53:08 - [Commands] Sending to C-2 (Tali) (version=DC comma 0010 | 00 00 01 00 77 77 77 77 7F 00 00 01 00 00 00 00 | wwww 0020 | 54 61 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 | Tali 0030 | 06 00 00 01 02 00 00 00 4C 00 00 00 00 06 00 00 | L -0040 | 00 00 00 00 00 00 00 00 06 00 01 10 00 00 00 00 | +0040 | 00 00 00 00 00 00 00 00 00 00 01 10 00 00 00 00 | 0050 | 02 00 00 00 4C 00 00 00 01 01 00 00 00 00 00 00 | L -0060 | 00 00 00 00 07 00 01 10 00 00 00 00 02 00 00 00 | +0060 | 00 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0070 | 4C 00 00 00 02 00 05 00 F4 01 01 00 00 00 00 00 | L -0080 | 08 00 01 10 00 00 19 00 01 00 00 00 10 00 00 00 | -0090 | 03 00 00 00 00 04 00 00 00 00 00 00 09 00 01 10 | +0080 | 02 00 01 10 00 00 19 00 01 00 00 00 10 00 00 00 | +0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 10 | 00A0 | 00 00 00 00 01 00 00 00 10 00 00 00 03 01 00 00 | -00B0 | 00 04 00 00 00 00 00 00 0A 00 01 10 00 00 00 00 | +00B0 | 00 04 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 01 00 00 00 04 00 00 00 00 06 00 00 00 00 00 00 | -00D0 | 00 00 00 00 0B 00 01 10 00 00 00 00 00 FF 00 00 | +00D0 | 00 00 00 00 05 00 01 10 00 00 00 00 00 FF 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00F0 | FF FF FF FF 00 00 00 00 00 FF 00 00 00 00 00 00 | 0100 | 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF | @@ -1360,18 +1360,18 @@ I 40992 2023-05-26 10:55:24 - [Commands] Sending to C-2 (Tali) (version=DC comma 0010 | 00 00 01 00 77 77 77 77 7F 00 00 01 00 00 00 00 | wwww 0020 | 54 61 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 | Tali 0030 | 07 00 00 01 02 00 00 00 4C 00 00 00 00 06 00 00 | L -0040 | 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 | +0040 | 00 00 00 00 00 00 00 00 00 00 01 10 00 00 00 00 | 0050 | 02 00 00 00 4C 00 00 00 01 01 00 00 00 00 00 00 | L -0060 | 00 00 00 00 01 00 01 00 00 00 00 00 02 00 00 00 | +0060 | 00 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0070 | 4C 00 00 00 02 00 05 00 F4 01 01 00 00 00 00 00 | L -0080 | 02 00 01 00 00 00 14 00 01 00 00 00 10 00 00 00 | -0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 00 | +0080 | 02 00 01 10 00 00 14 00 01 00 00 00 10 00 00 00 | +0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 10 | 00A0 | 00 00 00 00 01 00 00 00 10 00 00 00 03 01 00 00 | -00B0 | 00 04 00 00 00 00 00 00 04 00 01 00 00 00 00 00 | +00B0 | 00 04 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 01 00 00 00 04 00 00 00 00 06 00 00 00 00 00 00 | -00D0 | 00 00 00 00 05 00 01 00 00 00 00 00 01 00 00 00 | +00D0 | 00 00 00 00 05 00 01 10 00 00 00 00 01 00 00 00 | 00E0 | 10 00 00 00 03 06 00 00 00 01 00 00 00 00 00 00 | -00F0 | 7B 01 01 06 00 00 00 00 00 00 00 00 00 00 00 00 | { +00F0 | 06 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 | 0100 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0110 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0120 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | diff --git a/tests/GC-Episode3BattleWithSpectator.test.txt b/tests/GC-Episode3BattleWithSpectator.test.txt index d45c8d5e..ba054d61 100644 --- a/tests/GC-Episode3BattleWithSpectator.test.txt +++ b/tests/GC-Episode3BattleWithSpectator.test.txt @@ -3521,14 +3521,14 @@ I 17097 2023-09-19 21:52:50 - [Commands] Sending to C-2 (Tali) (version=GC comma 0010 | 00 00 01 00 11 11 11 11 7F 00 00 01 00 00 00 00 | 0020 | 54 61 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 | Tali 0030 | 05 00 00 01 01 00 00 00 08 00 00 00 00 06 00 00 | -0040 | 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 | +0040 | 00 00 00 00 00 00 00 00 00 00 01 10 00 00 00 00 | 0050 | 01 00 00 00 08 00 00 00 01 01 00 00 00 00 00 00 | -0060 | 00 00 00 00 01 00 01 00 00 00 00 00 01 00 00 00 | +0060 | 00 00 00 00 01 00 01 10 00 00 00 00 01 00 00 00 | 0070 | 08 00 00 00 02 00 05 00 F4 01 00 00 00 00 00 00 | -0080 | 02 00 01 00 12 00 00 28 01 00 00 00 00 00 00 00 | ( -0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 00 | +0080 | 02 00 01 10 12 00 00 28 01 00 00 00 00 00 00 00 | ( +0090 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 10 | 00A0 | 00 00 00 00 01 00 00 00 00 00 00 00 03 01 00 00 | -00B0 | 00 04 00 00 00 00 00 00 04 00 01 00 00 00 00 00 | +00B0 | 00 04 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | diff --git a/tests/GC-ForestGame.test.txt b/tests/GC-ForestGame.test.txt index 1429ae77..ffdfc7de 100644 --- a/tests/GC-ForestGame.test.txt +++ b/tests/GC-ForestGame.test.txt @@ -302,30 +302,30 @@ I 49108 2023-05-26 16:18:08 - [Commands] Sending to C-2 (Jess) (version=GC comma 0010 | 00 00 01 00 11 11 11 11 7F 00 00 01 00 00 00 00 | 0020 | 4A 65 73 73 00 00 00 00 00 00 00 00 00 00 00 00 | Jess 0030 | 0E 02 00 01 02 00 00 00 4C 00 00 00 02 0B 2D 29 | L -) -0040 | 2C 06 8E 03 14 02 72 06 1C 00 01 10 05 03 34 67 | , r 4g +0040 | 2C 06 8E 03 14 02 72 06 00 00 01 10 05 03 34 67 | , r 4g 0050 | 02 00 00 00 4C 00 00 00 01 01 07 00 00 01 00 00 | L -0060 | 01 00 00 00 1D 00 01 10 00 00 00 00 02 00 00 00 | +0060 | 01 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0070 | 4C 00 00 00 01 02 07 00 00 00 04 00 02 00 00 00 | L -0080 | 1E 00 01 10 00 00 00 00 02 00 00 00 4C 00 00 00 | L -0090 | 00 06 02 03 00 00 02 19 03 19 00 00 1F 00 01 10 | +0080 | 02 00 01 10 00 00 00 00 02 00 00 00 4C 00 00 00 | L +0090 | 00 06 02 03 00 00 02 19 03 19 00 00 03 00 01 10 | 00A0 | 00 00 00 00 02 00 00 00 4C 00 00 00 01 03 21 00 | L ! -00B0 | 00 00 00 00 00 00 00 00 20 00 01 10 00 00 00 00 | +00B0 | 00 00 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 01 00 00 00 44 00 00 00 01 03 04 00 00 00 FF FF | D -00D0 | 00 00 00 00 21 00 01 10 00 00 00 00 01 00 05 00 | ! +00D0 | 00 00 00 00 05 00 01 10 00 00 00 00 01 00 05 00 | 00E0 | 44 00 00 00 01 01 03 00 00 00 00 00 00 00 00 00 | D -00F0 | 22 00 01 10 00 00 00 00 01 00 00 00 50 00 00 00 | " P -0100 | 03 00 02 00 00 01 00 00 00 00 00 00 23 00 01 10 | # +00F0 | 06 00 01 10 00 00 00 00 01 00 00 00 50 00 00 00 | P +0100 | 03 00 02 00 00 01 00 00 00 00 00 00 07 00 01 10 | 0110 | 00 00 00 00 01 00 00 05 50 00 00 00 03 08 00 00 | P -0120 | 00 01 00 00 00 00 00 00 24 00 01 10 00 00 00 00 | $ +0120 | 00 01 00 00 00 00 00 00 08 00 01 10 00 00 00 00 | 0130 | 01 00 00 03 44 00 00 00 00 01 01 00 00 00 01 0F | D -0140 | 00 00 00 00 25 00 01 10 00 00 00 00 01 00 00 04 | % +0140 | 00 00 00 00 09 00 01 10 00 00 00 00 01 00 00 04 | 0150 | 44 00 00 00 01 01 06 00 00 00 01 00 00 00 00 00 | D -0160 | 26 00 01 10 00 00 00 00 01 00 00 02 44 00 00 00 | & D -0170 | 00 0A 01 00 00 00 02 14 00 00 00 00 27 00 01 10 | ' +0160 | 0A 00 01 10 00 00 00 00 01 00 00 02 44 00 00 00 | D +0170 | 00 0A 01 00 00 00 02 14 00 00 00 00 0B 00 01 10 | 0180 | 00 00 00 00 01 00 00 00 40 00 00 00 03 10 00 00 | @ -0190 | 00 02 00 00 00 00 00 00 28 00 01 10 00 00 00 00 | ( +0190 | 00 02 00 00 00 00 00 00 0C 00 01 10 00 00 00 00 | 01A0 | 01 00 00 01 50 00 00 00 03 01 00 00 00 01 00 00 | P -01B0 | 00 00 00 00 29 00 01 10 00 00 00 00 00 00 00 00 | ) +01B0 | 00 00 00 00 0D 00 01 10 00 00 00 00 00 00 00 00 | 01C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 01D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 01E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -511,30 +511,30 @@ I 49108 2023-05-26 16:18:21 - [Commands] Sending to C-2 (Jess) (version=GC comma 0010 | 00 00 01 00 11 11 11 11 7F 00 00 01 00 00 00 00 | 0020 | 4A 65 73 73 00 00 00 00 00 00 00 00 00 00 00 00 | Jess 0030 | 0E 02 00 01 02 00 00 00 4C 00 00 00 02 0B 2D 29 | L -) -0040 | 2C 06 8E 03 14 02 72 06 1C 00 01 10 05 03 34 67 | , r 4g +0040 | 2C 06 8E 03 14 02 72 06 00 00 01 10 05 03 34 67 | , r 4g 0050 | 02 00 00 00 4C 00 00 00 01 01 07 00 00 01 00 00 | L -0060 | 01 00 00 00 1D 00 01 10 00 00 00 00 02 00 00 00 | +0060 | 01 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0070 | 4C 00 00 00 01 02 07 00 00 00 04 00 02 00 00 00 | L -0080 | 1E 00 01 10 00 00 00 00 02 00 00 00 4C 00 00 00 | L -0090 | 00 06 02 03 00 00 02 19 03 19 00 00 1F 00 01 10 | +0080 | 02 00 01 10 00 00 00 00 02 00 00 00 4C 00 00 00 | L +0090 | 00 06 02 03 00 00 02 19 03 19 00 00 03 00 01 10 | 00A0 | 00 00 00 00 02 00 00 00 4C 00 00 00 01 03 21 00 | L ! -00B0 | 00 00 00 00 00 00 00 00 20 00 01 10 00 00 00 00 | +00B0 | 00 00 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 01 00 00 00 44 00 00 00 01 03 04 00 00 00 FF FF | D -00D0 | 00 00 00 00 21 00 01 10 00 00 00 00 01 00 05 00 | ! +00D0 | 00 00 00 00 05 00 01 10 00 00 00 00 01 00 05 00 | 00E0 | 44 00 00 00 01 01 03 00 00 00 00 00 00 00 00 00 | D -00F0 | 22 00 01 10 00 00 00 00 01 00 00 00 50 00 00 00 | " P -0100 | 03 00 02 00 00 01 00 00 00 00 00 00 23 00 01 10 | # +00F0 | 06 00 01 10 00 00 00 00 01 00 00 00 50 00 00 00 | P +0100 | 03 00 02 00 00 01 00 00 00 00 00 00 07 00 01 10 | 0110 | 00 00 00 00 01 00 00 05 50 00 00 00 03 08 00 00 | P -0120 | 00 01 00 00 00 00 00 00 24 00 01 10 00 00 00 00 | $ +0120 | 00 01 00 00 00 00 00 00 08 00 01 10 00 00 00 00 | 0130 | 01 00 00 03 44 00 00 00 00 01 01 00 00 00 01 0F | D -0140 | 00 00 00 00 25 00 01 10 00 00 00 00 01 00 00 04 | % +0140 | 00 00 00 00 09 00 01 10 00 00 00 00 01 00 00 04 | 0150 | 44 00 00 00 01 01 06 00 00 00 01 00 00 00 00 00 | D -0160 | 26 00 01 10 00 00 00 00 01 00 00 02 44 00 00 00 | & D -0170 | 00 0A 01 00 00 00 02 14 00 00 00 00 27 00 01 10 | ' +0160 | 0A 00 01 10 00 00 00 00 01 00 00 02 44 00 00 00 | D +0170 | 00 0A 01 00 00 00 02 14 00 00 00 00 0B 00 01 10 | 0180 | 00 00 00 00 01 00 00 00 40 00 00 00 03 10 00 00 | @ -0190 | 00 02 00 00 00 00 00 00 28 00 01 10 00 00 00 00 | ( +0190 | 00 02 00 00 00 00 00 00 0C 00 01 10 00 00 00 00 | 01A0 | 01 00 00 01 50 00 00 00 03 01 00 00 00 01 00 00 | P -01B0 | 00 00 00 00 29 00 01 10 00 00 00 00 00 00 00 00 | ) +01B0 | 00 00 00 00 0D 00 01 10 00 00 00 00 00 00 00 00 | 01C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 01D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 01E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -9834,63 +9834,63 @@ I 49108 2023-05-26 16:28:09 - [Commands] Sending to C-2 (Jess) (version=GC comma 0010 | 00 00 01 00 11 11 11 11 7F 00 00 01 00 00 00 00 | 0020 | 4A 65 73 73 00 00 00 00 00 00 00 00 00 00 00 00 | Jess 0030 | 1E 02 00 01 02 00 00 00 4C 00 00 00 02 0B 2D 29 | L -) -0040 | 2C 06 8E 03 14 02 72 06 00 00 01 00 05 03 34 67 | , r 4g +0040 | 2C 06 8E 03 14 02 72 06 00 00 01 10 05 03 34 67 | , r 4g 0050 | 02 00 00 00 4C 00 00 00 01 01 07 00 00 01 00 00 | L -0060 | 01 00 00 00 01 00 01 00 00 00 00 00 02 00 00 00 | +0060 | 01 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0070 | 4C 00 00 00 01 02 07 00 00 00 04 00 02 00 00 00 | L -0080 | 02 00 01 00 00 00 00 00 02 00 00 00 4C 00 00 00 | L -0090 | 00 06 02 03 00 00 02 19 03 19 00 00 03 00 01 00 | +0080 | 02 00 01 10 00 00 00 00 02 00 00 00 4C 00 00 00 | L +0090 | 00 06 02 03 00 00 02 19 03 19 00 00 03 00 01 10 | 00A0 | 00 00 00 00 02 00 00 00 4C 00 00 00 01 03 21 00 | L ! -00B0 | 00 00 00 00 00 00 00 00 04 00 01 00 00 00 00 00 | +00B0 | 00 00 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00C0 | 01 00 00 00 44 00 00 00 01 03 04 00 00 00 FF FF | D -00D0 | 00 00 00 00 05 00 01 00 00 00 00 00 01 00 05 00 | +00D0 | 00 00 00 00 05 00 01 10 00 00 00 00 01 00 05 00 | 00E0 | 44 00 00 00 01 01 03 00 00 00 00 00 00 00 00 00 | D -00F0 | 06 00 01 00 00 00 00 00 01 00 00 00 50 00 00 00 | P -0100 | 03 00 02 00 00 01 00 00 00 00 00 00 07 00 01 00 | +00F0 | 06 00 01 10 00 00 00 00 01 00 00 00 50 00 00 00 | P +0100 | 03 00 02 00 00 01 00 00 00 00 00 00 07 00 01 10 | 0110 | 00 00 00 00 01 00 00 05 50 00 00 00 03 08 00 00 | P -0120 | 00 01 00 00 00 00 00 00 08 00 01 00 00 00 00 00 | +0120 | 00 01 00 00 00 00 00 00 08 00 01 10 00 00 00 00 | 0130 | 01 00 00 03 44 00 00 00 00 01 01 00 00 00 01 0F | D -0140 | 00 00 00 00 09 00 01 00 00 00 00 00 01 00 00 04 | +0140 | 00 00 00 00 09 00 01 10 00 00 00 00 01 00 00 04 | 0150 | 44 00 00 00 01 01 06 00 00 00 01 00 00 00 00 00 | D -0160 | 0A 00 01 00 00 00 00 00 01 00 00 02 44 00 00 00 | D -0170 | 00 0A 01 00 00 00 02 14 00 00 00 00 0B 00 01 00 | +0160 | 0A 00 01 10 00 00 00 00 01 00 00 02 44 00 00 00 | D +0170 | 00 0A 01 00 00 00 02 14 00 00 00 00 0B 00 01 10 | 0180 | 00 00 00 00 01 00 00 00 40 00 00 00 03 10 00 00 | @ -0190 | 00 02 00 00 00 00 00 00 0C 00 01 00 00 00 00 00 | +0190 | 00 02 00 00 00 00 00 00 0C 00 01 10 00 00 00 00 | 01A0 | 01 00 00 01 50 00 00 00 03 01 00 00 00 0A 00 00 | P -01B0 | 00 00 00 00 0D 00 01 00 00 00 00 00 01 00 00 00 | +01B0 | 00 00 00 00 0D 00 01 10 00 00 00 00 01 00 00 00 | 01C0 | 50 00 00 00 03 06 01 00 00 02 00 00 00 00 00 00 | P -01D0 | 11 0D 01 06 00 00 00 00 01 00 00 00 50 00 00 00 | P -01E0 | 03 00 01 00 00 01 00 00 00 00 00 00 0F 0D 01 06 | +01D0 | 0E 00 01 10 00 00 00 00 01 00 00 00 50 00 00 00 | P +01E0 | 03 00 01 00 00 01 00 00 00 00 00 00 0F 00 01 10 | 01F0 | 00 00 00 00 01 00 00 00 50 00 00 00 03 06 00 00 | P -0200 | 00 01 00 00 00 00 00 00 04 0D 01 06 00 00 00 00 | +0200 | 00 01 00 00 00 00 00 00 10 00 01 10 00 00 00 00 | 0210 | 01 00 00 00 50 00 00 00 03 02 00 00 00 00 00 00 | P -0220 | 00 00 00 00 35 01 01 06 00 00 00 00 01 00 00 00 | 5 +0220 | 00 00 00 00 11 00 01 10 00 00 00 00 01 00 00 00 | 0230 | 44 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 | D -0240 | 33 01 01 06 00 00 00 00 01 00 00 00 44 00 00 00 | 3 D -0250 | 00 06 00 00 00 00 00 00 00 00 00 00 26 01 01 06 | & +0240 | 12 00 01 10 00 00 00 00 01 00 00 00 44 00 00 00 | D +0250 | 00 06 00 00 00 00 00 00 00 00 00 00 13 00 01 10 | 0260 | 00 00 00 00 01 00 00 00 44 00 00 00 00 01 00 00 | D -0270 | 00 00 00 00 00 00 00 00 24 01 01 06 00 00 00 00 | $ +0270 | 00 00 00 00 00 00 00 00 14 00 01 10 00 00 00 00 | 0280 | 01 00 00 00 44 00 00 00 01 01 00 00 00 00 00 00 | D -0290 | 01 00 00 00 D8 0C 01 06 00 00 00 00 01 00 00 00 | +0290 | 01 00 00 00 15 00 01 10 00 00 00 00 01 00 00 00 | 02A0 | 44 00 00 00 01 01 00 00 00 00 01 00 01 00 00 00 | D -02B0 | DA 0C 01 06 00 00 00 00 01 00 00 00 50 00 00 00 | P -02C0 | 03 00 00 00 00 03 00 00 00 00 00 00 43 01 01 06 | C +02B0 | 16 00 01 10 00 00 00 00 01 00 00 00 50 00 00 00 | P +02C0 | 03 00 00 00 00 03 00 00 00 00 00 00 17 00 01 10 | 02D0 | 00 00 00 00 01 00 00 00 50 00 00 00 03 07 00 00 | P -02E0 | 00 01 00 00 00 00 00 00 F8 0C 01 06 00 00 00 00 | +02E0 | 00 01 00 00 00 00 00 00 18 00 01 10 00 00 00 00 | 02F0 | 01 00 00 00 44 00 00 00 01 01 00 00 00 00 01 00 | D -0300 | 01 00 00 00 FA 0C 01 06 00 00 00 00 01 00 00 00 | +0300 | 01 00 00 00 19 00 01 10 00 00 00 00 01 00 00 00 | 0310 | 44 00 00 00 00 0A 00 00 00 00 01 05 00 00 00 00 | D -0320 | F2 0C 01 06 00 00 00 00 01 00 00 00 44 00 00 00 | D -0330 | 00 01 00 00 00 00 00 00 00 00 00 00 DC 0C 01 06 | +0320 | 1A 00 01 10 00 00 00 00 01 00 00 00 44 00 00 00 | D +0330 | 00 01 00 00 00 00 00 00 00 00 00 00 1B 00 01 10 | 0340 | 00 00 00 00 01 00 00 00 44 00 00 00 00 0A 00 00 | D -0350 | 00 00 00 00 00 00 00 00 E2 0C 01 06 00 00 00 00 | +0350 | 00 00 00 00 00 00 00 00 1C 00 01 10 00 00 00 00 | 0360 | 01 00 00 00 44 00 00 00 00 06 00 00 00 00 00 00 | D -0370 | 00 00 00 00 02 0D 01 06 00 00 00 00 7F 00 87 00 | +0370 | 00 00 00 00 1D 00 01 10 00 00 00 00 7F 00 87 00 | 0380 | A4 00 45 00 30 00 00 01 0A 00 28 00 64 66 92 41 | E 0 ( df A 0390 | 00 00 20 41 18 00 00 00 B5 9C 00 00 E6 00 00 00 | A 03A0 | 4A 65 73 73 00 00 00 00 00 00 00 00 00 00 00 00 | Jess 03B0 | 00 00 00 00 00 00 00 00 FF FF FF FF 00 00 00 00 | -03C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +03C0 | 00 00 00 00 00 00 00 00 00 00 00 00 56 47 29 B8 | VG) 03D0 | 05 0B 00 02 46 00 00 00 0E 00 01 00 02 00 00 00 | F 03E0 | 06 00 AD 00 15 00 FF 00 8C 11 C7 3D FA 7C 9A 35 | = | 5 03F0 | 00 00 00 00 01 06 00 00 02 00 01 00 02 01 01 00 | diff --git a/tests/PC-BasicGame.test.txt b/tests/PC-BasicGame.test.txt index 415686b3..8817e0f8 100644 --- a/tests/PC-BasicGame.test.txt +++ b/tests/PC-BasicGame.test.txt @@ -263,14 +263,14 @@ I 49484 2023-05-26 16:35:14 - [Commands] Sending to C-3 (Tali) (version=PC comma 0020 | 54 00 61 00 6C 00 69 00 00 00 00 00 00 00 00 00 | T a l i 0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0040 | 05 00 00 01 01 00 00 00 08 00 00 00 00 06 00 00 | -0050 | 00 00 00 00 00 00 00 00 FF FF FF FF 00 00 00 00 | +0050 | 00 00 00 00 00 00 00 00 00 00 01 10 00 00 00 00 | 0060 | 01 00 00 00 08 00 00 00 01 01 00 00 00 00 00 00 | -0070 | 00 00 00 00 FF FF FF FF 00 00 00 00 01 00 00 00 | +0070 | 00 00 00 00 01 00 01 10 00 00 00 00 01 00 00 00 | 0080 | 08 00 00 00 02 00 05 00 F4 01 01 00 01 00 01 00 | -0090 | FF FF FF FF 00 00 28 00 01 00 00 00 00 00 00 00 | ( -00A0 | 03 00 00 00 00 04 00 00 00 00 00 00 FF FF FF FF | +0090 | 02 00 01 10 00 00 28 00 01 00 00 00 00 00 00 00 | ( +00A0 | 03 00 00 00 00 04 00 00 00 00 00 00 03 00 01 10 | 00B0 | 00 00 00 00 01 00 00 00 00 00 00 00 03 01 00 00 | -00C0 | 00 04 00 00 00 00 00 00 FF FF FF FF 00 00 00 00 | +00C0 | 00 04 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -1004,14 +1004,14 @@ I 49484 2023-05-26 16:36:57 - [Commands] Sending to C-3 (Tali) (version=PC comma 0020 | 54 00 61 00 6C 00 69 00 00 00 00 00 00 00 00 00 | T a l i 0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0040 | 05 00 00 01 02 00 00 00 4C 00 00 00 00 06 00 00 | L -0050 | 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 | +0050 | 00 00 00 00 00 00 00 00 00 00 01 10 00 00 00 00 | 0060 | 02 00 00 00 4C 00 00 00 01 01 00 00 00 00 00 00 | L -0070 | 00 00 00 00 01 00 01 00 00 00 00 00 02 00 00 00 | +0070 | 00 00 00 00 01 00 01 10 00 00 00 00 02 00 00 00 | 0080 | 4C 00 00 00 02 00 05 00 F4 01 01 00 00 00 00 00 | L -0090 | 02 00 01 00 00 00 23 00 01 00 00 00 10 00 00 00 | # -00A0 | 03 00 00 00 00 08 00 00 00 00 00 00 03 00 01 00 | +0090 | 02 00 01 10 00 00 23 00 01 00 00 00 10 00 00 00 | # +00A0 | 03 00 00 00 00 08 00 00 00 00 00 00 03 00 01 10 | 00B0 | 00 00 00 00 01 00 00 00 10 00 00 00 03 01 00 00 | -00C0 | 00 04 00 00 00 00 00 00 04 00 01 00 00 00 00 00 | +00C0 | 00 04 00 00 00 00 00 00 04 00 01 10 00 00 00 00 | 00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | diff --git a/tests/XB-ForestGame.test.txt b/tests/XB-ForestGame.test.txt index e284bd15..ed5975bd 100644 --- a/tests/XB-ForestGame.test.txt +++ b/tests/XB-ForestGame.test.txt @@ -308,11 +308,11 @@ I 16496 2023-11-08 01:54:18 - [Commands] Sending to C-2 (Tali) (version=XB comma 0050 | 00 00 00 00 87 AA 3F BE 5B B1 47 B3 5A 2F 1A B0 | ? [ G Z/ 0060 | 00 00 00 00 54 61 6C 69 00 00 00 00 00 00 00 00 | Tali 0070 | 00 00 00 00 03 00 00 01 02 00 00 00 4C 00 00 00 | L -0080 | 02 00 05 00 F4 01 00 00 00 00 00 00 00 00 01 00 | +0080 | 02 00 05 00 F4 01 00 00 00 00 00 00 00 00 01 10 | 0090 | 12 00 00 09 02 00 00 00 4C 00 00 00 01 01 00 00 | L -00A0 | 00 00 00 00 00 00 00 00 01 00 01 00 00 00 00 00 | +00A0 | 00 00 00 00 00 00 00 00 01 00 01 10 00 00 00 00 | 00B0 | 02 00 00 00 4C 00 00 00 00 06 00 00 00 00 00 00 | L -00C0 | 00 00 00 00 02 00 01 00 00 00 00 00 00 00 00 00 | +00C0 | 00 00 00 00 02 00 01 10 00 00 00 00 00 00 00 00 | 00D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -1446,13 +1446,13 @@ I 16496 2023-11-08 01:56:37 - [Commands] Sending to C-2 (Tali) (version=XB comma 0050 | 00 00 00 00 87 AA 3F BE 5B B1 47 B3 5A 2F 1A B0 | ? [ G Z/ 0060 | 00 00 00 00 54 61 6C 69 00 00 00 00 00 00 00 00 | Tali 0070 | 00 00 00 00 04 00 00 01 02 00 00 00 4C 00 00 00 | L -0080 | 02 00 05 00 F4 01 00 00 00 00 00 00 00 00 01 00 | +0080 | 02 00 05 00 F4 01 00 00 00 00 00 00 00 00 01 10 | 0090 | 12 00 00 09 02 00 00 00 4C 00 00 00 01 01 00 00 | L -00A0 | 00 00 00 00 00 00 00 00 01 00 01 00 00 00 00 00 | +00A0 | 00 00 00 00 00 00 00 00 01 00 01 10 00 00 00 00 | 00B0 | 02 00 00 00 4C 00 00 00 00 06 00 00 00 00 00 00 | L -00C0 | 00 00 00 00 02 00 01 00 00 00 00 00 01 00 00 00 | +00C0 | 00 00 00 00 02 00 01 10 00 00 00 00 01 00 00 00 | 00D0 | 50 00 00 00 03 00 00 00 00 01 00 00 00 00 00 00 | P -00E0 | 7B 01 01 06 00 00 00 00 00 00 00 00 00 00 00 00 | { +00E0 | 03 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 | 00F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0100 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0110 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |