diff --git a/.gitignore b/.gitignore index 91cf872f..e0a3f866 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .DS_Store # Build products +src/Revision.cc newserv # CMake files diff --git a/CMakeLists.txt b/CMakeLists.txt index a6a80165..7d94e6f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,9 +38,24 @@ find_package(resource_file QUIET) +# Git metadata + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/Revision.cc ${CMAKE_CURRENT_SOURCE_DIR}/src/__Revision__.cc + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/Revision-generate.sh ${CMAKE_CURRENT_SOURCE_DIR}/src/Revision.cc + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src +) +add_custom_target( + newserv-Revision-cc + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/Revision.cc ${CMAKE_CURRENT_SOURCE_DIR}/src/__Revision__.cc +) + + + # Executable definition set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/src/Revision.cc src/AFSArchive.cc src/BattleParamsIndex.cc src/BMLArchive.cc @@ -100,6 +115,7 @@ set(SOURCES src/ReceiveCommands.cc src/ReceiveSubcommands.cc src/ReplaySession.cc + src/Revision.cc src/SaveFileFormats.cc src/SendCommands.cc src/Server.cc @@ -122,6 +138,7 @@ endif() add_executable(newserv ${SOURCES}) target_include_directories(newserv PUBLIC ${LIBEVENT_INCLUDE_DIR} ${Iconv_INCLUDE_DIRS}) target_link_libraries(newserv phosg ${LIBEVENT_LIBRARIES} ${Iconv_LIBRARIES} pthread) +add_dependencies(newserv newserv-Revision-cc) if(resource_file_FOUND) target_compile_definitions(newserv PUBLIC HAVE_RESOURCE_FILE) diff --git a/README.md b/README.md index 34647008..01ff3e9f 100644 --- a/README.md +++ b/README.md @@ -283,6 +283,7 @@ Some commands only work on the game server and not on the proxy server. The chat * Information commands * `$li`: Shows basic information about the lobby or game you're in. If you're on the proxy server, shows information about your connection instead (remote Guild Card number, client ID, etc.). + * `$si`: Shows basic information about the server. * `$ping`: Shows round-trip ping time from the server to you. On the proxy server, shows the ping time from you to the proxy and from the proxy to the server. * `$matcount` (game server only): Shows how many of each type of material you've used. * `$what` (game server only): Shows the type, name, and stats of the nearest item on the ground. @@ -305,6 +306,7 @@ Some commands only work on the game server and not on the proxy server. The chat * `$secid `: Sets your override section ID. After running this command, any games you create will use your override section ID for rare drops instead of your character's actual section ID. To revert to your actual section id, run `$secid` with no name after it. On the proxy server, this will not work if the remote server controls item drops (e.g. on BB, or on Schtserv with server drops enabled). If the server does not allow cheat mode anywhere (that is, "CheatModeBehavior" is "Off" in config.json), this command does nothing. * `$rand `: Sets your override random seed (specified as a 32-bit hex value). This will make any games you create use the given seed for rare enemies. This also makes item drops deterministic in Blue Burst games hosted by newserv. On the proxy server, this command can cause desyncs with other players in the same game, since they will not see the overridden random seed. To remove the override, run `$rand` with no arguments. If the server does not allow cheat mode anywhere (that is, "CheatModeBehavior" is "Off" in config.json), this command does nothing. * `$ln [name-or-type]`: Sets the lobby number. Visible only to you. This command exists because some non-lobby maps can be loaded as lobbies with invalid lobby numbers. See the "GC lobby types" and "Ep3 lobby types" entries in the information menu for acceptable values here. Note that non-lobby maps do not have a lobby counter, so there's no way to exit the lobby without using either `$ln` again or `$exit`. On the game server, `$ln` reloads the lobby immediately; on the proxy server, it doesn't take effect until you load another lobby yourself (which means you'll like have to use `$exit` to escape). Run this command with no argument to return to the default lobby. + * `$swa`: Enables or disables switch assist. When enabled, the server will attempt to automatically unlock two-player doors in non-quest games if you step on both switches sequentially. * `$exit`: If you're in a lobby, sends you to the main menu (which ends your proxy session, if you're in one). If you're in a game or spectator team, sends you to the lobby (but does not end your proxy session if you're in one). Does nothing if you're in a non-Episode 3 game and no quest is in progress. * `$patch `: Run a patch on your client. `` must exactly match the name of a patch on the server. @@ -339,7 +341,6 @@ Some commands only work on the game server and not on the proxy server. The chat * `$warpme `: Warps yourself to the given floor. * `$warpall `: Warps everyone in the game to the given floor. You must be the leader to use this command, unless you're on the proxy server. * `$next`: Warps yourself to the next floor. - * `$swa`: Enables or disables switch assist. When enabled, the server will attempt to automatically unlock two-player doors in solo games if you step on both switches sequentially. * `$item ` (or `$i `): Create an item. `desc` may be a description of the item (e.g. "Hell Saber +5 0/10/25/0/10") or a string of hex data specifying the item code. Item codes are 16 hex bytes; at least 2 bytes must be specified, and all unspecified bytes are zeroes. If you are on the proxy server, you must not be using Blue Burst for this command to work. On the game server, this command works for all versions. * `$unset `: In an Episode 3 battle, removes one of your set cards from the field. `` is the index of the set card as it appears on your screen - 1 is the card next to your SC's icon, 2 is the card to the right of 1, etc. This does not cause a Hunters-side SC to lose HP, as they normally do when their items are destroyed. diff --git a/TODO.md b/TODO.md index b84a944b..0f9eaa4e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,19 +1,14 @@ ## General -- Encapsulate BB server-side random state and make replays deterministic -- Write a simple status API -- Implement per-game logging - Make reloading happen on separate threads so compression doesn't block active clients - Implement decrypt/encrypt actions for VMS files - Make UI strings localizable (e.g. entries in menus, welcome message, etc.) -- Figure out what causes the corruption message on PC proxy sessions and fix it - Add an idle connection timeout for proxy sessions - Look into JP heart symbol bug on Linux ## Episode 3 - Enforce tournament deck restrictions (e.g. rank checks, No Assist option) when populating COMs at tournament start time -- Add support for recording battles on the proxy server (both in primary and spectator teams) - Make `reload licenses` not vulnerable to online players' licenses overwriting licenses on disk somehow - Implement ranks (based on total Meseta earned) @@ -25,5 +20,4 @@ ## PSOBB - Test all quest item subcommands -- Check if Commander Blade effect works and implement it if not - Figure out why Pouilly Slime EXP doesn't work diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 1c2d1b3f..055d2f33 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -14,6 +14,7 @@ #include "Loggers.hh" #include "ProxyServer.hh" #include "ReceiveCommands.hh" +#include "Revision.hh" #include "SendCommands.hh" #include "Server.hh" #include "StaticGameData.hh" @@ -91,6 +92,20 @@ static void check_is_leader(shared_ptr l, shared_ptr c) { //////////////////////////////////////////////////////////////////////////////// // Message commands +static void server_command_server_info(shared_ptr c, const std::string&) { + auto s = c->require_server_state(); + string uptime_str = format_duration(now() - s->creation_time); + string build_date = format_time(BUILD_TIMESTAMP); + send_text_message_printf(c, + "Revision: $C6%s$C7\n$C6%s$C7\nUptime: $C6%s$C7\nLobbies: $C6%zu$C7\nClients: $C6%zu$C7(g) $C6%zu$C7(p)", + GIT_REVISION_HASH, + build_date.c_str(), + uptime_str.c_str(), + s->id_to_lobby.size(), + s->channel_to_client.size(), + s->proxy_server->num_sessions()); +} + static void server_command_lobby_info(shared_ptr c, const std::string&) { vector lines; @@ -204,9 +219,6 @@ static void proxy_command_lobby_info(shared_ptr ses, } vector cheats_tokens; - if (ses->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) { - cheats_tokens.emplace_back("SWA"); - } if (ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) { cheats_tokens.emplace_back("HP"); } @@ -219,14 +231,17 @@ static void proxy_command_lobby_info(shared_ptr ses, } vector behaviors_tokens; + if (ses->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) { + behaviors_tokens.emplace_back("SWA"); + } if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) { - behaviors_tokens.emplace_back("SAVE"); + behaviors_tokens.emplace_back("SF"); } if (ses->config.check_flag(Client::Flag::PROXY_SUPPRESS_REMOTE_LOGIN)) { behaviors_tokens.emplace_back("SL"); } if (ses->config.check_flag(Client::Flag::PROXY_BLOCK_FUNCTION_CALLS)) { - behaviors_tokens.emplace_back("BFC"); + behaviors_tokens.emplace_back("BF"); } if (!behaviors_tokens.empty()) { msg += "\n$C7Flags: $C6"; @@ -1448,15 +1463,23 @@ static void server_command_infinite_hp(shared_ptr c, const std::string&) check_cheats_enabled(l, c); c->config.toggle_flag(Client::Flag::INFINITE_HP_ENABLED); - send_text_message_printf(c, "$C6Infinite HP %s", c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED) ? "enabled" : "disabled"); + bool enabled = c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED); + send_text_message_printf(c, "$C6Infinite HP %s", enabled ? "enabled" : "disabled"); + if (enabled && l->is_game()) { + send_remove_conditions(c); + } } static void proxy_command_infinite_hp(shared_ptr ses, const std::string&) { auto s = ses->require_server_state(); check_cheats_allowed(s, ses); ses->config.toggle_flag(Client::Flag::INFINITE_HP_ENABLED); - send_text_message_printf(ses->client_channel, "$C6Infinite HP %s", - ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED) ? "enabled" : "disabled"); + bool enabled = ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED); + send_text_message_printf(ses->client_channel, "$C6Infinite HP %s", enabled ? "enabled" : "disabled"); + if (enabled && ses->is_in_game) { + send_remove_conditions(ses->client_channel, ses->lobby_client_id); + send_remove_conditions(ses->server_channel, ses->lobby_client_id); + } } static void server_command_infinite_tp(shared_ptr c, const std::string&) { @@ -1481,7 +1504,6 @@ static void server_command_switch_assist(shared_ptr c, const std::string auto s = c->require_server_state(); auto l = c->require_lobby(); check_is_game(l, true); - check_cheats_enabled(l, c); c->config.toggle_flag(Client::Flag::SWITCH_ASSIST_ENABLED); send_text_message_printf(c, "$C6Switch assist %s", @@ -1490,7 +1512,6 @@ static void server_command_switch_assist(shared_ptr c, const std::string static void proxy_command_switch_assist(shared_ptr ses, const std::string&) { auto s = ses->require_server_state(); - check_cheats_allowed(s, ses); ses->config.toggle_flag(Client::Flag::SWITCH_ASSIST_ENABLED); send_text_message_printf(ses->client_channel, "$C6Switch assist %s", ses->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED) ? "enabled" : "disabled"); @@ -1602,7 +1623,7 @@ static void proxy_command_item(shared_ptr ses, const bool set_drop = (!args.empty() && (args[0] == '!')); ItemData item = s->item_name_index->parse_item_description(ses->version(), (set_drop ? args.substr(1) : args)); - item.id = random_object(); + item.id = random_object() | 0x80000000; if (set_drop) { ses->next_drop_item = item; @@ -1906,6 +1927,7 @@ static const unordered_map chat_commands({ {"$saverec", {server_command_saverec, nullptr}}, {"$sc", {server_command_send_client, proxy_command_send_client}}, {"$secid", {server_command_secid, proxy_command_secid}}, + {"$si", {server_command_server_info, nullptr}}, {"$silence", {server_command_silence, nullptr}}, {"$song", {server_command_song, proxy_command_song}}, {"$spec", {server_command_toggle_spectator_flag, nullptr}}, diff --git a/src/Client.cc b/src/Client.cc index 83c19c4e..8fda3b0c 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -23,7 +23,6 @@ static atomic next_id(1); void Client::Config::set_flags_for_version(Version version, int64_t sub_version) { this->set_flag(Flag::PROXY_CHAT_COMMANDS_ENABLED); - this->set_flag(Flag::PROXY_CHAT_FILTER_ENABLED); switch (sub_version) { case -1: // Initial check (before sub_version recognition) @@ -191,7 +190,8 @@ Client::Client( // Don't print data sent to patch clients to the logs. The patch server // protocol is fully understood and data logs for patch clients are generally // more annoying than helpful at this point. - if ((this->channel.version == Version::PC_PATCH) || (this->channel.version == Version::BB_PATCH)) { + if ((server->get_state()->hide_download_commands) && + ((this->channel.version == Version::PC_PATCH) || (this->channel.version == Version::BB_PATCH))) { this->channel.terminal_recv_color = TerminalFormat::END; this->channel.terminal_send_color = TerminalFormat::END; } diff --git a/src/Client.hh b/src/Client.hh index 16442fe2..46c6c257 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -59,9 +59,9 @@ public: SHOULD_SEND_ARTIFICIAL_ITEM_STATE = 0x0001000000000000, SHOULD_SEND_ARTIFICIAL_FLAG_STATE = 0x0002000000000000, SHOULD_SEND_ENABLE_SAVE = 0x0004000000000000, + SWITCH_ASSIST_ENABLED = 0x0000000100000000, // Cheat mode flags - SWITCH_ASSIST_ENABLED = 0x0000000100000000, INFINITE_HP_ENABLED = 0x0000000200000000, INFINITE_TP_ENABLED = 0x0000000400000000, DEBUG_ENABLED = 0x0000000800000000, @@ -69,7 +69,6 @@ public: // Proxy option flags PROXY_SAVE_FILES = 0x0000001000000000, PROXY_CHAT_COMMANDS_ENABLED = 0x0000002000000000, - PROXY_CHAT_FILTER_ENABLED = 0x0000004000000000, PROXY_PLAYER_NOTIFICATIONS_ENABLED = 0x0000008000000000, PROXY_SUPPRESS_CLIENT_PINGS = 0x0000010000000000, PROXY_SUPPRESS_REMOTE_LOGIN = 0x0000020000000000, @@ -83,8 +82,7 @@ public: // clang-format on }; - static constexpr uint64_t DEFAULT_FLAGS = static_cast(Flag::PROXY_CHAT_COMMANDS_ENABLED) | - static_cast(Flag::PROXY_CHAT_FILTER_ENABLED); + static constexpr uint64_t DEFAULT_FLAGS = static_cast(Flag::PROXY_CHAT_COMMANDS_ENABLED); struct Config { uint64_t enabled_flags = DEFAULT_FLAGS; // Client::Flag enum diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 0520023a..f68dd6ab 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -3335,8 +3335,9 @@ struct C_SetTeamFlag_BB_0FEA { parray flag_data; } __packed__; -// 10EA: Delete team result -// No arguments except header.flag +// 10EA: Delete team (C->S) and result (S->C) +// No arguments (C->S) +// No arguments except header.flag (S->C) // 11EA: Change team member privilege level // The format below is used only when the client sends this command; when the @@ -3371,16 +3372,20 @@ struct S_TeamMembershipInformation_BB_12EA { struct S_TeamInfoForPlayer_BB_13EA_15EA_Entry { // The client uses the first four of these to determine if the player is in a // team or not - if they are all zero, the player is not in a team. - le_uint32_t guild_card_number = 0; - le_uint32_t team_id = 0; - le_uint32_t unknown_a3 = 0; - le_uint32_t unknown_a4 = 0; - le_uint32_t privilege_level = 0; - pstring team_name; - le_uint32_t guild_card_number2 = 0; - le_uint32_t lobby_client_id = 0; - pstring player_name; - parray flag_data; + /* 0000 */ le_uint32_t guild_card_number = 0; + /* 0004 */ le_uint32_t team_id = 0; + /* 0008 */ le_uint32_t reward_flags = 0; + /* 000C */ le_uint32_t unknown_a6 = 0; + /* 0010 */ uint8_t privilege_level = 0; + /* 0011 */ uint8_t unknown_a7 = 0; + /* 0012 */ uint8_t unknown_a8 = 0; + /* 0013 */ uint8_t unknown_a9 = 0; + /* 0014 */ pstring team_name; + /* 0034 */ le_uint32_t guild_card_number2 = 0; + /* 0038 */ le_uint32_t lobby_client_id = 0; + /* 003C */ pstring player_name; + /* 005C */ parray flag_data; + /* 085C */ } __packed__; // 14EA (C->S): Get team info for lobby players @@ -3455,16 +3460,15 @@ struct S_CrossTeamRanking_BB_1CEA { // 1DEA (S->C): Update team rewards bitmask // header.flag specifies the new rewards bitmask. -// 1EEA (C->S): Unknown +// 1EEA (C->S): Rename team // header.flag is used, but it's unknown what the value means. -struct C_Unknown_BB_1EEA { - pstring unknown_a1; +struct C_RenameTeam_BB_1EEA { + pstring new_team_name; } __packed__; -// 1FEA (S->C): Action result -// This command behaves exactly like 02EA. This command is presumably the -// response to whatever 1EEA does. +// 1FEA (S->C): Rename team result +// This command behaves like 02EA, but is sent in response to 1EEA instead. // 20EA: Unknown // header.flag is used, but no other arguments. When sent by the server, @@ -3702,6 +3706,15 @@ struct S_SetShutdownCommand_BB_01EF { // commands must be used. The 60 and 62 commands exhibit undefined behavior if // this limit is exceeded. +// Some subcommands are "protected" on V3 and later (not including GC NTE); +// these commands are blocked by the client if they affect the local player. If +// a V3 or later client receives a protected subcommand that would affect its +// own player, it instead ignores the entire subcommand. This means that the +// server or other players cannot send these subcommands to affect other +// players; they can only send these commands to inform other clients about +// changes or actions from their own player. +// The protected subcommands are marked (protected) in the listings below. + // These common structures are used my many subcommands. struct G_ClientIDHeader { uint8_t subcommand = 0; @@ -3836,8 +3849,8 @@ struct G_BoxDestroyed_6x0B { le_uint32_t object_index = 0; } __packed__; -// 6x0C: Add condition (poison/slow/etc.) -// 6x0D: Remove condition (poison/slow/etc.) +// 6x0C: Add condition (poison/slow/etc.) (protected on V3/V4) +// 6x0D: Remove condition (poison/slow/etc.) (protected on V3/V4) struct G_AddOrRemoveCondition_6x0C_6x0D { G_ClientIDHeader header; @@ -3845,7 +3858,7 @@ struct G_AddOrRemoveCondition_6x0C_6x0D { le_uint32_t unknown_a2 = 0; } __packed__; -// 6x0E: Unknown +// 6x0E: Unknown (protected on V3/V4) struct G_Unknown_6x0E { G_ClientIDHeader header; @@ -3941,13 +3954,13 @@ struct G_DarkFalzActions_6x19 { // 6x1A: Invalid subcommand -// 6x1B: Unknown (not valid on Episode 3) +// 6x1B: Unknown (not valid on Episode 3) (protected on V3/V4) struct G_Unknown_6x1B { G_ClientIDHeader header; } __packed__; -// 6x1C: Destroy NPC +// 6x1C: Destroy NPC (protected on V3/V4) struct G_DestroyNPC_6x1C { G_ClientIDHeader header; @@ -3967,7 +3980,7 @@ struct G_SetPlayerFloor_6x1F { le_int32_t floor = 0; } __packed__; -// 6x20: Set position +// 6x20: Set position (protected on V3/V4) // Existing clients send this in response to a 6x1F command when a new client // joins a lobby or game, so the new client knows where to place them. @@ -3980,22 +3993,22 @@ struct G_SetPosition_6x20 { le_uint32_t unknown_a1 = 0; } __packed__; -// 6x21: Inter-level warp +// 6x21: Inter-level warp (protected on V3/V4) struct G_InterLevelWarp_6x21 { G_ClientIDHeader header; le_int32_t floor = 0; } __packed__; -// 6x22: Set player invisible -// 6x23: Set player visible +// 6x22: Set player invisible (protected on V3/V4) +// 6x23: Set player visible (protected on V3/V4) // These are generally used while a player is in the process of changing floors. struct G_SetPlayerVisibility_6x22_6x23 { G_ClientIDHeader header; } __packed__; -// 6x24: Teleport player +// 6x24: Teleport player (protected on V3/V4) struct G_TeleportPlayer_6x24 { G_ClientIDHeader header; @@ -4005,7 +4018,7 @@ struct G_TeleportPlayer_6x24 { le_float z = 0.0f; } __packed__; -// 6x25: Equip item +// 6x25: Equip item (protected on V3/V4) struct G_EquipItem_6x25 { G_ClientIDHeader header; @@ -4014,7 +4027,7 @@ struct G_EquipItem_6x25 { le_uint32_t equip_slot = 0; } __packed__; -// 6x26: Unequip item +// 6x26: Unequip item (protected on V3/V4) struct G_UnequipItem_6x26 { G_ClientIDHeader header; @@ -4022,14 +4035,14 @@ struct G_UnequipItem_6x26 { le_uint32_t unused = 0; } __packed__; -// 6x27: Use item +// 6x27: Use item (protected on V3/V4) struct G_UseItem_6x27 { G_ClientIDHeader header; le_uint32_t item_id = 0; } __packed__; -// 6x28: Feed MAG +// 6x28: Feed MAG (protected on V3/V4) struct G_FeedMAG_6x28 { G_ClientIDHeader header; @@ -4037,7 +4050,7 @@ struct G_FeedMAG_6x28 { le_uint32_t fed_item_id = 0; } __packed__; -// 6x29: Delete inventory item (via bank deposit / sale / feeding MAG) +// 6x29: Delete inventory item (via bank deposit / sale / feeding MAG) (protected on V3 but not V4) // This subcommand is also used for reducing the size of stacks - if amount is // less than the stack count, the item is not deleted and its ID remains valid. @@ -4047,7 +4060,7 @@ struct G_DeleteInventoryItem_6x29 { le_uint32_t amount = 0; } __packed__; -// 6x2A: Drop item +// 6x2A: Drop item (protected on V3/V4) struct G_DropItem_6x2A { G_ClientIDHeader header; @@ -4059,7 +4072,8 @@ struct G_DropItem_6x2A { le_float z = 0.0f; } __packed__; -// 6x2B: Create item in inventory (e.g. via tekker or bank withdraw) +// 6x2B: Create item in inventory (e.g. via tekker or bank withdraw) (protected on V3/V4) +// On BB, the 6xBE command is used instead of 6x2B to create inventory items. struct G_CreateInventoryItem_DC_6x2B { G_ClientIDHeader header; @@ -4072,7 +4086,7 @@ struct G_CreateInventoryItem_PC_V3_BB_6x2B : G_CreateInventoryItem_DC_6x2B { parray unused2 = 0; } __packed__; -// 6x2C: Talk to NPC +// 6x2C: Talk to NPC (protected on V3/V4) struct G_TalkToNPC_6x2C { G_ClientIDHeader header; @@ -4083,13 +4097,13 @@ struct G_TalkToNPC_6x2C { le_float unknown_a5 = 0.0f; } __packed__; -// 6x2D: Done talking to NPC +// 6x2D: Done talking to NPC (protected on V3/V4) struct G_EndTalkToNPC_6x2D { G_ClientIDHeader header; } __packed__; -// 6x2E: Set and/or clear player flags +// 6x2E: Set and/or clear player flags (protected on V3/V4) struct G_SetOrClearPlayerFlags_6x2E { G_ClientIDHeader header; @@ -4120,7 +4134,7 @@ struct G_LevelUp_6x30 { le_uint16_t unknown_a1 = 0; // Must be 0 or 1 } __packed__; -// 6x31: Resurrect player +// 6x31: Resurrect player (protected on V3/V4) struct G_UseMedicalCenter_6x31 { G_ClientIDHeader header; @@ -4132,7 +4146,7 @@ struct G_Unknown_6x32 { G_UnusedHeader header; } __packed__; -// 6x33: Resurrect player (with Moon Atomizer) +// 6x33: Resurrect player (with Moon Atomizer) (protected on V3/V4) struct G_RevivePlayer_6x33 { G_ClientIDHeader header; @@ -4148,7 +4162,7 @@ struct G_RevivePlayer_6x33 { // 6x36: Unknown (supported; game only) // This subcommand is completely ignored (at least, by PSO GC). -// 6x37: Photon blast +// 6x37: Photon blast (protected on V3/V4) struct G_PhotonBlast_6x37 { G_ClientIDHeader header; @@ -4156,7 +4170,7 @@ struct G_PhotonBlast_6x37 { le_uint16_t unused = 0; } __packed__; -// 6x38: Donate to photon blast +// 6x38: Donate to photon blast (protected on V3/V4) struct G_Unknown_6x38 { G_ClientIDHeader header; @@ -4164,19 +4178,19 @@ struct G_Unknown_6x38 { le_uint16_t unused = 0; } __packed__; -// 6x39: Photon blast ready +// 6x39: Photon blast ready (protected on V3/V4) struct G_PhotonBlastReady_6x38 { G_ClientIDHeader header; } __packed__; -// 6x3A: Unknown (supported; game only) +// 6x3A: Unknown (supported; game only) (protected on V3/V4) struct G_Unknown_6x3A { G_ClientIDHeader header; } __packed__; -// 6x3B: Unknown (supported; lobby & game) +// 6x3B: Unknown (supported; lobby & game) (protected on V3/V4) struct G_Unknown_6x3B { G_ClientIDHeader header; @@ -4185,7 +4199,7 @@ struct G_Unknown_6x3B { // 6x3C: Invalid subcommand // 6x3D: Invalid subcommand -// 6x3E: Stop moving +// 6x3E: Stop moving (protected on V3/V4) struct G_StopAtPosition_6x3E { G_ClientIDHeader header; @@ -4198,7 +4212,7 @@ struct G_StopAtPosition_6x3E { le_float z = 0.0f; } __packed__; -// 6x3F: Set position +// 6x3F: Set position (protected on V3/V4) struct G_SetPosition_6x3F { G_ClientIDHeader header; @@ -4211,7 +4225,7 @@ struct G_SetPosition_6x3F { le_float z = 0.0f; } __packed__; -// 6x40: Walk +// 6x40: Walk (protected on V3/V4) struct G_WalkToPosition_6x40 { G_ClientIDHeader header; @@ -4223,7 +4237,7 @@ struct G_WalkToPosition_6x40 { // 6x41: Unknown // This subcommand is completely ignored (at least, by PSO GC). -// 6x42: Run +// 6x42: Run (protected on V3/V4) struct G_RunToPosition_6x42 { G_ClientIDHeader header; @@ -4231,9 +4245,9 @@ struct G_RunToPosition_6x42 { le_float z = 0.0f; } __packed__; -// 6x43: First attack -// 6x44: Second attack -// 6x45: Third attack +// 6x43: First attack (protected on V3/V4) +// 6x44: Second attack (protected on V3/V4) +// 6x45: Third attack (protected on V3/V4) struct G_Attack_6x43_6x44_6x45 { G_ClientIDHeader header; @@ -4241,7 +4255,7 @@ struct G_Attack_6x43_6x44_6x45 { le_uint16_t unknown_a2 = 0; } __packed__; -// 6x46: Attack finished (sent after each of 43, 44, and 45) +// 6x46: Attack finished (sent after each of 43, 44, and 45) (protected on V3/V4) struct G_AttackFinished_6x46 { G_ClientIDHeader header; @@ -4254,7 +4268,7 @@ struct G_AttackFinished_6x46 { parray targets; } __packed__; -// 6x47: Cast technique +// 6x47: Cast technique (protected on V3/V4) struct G_CastTechnique_6x47 { G_ClientIDHeader header; @@ -4275,7 +4289,7 @@ struct G_CastTechnique_6x47 { parray targets; } __packed__; -// 6x48: Cast technique complete +// 6x48: Cast technique complete (protected on V3/V4) struct G_CastTechniqueComplete_6x48 { G_ClientIDHeader header; @@ -4285,7 +4299,7 @@ struct G_CastTechniqueComplete_6x48 { le_uint16_t level = 0; } __packed__; -// 6x49: Subtract photon blast energy +// 6x49: Subtract photon blast energy (protected on V3/V4) struct G_SubtractPBEnergy_6x49 { G_ClientIDHeader header; @@ -4302,14 +4316,14 @@ struct G_SubtractPBEnergy_6x49 { parray entries; } __packed__; -// 6x4A: Fully shield attack +// 6x4A: Fully shield attack (protected on V3/V4) struct G_ShieldAttack_6x4A { G_ClientIDHeader header; } __packed__; -// 6x4B: Hit by enemy -// 6x4C: Hit by enemy +// 6x4B: Hit by enemy (protected on V3/V4) +// 6x4C: Hit by enemy (protected on V3/V4) struct G_HitByEnemy_6x4B_6x4C { G_ClientIDHeader header; @@ -4319,26 +4333,26 @@ struct G_HitByEnemy_6x4B_6x4C { le_float z_velocity = 0.0f; } __packed__; -// 6x4D: Player died +// 6x4D: Player died (protected on V3/V4) struct G_PlayerDied_6x4D { G_ClientIDHeader header; le_uint32_t unknown_a1 = 0; } __packed__; -// 6x4E: Player died +// 6x4E: Player died (protected on V3/V4) struct G_PlayerDied_6x4E { G_ClientIDHeader header; } __packed__; -// 6x4F: Player resurrected (via Scape Doll) +// 6x4F: Player resurrected (via Scape Doll) (protected on V3/V4) struct G_PlayerUsedScapeDoll_6x4F { G_ClientIDHeader header; } __packed__; -// 6x50: Switch interaction +// 6x50: Switch interaction (protected on V3/V4) struct G_SwitchInteraction_6x50 { G_ClientIDHeader header; @@ -4347,7 +4361,7 @@ struct G_SwitchInteraction_6x50 { // 6x51: Invalid subcommand -// 6x52: Toggle counter (shop/bank) interaction +// 6x52: Toggle counter (shop/bank) interaction (protected on V3/V4) struct G_ToggleCounterInteraction_6x52 { G_ClientIDHeader header; @@ -4356,7 +4370,7 @@ struct G_ToggleCounterInteraction_6x52 { le_uint32_t unknown_a3 = 0; } __packed__; -// 6x53: Unknown (supported; game only) +// 6x53: Unknown (supported; game only) (protected on V3/V4) struct G_Unknown_6x53 { G_ClientIDHeader header; @@ -4365,7 +4379,7 @@ struct G_Unknown_6x53 { // 6x54: Unknown // This subcommand is completely ignored (at least, by PSO GC). -// 6x55: Intra-map warp +// 6x55: Intra-map warp (protected on V3/V4) struct G_IntraMapWarp_6x55 { G_ClientIDHeader header; @@ -4378,7 +4392,7 @@ struct G_IntraMapWarp_6x55 { le_float z2 = 0.0f; } __packed__; -// 6x56: Unknown (supported; lobby & game) +// 6x56: Unknown (supported; lobby & game) (protected on V3/V4) struct G_Unknown_6x56 { G_ClientIDHeader header; @@ -4388,13 +4402,13 @@ struct G_Unknown_6x56 { le_float z = 0.0f; } __packed__; -// 6x57: Unknown (supported; lobby & game) +// 6x57: Unknown (supported; lobby & game) (protected on V3/V4) struct G_Unknown_6x57 { G_ClientIDHeader header; } __packed__; -// 6x58: Lobby animation +// 6x58: Lobby animation (protected on V3/V4) struct G_LobbyAnimation_6x58 { G_ClientIDHeader header; @@ -4925,13 +4939,13 @@ struct G_GogoBall_6x79 { parray unused; } __packed__; -// 6x7A: Unknown +// 6x7A: Unknown (protected on V3/V4) struct G_Unknown_6x7A { G_ClientIDHeader header; } __packed__; -// 6x7B: Unknown +// 6x7B: Unknown (protected on V3/V4) struct G_Unknown_6x7B { G_ClientIDHeader header; @@ -4989,19 +5003,19 @@ struct G_TriggerTrap_6x80 { le_uint16_t unknown_a2 = 0; } __packed__; -// 6x81: Unknown +// 6x81: Unknown (protected on V3/V4) struct G_Unknown_6x81 { G_ClientIDHeader header; } __packed__; -// 6x82: Unknown +// 6x82: Unknown (protected on V3/V4) struct G_Unknown_6x82 { G_ClientIDHeader header; } __packed__; -// 6x83: Place trap +// 6x83: Place trap (protected on V3/V4) struct G_PlaceTrap_6x83 { G_ClientIDHeader header; @@ -5037,20 +5051,20 @@ struct G_HitDestructibleObject_6x86 { le_uint16_t unknown_a4 = 0; } __packed__; -// 6x87: Shrink player +// 6x87: Shrink player (protected on V3/V4) struct G_ShrinkPlayer_6x87 { G_ClientIDHeader header; le_float unknown_a1 = 0.0f; } __packed__; -// 6x88: Restore shrunken player +// 6x88: Restore shrunken player (protected on V3/V4) struct G_RestoreShrunkenPlayer_6x88 { G_ClientIDHeader header; } __packed__; -// 6x89: Player killed by monster +// 6x89: Player killed by monster (protected on V3/V4) struct G_PlayerKilledByMonster_6x89 { G_ClientIDHeader header; @@ -5071,7 +5085,7 @@ struct G_Unknown_6x8A { // 6x8C: Unknown (not valid on Episode 3) // This subcommand is completely ignored (at least, by PSO GC). -// 6x8D: Set technique level override +// 6x8D: Set technique level override (protected on V3/V4) // This command is sent immediately before 6x47 if the technique level is above // 15. Presumably this was done for compatibility between v1 and v2. @@ -5093,7 +5107,7 @@ struct G_Unknown_6x8F { le_uint16_t unknown_a1 = 0; } __packed__; -// 6x90: Unknown (not valid on Episode 3) +// 6x90: Unknown (not valid on Episode 3) (protected on V3/V4) struct G_Unknown_6x90 { G_ClientIDHeader header; @@ -5182,7 +5196,7 @@ struct G_UpdatePlayerStat_6x9A { uint8_t amount = 0; } __packed__; -// 6x9B: Unknown +// 6x9B: Unknown (protected on V3/V4) struct G_Unknown_6x9B { G_UnusedHeader header; @@ -5229,7 +5243,7 @@ struct G_GalGryphonBossActions_6xA0 { parray unknown_a4; } __packed__; -// 6xA1: Unknown (not valid on pre-V3) +// 6xA1: Unknown (not valid on pre-V3) (protected on V3/V4) // Part of revive process. Occurs right after revive command; function unclear. struct G_Unknown_6xA1 { @@ -5322,7 +5336,7 @@ struct G_BarbaRayBossActions_6xAA { le_uint32_t unknown_a3 = 0; } __packed__; -// 6xAB: Create lobby chair (not valid on pre-V3) +// 6xAB: Create lobby chair (not valid on pre-V3) (protected on V3/V4) struct G_CreateLobbyChair_6xAB { G_ClientIDHeader header; @@ -5330,7 +5344,7 @@ struct G_CreateLobbyChair_6xAB { le_uint16_t unknown_a2 = 0; } __packed__; -// 6xAC: Unknown (not valid on pre-V3) +// 6xAC: Unknown (not valid on pre-V3) (protected on V3/V4) // This command's appears to be different on GC NTE than on any other version. // It also seems that no version (other than perhaps GC NTE) ever sends this // command. @@ -5368,14 +5382,14 @@ struct G_SetLobbyChairState_6xAE { le_uint32_t unknown_a4 = 0; } __packed__; -// 6xAF: Turn lobby chair (not valid on pre-V3 or GC Trial Edition) +// 6xAF: Turn lobby chair (not valid on pre-V3 or GC Trial Edition) (protected on V3/V4) struct G_TurnLobbyChair_6xAF { G_ClientIDHeader header; le_uint32_t angle = 0; // In range [0x0000, 0xFFFF] } __packed__; -// 6xB0: Move lobby chair (not valid on pre-V3 or GC Trial Edition) +// 6xB0: Move lobby chair (not valid on pre-V3 or GC Trial Edition) (protected on V3/V4) struct G_MoveLobbyChair_6xB0 { G_ClientIDHeader header; @@ -5664,7 +5678,7 @@ struct G_GiveExperience_BB_6xBF { le_uint32_t amount = 0; } __packed__; -// 6xC0: BB sell item at shop +// 6xC0: Sell item at shop (BB) (protected on V3/V4) struct G_SellItemAtShop_BB_6xC0 { G_UnusedHeader header; @@ -5759,7 +5773,7 @@ struct G_TransferItemViaMailMessage_BB_6xCB { le_uint32_t target_guild_card_number = 0; } __packed__; -// 6xCC: Exchange item for team points (BB) +// 6xCC: Exchange item for team points (BB) (protected on V3/V4) struct G_ExchangeItemForTeamPoints_BB_6xCC { G_ClientIDHeader header; diff --git a/src/GVMEncoder.hh b/src/GVMEncoder.hh index fa0a17cf..10c8a527 100644 --- a/src/GVMEncoder.hh +++ b/src/GVMEncoder.hh @@ -37,23 +37,23 @@ constexpr uint32_t encode_argb8888(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return (a << 24) | (r << 16) | (g << 8) | b; } -constexpr uint16_t encode_xrgb8888_to_xrgb1555(uint32_t xrgb8888) { +constexpr uint16_t encode_argb8888_to_argb1555(uint32_t argb8888) { + // In: aaaaaaaarrrrrrrrggggggggbbbbbbbb + // Out: arrrrrgggggbbbbb + return ((argb8888 >> 9) & 0x7C00) | ((argb8888 >> 6) & 0x03E0) | ((argb8888 >> 3) & 0x001F) | ((argb8888 >> 16) & 0x8000); +} + +constexpr uint16_t encode_rgba8888_to_argb1555(uint32_t rgba8888) { // In: rrrrrrrrggggggggbbbbbbbbaaaaaaaa - // Out: -rrrrrgggggbbbbb - return ((xrgb8888 >> 9) & 0x7C00) | ((xrgb8888 >> 6) & 0x03E0) | ((xrgb8888 >> 3) & 0x001F); + // Out: arrrrrgggggbbbbb + return ((rgba8888 >> 17) & 0x7C00) | ((rgba8888 >> 14) & 0x03E0) | ((rgba8888 >> 11) & 0x001F) | ((rgba8888 << 8) & 0x8000); } -constexpr uint16_t encode_rgbx8888_to_xrgb1555(uint32_t rgbx8888) { - // In: rrrrrrrrggggggggbbbbbbbbxxxxxxxx - // Out: -rrrrrgggggbbbbb - return ((rgbx8888 >> 17) & 0x7C00) | ((rgbx8888 >> 14) & 0x03E0) | ((rgbx8888 >> 11) & 0x001F); -} - -constexpr uint32_t decode_xrgb1555_to_rgba8888(uint16_t xrgb1555) { - // In: -rrrrrgggggbbbbb - // Out: rrrrrrrrggggggggbbbbbbbbaaaaaaaa (a is always FF) - return ((xrgb1555 << 17) & 0xF8000000) | ((xrgb1555 << 12) & 0x07000000) | - ((xrgb1555 << 14) & 0x00F80000) | ((xrgb1555 << 9) & 0x00070000) | - ((xrgb1555 << 11) & 0x0000F800) | ((xrgb1555 << 6) & 0x00000700) | - 0x000000FF; +constexpr uint32_t decode_argb1555_to_rgba8888(uint16_t argb1555) { + // In: arrrrrgggggbbbbb + // Out: rrrrrrrrggggggggbbbbbbbbaaaaaaaa + return ((argb1555 << 17) & 0xF8000000) | ((argb1555 << 12) & 0x07000000) | + ((argb1555 << 14) & 0x00F80000) | ((argb1555 << 9) & 0x00070000) | + ((argb1555 << 11) & 0x0000F800) | ((argb1555 << 6) & 0x00000700) | + ((argb1555 & 0x8000) ? 0x000000FF : 0x00000000); } diff --git a/src/Items.cc b/src/Items.cc index a4c93a7a..a02676f8 100644 --- a/src/Items.cc +++ b/src/Items.cc @@ -2,13 +2,11 @@ #include -#include - #include "SendCommands.hh" using namespace std; -void player_use_item(shared_ptr c, size_t item_index) { +void player_use_item(shared_ptr c, size_t item_index, shared_ptr random_crypt) { auto s = c->require_server_state(); // On PC (and presumably DC), the client sends a 6x29 after this to delete the @@ -179,7 +177,7 @@ void player_use_item(shared_ptr c, size_t item_index) { if (sum == 0) { throw runtime_error("no unwrap results available for event"); } - size_t det = random_object() % sum; + size_t det = random_crypt->next() % sum; for (size_t z = 0; z < table.second; z++) { const auto& entry = table.first[z]; if (det > entry.probability) { diff --git a/src/Items.hh b/src/Items.hh index 381a036d..44afa880 100644 --- a/src/Items.hh +++ b/src/Items.hh @@ -6,8 +6,9 @@ #include #include "Client.hh" +#include "PSOEncryption.hh" #include "ServerState.hh" #include "StaticGameData.hh" -void player_use_item(std::shared_ptr c, size_t item_index); +void player_use_item(std::shared_ptr c, size_t item_index, std::shared_ptr random_crypt); void player_feed_mag(std::shared_ptr c, size_t mag_item_index, size_t fed_item_index); diff --git a/src/Lobby.cc b/src/Lobby.cc index 238181e3..22c1ac63 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -254,7 +254,7 @@ void Lobby::create_item_creator() { void Lobby::load_maps() { auto s = this->require_server_state(); - this->map = make_shared(this->lobby_id); + this->map = make_shared(this->lobby_id, this->random_crypt); if (this->quest) { auto leader_c = this->clients.at(this->leader_id); @@ -480,11 +480,7 @@ void Lobby::add_client(shared_ptr c, ssize_t required_client_id) { this->next_game_item_id = m.reassign_all_item_ids(this->next_game_item_id); } } - // On DC NTE and 11/2000, the game assigns item IDs immediately when a - // player joins a game, then assigns them again after the 6x6D equivalent is - // received. For this reason, we consume item IDs here only if the client is - // NTE or 11/2000. - this->assign_inventory_and_bank_item_ids(c, is_pre_v1(c->version())); + this->assign_inventory_and_bank_item_ids(c); // On BB, we send artificial flag state to fix an Episode 2 bug where the // CCA door lock state is overwritten by quests. if (c->version() == Version::BB_V4) { @@ -711,16 +707,12 @@ void Lobby::on_item_id_generated_externally(uint32_t item_id) { } } -void Lobby::assign_inventory_and_bank_item_ids(shared_ptr c, bool consume_ids) { +void Lobby::assign_inventory_and_bank_item_ids(shared_ptr c) { auto p = c->character(); - uint32_t start_item_id = this->next_item_id_for_client[c->lobby_client_id]; for (size_t z = 0; z < p->inventory.num_items; z++) { p->inventory.items[z].data.id = this->generate_item_id(c->lobby_client_id); } - if (!consume_ids) { - this->next_item_id_for_client[c->lobby_client_id] = start_item_id; - } - if (c->log.info("Assigned inventory item IDs%s", consume_ids ? "" : " but did not mark IDs as used")) { + if (c->log.info("Assigned inventory item IDs")) { p->print_inventory(stderr, c->version(), c->require_server_state()->item_name_index); if (p->bank.num_items) { p->bank.assign_ids(0x99000000 + (c->lobby_client_id << 20)); diff --git a/src/Lobby.hh b/src/Lobby.hh index 3377bd97..3efacc4b 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -233,7 +233,7 @@ struct Lobby : public std::enable_shared_from_this { uint32_t generate_item_id(uint8_t client_id); void on_item_id_generated_externally(uint32_t item_id); - void assign_inventory_and_bank_item_ids(std::shared_ptr c, bool consume_ids); + void assign_inventory_and_bank_item_ids(std::shared_ptr c); QuestIndex::IncludeCondition quest_include_condition() const; diff --git a/src/Main.cc b/src/Main.cc index 7306a94a..415b0bd2 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -35,6 +35,7 @@ #include "Quest.hh" #include "QuestScript.hh" #include "ReplaySession.hh" +#include "Revision.hh" #include "SaveFileFormats.hh" #include "SendCommands.hh" #include "Server.hh" @@ -49,6 +50,7 @@ using namespace std; bool use_terminal_colors = false; +void print_version_info(); void print_usage(); template @@ -204,6 +206,14 @@ Action a_help( print_usage(); }); +Action a_version( + "version", "\ + version\n\ + Show newserv\'s revision and build date.\n", + +[](Arguments&) -> void { + print_version_info(); + }); + static void a_compress_decompress_fn(Arguments& args) { const auto& action = args.get(0); bool is_prs = ends_with(action, "-prs"); @@ -1890,8 +1900,14 @@ Action a_run_server_replay_log( state->proxy_server.reset(); // Break reference cycle }); +void print_version_info() { + string build_date = format_time(BUILD_TIMESTAMP); + fprintf(stderr, "newserv-%s built %s UTC\n", GIT_REVISION_HASH, build_date.c_str()); +} + void print_usage() { - fputs("\ + print_version_info(); + fputs("\n\ Usage:\n\ newserv [ACTION] [OPTIONS...]\n\ \n\ diff --git a/src/Map.cc b/src/Map.cc index 4d10eb55..941323aa 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -131,8 +131,9 @@ string Map::Object::str(shared_ptr name_index) const { } } -Map::Map(uint32_t lobby_id) - : log(string_printf("[Lobby:%08" PRIX32 ":map] ", lobby_id), lobby_log.min_level) {} +Map::Map(uint32_t lobby_id, std::shared_ptr random_crypt) + : log(string_printf("[Lobby:%08" PRIX32 ":map] ", lobby_id), lobby_log.min_level), + random_crypt(random_crypt) {} void Map::clear() { this->objects.clear(); @@ -167,7 +168,7 @@ bool Map::check_and_log_rare_enemy(bool default_is_rare, uint32_t rare_rate) { if (default_is_rare) { return true; } - if ((this->rare_enemy_indexes.size() < 0x10) && (random_object() < rare_rate)) { + if ((this->rare_enemy_indexes.size() < 0x10) && (this->random_crypt->next() < rare_rate)) { this->rare_enemy_indexes.emplace_back(this->enemies.size()); return true; } diff --git a/src/Map.hh b/src/Map.hh index 1137550d..9ac77338 100644 --- a/src/Map.hh +++ b/src/Map.hh @@ -253,7 +253,7 @@ struct Map { void generate_shuffled_location_table(const Map::RandomEnemyLocationsHeader& header, StringReader r, uint16_t section); }; - explicit Map(uint32_t lobby_id); + Map(uint32_t lobby_id, std::shared_ptr random_crypt); ~Map() = default; void clear(); @@ -309,6 +309,7 @@ struct Map { static std::string disassemble_quest_data(const void* data, size_t size); PrefixedLogger log; + std::shared_ptr random_crypt; std::vector objects; std::vector enemies; std::vector rare_enemy_indexes; diff --git a/src/Menu.hh b/src/Menu.hh index 33468a60..467e893a 100644 --- a/src/Menu.hh +++ b/src/Menu.hh @@ -60,22 +60,21 @@ constexpr uint32_t GO_BACK = 0x99FFFF99; namespace ProxyOptionsMenuItemID { constexpr uint32_t GO_BACK = 0xAAFFFFAA; constexpr uint32_t CHAT_COMMANDS = 0xAA0101AA; -constexpr uint32_t CHAT_FILTER = 0xAA0202AA; -constexpr uint32_t PLAYER_NOTIFICATIONS = 0xAA0303AA; -constexpr uint32_t BLOCK_PINGS = 0xAA0404AA; -constexpr uint32_t INFINITE_HP = 0xAA0505AA; -constexpr uint32_t INFINITE_TP = 0xAA0606AA; -constexpr uint32_t SWITCH_ASSIST = 0xAA0707AA; -constexpr uint32_t BLOCK_EVENTS = 0xAA0808AA; -constexpr uint32_t BLOCK_PATCHES = 0xAA0909AA; -constexpr uint32_t SAVE_FILES = 0xAA0A0AAA; -constexpr uint32_t RED_NAME = 0xAA0B0BAA; -constexpr uint32_t BLANK_NAME = 0xAA0C0CAA; -constexpr uint32_t SUPPRESS_LOGIN = 0xAA0D0DAA; -constexpr uint32_t SKIP_CARD = 0xAA0E0EAA; -constexpr uint32_t EP3_INFINITE_MESETA = 0xAA0F0FAA; -constexpr uint32_t EP3_INFINITE_TIME = 0xAA1010AA; -constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA1111AA; +constexpr uint32_t PLAYER_NOTIFICATIONS = 0xAA0202AA; +constexpr uint32_t BLOCK_PINGS = 0xAA0303AA; +constexpr uint32_t INFINITE_HP = 0xAA0404AA; +constexpr uint32_t INFINITE_TP = 0xAA0505AA; +constexpr uint32_t SWITCH_ASSIST = 0xAA0606AA; +constexpr uint32_t BLOCK_EVENTS = 0xAA0707AA; +constexpr uint32_t BLOCK_PATCHES = 0xAA0808AA; +constexpr uint32_t SAVE_FILES = 0xAA0909AA; +constexpr uint32_t RED_NAME = 0xAA0A0AAA; +constexpr uint32_t BLANK_NAME = 0xAA0B0BAA; +constexpr uint32_t SUPPRESS_LOGIN = 0xAA0C0CAA; +constexpr uint32_t SKIP_CARD = 0xAA0D0DAA; +constexpr uint32_t EP3_INFINITE_MESETA = 0xAA0E0EAA; +constexpr uint32_t EP3_INFINITE_TIME = 0xAA0F0FAA; +constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA1010AA; } // namespace ProxyOptionsMenuItemID namespace TeamRewardMenuItemID { diff --git a/src/PlayerSubordinates.cc b/src/PlayerSubordinates.cc index 6bc5bc7e..fca44136 100644 --- a/src/PlayerSubordinates.cc +++ b/src/PlayerSubordinates.cc @@ -265,7 +265,7 @@ void PlayerLobbyDataXB::clear() { void PlayerLobbyDataBB::clear() { this->player_tag = 0; this->guild_card_number = 0; - this->team_guild_card_number = 0; + this->team_master_guild_card_number = 0; this->team_id = 0; this->unknown_a1.clear(0); this->client_id = 0; diff --git a/src/PlayerSubordinates.hh b/src/PlayerSubordinates.hh index 3394e764..28066e5c 100644 --- a/src/PlayerSubordinates.hh +++ b/src/PlayerSubordinates.hh @@ -283,24 +283,26 @@ struct PlayerLobbyDataDCGC { } __attribute__((packed)); struct XBNetworkLocation { - le_uint32_t internal_ipv4_address = 0x0A0A0A0A; - le_uint32_t external_ipv4_address = 0x23232323; - le_uint16_t port = 9500; - parray mac_address = 0x77; - le_uint32_t unknown_a1; - le_uint32_t unknown_a2; - le_uint64_t account_id = 0xFFFFFFFFFFFFFFFF; - parray unknown_a3; + /* 00 */ le_uint32_t internal_ipv4_address = 0x0A0A0A0A; + /* 04 */ le_uint32_t external_ipv4_address = 0x23232323; + /* 08 */ le_uint16_t port = 9500; + /* 0A */ parray mac_address = 0x77; + /* 10 */ le_uint32_t unknown_a1; + /* 14 */ le_uint32_t unknown_a2; + /* 18 */ le_uint64_t account_id = 0xFFFFFFFFFFFFFFFF; + /* 20 */ parray unknown_a3; + /* 24 */ void clear(); } __attribute__((packed)); struct PlayerLobbyDataXB { - le_uint32_t player_tag = 0; - le_uint32_t guild_card_number = 0; - XBNetworkLocation netloc; - le_uint32_t client_id = 0; - pstring name; + /* 00 */ le_uint32_t player_tag = 0; + /* 04 */ le_uint32_t guild_card_number = 0; + /* 08 */ XBNetworkLocation netloc; + /* 2C */ le_uint32_t client_id = 0; + /* 30 */ pstring name; + /* 40 */ void clear(); } __attribute__((packed)); @@ -308,7 +310,7 @@ struct PlayerLobbyDataXB { struct PlayerLobbyDataBB { /* 00 */ le_uint32_t player_tag = 0; /* 04 */ le_uint32_t guild_card_number = 0; - /* 08 */ le_uint32_t team_guild_card_number = 0; + /* 08 */ le_uint32_t team_master_guild_card_number = 0; /* 0C */ le_uint32_t team_id = 0; /* 10 */ parray unknown_a1; /* 1C */ le_uint32_t client_id = 0; diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index 2fc6cb55..458aba8c 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -311,7 +311,9 @@ static HandlerResult S_V123P_02_17( cmd.sub_version = ses->sub_version; cmd.serial_number.encode(string_printf("%08" PRIX32 "", ses->license->serial_number)); cmd.access_key.encode(ses->license->access_key); - cmd.access_key.clear_after_bytes(8); + if (ses->version() != Version::GC_NTE) { + cmd.access_key.clear_after_bytes(8); + } cmd.serial_number2 = cmd.serial_number; cmd.access_key2 = cmd.access_key; // TODO: We probably should set email_address, but we currently don't @@ -335,7 +337,9 @@ static HandlerResult S_V123P_02_17( cmd.language = ses->language(); cmd.serial_number.encode(string_printf("%08" PRIX32 "", ses->license->serial_number)); cmd.access_key.encode(ses->license->access_key); - cmd.access_key.clear_after_bytes(8); + if (ses->version() != Version::GC_NTE) { + cmd.access_key.clear_after_bytes(8); + } cmd.serial_number2 = cmd.serial_number; cmd.access_key2 = cmd.access_key; if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED)) { @@ -1067,9 +1071,7 @@ static HandlerResult C_GXB_61(shared_ptr ses, uint16 if (is_v4(ses->version())) { auto& pd = check_size_t(data, 0xFFFF); - if (ses->config.check_flag(Client::Flag::PROXY_CHAT_FILTER_ENABLED)) { - pd.info_board.encode(add_color(pd.info_board.decode(ses->language())), ses->language()); - } + pd.info_board.encode(add_color(pd.info_board.decode(ses->language())), ses->language()); if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED)) { pd.disp.name.encode(" ", ses->language()); modified = true; @@ -1083,7 +1085,7 @@ static HandlerResult C_GXB_61(shared_ptr ses, uint16 modified = true; } if (!ses->challenge_rank_title_override.empty()) { - pd.records.challenge.title_color = encode_xrgb8888_to_xrgb1555(ses->challenge_rank_color_override); + pd.records.challenge.title_color = encode_rgba8888_to_argb1555(ses->challenge_rank_color_override); pd.records.challenge.rank_title.encode(ses->challenge_rank_title_override, ses->language()); } @@ -1108,9 +1110,7 @@ static HandlerResult C_GXB_61(shared_ptr ses, uint16 } pd = &check_size_t(data, 0xFFFF); } - if (ses->config.check_flag(Client::Flag::PROXY_CHAT_FILTER_ENABLED)) { - pd->info_board.encode(add_color(pd->info_board.decode(ses->language())), ses->language()); - } + pd->info_board.encode(add_color(pd->info_board.decode(ses->language())), ses->language()); if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED)) { pd->disp.visual.name.encode(" ", ses->language()); modified = true; @@ -1124,7 +1124,7 @@ static HandlerResult C_GXB_61(shared_ptr ses, uint16 modified = true; } if (!ses->challenge_rank_title_override.empty()) { - pd->records.challenge.stats.title_color = encode_xrgb8888_to_xrgb1555(ses->challenge_rank_color_override); + pd->records.challenge.stats.title_color = encode_rgba8888_to_argb1555(ses->challenge_rank_color_override); pd->records.challenge.rank_title.encode(ses->challenge_rank_title_override, ses->language()); } } @@ -1132,28 +1132,24 @@ static HandlerResult C_GXB_61(shared_ptr ses, uint16 return modified ? HandlerResult::Type::MODIFIED : HandlerResult::Type::FORWARD; } -static HandlerResult C_GX_D9(shared_ptr ses, uint16_t, uint32_t, string& data) { - if (ses->config.check_flag(Client::Flag::PROXY_CHAT_FILTER_ENABLED)) { - data = add_color(data); - // TODO: We should check if the info board text was actually modified and - // return MODIFIED if so. - } - return HandlerResult::Type::FORWARD; +static HandlerResult C_GX_D9(shared_ptr, uint16_t, uint32_t, string& data) { + data = add_color(data); + // TODO: We should check if the info board text was actually modified and + // return FORWARD if not. + return HandlerResult::Type::MODIFIED; } static HandlerResult C_B_D9(shared_ptr ses, uint16_t, uint32_t, string& data) { - if (ses->config.check_flag(Client::Flag::PROXY_CHAT_FILTER_ENABLED)) { - try { - string decoded = tt_utf16_to_utf8(data.data(), data.size()); - add_color_inplace(decoded); - data = tt_utf8_to_utf16(data.data(), data.size()); - } catch (const runtime_error& e) { - ses->log.warning("Failed to replace escape characters in D9 command: %s", e.what()); - } - // TODO: We should check if the info board text was actually modified and - // return HandlerResult::MODIFIED if so. + try { + string decoded = tt_utf16_to_utf8(data.data(), data.size()); + add_color_inplace(decoded); + data = tt_utf8_to_utf16(data.data(), data.size()); + } catch (const runtime_error& e) { + ses->log.warning("Failed to replace escape characters in D9 command: %s", e.what()); } - return HandlerResult::Type::FORWARD; + // TODO: We should check if the info board text was actually modified and + // return HandlerResult::FORWARD if not. + return HandlerResult::Type::MODIFIED; } template @@ -1458,7 +1454,7 @@ static HandlerResult S_64(shared_ptr ses, uint16_t, // use it here, so we allow it to be omitted. cmd = &check_size_t(data.data(), data.size(), sizeof(CmdT) - 0x18, sizeof(CmdT)); } else { - cmd = &check_size_t(data); + cmd = &check_size_t(data, 0xFFFF); } ses->clear_lobby_players(4); @@ -1638,21 +1634,13 @@ static HandlerResult C_06(shared_ptr ses, uint16_t, offset += (text[offset] == command_sentinel) ? 0 : 2; text = text.substr(offset); if (text.size() >= 2 && text[1] == command_sentinel) { - if (ses->config.check_flag(Client::Flag::PROXY_CHAT_FILTER_ENABLED)) { - send_chat_message_from_client(ses->server_channel, add_color(text.substr(1)), private_flags); - } else { - send_chat_message_from_client(ses->server_channel, text.substr(1), private_flags); - } + send_chat_message_from_client(ses->server_channel, text.substr(1), private_flags); return HandlerResult::Type::SUPPRESS; } else { on_chat_command(ses, text); return HandlerResult::Type::SUPPRESS; } - } else if (ses->config.check_flag(Client::Flag::PROXY_CHAT_FILTER_ENABLED)) { - send_chat_message_from_client(ses->server_channel, add_color(text), private_flags); - return HandlerResult::Type::SUPPRESS; - } else { return HandlerResult::Type::FORWARD; } @@ -1723,6 +1711,11 @@ static HandlerResult C_6x(shared_ptr ses, uint16_t c const auto& cmd = check_size_t(data); ses->floor = cmd.floor; + } else if (data[0] == 0x0C) { + if (ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) { + send_remove_conditions(ses->client_channel, ses->lobby_client_id); + send_remove_conditions(ses->server_channel, ses->lobby_client_id); + } } else if (data[0] == 0x2F || data[0] == 0x4B || data[0] == 0x4C) { if (ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) { send_player_stats_change(ses->client_channel, diff --git a/src/ProxyServer.cc b/src/ProxyServer.cc index 790a1e85..31090eca 100644 --- a/src/ProxyServer.cc +++ b/src/ProxyServer.cc @@ -910,6 +910,10 @@ void ProxyServer::destroy_sessions() { this->unlinked_sessions_to_destroy.clear(); } +size_t ProxyServer::num_sessions() const { + return this->id_to_session.size(); +} + size_t ProxyServer::delete_disconnected_sessions() { size_t count = 0; for (auto it = this->id_to_session.begin(); it != this->id_to_session.end();) { diff --git a/src/ProxyServer.hh b/src/ProxyServer.hh index b74e7e42..841ecec7 100644 --- a/src/ProxyServer.hh +++ b/src/ProxyServer.hh @@ -190,6 +190,8 @@ public: void delete_session(uint64_t id); void delete_session(struct bufferevent* bev); + size_t num_sessions() const; + size_t delete_disconnected_sessions(); private: diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 44aa022e..8c907ec7 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -49,20 +49,22 @@ static shared_ptr proxy_options_menu_for_client(shared_ptrconfig.override_lobby_event != 0xFF), + "Block events", "Disable seasonal\nevents in the lobby\nand in games"); + add_option(ProxyOptionsMenuItemID::BLOCK_PATCHES, Client::Flag::PROXY_BLOCK_FUNCTION_CALLS, + "Block patches", "Disable patches sent\nby the remote server"); + add_option(ProxyOptionsMenuItemID::SWITCH_ASSIST, Client::Flag::SWITCH_ASSIST_ENABLED, + "Switch assist", "Automatically try\nto unlock 2-player\ndoors when you step\non both switches\nsequentially"); if ((s->cheat_mode_behavior != ServerState::BehaviorSwitch::OFF) || (c->license->flags & License::Flag::CHEAT_ANYWHERE)) { if (!is_ep3(c->version())) { add_option(ProxyOptionsMenuItemID::INFINITE_HP, Client::Flag::INFINITE_HP_ENABLED, "Infinite HP", "Enable automatic HP\nrestoration when\nyou are hit by an\nenemy or trap\n\nCannot revive you\nfrom one-hit kills"); add_option(ProxyOptionsMenuItemID::INFINITE_TP, Client::Flag::INFINITE_TP_ENABLED, "Infinite TP", "Enable automatic TP\nrestoration when\nyou cast any\ntechnique"); - add_option(ProxyOptionsMenuItemID::SWITCH_ASSIST, Client::Flag::SWITCH_ASSIST_ENABLED, - "Switch assist", "Automatically try\nto unlock 2-player\ndoors when you step\non both switches\nsequentially"); } else { // Note: This option's text is the maximum possible length for any menu item add_option(ProxyOptionsMenuItemID::EP3_INFINITE_MESETA, Client::Flag::PROXY_EP3_INFINITE_MESETA_ENABLED, @@ -73,10 +75,6 @@ static shared_ptr proxy_options_menu_for_client(shared_ptrconfig.override_lobby_event != 0xFF), - "Block events", "Disable seasonal\nevents in the lobby\nand in games"); - add_option(ProxyOptionsMenuItemID::BLOCK_PATCHES, Client::Flag::PROXY_BLOCK_FUNCTION_CALLS, - "Block patches", "Disable patches sent\nby the remote server"); if (s->proxy_allow_save_files) { add_option(ProxyOptionsMenuItemID::SAVE_FILES, Client::Flag::PROXY_SAVE_FILES, "Save files", "Save local copies of\nfiles from the\nremote server\n(quests, etc.)"); @@ -1135,7 +1133,7 @@ static void on_93_BB(shared_ptr c, uint16_t, uint32_t, string& data) { // command. on_login_complete(c); - } else { + } else if (s->hide_download_commands) { // The BB data server protocol is fairly well-understood and has some large // commands, so we omit data logging for clients on the data server. c->log.info("Client is in the BB data server phase; disabling command data logging for the rest of this client\'s session"); @@ -1959,7 +1957,7 @@ static void on_quest_loaded(shared_ptr l) { lc->use_default_bank(); lc->create_challenge_overlay(lc->version(), l->quest->challenge_template_index, s->level_table); lc->log.info("Created challenge overlay"); - l->assign_inventory_and_bank_item_ids(lc, true); + l->assign_inventory_and_bank_item_ids(lc); } } } @@ -2207,9 +2205,6 @@ static void on_10(shared_ptr c, uint16_t, uint32_t, string& data) { case ProxyOptionsMenuItemID::CHAT_COMMANDS: c->config.toggle_flag(Client::Flag::PROXY_CHAT_COMMANDS_ENABLED); goto resend_proxy_options_menu; - case ProxyOptionsMenuItemID::CHAT_FILTER: - c->config.toggle_flag(Client::Flag::PROXY_CHAT_FILTER_ENABLED); - goto resend_proxy_options_menu; case ProxyOptionsMenuItemID::PLAYER_NOTIFICATIONS: c->config.toggle_flag(Client::Flag::PROXY_PLAYER_NOTIFICATIONS_ENABLED); goto resend_proxy_options_menu; @@ -3541,7 +3536,7 @@ static void on_DF_BB(shared_ptr c, uint16_t command, uint32_t, string& d lc->use_default_bank(); lc->create_challenge_overlay(lc->version(), l->quest->challenge_template_index, s->level_table); lc->log.info("Created challenge overlay"); - l->assign_inventory_and_bank_item_ids(lc, true); + l->assign_inventory_and_bank_item_ids(lc); } } @@ -4275,15 +4270,12 @@ static void on_6F(shared_ptr c, uint16_t command, uint32_t, string& data } c->config.clear_flag(Client::Flag::LOADING); - if (command == 0x006F) { - l->assign_inventory_and_bank_item_ids(c, true); - } - send_server_time(c); if (l->base_version == Version::BB_V4) { send_set_exp_multiplier(l); } if (c->version() == Version::BB_V4) { + send_update_team_reward_flags(c); send_all_nearby_team_metadatas_to_client(c, false); } @@ -4893,6 +4885,29 @@ static void on_EA_BB(shared_ptr c, uint16_t command, uint32_t flag, stri case 0x1CEA: send_cross_team_ranking(c); break; + case 0x1EEA: { + const auto& cmd = check_size_t(data); + auto team = c->team(); + string new_team_name = cmd.new_team_name.decode(c->language()); + if (!team) { + // TODO: What's the right error code to use here? + send_command(c, 0x1FEA, 0x00000001); + } else if (s->team_index->get_by_name(new_team_name)) { + send_command(c, 0x1FEA, 0x00000002); + } else { + s->team_index->rename(team->team_id, new_team_name); + send_command(c, 0x1FEA, 0x00000000); + for (const auto& it : team->members) { + try { + auto member_c = s->find_client(nullptr, it.second.serial_number); + send_update_team_metadata_for_client(c); + send_team_membership_info(c); + } catch (const out_of_range&) { + } + } + } + break; + } default: throw runtime_error("invalid team command"); } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index bbaafdaa..17bae742 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -891,6 +891,7 @@ static void on_set_player_visible(shared_ptr c, uint8_t command, uint8_t send_lobby_message_box(c, ""); } if (c->version() == Version::BB_V4) { + send_update_team_reward_flags(c); send_all_nearby_team_metadatas_to_client(c, false); } } @@ -903,12 +904,13 @@ static void on_set_player_visible(shared_ptr c, uint8_t command, uint8_t static void on_change_floor_6x1F(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { if (is_pre_v1(c->version())) { check_size_t(data, size); - // DC NTE and 11/2000 don't send 6F when they're done loading, so we do the - // relevant things 6F would do here instead. + // DC NTE and 11/2000 don't send 6F when they're done loading, so we clear + // the loading flag here instead. On these versions, it also seems to be + // necessary to assign item IDs again here. if (c->config.check_flag(Client::Flag::LOADING)) { c->config.clear_flag(Client::Flag::LOADING); auto l = c->require_lobby(); - l->assign_inventory_and_bank_item_ids(c, true); + l->assign_inventory_and_bank_item_ids(c); } } else { @@ -948,7 +950,21 @@ static void on_player_died(shared_ptr c, uint8_t command, uint8_t flag, forward_subcommand(c, command, flag, data, size); } -// When a player is hit by an enemy, heal them if infinite HP is enabled +static void on_received_condition(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(); + if (l->is_game()) { + forward_subcommand(c, command, flag, data, size); + if (cmd.client_id == c->lobby_client_id) { + bool player_cheats_enabled = l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->flags & License::Flag::CHEAT_ANYWHERE); + if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) { + send_remove_conditions(c); + } + } + } +} + static void on_hit_by_enemy(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { const auto& cmd = check_size_t(data, size, 0xFFFF); @@ -963,7 +979,6 @@ static void on_hit_by_enemy(shared_ptr c, uint8_t command, uint8_t flag, } } -// When a player casts a tech, restore TP if infinite TP is enabled static void on_cast_technique_finished(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { const auto& cmd = check_size_t(data, size); @@ -1032,8 +1047,7 @@ static void on_switch_state_changed(shared_ptr c, uint8_t command, uint8 forward_subcommand(c, command, flag, data, size); if (cmd.flags && cmd.header.object_id != 0xFFFF) { - bool player_cheats_enabled = l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->flags & License::Flag::CHEAT_ANYWHERE); - if (player_cheats_enabled && + if (!l->quest && c->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED) && (c->last_switch_enabled_command.header.subcommand == 0x05)) { c->log.info("[Switch assist] Replaying previous enable command"); @@ -1453,7 +1467,7 @@ static void on_use_item( const auto& item = p->inventory.items[index].data; name = s->describe_item(c->version(), item, false); } - player_use_item(c, index); + player_use_item(c, index, l->random_crypt); if (l->log.should_log(LogLevel::INFO)) { l->log.info("Player %hhu used item %hu:%08" PRIX32 " (%s)", @@ -2803,7 +2817,7 @@ static void on_secret_lottery_ticket_exchange_bb(shared_ptr c, uint8_t, ItemData item = (s->secret_lottery_results.size() == 1) ? s->secret_lottery_results[0] - : s->secret_lottery_results[random_object() % s->secret_lottery_results.size()]; + : s->secret_lottery_results[l->random_crypt->next() % s->secret_lottery_results.size()]; item.enforce_min_stack_size(); item.id = l->generate_item_id(c->lobby_client_id); p->add_item(item); @@ -2819,7 +2833,7 @@ static void on_secret_lottery_ticket_exchange_bb(shared_ptr c, uint8_t, out_cmd.unknown_a3.clear(1); } else { for (size_t z = 0; z < out_cmd.unknown_a3.size(); z++) { - out_cmd.unknown_a3[z] = random_object() % s->secret_lottery_results.size(); + out_cmd.unknown_a3[z] = l->random_crypt->next() % s->secret_lottery_results.size(); } } send_command_t(c, 0x24, (slt_index >= 0) ? 0 : 1, out_cmd); @@ -2849,7 +2863,7 @@ static void on_quest_F95E_result_bb(shared_ptr c, uint8_t, uint8_t, void if (results.empty()) { throw runtime_error("invalid result type"); } - ItemData item = (results.size() == 1) ? results[0] : results[random_object() % results.size()]; + ItemData item = (results.size() == 1) ? results[0] : results[l->random_crypt->next() % results.size()]; if (item.data1[0] == 0x04) { // Meseta // TODO: What is the right amount of Meseta to use here? Presumably it // should be random within a certain range, but it's not obvious what @@ -2926,10 +2940,10 @@ static void on_quest_F960_result_bb(shared_ptr c, uint8_t, uint8_t, void size_t tier = cmd.result_tier - num_failures; const auto& results = s->quest_F960_success_results.at(tier); uint64_t probability = results.base_probability + num_failures * results.probability_upgrade; - if (random_object() <= probability) { + if (l->random_crypt->next() <= probability) { c->log.info("Tier %zu yielded a prize", tier); const auto& result_items = results.results.at(weekday); - item = result_items[random_object() % result_items.size()]; + item = result_items[l->random_crypt->next() % result_items.size()]; break; } else { c->log.info("Tier %zu did not yield a prize", tier); @@ -2938,7 +2952,7 @@ static void on_quest_F960_result_bb(shared_ptr c, uint8_t, uint8_t, void if (item.empty()) { c->log.info("Choosing result from failure tier"); const auto& result_items = s->quest_F960_failure_results.results.at(weekday); - item = result_items[random_object() % result_items.size()]; + item = result_items[l->random_crypt->next() % result_items.size()]; } if (item.empty()) { throw runtime_error("no item produced, even from failure tier"); @@ -2959,12 +2973,18 @@ static void on_quest_F960_result_bb(shared_ptr c, uint8_t, uint8_t, void G_SetMesetaSlotPrizeResult_BB_6xE3 cmd_6xE3 = {{0xE3, sizeof(G_SetMesetaSlotPrizeResult_BB_6xE3) >> 2, 0x0000}, item}; send_command_t(c, 0x60, 0x00, cmd_6xE3); - p->add_item(item); - send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); - - if (c->log.should_log(LogLevel::INFO)) { - string name = s->item_name_index->describe_item(c->version(), item); - c->log.info("Awarded item %s", name.c_str()); + try { + p->add_item(item); + send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); + if (c->log.should_log(LogLevel::INFO)) { + string name = s->item_name_index->describe_item(c->version(), item); + c->log.info("Awarded item %s", name.c_str()); + } + } catch (const out_of_range&) { + if (c->log.should_log(LogLevel::INFO)) { + string name = s->item_name_index->describe_item(c->version(), item); + c->log.info("Attempted to award item %s, but inventory was full", name.c_str()); + } } } } @@ -3072,7 +3092,7 @@ const SubcommandDefinition subcommand_definitions[0x100] = { /* 6x09 */ {0x09, 0x09, 0x09, nullptr}, /* 6x0A */ {0x0A, 0x0A, 0x0A, on_enemy_hit}, /* 6x0B */ {0x0B, 0x0B, 0x0B, on_forward_check_game}, - /* 6x0C */ {0x0C, 0x0C, 0x0C, on_forward_check_game}, + /* 6x0C */ {0x0C, 0x0C, 0x0C, on_received_condition}, /* 6x0D */ {0x00, 0x00, 0x0D, on_forward_check_game}, /* 6x0E */ {0x00, 0x00, 0x0E, nullptr}, /* 6x0F */ {0x00, 0x00, 0x0F, on_invalid}, diff --git a/src/Revision-generate.sh b/src/Revision-generate.sh new file mode 100755 index 00000000..8f8f7da3 --- /dev/null +++ b/src/Revision-generate.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +OUTPUT_FILENAME="$1" +OUTPUT_DIRNAME=$(dirname $OUTPUT_FILENAME) + +GIT_REVISION_HASH=$(git rev-parse --short HEAD) +TIMESTAMP_SECS=$(date +%s) + +if [ -z "$GIT_REVISION_HASH" ]; then + GIT_REVISION_HASH="????" +else + if ! git diff-index --quiet HEAD -- ; then + GIT_REVISION_HASH="$GIT_REVISION_HASH+" + fi +fi + +mkdir -p "$OUTPUT_DIRNAME" +cat > $OUTPUT_FILENAME <($TIMESTAMP_SECS) * 1000000; +EOF diff --git a/src/Revision.hh b/src/Revision.hh new file mode 100644 index 00000000..c6430b9d --- /dev/null +++ b/src/Revision.hh @@ -0,0 +1,6 @@ +#pragma once + +#include + +extern const char* GIT_REVISION_HASH; +extern const uint64_t BUILD_TIMESTAMP; diff --git a/src/SaveFileFormats.hh b/src/SaveFileFormats.hh index c2bbcc55..5da10b92 100644 --- a/src/SaveFileFormats.hh +++ b/src/SaveFileFormats.hh @@ -134,9 +134,9 @@ struct PSOBBMinimalSystemFile { } __attribute__((packed)); struct PSOBBTeamMembership { - /* 0000 */ le_uint32_t guild_card_number = 0; + /* 0000 */ le_uint32_t team_master_guild_card_number = 0; /* 0004 */ le_uint32_t team_id = 0; - /* 0008 */ le_uint32_t unknown_a4 = 0; + /* 0008 */ le_uint32_t unknown_a5 = 0; /* 000C */ le_uint32_t unknown_a6 = 0; /* 0010 */ uint8_t privilege_level = 0; /* 0011 */ uint8_t unknown_a7 = 0; diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 5b124c24..b9483963 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -501,11 +501,12 @@ void send_pc_console_split_reconnect(shared_ptr c, uint32_t address, } void send_client_init_bb(shared_ptr c, uint32_t error_code) { + auto team = c->team(); S_ClientInit_BB_00E6 cmd; cmd.error_code = error_code; cmd.player_tag = 0x00010000; cmd.guild_card_number = c->license->serial_number; - cmd.team_id = static_cast(random_object()); + cmd.team_id = team ? team->team_id : 0; c->config.serialize_into(cmd.client_config); cmd.can_create_team = 1; cmd.episode_4_unlocked = 1; @@ -1616,6 +1617,51 @@ void send_player_records_t(shared_ptr c, shared_ptr l, shared_ptr send_command_vt(c->channel, 0xC5, entries.size(), entries); } +template +void populate_lobby_data_for_client(LobbyDataT& ret, shared_ptr c, shared_ptr viewer_c) { + ret.player_tag = 0x00010000; + ret.guild_card_number = c->license->serial_number; + ret.client_id = c->lobby_client_id; + string name = c->character()->disp.name.decode(c->language()); + ret.name.encode(name, viewer_c->language()); +} + +template <> +void populate_lobby_data_for_client(PlayerLobbyDataXB& ret, shared_ptr c, shared_ptr viewer_c) { + ret.player_tag = 0x00010000; + ret.guild_card_number = c->license->serial_number; + if (c->xb_netloc) { + ret.netloc = *c->xb_netloc; + } else { + ret.netloc.account_id = 0xAE00000000000000 | c->license->serial_number; + } + ret.client_id = c->lobby_client_id; + string name = c->character()->disp.name.decode(c->language()); + ret.name.encode(name, viewer_c->language()); +} + +template <> +void populate_lobby_data_for_client(PlayerLobbyDataBB& ret, shared_ptr c, shared_ptr viewer_c) { + ret.player_tag = 0x00010000; + ret.guild_card_number = c->license->serial_number; + ret.client_id = c->lobby_client_id; + auto team = c->team(); + if (team) { + ret.team_master_guild_card_number = team->master_serial_number; + ret.team_id = team->team_id; + } else { + ret.team_master_guild_card_number = 0; + ret.team_id = 0; + } + string name = c->character()->disp.name.decode(c->language()); + if ((name.size() >= 2) && (name[0] == '\t') && (name[1] != 'C')) { + ret.name.encode(name, viewer_c->language()); + } else { + const char* marker = c->language() ? "\tE" : "\tJ"; + ret.name.encode(marker + name, viewer_c->language()); + } +} + static void send_join_spectator_team(shared_ptr c, shared_ptr l) { if (!is_ep3(c->version())) { throw runtime_error("client is not Episode 3"); @@ -1650,10 +1696,7 @@ static void send_join_spectator_team(shared_ptr c, shared_ptr l) } auto wc_p = wc->character(); auto& p = cmd.players[z]; - p.lobby_data.player_tag = 0x00010000; - p.lobby_data.guild_card_number = wc->license->serial_number; - 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()); + populate_lobby_data_for_client(p.lobby_data, wc, c); p.inventory = wc_p->inventory; p.inventory.encode_for_client(c); p.disp = wc_p->disp.to_dcpcv3(c->language(), p.inventory.language); @@ -1718,10 +1761,7 @@ static void send_join_spectator_team(shared_ptr c, shared_ptr l) auto other_p = other_c->character(); auto& cmd_p = cmd.spectator_players[z - 4]; auto& cmd_e = cmd.entries[z]; - cmd_p.lobby_data.player_tag = 0x00010000; - cmd_p.lobby_data.guild_card_number = other_c->license->serial_number; - cmd_p.lobby_data.client_id = other_c->lobby_client_id; - cmd_p.lobby_data.name.encode(other_p->disp.name.decode(other_p->inventory.language), c->language()); + 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); @@ -1754,10 +1794,7 @@ void send_join_game(shared_ptr c, shared_ptr l) { for (size_t x = 0; x < 4; x++) { auto lc = l->clients[x]; if (lc) { - cmd.lobby_data[x].player_tag = 0x00010000; - cmd.lobby_data[x].guild_card_number = lc->license->serial_number; - cmd.lobby_data[x].client_id = lc->lobby_client_id; - cmd.lobby_data[x].name.encode(lc->character()->disp.name.decode(lc->language()), c->language()); + populate_lobby_data_for_client(cmd.lobby_data[x], lc, c); player_count++; } else { cmd.lobby_data[x].clear(); @@ -1851,16 +1888,6 @@ void send_join_game(shared_ptr c, shared_ptr l) { case Version::XB_V3: { S_JoinGame_XB_64 cmd; size_t player_count = populate_v3_cmd(cmd); - for (size_t x = 0; x < 4; x++) { - auto lc = l->clients[x]; - if (lc) { - if (lc->xb_netloc) { - cmd.lobby_data[x].netloc = *lc->xb_netloc; - } else { - cmd.lobby_data[x].netloc.account_id = 0xAE00000000000000 | lc->license->serial_number; - } - } - } send_command_t(c, 0x64, player_count, cmd); break; } @@ -1882,7 +1909,7 @@ void send_join_game(shared_ptr c, shared_ptr l) { send_command(c, 0x1D, 0x00); } -template +template void send_join_lobby_t(shared_ptr c, shared_ptr l, shared_ptr joining_client = nullptr) { auto s = c->require_server_state(); @@ -1956,17 +1983,7 @@ void send_join_lobby_t(shared_ptr c, shared_ptr l, shared_ptrcharacter(); auto& e = cmd.entries[used_entries++]; - e.lobby_data.player_tag = 0x00010000; - e.lobby_data.guild_card_number = lc->license->serial_number; - e.lobby_data.client_id = lc->lobby_client_id; - string name = lp->disp.name.decode(lp->inventory.language); - bool name_has_marker = (name.size() >= 2) && (name[0] == '\t') && (name[1] != 'C'); - if (UseLanguageMarkerInName && !name_has_marker) { - const char* marker = c->language() ? "\tE" : "\tJ"; - e.lobby_data.name.encode(marker + name, c->language()); - } else { - e.lobby_data.name.encode(name, c->language()); - } + populate_lobby_data_for_client(e.lobby_data, lc, c); e.inventory = lp->inventory; e.inventory.encode_for_client(c); if ((lc == c) && is_v1_or_v2(c->version()) && lc->v1_v2_last_reported_disp) { @@ -2035,15 +2052,7 @@ void send_join_lobby_xb(shared_ptr c, shared_ptr l, shared_ptrcharacter(); auto& e = cmd.entries[used_entries++]; - e.lobby_data.player_tag = 0x00010000; - e.lobby_data.guild_card_number = lc->license->serial_number; - if (lc->xb_netloc) { - e.lobby_data.netloc = *lc->xb_netloc; - } else { - e.lobby_data.netloc.account_id = 0xAE00000000000000 | lc->license->serial_number; - } - e.lobby_data.client_id = lc->lobby_client_id; - e.lobby_data.name.encode(lp->disp.name.decode(lp->inventory.language), c->language()); + populate_lobby_data_for_client(e.lobby_data, lc, c); e.inventory = lp->inventory; e.inventory.encode_for_client(c); e.disp = convert_player_disp_data(lp->disp, c->language(), lp->inventory.language); @@ -2088,10 +2097,7 @@ void send_join_lobby_dc_nte(shared_ptr c, shared_ptr l, for (const auto& lc : lobby_clients) { auto lp = lc->character(); auto& e = cmd.entries[used_entries++]; - e.lobby_data.player_tag = 0x00010000; - e.lobby_data.guild_card_number = lc->license->serial_number; - e.lobby_data.client_id = lc->lobby_client_id; - e.lobby_data.name.encode(lp->disp.name.decode(lp->inventory.language), c->language()); + populate_lobby_data_for_client(e.lobby_data, lc, c); e.inventory = lp->inventory; e.inventory.encode_for_client(c); e.disp = convert_player_disp_data(lp->disp, c->language(), lp->inventory.language); @@ -2112,23 +2118,23 @@ void send_join_lobby(shared_ptr c, shared_ptr l) { break; case Version::DC_V1: case Version::DC_V2: - send_join_lobby_t(c, l); + send_join_lobby_t(c, l); break; case Version::PC_NTE: case Version::PC_V2: - send_join_lobby_t(c, l); + send_join_lobby_t(c, l); break; case Version::GC_NTE: case Version::GC_V3: case Version::GC_EP3_TRIAL_EDITION: case Version::GC_EP3: - send_join_lobby_t(c, l); + send_join_lobby_t(c, l); break; case Version::XB_V3: send_join_lobby_xb(c, l); break; case Version::BB_V4: - send_join_lobby_t(c, l); + send_join_lobby_t(c, l); break; default: throw logic_error("unimplemented versioned command"); @@ -2152,23 +2158,23 @@ void send_player_join_notification(shared_ptr c, break; case Version::DC_V1: case Version::DC_V2: - send_join_lobby_t(c, l, joining_client); + send_join_lobby_t(c, l, joining_client); break; case Version::PC_NTE: case Version::PC_V2: - send_join_lobby_t(c, l, joining_client); + send_join_lobby_t(c, l, joining_client); break; case Version::GC_NTE: case Version::GC_V3: case Version::GC_EP3_TRIAL_EDITION: case Version::GC_EP3: - send_join_lobby_t(c, l, joining_client); + send_join_lobby_t(c, l, joining_client); break; case Version::XB_V3: send_join_lobby_xb(c, l, joining_client); break; case Version::BB_V4: - send_join_lobby_t(c, l, joining_client); + send_join_lobby_t(c, l, joining_client); break; default: throw logic_error("unimplemented versioned command"); @@ -2304,6 +2310,26 @@ void send_player_stats_change(Channel& ch, uint16_t client_id, PlayerStatsChange send_command_vt(ch, (subs.size() > 0x400 / sizeof(G_UpdatePlayerStat_6x9A)) ? 0x6C : 0x60, 0x00, subs); } +void send_remove_conditions(shared_ptr c) { + auto l = c->require_lobby(); + for (auto& lc : l->clients) { + if (lc) { + send_remove_conditions(lc->channel, c->lobby_client_id); + } + } +} + +void send_remove_conditions(Channel& ch, uint16_t client_id) { + parray cmds; + for (size_t z = 0; z < 4; z++) { + auto& cmd = cmds[z]; + cmd.header = {0x0D, sizeof(G_AddOrRemoveCondition_6x0C_6x0D) >> 2, client_id}; + cmd.unknown_a1 = z; + cmd.unknown_a2 = 0; + } + ch.send(0x60, 0x00, &cmds, sizeof(cmds)); +} + void send_warp(Channel& ch, uint8_t client_id, uint32_t floor, bool is_private) { G_InterLevelWarp_6x94 cmd = {{0x94, 0x02, 0}, floor, {}}; ch.send(is_private ? 0x62 : 0x60, client_id, &cmd, sizeof(cmd)); @@ -3205,7 +3231,8 @@ void send_quest_file_chunk( cmd.data_size = size; c->log.info("Sending quest file chunk %s:%zu", filename.c_str(), chunk_index); - c->channel.send(is_download_quest ? 0xA7 : 0x13, chunk_index, &cmd, sizeof(cmd), true); + const auto& s = c->require_server_state(); + c->channel.send(is_download_quest ? 0xA7 : 0x13, chunk_index, &cmd, sizeof(cmd), s->hide_download_commands); } template diff --git a/src/SendCommands.hh b/src/SendCommands.hh index 54f0e396..c36e3fe3 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -288,8 +288,9 @@ enum PlayerStatsChange { }; void send_player_stats_change(std::shared_ptr c, PlayerStatsChange stat, uint32_t amount); -void send_player_stats_change( - Channel& ch, uint16_t client_id, PlayerStatsChange stat, uint32_t amount); +void send_player_stats_change(Channel& ch, uint16_t client_id, PlayerStatsChange stat, uint32_t amount); +void send_remove_conditions(std::shared_ptr c); +void send_remove_conditions(Channel& ch, uint16_t client_id); void send_warp(Channel& ch, uint8_t client_id, uint32_t floor, bool is_private); void send_warp(std::shared_ptr c, uint32_t floor, bool is_private); void send_warp(std::shared_ptr l, uint32_t floor, bool is_private); diff --git a/src/ServerShell.cc b/src/ServerShell.cc index b7d69887..91c396d5 100644 --- a/src/ServerShell.cc +++ b/src/ServerShell.cc @@ -772,10 +772,6 @@ Proxy session commands:\n\ auto ses = this->get_proxy_session(session_name); ses->challenge_rank_color_override = stoul(command_args, nullptr, 16); - } else if (command_name == "set-chat-filter") { - auto ses = this->get_proxy_session(session_name); - set_flag(ses->config, Client::Flag::PROXY_CHAT_FILTER_ENABLED, command_args); - } else if (command_name == "set-infinite-hp") { auto ses = this->get_proxy_session(session_name); set_flag(ses->config, Client::Flag::INFINITE_HP_ENABLED, command_args); @@ -811,7 +807,7 @@ Proxy session commands:\n\ auto s = ses->require_server_state(); ItemData item = s->item_name_index->parse_item_description(ses->version(), command_args); - item.id = random_object(); + item.id = random_object() | 0x80000000; if (command_name == "set-next-item") { ses->next_drop_item = item; diff --git a/src/ServerState.cc b/src/ServerState.cc index 153617ee..5e6e2776 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -30,7 +30,8 @@ ServerState::QuestF960Result::QuestF960Result(const JSON& json, std::shared_ptr< } ServerState::ServerState(shared_ptr base, const string& config_filename, bool is_replay) - : base(base), + : creation_time(now()), + base(base), config_filename(config_filename), is_replay(is_replay), dns_server_port(0), @@ -66,6 +67,7 @@ ServerState::ServerState(shared_ptr base, const string& confi ep3_final_round_meseta_bonus(300), ep3_jukebox_is_free(false), ep3_behavior_flags(0), + hide_download_commands(true), run_shell_behavior(RunShellBehavior::DEFAULT), cheat_mode_behavior(BehaviorSwitch::OFF_BY_DEFAULT), bb_global_exp_multiplier(1), @@ -718,6 +720,7 @@ void ServerState::parse_config(const JSON& json, bool is_reload) { this->ep3_jukebox_is_free = json.get_bool("Episode3JukeboxIsFree", this->ep3_jukebox_is_free); this->ep3_behavior_flags = json.get_int("Episode3BehaviorFlags", this->ep3_behavior_flags); this->ep3_card_auction_points = json.get_int("CardAuctionPoints", this->ep3_card_auction_points); + this->hide_download_commands = json.get_bool("HideDownloadCommands", this->hide_download_commands); this->proxy_allow_save_files = json.get_bool("ProxyAllowSaveFiles", this->proxy_allow_save_files); this->proxy_enable_login_options = json.get_bool("ProxyEnableLoginOptions", this->proxy_enable_login_options); diff --git a/src/ServerState.hh b/src/ServerState.hh index 02fce67b..fdfc7e79 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -59,6 +59,7 @@ struct ServerState : public std::enable_shared_from_this { return (b == BehaviorSwitch::OFF_BY_DEFAULT) || (b == BehaviorSwitch::ON_BY_DEFAULT); } + uint64_t creation_time; std::shared_ptr base; std::string config_filename; @@ -104,6 +105,7 @@ struct ServerState : public std::enable_shared_from_this { uint32_t ep3_final_round_meseta_bonus; bool ep3_jukebox_is_free; uint32_t ep3_behavior_flags; + bool hide_download_commands; RunShellBehavior run_shell_behavior; BehaviorSwitch cheat_mode_behavior; std::vector> bb_private_keys; diff --git a/src/TeamIndex.cc b/src/TeamIndex.cc index 158a462f..5d606978 100644 --- a/src/TeamIndex.cc +++ b/src/TeamIndex.cc @@ -58,6 +58,9 @@ void TeamIndex::Team::load_config() { Member m(*member_it); this->points += m.points; uint32_t serial_number = m.serial_number; + if (m.check_flag(Member::Flag::IS_MASTER)) { + this->master_serial_number = serial_number; + } this->members.emplace(serial_number, std::move(m)); } try { @@ -96,7 +99,7 @@ void TeamIndex::Team::load_flag() { this->flag_data.reset(new parray()); for (size_t y = 0; y < 32; y++) { for (size_t x = 0; x < 32; x++) { - this->flag_data->at(y * 0x20 + x) = encode_rgbx8888_to_xrgb1555(img.read_pixel(x, y)); + this->flag_data->at(y * 0x20 + x) = encode_rgba8888_to_argb1555(img.read_pixel(x, y)); } } } @@ -108,7 +111,7 @@ void TeamIndex::Team::save_flag() const { Image img(32, 32, false); for (size_t y = 0; y < 32; y++) { for (size_t x = 0; x < 32; x++) { - img.write_pixel(x, y, decode_xrgb1555_to_rgba8888(this->flag_data->at(y * 0x20 + x))); + img.write_pixel(x, y, decode_argb1555_to_rgba8888(this->flag_data->at(y * 0x20 + x))); } } img.save(this->flag_filename(), Image::Format::WINDOWS_BITMAP); @@ -125,11 +128,11 @@ PSOBBTeamMembership TeamIndex::Team::membership_for_member(uint32_t serial_numbe const auto& m = this->members.at(serial_number); PSOBBTeamMembership ret; - ret.guild_card_number = serial_number; + ret.team_master_guild_card_number = this->master_serial_number; ret.team_id = this->team_id; - ret.unknown_a4 = 0; - ret.privilege_level = m.privilege_level(); + ret.unknown_a5 = 0; ret.unknown_a6 = 0; + ret.privilege_level = m.privilege_level(); ret.unknown_a7 = 0; ret.unknown_a8 = 0; ret.unknown_a9 = 0; @@ -292,7 +295,7 @@ vector> TeamIndex::all() const { return ret; } -shared_ptr TeamIndex::create(string& name, uint32_t master_serial_number, const string& master_name) { +shared_ptr TeamIndex::create(const string& name, uint32_t master_serial_number, const string& master_name) { auto team = make_shared(this->next_team_id++); save_file(this->directory + "/base.json", JSON::dict({{"NextTeamID", this->next_team_id}}).serialize()); @@ -316,6 +319,16 @@ void TeamIndex::disband(uint32_t team_id) { team->delete_files(); } +void TeamIndex::rename(uint32_t team_id, const std::string& new_team_name) { + auto team = this->id_to_team.at(team_id); + if (!this->name_to_team.emplace(new_team_name, team).second) { + throw runtime_error("team name is already in use"); + } + this->name_to_team.erase(team->name); + team->name = new_team_name; + team->save_config(); +} + void TeamIndex::add_member(uint32_t team_id, uint32_t serial_number, const string& name) { auto team = this->id_to_team.at(team_id); if (!this->serial_number_to_team.emplace(serial_number, team).second) { @@ -411,6 +424,7 @@ void TeamIndex::change_master(uint32_t master_serial_number, uint32_t new_master master_m.set_flag(TeamIndex::Team::Member::Flag::IS_LEADER); new_master_m.clear_flag(TeamIndex::Team::Member::Flag::IS_LEADER); new_master_m.set_flag(TeamIndex::Team::Member::Flag::IS_MASTER); + team->master_serial_number = new_master_serial_number; team->save_config(); } diff --git a/src/TeamIndex.hh b/src/TeamIndex.hh index 2b01be75..ac7303f8 100644 --- a/src/TeamIndex.hh +++ b/src/TeamIndex.hh @@ -60,6 +60,7 @@ public: uint32_t points = 0; uint32_t spent_points = 0; std::string name; + uint32_t master_serial_number = 0; std::unordered_map members; uint32_t reward_flags = 0; std::unordered_set reward_keys; @@ -127,8 +128,9 @@ public: std::shared_ptr get_by_serial_number(uint32_t serial_number) const; std::vector> all() const; - std::shared_ptr create(std::string& name, uint32_t master_serial_number, const std::string& master_name); + std::shared_ptr create(const std::string& name, uint32_t master_serial_number, const std::string& master_name); void disband(uint32_t team_id); + void rename(uint32_t team_id, const std::string& new_name); void add_member(uint32_t team_id, uint32_t serial_number, const std::string& name); void remove_member(uint32_t serial_number); diff --git a/system/config.example.json b/system/config.example.json index a3b7cea9..f4fc6b79 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -226,6 +226,11 @@ // Static game data messages describe the loading of any kind of game data. "StaticGameData": "INFO", }, + // Some large commands (especially during the BB login sequence) can clutter + // up logs, so we hide these commands by default. If you're investigating or + // submitting a bug report that occurs on BB clients, set this to false to get + // a full session log before submitting your report. + "HideDownloadCommands": true, // If this option is disabled, the server only allows users who have licenses // on the server to connect. If this is enabled, all users will be allowed to diff --git a/tests/GC-Episode3Battle.test.txt b/tests/GC-Episode3Battle.test.txt index 2329dc30..838ac202 100644 --- a/tests/GC-Episode3Battle.test.txt +++ b/tests/GC-Episode3Battle.test.txt @@ -58,7 +58,7 @@ I 16332 2023-09-17 10:14:34 - [Commands] Received from C-1 (version=GC command=9 0140 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | I 16332 2023-09-17 10:14:34 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 42 60 00 00 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 42 20 00 00 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16332 2023-09-17 10:14:34 - [Commands] Sending to C-1 (version=GC command=B7 flag=00) 0000 | B7 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -73,7 +73,7 @@ I 16332 2023-09-17 10:14:34 - [Commands] Received from C-1 (version=GC command=9 0000 | 96 00 0C 00 03 1B 8F 2C C0 00 00 00 | , I 16332 2023-09-17 10:14:34 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 42 60 00 04 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 42 20 00 04 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16332 2023-09-17 10:14:34 - [Commands] Sending to C-1 (version=GC command=B1 flag=00) 0000 | B1 00 1C 00 32 30 32 33 3A 30 39 3A 31 37 3A 20 | 2023:09:17: @@ -2623,7 +2623,7 @@ I 16332 2023-09-17 10:14:34 - [Commands] Sending to C-1 (version=GC command=CC f 0500 | 00 00 00 00 00 00 00 00 00 00 00 00 | I 16332 2023-09-17 10:14:34 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 4A 60 00 04 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 4A 20 00 04 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16332 2023-09-17 10:14:34 - [Commands] Received from C-1 (Tali) (version=GC command=99 flag=00) 0000 | 99 00 04 00 | @@ -2631,7 +2631,7 @@ I 16332 2023-09-17 10:14:34 - [Commands] Received from C-1 (Tali) (version=GC co 0000 | D6 00 04 00 | I 16332 2023-09-17 10:14:34 - [Commands] Sending to C-1 (Tali) (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 48 60 00 04 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 48 20 00 04 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16332 2023-09-17 10:14:34 - [Commands] Sending to C-1 (Tali) (version=GC command=07 flag=06) 0000 | 07 06 C8 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al @@ -2655,7 +2655,7 @@ I 16332 2023-09-17 10:14:35 - [Commands] Sending to C-1 (Tali) (version=GC comma 0000 | 97 01 04 00 | I 16332 2023-09-17 10:14:35 - [Commands] Sending to C-1 (Tali) (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 4C 60 00 00 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 4C 20 00 00 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16332 2023-09-17 10:14:35 - [Commands] Received from C-1 (Tali) (version=GC command=B1 flag=00) 0000 | B1 00 04 00 | @@ -2706,11 +2706,11 @@ I 16332 2023-09-17 10:14:37 - [Commands] Received from C-2 (version=GC command=9 00A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00B0 | 00 00 00 00 00 00 00 00 00 00 00 00 54 61 6C 69 | Tali 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , 2 -00D0 | 30 45 53 33 00 91 00 4C 60 00 00 00 00 00 00 00 | 0ES3@ ` +00D0 | 30 45 53 33 00 91 00 4C 20 00 00 00 00 00 00 00 | 0ES3@ ` 00E0 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16332 2023-09-17 10:14:37 - [Commands] Sending to C-2 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 4C 60 00 00 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 4C 20 00 00 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16332 2023-09-17 10:14:37 - [Commands] Sending to C-2 (version=GC command=83 flag=14) 0000 | B7 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -3588,7 +3588,7 @@ I 16332 2023-09-17 10:14:37 - [Commands] Sending to C-2 (Tali) (version=GC comma 0440 | FF FF FF FF FF FF FF FF FF FF FF FF | I 16332 2023-09-17 10:14:37 - [Commands] Sending to C-2 (Tali) (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 93 00 4C 60 00 00 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 93 00 4C 20 00 00 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16332 2023-09-17 10:14:39 - [Commands] Received from C-2 (Tali) (version=GC command=60 flag=00) 0000 | 60 00 1C 00 3F 06 00 00 00 00 00 00 0F 00 FF FF | ` ? diff --git a/tests/GC-Episode3BattleWithSpectator.test.txt b/tests/GC-Episode3BattleWithSpectator.test.txt index 49096dc9..7c3852ed 100644 --- a/tests/GC-Episode3BattleWithSpectator.test.txt +++ b/tests/GC-Episode3BattleWithSpectator.test.txt @@ -58,7 +58,7 @@ I 17097 2023-09-19 21:52:47 - [Commands] Received from C-1 (version=GC command=9 0140 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | I 17097 2023-09-19 21:52:47 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 42 60 00 00 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 42 20 00 00 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:52:47 - [Commands] Sending to C-1 (version=GC command=B7 flag=00) 0000 | B7 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -73,7 +73,7 @@ I 17097 2023-09-19 21:52:47 - [Commands] Received from C-1 (version=GC command=9 0000 | 96 00 0C 00 03 1B 8F 2C 06 01 00 00 | , I 17097 2023-09-19 21:52:47 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 42 60 00 04 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 42 20 00 04 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:52:47 - [Commands] Sending to C-1 (version=GC command=B1 flag=00) 0000 | B1 00 1C 00 32 30 32 33 3A 30 39 3A 32 30 3A 20 | 2023:09:20: @@ -2623,7 +2623,7 @@ I 17097 2023-09-19 21:52:47 - [Commands] Sending to C-1 (version=GC command=CC f 0500 | 00 00 00 00 00 00 00 00 00 00 00 00 | I 17097 2023-09-19 21:52:47 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 4A 60 00 04 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 4A 20 00 04 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:52:47 - [Commands] Received from C-1 (Tali) (version=GC command=99 flag=00) 0000 | 99 00 04 00 | @@ -2631,7 +2631,7 @@ I 17097 2023-09-19 21:52:47 - [Commands] Received from C-1 (Tali) (version=GC co 0000 | D6 00 04 00 | I 17097 2023-09-19 21:52:47 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 48 60 00 04 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 48 20 00 04 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:52:47 - [Commands] Sending to C-1 (Tali) (version=GC command=07 flag=06) 0000 | 07 06 C8 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al @@ -2655,7 +2655,7 @@ I 17097 2023-09-19 21:52:48 - [Commands] Sending to C-1 (Tali) (version=GC comma 0000 | 97 01 04 00 | I 17097 2023-09-19 21:52:48 - [Commands] Sending to C-1 (Tali) (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 4C 60 00 00 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 4C 20 00 00 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:52:49 - [Commands] Received from C-1 (Tali) (version=GC command=B1 flag=00) 0000 | B1 00 04 00 | @@ -2706,11 +2706,11 @@ I 17097 2023-09-19 21:52:50 - [Commands] Received from C-2 (version=GC command=9 00A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00B0 | 00 00 00 00 00 00 00 00 00 00 00 00 54 61 6C 69 | Tali 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , 2 -00D0 | 30 45 53 33 00 91 00 4C 60 00 00 00 00 00 00 00 | 0ES3@ ` +00D0 | 30 45 53 33 00 91 00 4C 20 00 00 00 00 00 00 00 | 0ES3@ ` 00E0 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:52:50 - [Commands] Sending to C-2 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 91 00 4C 60 00 00 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 91 00 4C 20 00 00 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:52:50 - [Commands] Sending to C-2 (version=GC command=04 flag=00) 0000 | B7 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -3588,7 +3588,7 @@ I 17097 2023-09-19 21:52:50 - [Commands] Sending to C-2 (Tali) (version=GC comma 0440 | FF FF FF FF FF FF FF FF FF FF FF FF | I 17097 2023-09-19 21:52:50 - [Commands] Sending to C-2 (Tali) (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 45 53 33 00 93 00 4C 60 00 00 00 00 00 00 00 | 0ES3@ ` +0010 | 30 45 53 33 00 93 00 4C 20 00 00 00 00 00 00 00 | 0ES3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:52:52 - [Commands] Received from C-2 (Tali) (version=GC command=60 flag=00) 0000 | 60 00 1C 00 3F 06 00 00 00 00 00 00 0F 00 FF FF | ` ? @@ -5873,7 +5873,7 @@ I 17097 2023-09-19 21:53:53 - [Commands] Received from C-3 (version=GC command=9 0140 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2 -0010 | 00 00 00 33 00 A1 00 42 60 00 00 00 00 00 00 00 | 3@ ` +0010 | 00 00 00 33 00 A1 00 42 20 00 00 00 00 00 00 00 | 3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (version=GC command=B7 flag=00) 0000 | B7 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -5888,7 +5888,7 @@ I 17097 2023-09-19 21:53:53 - [Commands] Received from C-3 (version=GC command=9 0000 | 96 00 0C 00 30 AA 74 2C 27 00 00 00 | 0 t,' I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2 -0010 | 00 00 00 33 00 A1 00 42 60 00 04 00 00 00 00 00 | 3@ ` +0010 | 00 00 00 33 00 A1 00 42 20 00 04 00 00 00 00 00 | 3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (version=GC command=B1 flag=00) 0000 | B1 00 1C 00 32 30 32 33 3A 30 39 3A 32 30 3A 20 | 2023:09:20: @@ -8438,7 +8438,7 @@ I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (version=GC command=CC f 0500 | 00 00 00 00 00 00 00 00 00 00 00 00 | I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2 -0010 | 30 4A 53 33 00 A1 00 4A 60 00 04 00 00 00 00 00 | 3@ ` +0010 | 30 4A 53 33 00 A1 00 4A 20 00 04 00 00 00 00 00 | 3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:53:53 - [Commands] Received from C-3 (Tali) (version=GC command=99 flag=00) 0000 | 99 00 04 00 | @@ -8446,7 +8446,7 @@ I 17097 2023-09-19 21:53:53 - [Commands] Received from C-3 (Tali) (version=GC co 0000 | D6 00 04 00 | I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (Tali) (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2 -0010 | 30 4A 53 33 00 A1 00 48 60 00 04 00 00 00 00 00 | 3@ ` +0010 | 30 4A 53 33 00 A1 00 48 20 00 04 00 00 00 00 00 | 3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (Tali) (version=GC command=07 flag=08) 0000 | 07 06 C8 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al @@ -8470,7 +8470,7 @@ I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (Tali) (version=GC comma 0000 | 97 01 04 00 | I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (Tali) (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2 -0010 | 30 4A 53 33 00 A1 00 4C 60 00 00 00 00 00 00 00 | 3@ ` +0010 | 30 4A 53 33 00 A1 00 4C 20 00 00 00 00 00 00 00 | 3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:53:53 - [Commands] Received from C-3 (Tali) (version=GC command=B1 flag=00) 0000 | B1 00 04 00 | @@ -8521,11 +8521,11 @@ I 17097 2023-09-19 21:53:54 - [Commands] Received from C-4 (version=GC command=9 00A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00B0 | 00 00 00 00 00 00 00 00 00 00 00 00 54 61 6C 69 | Tali 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , """"2 -00D0 | 30 4A 53 33 00 A1 00 4C 60 00 00 00 00 00 00 00 | 3@ ` +00D0 | 30 4A 53 33 00 A1 00 4C 20 00 00 00 00 00 00 00 | 3@ ` 00E0 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:53:54 - [Commands] Sending to C-4 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2 -0010 | 30 4A 53 33 00 A1 00 4C 60 00 00 00 00 00 00 00 | 3@ ` +0010 | 30 4A 53 33 00 A1 00 4C 20 00 00 00 00 00 00 00 | 3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:53:54 - [Commands] Sending to C-4 (version=GC command=04 flag=00) 0000 | B7 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -9403,7 +9403,7 @@ I 17097 2023-09-19 21:53:54 - [Commands] Sending to C-4 (Tali) (version=GC comma 0440 | FF FF FF FF FF FF FF FF FF FF FF FF | I 17097 2023-09-19 21:53:54 - [Commands] Sending to C-4 (Tali) (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2 -0010 | 30 4A 53 33 00 A3 00 4C 60 00 00 00 00 00 00 00 | 3@ ` +0010 | 30 4A 53 33 00 A3 00 4C 20 00 00 00 00 00 00 00 | 3@ ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 17097 2023-09-19 21:53:54 - [Commands] Received from C-4 (Tali) (version=GC command=60 flag=00) 0000 | 60 00 1C 00 3F 06 00 00 00 00 00 00 0F 00 FF FF | ` ? diff --git a/tests/GC-Episode3TrialEditionLobbySmokeTest.test.txt b/tests/GC-Episode3TrialEditionLobbySmokeTest.test.txt index 48d8a0a0..56a48cc8 100644 --- a/tests/GC-Episode3TrialEditionLobbySmokeTest.test.txt +++ b/tests/GC-Episode3TrialEditionLobbySmokeTest.test.txt @@ -62,7 +62,7 @@ I 25793 2023-11-24 23:03:38 - [Commands] Received from C-4 (version=GC_EP3 comma 0140 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | I 25793 2023-11-24 23:03:38 - [Commands] Sending to C-4 (version=GC_EP3 command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 00 00 00 33 00 A1 00 42 60 00 00 00 00 00 00 00 | 3 B` +0010 | 00 00 00 33 00 A1 00 42 20 00 00 00 00 00 00 00 | 3 B` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:03:38 - [Commands] Sending to C-4 (version=GC_EP3 command=B7 flag=00) 0000 | B7 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -77,7 +77,7 @@ I 25793 2023-11-24 23:03:38 - [Commands] Received from C-4 (version=GC_EP3 comma 0000 | 96 00 0C 00 5C E6 6B 2C 3B 00 00 00 | \ k,; I 25793 2023-11-24 23:03:38 - [Commands] Sending to C-4 (version=GC_EP3 command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 00 00 00 33 00 A1 00 42 60 00 04 00 00 00 00 00 | 3 B` +0010 | 00 00 00 33 00 A1 00 42 20 00 04 00 00 00 00 00 | 3 B` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:03:38 - [Commands] Sending to C-4 (version=GC_EP3 command=B1 flag=00) 0000 | B1 00 1C 00 32 30 32 33 3A 31 31 3A 32 35 3A 20 | 2023:11:25: @@ -2134,7 +2134,7 @@ I 25793 2023-11-24 23:03:38 - [Commands] Sending to C-4 (version=GC_EP3_TRIAL_ED 7940 | 00 00 00 00 | I 25793 2023-11-24 23:03:38 - [Commands] Sending to C-4 (version=GC_EP3_TRIAL_EDITION command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 54 4A 53 33 00 81 00 4A 60 00 04 00 00 00 00 00 | TJS3 J` +0010 | 54 4A 53 33 00 81 00 4A 20 00 04 00 00 00 00 00 | TJS3 J` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:03:38 - [Commands] Received from C-4 (Tali) (version=GC_EP3_TRIAL_EDITION command=99 flag=00) 0000 | 99 00 04 00 | @@ -2142,7 +2142,7 @@ I 25793 2023-11-24 23:03:38 - [Commands] Received from C-4 (Tali) (version=GC_EP 0000 | D6 00 04 00 | I 25793 2023-11-24 23:03:38 - [Commands] Sending to C-4 (version=GC_EP3_TRIAL_EDITION command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 54 4A 53 33 00 81 00 48 60 00 04 00 00 00 00 00 | TJS3 J` +0010 | 54 4A 53 33 00 81 00 48 20 00 04 00 00 00 00 00 | TJS3 J` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:03:38 - [Commands] Sending to C-4 (Tali) (version=GC_EP3_TRIAL_EDITION command=07 flag=08) 0000 | 07 06 C8 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al @@ -2174,7 +2174,7 @@ I 25793 2023-11-24 23:03:41 - [Commands] Sending to C-4 (Tali) (version=GC_EP3_T 0000 | 97 01 04 00 | I 25793 2023-11-24 23:03:41 - [Commands] Sending to C-4 (Tali) (version=GC_EP3_TRIAL_EDITION command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 54 4A 53 33 00 81 00 4C 60 00 00 00 00 00 00 00 | TJS3 L` +0010 | 54 4A 53 33 00 81 00 4C 20 00 00 00 00 00 00 00 | TJS3 L` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:03:42 - [Commands] Received from C-4 (Tali) (version=GC_EP3_TRIAL_EDITION command=B1 flag=00) 0000 | B1 00 04 00 | @@ -2223,12 +2223,12 @@ I 25793 2023-11-24 23:03:44 - [Commands] Received from C-5 (version=GC_V3 comman 00A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00B0 | 00 00 00 00 00 00 00 00 00 00 00 00 54 61 6C 69 | Tali 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | 2 -00D0 | 54 4A 53 33 00 81 00 4C 60 00 00 00 00 00 00 00 | TJS3 L` +00D0 | 54 4A 53 33 00 81 00 4C 20 00 00 00 00 00 00 00 | TJS3 L` 00E0 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:03:44 - [C-5] Game version changed to GC_EP3 I 25793 2023-11-24 23:03:44 - [Commands] Sending to C-5 (version=GC_EP3 command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 54 4A 53 33 00 A1 00 4C 60 00 00 00 00 00 00 00 | TJS3 L` +0010 | 54 4A 53 33 00 A1 00 4C 20 00 00 00 00 00 00 00 | TJS3 L` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:03:44 - [Commands] Sending to C-5 (version=GC_EP3 command=B7 flag=00) 0000 | B7 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -2362,7 +2362,7 @@ I 25793 2023-11-24 23:03:44 - [Commands] Received from C-5 (version=GC_EP3 comma I 25793 2023-11-24 23:03:44 - [C-5] Game version changed to GC_EP3_TRIAL_EDITION I 25793 2023-11-24 23:03:44 - [Commands] Sending to C-5 (Tali) (version=GC_EP3_TRIAL_EDITION command=C5 flag=01) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 54 4A 53 33 00 81 00 4C 60 00 00 00 00 00 00 00 | TJS3 L` +0010 | 54 4A 53 33 00 81 00 4C 20 00 00 00 00 00 00 00 | TJS3 L` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:03:44 - [Commands] Sending to C-5 (Tali) (version=GC_EP3_TRIAL_EDITION command=C5 flag=01) 0000 | C5 01 20 01 00 00 00 00 FF 7F 00 00 00 00 00 00 | @@ -2455,7 +2455,7 @@ I 25793 2023-11-24 23:03:44 - [Commands] Sending to C-5 (Tali) (version=GC_EP3_T 0440 | FF FF FF FF FF FF FF FF FF FF FF FF | I 25793 2023-11-24 23:03:44 - [Commands] Sending to C-5 (Tali) (version=GC_EP3_TRIAL_EDITION command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 54 4A 53 33 00 83 00 4C 60 00 00 00 00 00 00 00 | TJS3 L` +0010 | 54 4A 53 33 00 83 00 4C 20 00 00 00 00 00 00 00 | TJS3 L` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:03:45 - [Commands] Received from C-5 (Tali) (version=GC_EP3_TRIAL_EDITION command=60 flag=00) 0000 | 60 00 1C 00 3F 06 00 00 00 00 00 00 0F 00 FF FF | ` ? @@ -2687,7 +2687,7 @@ I 25793 2023-11-24 23:04:08 - [Commands] Received from C-6 (version=GC_EP3 comma 00A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00B0 | 00 00 00 00 00 00 00 00 00 00 00 00 54 61 6C 69 | Tali 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | 2 -00D0 | 54 4A 53 33 00 83 00 4C 60 00 00 00 00 00 00 00 | TJS3 L` +00D0 | 54 4A 53 33 00 83 00 4C 20 00 00 00 00 00 00 00 | TJS3 L` 00E0 | 00 00 00 00 00 00 FF FF 80 FF FF FF 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 | @@ -2697,7 +2697,7 @@ I 25793 2023-11-24 23:04:08 - [Commands] Received from C-6 (version=GC_EP3 comma 0140 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | I 25793 2023-11-24 23:04:08 - [Commands] Sending to C-6 (version=GC_EP3 command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 54 4A 53 33 00 A3 00 4C 60 00 00 00 00 00 00 00 | TJS3 L` +0010 | 54 4A 53 33 00 A3 00 4C 20 00 00 00 00 00 00 00 | TJS3 L` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 25793 2023-11-24 23:04:08 - [Commands] Sending to C-6 (version=GC_EP3 command=B7 flag=00) 0000 | B7 00 20 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 532303d7..d0a3b8aa 100644 --- a/tests/GC-ForestGame.test.txt +++ b/tests/GC-ForestGame.test.txt @@ -58,7 +58,7 @@ I 49108 2023-05-26 16:18:01 - [Commands] Received from C-1 (version=GC command=9 0140 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | I 49108 2023-05-26 16:18:01 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 50 4F 33 00 01 00 42 60 00 00 00 00 00 00 00 | 3 ` +0010 | 30 50 4F 33 00 01 00 42 20 00 00 00 00 00 00 00 | 3 ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 49108 2023-05-26 16:18:01 - [Commands] Sending to C-1 (version=GC command=D5 flag=00) 0000 | D5 00 2C 00 59 6F 75 20 61 72 65 20 63 6F 6E 6E | , You are conn @@ -68,7 +68,7 @@ I 49108 2023-05-26 16:18:01 - [Commands] Received from C-1 (version=GC command=9 0000 | 96 00 0C 00 C7 32 CE 2A 57 00 00 00 | 2 *W I 49108 2023-05-26 16:18:01 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 50 4F 33 00 01 00 42 60 00 04 00 00 00 00 00 | 3 ` +0010 | 30 50 4F 33 00 01 00 42 20 00 04 00 00 00 00 00 | 3 ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 49108 2023-05-26 16:18:01 - [Commands] Sending to C-1 (version=GC command=B1 flag=00) 0000 | B1 00 1C 00 32 30 32 33 3A 30 35 3A 32 36 3A 20 | 2023:05:26: @@ -79,7 +79,7 @@ I 49108 2023-05-26 16:18:02 - [Commands] Received from C-1 (version=GC command=D 0000 | D6 00 04 00 | I 49108 2023-05-26 16:18:02 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 50 4F 33 00 01 00 40 60 00 04 00 00 00 00 00 | 3 ` +0010 | 30 50 4F 33 00 01 00 40 20 00 04 00 00 00 00 00 | 3 ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 49108 2023-05-26 16:18:02 - [Commands] Sending to C-1 (version=GC command=07 flag=08) 0000 | 07 06 C8 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al @@ -101,7 +101,7 @@ I 49108 2023-05-26 16:18:06 - [Commands] Sending to C-1 (version=GC command=97 f 0000 | 97 01 04 00 | I 49108 2023-05-26 16:18:06 - [Commands] Sending to C-1 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 50 4F 33 00 01 00 44 60 00 00 00 00 00 00 00 | 3 ` +0010 | 30 50 4F 33 00 01 00 44 20 00 00 00 00 00 00 00 | 3 ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 49108 2023-05-26 16:18:06 - [Commands] Received from C-1 (version=GC command=B1 flag=00) 0000 | B1 00 04 00 | @@ -152,11 +152,11 @@ I 49108 2023-05-26 16:18:08 - [Commands] Received from C-2 (version=GC command=9 00A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00B0 | 00 00 00 00 00 00 00 00 00 00 00 00 4A 65 73 73 | Jess 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , 2 -00D0 | 30 50 4F 33 00 01 00 44 60 00 00 00 00 00 00 00 | 3 ` +00D0 | 30 50 4F 33 00 01 00 44 20 00 00 00 00 00 00 00 | 3 ` 00E0 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 49108 2023-05-26 16:18:08 - [Commands] Sending to C-2 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 50 4F 33 00 01 00 44 60 00 00 00 00 00 00 00 | 3 ` +0010 | 30 50 4F 33 00 01 00 44 20 00 00 00 00 00 00 00 | 3 ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 49108 2023-05-26 16:18:08 - [Commands] Sending to C-2 (version=GC command=83 flag=0F) 0000 | 83 0F B8 00 33 00 00 33 01 00 00 00 00 00 00 00 | 3 3 @@ -369,7 +369,7 @@ I 49108 2023-05-26 16:18:08 - [Commands] Sending to C-2 (Jess) (version=GC comma 0440 | 0D FF 05 03 01 01 00 04 00 FF FF FF | I 49108 2023-05-26 16:18:08 - [Commands] Sending to C-2 (Jess) (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 50 4F 33 00 03 00 44 60 00 00 00 00 00 00 00 | 3 ` +0010 | 30 50 4F 33 00 03 00 44 20 00 00 00 00 00 00 00 | 3 ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 49108 2023-05-26 16:18:08 - [Commands] Received from C-2 (Jess) (version=GC command=60 flag=00) 0000 | 60 00 1C 00 3F 06 00 00 00 00 00 80 0F 00 FF FF | ` ? @@ -1218,6 +1218,11 @@ I 49108 2023-05-26 16:19:14 - [Commands] Sending to C-2 (Jess) (version=GC comma 0000 | B0 00 24 00 00 00 00 00 00 00 00 00 09 43 36 49 | $ C6I 0010 | 6E 66 69 6E 69 74 65 20 48 50 20 65 6E 61 62 6C | nfinite HP enabl 0020 | 65 64 00 00 | ed +I 49108 2023-05-26 16:19:14 - [Commands] Sending to C-2 (Jess) (version=GC command=B0 flag=00) +0000 | 60 00 34 00 0D 03 00 00 00 00 00 00 00 00 00 00 | ` 4 +0010 | 0D 03 00 00 01 00 00 00 00 00 00 00 0D 03 00 00 | +0020 | 02 00 00 00 00 00 00 00 0D 03 00 00 03 00 00 00 | +0030 | 00 00 00 00 | I 49108 2023-05-26 16:19:16 - [Commands] Received from C-2 (Jess) (version=GC command=06 flag=00) 0000 | 06 00 18 00 00 00 00 00 00 00 00 00 09 45 24 69 | E$i 0010 | 6E 66 74 70 00 00 00 00 | nftp @@ -10014,7 +10019,7 @@ I 49108 2023-05-26 16:28:29 - [Commands] Received from C-3 (version=GC command=9 00A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00B0 | 00 00 00 00 00 00 00 00 00 00 00 00 4A 65 73 73 | Jess 00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , 2 -00D0 | 30 50 4F 33 00 03 00 44 60 00 00 00 00 00 00 00 | 3 ` +00D0 | 30 50 4F 33 00 03 00 44 20 00 00 00 00 00 00 00 | 3 ` 00E0 | 00 00 00 00 00 00 FF FF 80 FF FF FF 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 | @@ -10024,7 +10029,7 @@ I 49108 2023-05-26 16:28:29 - [Commands] Received from C-3 (version=GC command=9 0140 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | I 49108 2023-05-26 16:28:29 - [Commands] Sending to C-3 (version=GC command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2 -0010 | 30 50 4F 33 00 03 00 44 60 00 00 00 00 00 00 00 | 3 ` +0010 | 30 50 4F 33 00 03 00 44 20 00 00 00 00 00 00 00 | 3 ` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 49108 2023-05-26 16:28:29 - [Commands] Sending to C-3 (version=GC command=07 flag=06) 0000 | 07 05 AC 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al diff --git a/tests/XB-ForestGame.test.txt b/tests/XB-ForestGame.test.txt index 59e8db7b..e284bd15 100644 --- a/tests/XB-ForestGame.test.txt +++ b/tests/XB-ForestGame.test.txt @@ -52,7 +52,7 @@ I 16496 2023-11-08 01:54:08 - [Commands] Received from C-1 (version=XB command=9 0020 | 00 00 00 00 | I 16496 2023-11-08 01:54:08 - [Commands] Sending to C-1 (version=XB command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 E2 D4 45 39 32 AC 99 83 | , E92 -0010 | 00 00 00 00 00 81 00 42 60 00 00 00 00 00 00 00 | 4 B` +0010 | 00 00 00 00 00 81 00 42 20 00 00 00 00 00 00 00 | 4 B` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16496 2023-11-08 01:54:08 - [Commands] Sending to C-1 (version=XB command=D5 flag=00) 0000 | D5 00 2C 00 59 6F 75 20 61 72 65 20 63 6F 6E 6E | , You are conn @@ -62,7 +62,7 @@ I 16496 2023-11-08 01:54:09 - [Commands] Received from C-1 (version=XB command=9 0000 | 96 00 0C 00 7C 9C DA 2C 62 00 00 00 | | ,b I 16496 2023-11-08 01:54:09 - [Commands] Sending to C-1 (version=XB command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 E2 D4 45 39 32 AC 99 83 | , E92 -0010 | 00 00 00 00 00 81 00 42 60 00 04 00 00 00 00 00 | 4 @` +0010 | 00 00 00 00 00 81 00 42 20 00 04 00 00 00 00 00 | 4 @` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16496 2023-11-08 01:54:09 - [Commands] Sending to C-1 (version=XB command=B1 flag=00) 0000 | B1 00 1C 00 32 30 32 33 3A 31 31 3A 30 38 3A 20 | 2023:11:08: @@ -73,7 +73,7 @@ I 16496 2023-11-08 01:54:13 - [Commands] Received from C-1 (version=XB command=D 0000 | D6 00 04 00 | I 16496 2023-11-08 01:54:13 - [Commands] Sending to C-1 (version=XB command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 E2 D4 45 39 32 AC 99 83 | , E92 -0010 | 00 00 00 00 00 81 00 40 60 00 04 00 00 00 00 00 | 4 @` +0010 | 00 00 00 00 00 81 00 40 20 00 04 00 00 00 00 00 | 4 @` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16496 2023-11-08 01:54:13 - [Commands] Sending to C-1 (version=XB command=07 flag=05) 0000 | 07 04 90 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al @@ -91,7 +91,7 @@ I 16496 2023-11-08 01:54:15 - [Commands] Sending to C-1 (version=XB command=97 f 0000 | 97 01 04 00 | I 16496 2023-11-08 01:54:15 - [Commands] Sending to C-1 (version=XB command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 E2 D4 45 39 32 AC 99 83 | , E92 -0010 | 00 00 00 00 00 81 00 44 60 00 00 00 00 00 00 00 | 4 D` +0010 | 00 00 00 00 00 81 00 44 20 00 00 00 00 00 00 00 | 4 D` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16496 2023-11-08 01:54:16 - [Commands] Received from C-1 (version=XB command=B1 flag=00) 0000 | B1 00 04 00 | @@ -154,11 +154,11 @@ I 16496 2023-11-08 01:54:17 - [Commands] Sending to C-2 (version=XB command=9F f 0000 | 9F 00 04 00 | I 16496 2023-11-08 01:54:18 - [Commands] Received from C-2 (version=XB command=9F flag=00) 0000 | 9F 00 24 00 32 AC 99 83 00 00 00 00 00 81 00 44 | $ 2 4 D -0010 | 60 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF | ` +0010 | 20 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF | ` 0020 | 80 FF FF FF | I 16496 2023-11-08 01:54:18 - [Commands] Sending to C-2 (version=XB command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 E2 D4 45 39 32 AC 99 83 | , E92 -0010 | 00 00 00 00 00 81 00 44 60 00 00 00 00 00 00 00 | 4 D` +0010 | 00 00 00 00 00 81 00 44 20 00 00 00 00 00 00 00 | 4 D` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16496 2023-11-08 01:54:18 - [Commands] Sending to C-2 (version=XB command=83 flag=0F) 0000 | 83 0F B8 00 33 00 00 33 01 00 00 00 00 00 00 00 | 3 3 @@ -375,7 +375,7 @@ I 16496 2023-11-08 01:54:18 - [Commands] Sending to C-2 (Tali) (version=XB comma 0480 | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF | I 16496 2023-11-08 01:54:18 - [Commands] Sending to C-2 (Tali) (version=XB command=04 flag=00) 0000 | 04 00 2C 00 00 00 01 00 E2 D4 45 39 32 AC 99 83 | , E92 -0010 | 00 00 00 00 00 83 00 44 60 00 00 00 00 00 00 00 | 4 D` +0010 | 00 00 00 00 00 83 00 44 20 00 00 00 00 00 00 00 | 4 D` 0020 | 00 00 00 00 00 00 FF FF 80 FF FF FF | I 16496 2023-11-08 01:54:19 - [Commands] Received from C-2 (Tali) (version=XB command=60 flag=00) 0000 | 60 00 1C 00 3F 06 00 00 00 00 00 80 0F 00 FF FF | ` ? diff --git a/tests/config.json b/tests/config.json index 95024f5f..2c20c168 100644 --- a/tests/config.json +++ b/tests/config.json @@ -105,6 +105,7 @@ "GameServer": "INFO", "StaticGameData": "INFO", }, + "HideDownloadCommands": true, "AllowUnregisteredUsers": true, "AllowPCNTE": true,