rewrite client config; use BB state from login command
This commit is contained in:
+120
-106
@@ -3,6 +3,7 @@
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Channel.hh"
|
||||
#include "CommandFormats.hh"
|
||||
@@ -23,100 +24,126 @@ extern const uint64_t CLIENT_CONFIG_MAGIC;
|
||||
class Server;
|
||||
struct Lobby;
|
||||
|
||||
struct ClientOptions {
|
||||
// Options used on both game and proxy server
|
||||
bool switch_assist;
|
||||
bool infinite_hp;
|
||||
bool infinite_tp;
|
||||
bool debug;
|
||||
int16_t override_section_id; // -1 = no override
|
||||
int16_t override_lobby_event; // -1 = no override
|
||||
int16_t override_lobby_number; // -1 = no override
|
||||
int64_t override_random_seed;
|
||||
|
||||
// Options used only on proxy server
|
||||
bool save_files;
|
||||
bool enable_chat_commands;
|
||||
bool enable_chat_filter;
|
||||
bool enable_player_notifications;
|
||||
bool suppress_client_pings;
|
||||
bool suppress_remote_login;
|
||||
bool zero_remote_guild_card;
|
||||
bool ep3_infinite_meseta;
|
||||
bool ep3_infinite_time;
|
||||
bool red_name;
|
||||
bool blank_name;
|
||||
int64_t function_call_return_value; // -1 = don't block function calls
|
||||
|
||||
ClientOptions();
|
||||
};
|
||||
|
||||
struct Client : public std::enable_shared_from_this<Client> {
|
||||
enum Flag {
|
||||
// Client is DC Network Trial Edition, which is missing a lot of features
|
||||
// and uses some different command numbers than any other version
|
||||
IS_DC_TRIAL_EDITION = 0x00002000,
|
||||
// A 90 01 command has been sent (which proto will send a 93 in response to,
|
||||
// and actual DCv1 will send a 92)
|
||||
CHECKED_FOR_DC_V1_PROTOTYPE = 0x00080000,
|
||||
// Client is DC v1 prototype
|
||||
IS_DC_V1_PROTOTYPE = 0x00040000,
|
||||
// Client is DC v1
|
||||
IS_DC_V1 = 0x00000010,
|
||||
// Client is GC Episodes 1&2 Trial Edition, which is much more like PC than
|
||||
// actual GC Episodes 1&2 - it uses PC encryption and is missing most of the
|
||||
// features added in Episodes 1&2
|
||||
IS_GC_TRIAL_EDITION = 0x00200000,
|
||||
// Client is GC Episode 3 Trial Edition, which is fairly close to the final
|
||||
// Episode 3 build, but is missing a few commands that we'll have to avoid
|
||||
// sending
|
||||
IS_EP3_TRIAL_EDITION = 0x00400000,
|
||||
// For patch server clients, client is Blue Burst rather than PC
|
||||
IS_BB_PATCH = 0x00000001,
|
||||
// After joining a lobby, client will no longer send D6 commands when they
|
||||
// close message boxes
|
||||
NO_D6_AFTER_LOBBY = 0x00000002,
|
||||
// Client has the above flag and has already joined a lobby, or is not GC
|
||||
NO_D6 = 0x00000004,
|
||||
// Client is Episode 3, should be able to see CARD lobbies, and should only
|
||||
// be able to see/join games with the EPISODE_3_ONLY flag
|
||||
IS_EPISODE_3 = 0x00000008,
|
||||
// Client disconnects if it receives B2 (send_function_call)
|
||||
NO_SEND_FUNCTION_CALL = 0x00000200,
|
||||
// Client requires doubly-encrypted code section in send_function_call
|
||||
ENCRYPTED_SEND_FUNCTION_CALL = 0x00000800,
|
||||
// Client supports send_function_call but does not actually run the code
|
||||
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x00001000,
|
||||
// Client supports send_function_call and clears its caches properly before
|
||||
// calling the function (so we don't need to patch it)
|
||||
SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x00020000,
|
||||
// Client is vulnerable to a buffer overflow that we can use to enable
|
||||
// send_function_call
|
||||
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x00008000,
|
||||
enum class Flag : uint64_t {
|
||||
// clang-format off
|
||||
|
||||
// Client is loading into a game
|
||||
LOADING = 0x00000020,
|
||||
// Client is loading a quest
|
||||
LOADING_QUEST = 0x00000040,
|
||||
// Client is loading a joinable quest that has already started
|
||||
LOADING_RUNNING_QUEST = 0x00100000,
|
||||
// Client is waiting for other players to join a tournament game
|
||||
LOADING_TOURNAMENT = 0x00010000,
|
||||
// Client is in the information menu (login server only)
|
||||
IN_INFORMATION_MENU = 0x00000080,
|
||||
// Client is at the welcome message (login server only)
|
||||
AT_WELCOME_MESSAGE = 0x00000100,
|
||||
// Client has already received a 97 (enable saves) command, so don't show
|
||||
// the programs menu anymore
|
||||
SAVE_ENABLED = 0x00000400,
|
||||
// Client has received newserv's Episode 3 card definitions, so don't send
|
||||
// them again
|
||||
HAS_EP3_CARD_DEFS = 0x00004000,
|
||||
// Client has received newserv's Episode 3 media updates, so don't send them
|
||||
// again
|
||||
HAS_EP3_MEDIA_UPDATES = 0x00800000,
|
||||
// Version-related flags
|
||||
IS_DC_TRIAL_EDITION = 0x0000000000000001,
|
||||
CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002,
|
||||
IS_DC_V1_PROTOTYPE = 0x0000000000000004,
|
||||
IS_DC_V1 = 0x0000000000000008,
|
||||
IS_GC_TRIAL_EDITION = 0x0000000000000010,
|
||||
IS_EP3_TRIAL_EDITION = 0x0000000000000020,
|
||||
IS_EPISODE_3 = 0x0000000000000040,
|
||||
IS_BB_PATCH = 0x0000000000000080,
|
||||
NO_D6_AFTER_LOBBY = 0x0000000000000100,
|
||||
NO_D6 = 0x0000000000000200,
|
||||
|
||||
UNUSED_FLAG_BITS = 0xFF010000,
|
||||
// Flags describing the behavior for send_function_call
|
||||
NO_SEND_FUNCTION_CALL = 0x0000000000001000,
|
||||
ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000,
|
||||
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000,
|
||||
SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000,
|
||||
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000,
|
||||
|
||||
// State flags
|
||||
LOADING = 0x0000000000100000,
|
||||
LOADING_QUEST = 0x0000000000200000,
|
||||
LOADING_RUNNING_JOINABLE_QUEST = 0x0000000000400000,
|
||||
LOADING_TOURNAMENT = 0x0000000000800000,
|
||||
IN_INFORMATION_MENU = 0x0000000001000000,
|
||||
AT_WELCOME_MESSAGE = 0x0000000002000000,
|
||||
SAVE_ENABLED = 0x0000000004000000,
|
||||
HAS_EP3_CARD_DEFS = 0x0000000008000000,
|
||||
HAS_EP3_MEDIA_UPDATES = 0x0000000010000000,
|
||||
USE_OVERRIDE_RANDOM_SEED = 0x0000000020000000,
|
||||
HAS_GUILD_CARD_NUMBER = 0x0000000040000000,
|
||||
|
||||
// Cheat mode flags
|
||||
SWITCH_ASSIST_ENABLED = 0x0000000100000000,
|
||||
INFINITE_HP_ENABLED = 0x0000000200000000,
|
||||
INFINITE_TP_ENABLED = 0x0000000400000000,
|
||||
DEBUG_ENABLED = 0x0000000800000000,
|
||||
|
||||
// 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,
|
||||
PROXY_ZERO_REMOTE_GUILD_CARD = 0x0000040000000000,
|
||||
PROXY_EP3_INFINITE_MESETA_ENABLED = 0x0000080000000000,
|
||||
PROXY_EP3_INFINITE_TIME_ENABLED = 0x0000100000000000,
|
||||
PROXY_RED_NAME_ENABLED = 0x0000200000000000,
|
||||
PROXY_BLANK_NAME_ENABLED = 0x0000400000000000,
|
||||
PROXY_BLOCK_FUNCTION_CALLS = 0x0000800000000000,
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
struct Config {
|
||||
uint64_t enabled_flags = 0; // Client::Flag enum
|
||||
uint32_t specific_version = 0;
|
||||
int32_t override_random_seed = 0;
|
||||
uint8_t override_section_id = 0xFF; // FF = no override
|
||||
uint8_t override_lobby_event = 0xFF; // FF = no override
|
||||
uint8_t override_lobby_number = 0x80; // 80 = no override
|
||||
uint32_t proxy_destination_address = 0;
|
||||
uint16_t proxy_destination_port = 0;
|
||||
|
||||
Config() = default;
|
||||
|
||||
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
||||
return !!(this->enabled_flags & static_cast<uint64_t>(flag));
|
||||
}
|
||||
inline void set_flag(Flag flag) {
|
||||
this->enabled_flags |= static_cast<uint64_t>(flag);
|
||||
}
|
||||
inline void clear_flag(Flag flag) {
|
||||
this->enabled_flags &= (~static_cast<uint64_t>(flag));
|
||||
}
|
||||
inline void toggle_flag(Flag flag) {
|
||||
this->enabled_flags ^= static_cast<uint64_t>(flag);
|
||||
}
|
||||
|
||||
void set_flags_for_version(GameVersion version, int64_t sub_version);
|
||||
|
||||
template <size_t Bytes>
|
||||
void parse_from(const parray<uint8_t, Bytes>& data) {
|
||||
StringReader r(data.data(), data.size());
|
||||
if (r.get_u32l() != CLIENT_CONFIG_MAGIC) {
|
||||
throw std::invalid_argument("config signature is incorrect");
|
||||
}
|
||||
this->specific_version = r.get_u32l();
|
||||
this->enabled_flags = r.get_u64l();
|
||||
this->override_random_seed = r.get_u32l();
|
||||
this->proxy_destination_address = r.get_u32b();
|
||||
this->proxy_destination_port = r.get_u16l();
|
||||
this->override_section_id = r.get_u8();
|
||||
this->override_lobby_event = r.get_u8();
|
||||
this->override_lobby_number = r.get_u8();
|
||||
}
|
||||
|
||||
template <size_t Bytes>
|
||||
void serialize_into(parray<uint8_t, Bytes>& data) const {
|
||||
StringWriter w;
|
||||
w.put_u32l(CLIENT_CONFIG_MAGIC);
|
||||
w.put_u32l(this->specific_version);
|
||||
w.put_u64l(this->enabled_flags);
|
||||
w.put_u32l(this->override_random_seed);
|
||||
w.put_u32b(this->proxy_destination_address);
|
||||
w.put_u16l(this->proxy_destination_port);
|
||||
w.put_u8(this->override_section_id);
|
||||
w.put_u8(this->override_lobby_event);
|
||||
w.put_u8(this->override_lobby_number);
|
||||
|
||||
const auto& s = w.str();
|
||||
for (size_t z = 0; z < s.size(); z++) {
|
||||
data[z] = s[z];
|
||||
}
|
||||
data.clear_after(s.size(), 0xFF);
|
||||
}
|
||||
};
|
||||
|
||||
std::weak_ptr<Server> server;
|
||||
@@ -127,13 +154,6 @@ struct Client : public std::enable_shared_from_this<Client> {
|
||||
// License & account
|
||||
std::shared_ptr<License> license;
|
||||
|
||||
// Note: these fields are included in the client config. On GC, the client
|
||||
// config can be up to 0x20 bytes; on BB it can be 0x28 bytes. We don't use
|
||||
// all of that space.
|
||||
uint8_t bb_game_state;
|
||||
uint32_t flags;
|
||||
uint32_t specific_version;
|
||||
|
||||
// Network
|
||||
Channel channel;
|
||||
struct sockaddr_storage next_connection_addr;
|
||||
@@ -141,15 +161,14 @@ struct Client : public std::enable_shared_from_this<Client> {
|
||||
bool should_disconnect;
|
||||
bool should_send_to_lobby_server;
|
||||
bool should_send_to_proxy_server;
|
||||
uint32_t proxy_destination_address;
|
||||
uint16_t proxy_destination_port;
|
||||
std::unordered_map<std::string, std::function<void()>> disconnect_hooks;
|
||||
uint8_t bb_connection_phase;
|
||||
|
||||
// Patch server
|
||||
std::vector<PatchFileChecksumRequest> patch_file_checksum_requests;
|
||||
|
||||
// Lobby/positioning
|
||||
ClientOptions options;
|
||||
Config config;
|
||||
float x;
|
||||
float z;
|
||||
uint32_t area;
|
||||
@@ -203,11 +222,6 @@ struct Client : public std::enable_shared_from_this<Client> {
|
||||
std::shared_ptr<ServerState> require_server_state() const;
|
||||
std::shared_ptr<Lobby> require_lobby() const;
|
||||
|
||||
ClientConfig export_config() const;
|
||||
ClientConfigBB export_config_bb() const;
|
||||
void import_config(const ClientConfig& cc);
|
||||
void import_config(const ClientConfigBB& cc);
|
||||
|
||||
static void dispatch_save_game_data(evutil_socket_t, short, void* ctx);
|
||||
void save_game_data();
|
||||
static void dispatch_send_ping(evutil_socket_t, short, void* ctx);
|
||||
|
||||
Reference in New Issue
Block a user