rewrite client config; use BB state from login command
This commit is contained in:
+107
-96
@@ -65,13 +65,13 @@ static void check_is_game(shared_ptr<Lobby> l, bool is_game) {
|
||||
}
|
||||
|
||||
static void check_is_ep3(shared_ptr<Client> c, bool is_ep3) {
|
||||
if (!!(c->flags & Client::Flag::IS_EPISODE_3) != is_ep3) {
|
||||
if (c->config.check_flag(Client::Flag::IS_EPISODE_3) != is_ep3) {
|
||||
throw precondition_failed(is_ep3 ? "$C6This command can only\nbe used in Episode 3." : "$C6This command cannot\nbe used in Episode 3.");
|
||||
}
|
||||
}
|
||||
|
||||
static void check_cheats_enabled(shared_ptr<Lobby> l) {
|
||||
if (!(l->flags & Lobby::Flag::CHEATS_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::CHEATS_ENABLED)) {
|
||||
throw precondition_failed("$C6This command can\nonly be used in\ncheat mode.");
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ static void server_command_lobby_info(shared_ptr<Client> c, const std::string&)
|
||||
}
|
||||
lines.emplace_back(string_printf("$C7Section ID: $C6%s$C7", name_for_section_id(l->section_id).c_str()));
|
||||
|
||||
if (l->flags & Lobby::Flag::DROPS_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::DROPS_ENABLED)) {
|
||||
if (l->item_creator) {
|
||||
lines.emplace_back("Server item table");
|
||||
} else {
|
||||
@@ -123,7 +123,7 @@ static void server_command_lobby_info(shared_ptr<Client> c, const std::string&)
|
||||
} else {
|
||||
lines.emplace_back("No item drops");
|
||||
}
|
||||
if (l->flags & Lobby::Flag::CHEATS_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::CHEATS_ENABLED)) {
|
||||
lines.emplace_back("Cheats enabled");
|
||||
}
|
||||
|
||||
@@ -186,13 +186,13 @@ static void proxy_command_lobby_info(shared_ptr<ProxyServer::LinkedSession> ses,
|
||||
}
|
||||
|
||||
vector<const char*> cheats_tokens;
|
||||
if (ses->options.switch_assist) {
|
||||
if (ses->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) {
|
||||
cheats_tokens.emplace_back("SWA");
|
||||
}
|
||||
if (ses->options.infinite_hp) {
|
||||
if (ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
cheats_tokens.emplace_back("HP");
|
||||
}
|
||||
if (ses->options.infinite_tp) {
|
||||
if (ses->config.check_flag(Client::Flag::INFINITE_TP_ENABLED)) {
|
||||
cheats_tokens.emplace_back("TP");
|
||||
}
|
||||
if (!cheats_tokens.empty()) {
|
||||
@@ -201,13 +201,13 @@ static void proxy_command_lobby_info(shared_ptr<ProxyServer::LinkedSession> ses,
|
||||
}
|
||||
|
||||
vector<const char*> behaviors_tokens;
|
||||
if (ses->options.save_files) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
behaviors_tokens.emplace_back("SAVE");
|
||||
}
|
||||
if (ses->options.suppress_remote_login) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SUPPRESS_REMOTE_LOGIN)) {
|
||||
behaviors_tokens.emplace_back("SL");
|
||||
}
|
||||
if (ses->options.function_call_return_value >= 0) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_BLOCK_FUNCTION_CALLS)) {
|
||||
behaviors_tokens.emplace_back("BFC");
|
||||
}
|
||||
if (!behaviors_tokens.empty()) {
|
||||
@@ -215,9 +215,9 @@ static void proxy_command_lobby_info(shared_ptr<ProxyServer::LinkedSession> ses,
|
||||
msg += join(behaviors_tokens, ",");
|
||||
}
|
||||
|
||||
if (ses->options.override_section_id >= 0) {
|
||||
if (ses->config.override_section_id != 0xFF) {
|
||||
msg += "\n$C7SecID*: $C6";
|
||||
msg += name_for_section_id(ses->options.override_section_id);
|
||||
msg += name_for_section_id(ses->config.override_section_id);
|
||||
}
|
||||
|
||||
send_text_message(ses->client_channel, msg);
|
||||
@@ -248,9 +248,8 @@ static void proxy_command_arrow(shared_ptr<ProxyServer::LinkedSession> ses, cons
|
||||
|
||||
static void server_command_debug(shared_ptr<Client> c, const std::string&) {
|
||||
check_license_flags(c, License::Flag::DEBUG);
|
||||
c->options.debug = !c->options.debug;
|
||||
send_text_message_printf(c, "Debug %s",
|
||||
c->options.debug ? "enabled" : "disabled");
|
||||
c->config.toggle_flag(Client::Flag::DEBUG_ENABLED);
|
||||
send_text_message_printf(c, "Debug %s", (c->config.check_flag(Client::Flag::DEBUG_ENABLED) ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
static void server_command_show_material_counts(shared_ptr<Client> c, const std::string&) {
|
||||
@@ -293,7 +292,7 @@ static void server_command_patch(shared_ptr<Client> c, const std::string& args)
|
||||
// Note: We can't look this up outside of the closure because
|
||||
// c->specific_version can change during prepare_client_for_patches
|
||||
auto fn = s->function_code_index->name_and_specific_version_to_patch_function.at(
|
||||
string_printf("%s-%08" PRIX32, args.c_str(), c->specific_version));
|
||||
string_printf("%s-%08" PRIX32, args.c_str(), c->config.specific_version));
|
||||
send_function_call(c, fn);
|
||||
c->function_call_response_queue.emplace_back(empty_function_call_response_handler);
|
||||
} catch (const out_of_range&) {
|
||||
@@ -307,15 +306,14 @@ static void empty_patch_return_handler(uint32_t, uint32_t) {}
|
||||
static void proxy_command_patch(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
|
||||
auto send_call = [args, ses](uint32_t specific_version, uint32_t) {
|
||||
try {
|
||||
if (ses->newserv_client_config.cfg.specific_version != specific_version) {
|
||||
ses->newserv_client_config.cfg.specific_version = specific_version;
|
||||
ses->log.info("Version detected as %08" PRIX32, ses->newserv_client_config.cfg.specific_version);
|
||||
if (ses->config.specific_version != specific_version) {
|
||||
ses->config.specific_version = specific_version;
|
||||
ses->log.info("Version detected as %08" PRIX32, ses->config.specific_version);
|
||||
}
|
||||
auto s = ses->require_server_state();
|
||||
auto fn = s->function_code_index->name_and_specific_version_to_patch_function.at(
|
||||
string_printf("%s-%08" PRIX32, args.c_str(), ses->newserv_client_config.cfg.specific_version));
|
||||
send_function_call(
|
||||
ses->client_channel, ses->newserv_client_config.cfg.flags, fn);
|
||||
string_printf("%s-%08" PRIX32, args.c_str(), ses->config.specific_version));
|
||||
send_function_call(ses->client_channel, ses->config, fn);
|
||||
// Don't forward the patch response to the server
|
||||
ses->function_call_return_handler_queue.emplace_back(empty_patch_return_handler);
|
||||
} catch (const out_of_range&) {
|
||||
@@ -325,28 +323,28 @@ static void proxy_command_patch(shared_ptr<ProxyServer::LinkedSession> ses, cons
|
||||
|
||||
auto send_version_detect_or_send_call = [args, ses, send_call]() {
|
||||
if (ses->version() == GameVersion::GC &&
|
||||
ses->newserv_client_config.cfg.specific_version == default_specific_version_for_version(GameVersion::GC, -1)) {
|
||||
ses->config.specific_version == default_specific_version_for_version(GameVersion::GC, -1)) {
|
||||
auto s = ses->require_server_state();
|
||||
send_function_call(
|
||||
ses->client_channel,
|
||||
ses->newserv_client_config.cfg.flags,
|
||||
ses->config,
|
||||
s->function_code_index->name_to_function.at("VersionDetect"));
|
||||
ses->function_call_return_handler_queue.emplace_back(send_call);
|
||||
} else {
|
||||
send_call(ses->newserv_client_config.cfg.specific_version, 0);
|
||||
send_call(ses->config.specific_version, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// This mirrors the implementation in prepare_client_for_patches
|
||||
if (!(ses->newserv_client_config.cfg.flags & Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH)) {
|
||||
if (!ses->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH)) {
|
||||
auto s = ses->require_server_state();
|
||||
send_function_call(
|
||||
ses->client_channel, ses->newserv_client_config.cfg.flags, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2734EC);
|
||||
ses->client_channel, ses->config, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2734EC);
|
||||
ses->function_call_return_handler_queue.emplace_back([s, ses, send_version_detect_or_send_call](uint32_t, uint32_t) -> void {
|
||||
send_function_call(
|
||||
ses->client_channel, ses->newserv_client_config.cfg.flags, s->function_code_index->name_to_function.at("CacheClearFix-Phase2"));
|
||||
ses->client_channel, ses->config, s->function_code_index->name_to_function.at("CacheClearFix-Phase2"));
|
||||
ses->function_call_return_handler_queue.emplace_back([ses, send_version_detect_or_send_call](uint32_t, uint32_t) -> void {
|
||||
ses->newserv_client_config.cfg.flags |= Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
ses->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
send_version_detect_or_send_call();
|
||||
});
|
||||
});
|
||||
@@ -358,21 +356,21 @@ static void proxy_command_patch(shared_ptr<ProxyServer::LinkedSession> ses, cons
|
||||
static void server_command_persist(shared_ptr<Client> c, const std::string&) {
|
||||
check_license_flags(c, License::Flag::DEBUG);
|
||||
auto l = c->require_lobby();
|
||||
if (l->flags & Lobby::Flag::DEFAULT) {
|
||||
if (l->check_flag(Lobby::Flag::DEFAULT)) {
|
||||
send_text_message(c, "$C6Default lobbies\ncannot be marked\ntemporary");
|
||||
} else {
|
||||
l->flags ^= Lobby::Flag::PERSISTENT;
|
||||
l->toggle_flag(Lobby::Flag::PERSISTENT);
|
||||
send_text_message_printf(c, "Lobby persistence\n%s",
|
||||
(l->flags & Lobby::Flag::PERSISTENT) ? "enabled" : "disabled");
|
||||
l->check_flag(Lobby::Flag::PERSISTENT) ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
static void server_command_exit(shared_ptr<Client> c, const std::string&) {
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game()) {
|
||||
if (c->flags & Client::Flag::IS_EPISODE_3) {
|
||||
if (c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
c->channel.send(0xED, 0x00);
|
||||
} else if (l->flags & (Lobby::Flag::QUEST_IN_PROGRESS | Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) {
|
||||
} else if (l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS) || l->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) {
|
||||
G_UnusedHeader cmd = {0x73, 0x01, 0x0000};
|
||||
c->channel.send(0x60, 0x00, cmd);
|
||||
c->area = 0;
|
||||
@@ -381,7 +379,7 @@ static void server_command_exit(shared_ptr<Client> c, const std::string&) {
|
||||
}
|
||||
} else {
|
||||
send_self_leave_notification(c);
|
||||
if (!(c->flags & Client::Flag::NO_D6)) {
|
||||
if (!c->config.check_flag(Client::Flag::NO_D6)) {
|
||||
send_message_box(c, "");
|
||||
}
|
||||
|
||||
@@ -393,7 +391,7 @@ static void server_command_exit(shared_ptr<Client> c, const std::string&) {
|
||||
|
||||
static void proxy_command_exit(shared_ptr<ProxyServer::LinkedSession> ses, const std::string&) {
|
||||
if (ses->is_in_game) {
|
||||
if (ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3) {
|
||||
if (ses->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
ses->client_channel.send(0xED, 0x00);
|
||||
} else if (ses->is_in_quest) {
|
||||
G_UnusedHeader cmd = {0x73, 0x01, 0x0000};
|
||||
@@ -477,11 +475,11 @@ static void server_command_cheat(shared_ptr<Client> c, const std::string&) {
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, true);
|
||||
check_is_leader(l, c);
|
||||
if (l->flags & Lobby::Flag::CANNOT_CHANGE_CHEAT_MODE) {
|
||||
if (l->check_flag(Lobby::Flag::CANNOT_CHANGE_CHEAT_MODE)) {
|
||||
send_text_message(c, "$C6Cheat mode cannot\nbe changed on this\nserver");
|
||||
} else {
|
||||
l->flags ^= Lobby::Flag::CHEATS_ENABLED;
|
||||
send_text_message_printf(l, "Cheat mode %s", (l->flags & Lobby::Flag::CHEATS_ENABLED) ? "enabled" : "disabled");
|
||||
l->toggle_flag(Lobby::Flag::CHEATS_ENABLED);
|
||||
send_text_message_printf(l, "Cheat mode %s", l->check_flag(Lobby::Flag::CHEATS_ENABLED) ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,18 +500,18 @@ static void server_command_lobby_event(shared_ptr<Client> c, const std::string&
|
||||
|
||||
static void proxy_command_lobby_event(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
|
||||
if (args.empty()) {
|
||||
ses->options.override_lobby_event = -1;
|
||||
ses->config.override_lobby_event = 0xFF;
|
||||
} else {
|
||||
uint8_t new_event = event_for_name(args);
|
||||
if (new_event == 0xFF) {
|
||||
send_text_message(ses->client_channel, "$C6No such lobby event.");
|
||||
} else {
|
||||
ses->options.override_lobby_event = new_event;
|
||||
ses->config.override_lobby_event = new_event;
|
||||
// This command is supported on all V3 versions except Ep1&2 Trial
|
||||
if ((ses->version() == GameVersion::GC && !(ses->newserv_client_config.cfg.flags & Client::Flag::IS_GC_TRIAL_EDITION)) ||
|
||||
if ((ses->version() == GameVersion::GC && !ses->config.check_flag(Client::Flag::IS_GC_TRIAL_EDITION)) ||
|
||||
(ses->version() == GameVersion::XB) ||
|
||||
(ses->version() == GameVersion::BB)) {
|
||||
ses->client_channel.send(0xDA, ses->options.override_lobby_event);
|
||||
ses->client_channel.send(0xDA, ses->config.override_lobby_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -530,7 +528,7 @@ static void server_command_lobby_event_all(shared_ptr<Client> c, const std::stri
|
||||
|
||||
auto s = c->require_server_state();
|
||||
for (auto l : s->all_lobbies()) {
|
||||
if (l->is_game() || !(l->flags & Lobby::Flag::DEFAULT)) {
|
||||
if (l->is_game() || !l->check_flag(Lobby::Flag::DEFAULT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -543,26 +541,33 @@ static void server_command_lobby_type(shared_ptr<Client> c, const std::string& a
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, false);
|
||||
|
||||
uint8_t new_type = args.empty() ? 0 : lobby_type_for_name(args);
|
||||
if (new_type == 0x80) {
|
||||
send_text_message(c, "$C6No such lobby type");
|
||||
return;
|
||||
uint8_t new_type;
|
||||
if (args.empty()) {
|
||||
new_type = 0x80;
|
||||
} else {
|
||||
new_type = lobby_type_for_name(args);
|
||||
if (new_type == 0x80) {
|
||||
send_text_message(c, "$C6No such lobby type");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t max_standard_type = ((c->flags & Client::Flag::IS_EPISODE_3) ? 20 : 15);
|
||||
c->options.override_lobby_number = (new_type < max_standard_type) ? -1 : new_type;
|
||||
c->config.override_lobby_number = new_type;
|
||||
send_join_lobby(c, l);
|
||||
}
|
||||
|
||||
static void proxy_command_lobby_type(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
|
||||
uint8_t new_type = args.empty() ? 0 : lobby_type_for_name(args);
|
||||
if (new_type == 0x80) {
|
||||
send_text_message(ses->client_channel, "$C6No such lobby type");
|
||||
return;
|
||||
uint8_t new_type;
|
||||
if (args.empty()) {
|
||||
new_type = 0x80;
|
||||
} else {
|
||||
new_type = lobby_type_for_name(args);
|
||||
if (new_type == 0x80) {
|
||||
send_text_message(ses->client_channel, "$C6No such lobby type");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t max_standard_type = ((ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3) ? 20 : 15);
|
||||
ses->options.override_lobby_number = (new_type < max_standard_type) ? -1 : new_type;
|
||||
ses->config.override_lobby_number = new_type;
|
||||
}
|
||||
|
||||
static string file_path_for_recording(const std::string& args, uint32_t serial_number) {
|
||||
@@ -587,7 +592,7 @@ static void server_command_saverec(shared_ptr<Client> c, const std::string& args
|
||||
}
|
||||
|
||||
static void server_command_playrec(shared_ptr<Client> c, const std::string& args) {
|
||||
if (!(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (!c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
send_text_message(c, "$C4This command can\nonly be used on\nEpisode 3");
|
||||
return;
|
||||
}
|
||||
@@ -599,10 +604,9 @@ static void server_command_playrec(shared_ptr<Client> c, const std::string& args
|
||||
string file_path = file_path_for_recording(args, c->license->serial_number);
|
||||
|
||||
auto s = c->require_server_state();
|
||||
uint32_t flags = Lobby::Flag::IS_SPECTATOR_TEAM;
|
||||
string filename = args;
|
||||
if (filename[0] == '!') {
|
||||
flags |= Lobby::Flag::START_BATTLE_PLAYER_IMMEDIATELY;
|
||||
bool start_battle_player_immediately = (filename[0] == '!');
|
||||
if (start_battle_player_immediately) {
|
||||
filename = filename.substr(1);
|
||||
}
|
||||
|
||||
@@ -617,10 +621,13 @@ static void server_command_playrec(shared_ptr<Client> c, const std::string& args
|
||||
shared_ptr<Episode3::BattleRecordPlayer> battle_player(
|
||||
new Episode3::BattleRecordPlayer(record, s->game_server->get_base()));
|
||||
auto game = create_game_generic(
|
||||
s, c, args, "", Episode::EP3, GameMode::NORMAL, 0, flags, false, nullptr, battle_player);
|
||||
s, c, args, "", Episode::EP3, GameMode::NORMAL, 0, false, nullptr, battle_player);
|
||||
if (game) {
|
||||
if (start_battle_player_immediately) {
|
||||
game->set_flag(Lobby::Flag::START_BATTLE_PLAYER_IMMEDIATELY);
|
||||
}
|
||||
s->change_client_lobby(c, game);
|
||||
c->flags |= Client::Flag::LOADING;
|
||||
c->config.set_flag(Client::Flag::LOADING);
|
||||
}
|
||||
} else {
|
||||
send_text_message(c, "$C4This command cannot\nbe used in a game");
|
||||
@@ -629,7 +636,7 @@ static void server_command_playrec(shared_ptr<Client> c, const std::string& args
|
||||
|
||||
static void server_command_meseta(shared_ptr<Client> c, const std::string& args) {
|
||||
check_is_ep3(c, true);
|
||||
if (!c->options.debug) {
|
||||
if (!c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message(c, "$C6This command can only\nbe run in debug mode\n(run %sdebug first)");
|
||||
return;
|
||||
}
|
||||
@@ -651,14 +658,14 @@ static void server_command_secid(shared_ptr<Client> c, const std::string& args)
|
||||
check_cheats_allowed(c->require_server_state());
|
||||
|
||||
if (!args[0]) {
|
||||
c->options.override_section_id = -1;
|
||||
c->config.override_section_id = 0xFF;
|
||||
send_text_message(c, "$C6Override section ID\nremoved");
|
||||
} else {
|
||||
uint8_t new_secid = section_id_for_name(args);
|
||||
if (new_secid == 0xFF) {
|
||||
send_text_message(c, "$C6Invalid section ID");
|
||||
} else {
|
||||
c->options.override_section_id = new_secid;
|
||||
c->config.override_section_id = new_secid;
|
||||
string name = name_for_section_id(new_secid);
|
||||
send_text_message_printf(c, "$C6Override section ID\nset to %s", name.c_str());
|
||||
}
|
||||
@@ -668,14 +675,14 @@ static void server_command_secid(shared_ptr<Client> c, const std::string& args)
|
||||
static void proxy_command_secid(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
|
||||
check_cheats_allowed(ses->require_server_state());
|
||||
if (!args[0]) {
|
||||
ses->options.override_section_id = -1;
|
||||
ses->config.override_section_id = 0xFF;
|
||||
send_text_message(ses->client_channel, "$C6Override section ID\nremoved");
|
||||
} else {
|
||||
uint8_t new_secid = section_id_for_name(args);
|
||||
if (new_secid == 0xFF) {
|
||||
send_text_message(ses->client_channel, "$C6Invalid section ID");
|
||||
} else {
|
||||
ses->options.override_section_id = new_secid;
|
||||
ses->config.override_section_id = new_secid;
|
||||
string name = name_for_section_id(new_secid);
|
||||
send_text_message(ses->client_channel, "$C6Override section ID\nset to " + name);
|
||||
}
|
||||
@@ -689,10 +696,12 @@ static void server_command_rand(shared_ptr<Client> c, const std::string& args) {
|
||||
check_cheats_allowed(s);
|
||||
|
||||
if (!args[0]) {
|
||||
c->options.override_random_seed = -1;
|
||||
c->config.clear_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED);
|
||||
c->config.override_random_seed = 0;
|
||||
send_text_message(c, "$C6Override seed\nremoved");
|
||||
} else {
|
||||
c->options.override_random_seed = stoul(args, 0, 16);
|
||||
c->config.set_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED);
|
||||
c->config.override_random_seed = stoul(args, 0, 16);
|
||||
send_text_message(c, "$C6Override seed\nset");
|
||||
}
|
||||
}
|
||||
@@ -700,10 +709,12 @@ static void server_command_rand(shared_ptr<Client> c, const std::string& args) {
|
||||
static void proxy_command_rand(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
|
||||
check_proxy_cheats_allowed(ses->require_server_state());
|
||||
if (!args[0]) {
|
||||
ses->options.override_random_seed = -1;
|
||||
ses->config.clear_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED);
|
||||
ses->config.override_random_seed = 0;
|
||||
send_text_message(ses->client_channel, "$C6Override seed\nremoved");
|
||||
} else {
|
||||
ses->options.override_random_seed = stoul(args, 0, 16);
|
||||
ses->config.set_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED);
|
||||
ses->config.override_random_seed = stoul(args, 0, 16);
|
||||
send_text_message(ses->client_channel, "$C6Override seed\nset");
|
||||
}
|
||||
}
|
||||
@@ -738,16 +749,16 @@ static void server_command_toggle_spectator_flag(shared_ptr<Client> c, const std
|
||||
check_is_leader(l, c);
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::IS_SPECTATOR_TEAM) {
|
||||
if (l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
send_text_message(c, "$C6This command cannot\nbe used in a spectator\nteam");
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::SPECTATORS_FORBIDDEN) {
|
||||
l->flags &= ~Lobby::Flag::SPECTATORS_FORBIDDEN;
|
||||
if (l->check_flag(Lobby::Flag::SPECTATORS_FORBIDDEN)) {
|
||||
l->clear_flag(Lobby::Flag::SPECTATORS_FORBIDDEN);
|
||||
send_text_message(l, "$C6Spectators allowed");
|
||||
|
||||
} else {
|
||||
l->flags |= Lobby::Flag::SPECTATORS_FORBIDDEN;
|
||||
l->set_flag(Lobby::Flag::SPECTATORS_FORBIDDEN);
|
||||
for (auto watcher_l : l->watcher_lobbies) {
|
||||
send_command(watcher_l, 0xED, 0x00);
|
||||
}
|
||||
@@ -1127,7 +1138,7 @@ static void server_command_what(shared_ptr<Client> c, const std::string&) {
|
||||
if (!episode_has_arpg_semantics(l->episode)) {
|
||||
return;
|
||||
}
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
send_text_message(c, "$C4Item tracking is\nnot available");
|
||||
} else {
|
||||
float min_dist2 = 0.0f;
|
||||
@@ -1178,15 +1189,16 @@ static void server_command_infinite_hp(shared_ptr<Client> c, const std::string&)
|
||||
check_is_game(l, true);
|
||||
check_cheats_enabled(l);
|
||||
|
||||
c->options.infinite_hp = !c->options.infinite_hp;
|
||||
send_text_message_printf(c, "$C6Infinite HP %s", c->options.infinite_hp ? "enabled" : "disabled");
|
||||
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");
|
||||
}
|
||||
|
||||
static void proxy_command_infinite_hp(shared_ptr<ProxyServer::LinkedSession> ses, const std::string&) {
|
||||
auto s = ses->require_server_state();
|
||||
check_proxy_cheats_allowed(s);
|
||||
ses->options.infinite_hp = !ses->options.infinite_hp;
|
||||
send_text_message_printf(ses->client_channel, "$C6Infinite HP %s", ses->options.infinite_hp ? "enabled" : "disabled");
|
||||
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");
|
||||
}
|
||||
|
||||
static void server_command_infinite_tp(shared_ptr<Client> c, const std::string&) {
|
||||
@@ -1195,17 +1207,16 @@ static void server_command_infinite_tp(shared_ptr<Client> c, const std::string&)
|
||||
check_is_game(l, true);
|
||||
check_cheats_enabled(l);
|
||||
|
||||
c->options.infinite_tp = !c->options.infinite_tp;
|
||||
send_text_message_printf(c, "$C6Infinite TP %s",
|
||||
c->options.infinite_tp ? "enabled" : "disabled");
|
||||
c->config.toggle_flag(Client::Flag::INFINITE_TP_ENABLED);
|
||||
send_text_message_printf(c, "$C6Infinite TP %s", c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED) ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static void proxy_command_infinite_tp(shared_ptr<ProxyServer::LinkedSession> ses, const std::string&) {
|
||||
auto s = ses->require_server_state();
|
||||
check_proxy_cheats_allowed(s);
|
||||
ses->options.infinite_tp = !ses->options.infinite_tp;
|
||||
ses->config.toggle_flag(Client::Flag::INFINITE_TP_ENABLED);
|
||||
send_text_message_printf(ses->client_channel, "$C6Infinite TP %s",
|
||||
ses->options.infinite_tp ? "enabled" : "disabled");
|
||||
ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED) ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static void server_command_switch_assist(shared_ptr<Client> c, const std::string&) {
|
||||
@@ -1214,28 +1225,28 @@ static void server_command_switch_assist(shared_ptr<Client> c, const std::string
|
||||
check_is_game(l, true);
|
||||
check_cheats_enabled(l);
|
||||
|
||||
c->options.switch_assist = !c->options.switch_assist;
|
||||
c->config.toggle_flag(Client::Flag::SWITCH_ASSIST_ENABLED);
|
||||
send_text_message_printf(c, "$C6Switch assist %s",
|
||||
c->options.switch_assist ? "enabled" : "disabled");
|
||||
c->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED) ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static void proxy_command_switch_assist(shared_ptr<ProxyServer::LinkedSession> ses, const std::string&) {
|
||||
auto s = ses->require_server_state();
|
||||
check_proxy_cheats_allowed(s);
|
||||
ses->options.switch_assist = !ses->options.switch_assist;
|
||||
ses->config.toggle_flag(Client::Flag::SWITCH_ASSIST_ENABLED);
|
||||
send_text_message_printf(ses->client_channel, "$C6Switch assist %s",
|
||||
ses->options.switch_assist ? "enabled" : "disabled");
|
||||
ses->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED) ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static void server_command_drop(shared_ptr<Client> c, const std::string&) {
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, true);
|
||||
check_is_leader(l, c);
|
||||
if (l->flags & Lobby::Flag::CANNOT_CHANGE_DROPS_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::CANNOT_CHANGE_DROPS_ENABLED)) {
|
||||
send_text_message(c, "Drop mode cannot\nbe changed on this\nserver");
|
||||
} else {
|
||||
l->flags ^= Lobby::Flag::DROPS_ENABLED;
|
||||
send_text_message_printf(l, "Drops %s", (l->flags & Lobby::Flag::DROPS_ENABLED) ? "enabled" : "disabled");
|
||||
l->toggle_flag(Lobby::Flag::DROPS_ENABLED);
|
||||
send_text_message_printf(l, "Drops %s", l->check_flag(Lobby::Flag::DROPS_ENABLED) ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1244,11 +1255,11 @@ static void server_command_itemtable(shared_ptr<Client> c, const std::string&) {
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, true);
|
||||
check_is_leader(l, c);
|
||||
if (l->flags & Lobby::Flag::CANNOT_CHANGE_ITEM_TABLE) {
|
||||
if (l->check_flag(Lobby::Flag::CANNOT_CHANGE_ITEM_TABLE)) {
|
||||
send_text_message(c, "Cannot switch item\ntables on this\nserver");
|
||||
} else if (l->base_version == GameVersion::BB) {
|
||||
send_text_message(c, "Cannot use client\nitem table on BB");
|
||||
} else if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
} else if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
send_text_message(c, "Cannot use server\nitem tables if item\ntracking is off");
|
||||
} else if (l->item_creator) {
|
||||
l->item_creator.reset();
|
||||
@@ -1312,7 +1323,7 @@ static void proxy_command_item(shared_ptr<ProxyServer::LinkedSession> ses, const
|
||||
}
|
||||
|
||||
static void server_command_enable_ep3_battle_debug_menu(shared_ptr<Client> c, const std::string& args) {
|
||||
if (!c->options.debug) {
|
||||
if (!c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message(c, "$C6This command can only\nbe run in debug mode\n(run %sdebug first)");
|
||||
return;
|
||||
}
|
||||
|
||||
+119
-67
@@ -17,31 +17,119 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
const uint64_t CLIENT_CONFIG_MAGIC = 0x492A890E82AC9839;
|
||||
const uint64_t CLIENT_CONFIG_MAGIC = 0x8399AC32;
|
||||
|
||||
static atomic<uint64_t> next_id(1);
|
||||
|
||||
ClientOptions::ClientOptions()
|
||||
: switch_assist(false),
|
||||
infinite_hp(false),
|
||||
infinite_tp(false),
|
||||
debug(false),
|
||||
override_section_id(-1),
|
||||
override_lobby_event(-1),
|
||||
override_lobby_number(-1),
|
||||
override_random_seed(-1),
|
||||
save_files(false),
|
||||
enable_chat_commands(true),
|
||||
enable_chat_filter(true),
|
||||
enable_player_notifications(false),
|
||||
suppress_client_pings(false),
|
||||
suppress_remote_login(false),
|
||||
zero_remote_guild_card(false),
|
||||
ep3_infinite_meseta(false),
|
||||
ep3_infinite_time(false),
|
||||
red_name(false),
|
||||
blank_name(false),
|
||||
function_call_return_value(-1) {}
|
||||
void Client::Config::set_flags_for_version(GameVersion 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)
|
||||
switch (version) {
|
||||
case GameVersion::DC:
|
||||
this->set_flag(Flag::NO_D6);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
case GameVersion::GC:
|
||||
break;
|
||||
case GameVersion::XB:
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
case GameVersion::PC:
|
||||
this->set_flag(Flag::NO_D6);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
case GameVersion::PATCH:
|
||||
this->set_flag(Flag::NO_D6);
|
||||
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
|
||||
break;
|
||||
case GameVersion::BB:
|
||||
this->set_flag(Flag::NO_D6);
|
||||
this->set_flag(Flag::SAVE_ENABLED);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
default:
|
||||
throw logic_error("invalid game version");
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x20: // DCNTE, possibly also DCv1 JP
|
||||
case 0x21: // DCv1 US
|
||||
this->set_flag(Flag::IS_DC_V1);
|
||||
this->set_flag(Flag::NO_D6);
|
||||
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
|
||||
// In the case of DCNTE, the IS_DC_TRIAL_EDITION flag is already set when
|
||||
// we get here
|
||||
break;
|
||||
case 0x23: // DCv1 EU
|
||||
this->set_flag(Flag::IS_DC_V1);
|
||||
this->set_flag(Flag::NO_D6);
|
||||
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
|
||||
break;
|
||||
case 0x25: // DCv2 JP
|
||||
case 0x26: // DCv2 US
|
||||
case 0x28: // DCv2 EU
|
||||
this->set_flag(Flag::NO_D6);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
case 0x29: // PC
|
||||
this->set_flag(Flag::NO_D6);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 JP v1.02, at least one version of PSO XB
|
||||
case 0x31: // GC Ep1&2 US v1.00, GC US v1.01, GC EU v1.00, GC JP v1.00
|
||||
case 0x34: // GC Ep1&2 JP v1.03
|
||||
// In the case of GC Trial Edition, the IS_GC_TRIAL_EDITION flag is
|
||||
// already set when we get here (because the client has used V2 encryption
|
||||
// instead of V3)
|
||||
break;
|
||||
case 0x32: // GC Ep1&2 EU 50Hz
|
||||
case 0x33: // GC Ep1&2 EU 60Hz
|
||||
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
|
||||
break;
|
||||
case 0x35: // GC Ep1&2 JP v1.04 (Plus)
|
||||
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
|
||||
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
case 0x36: // GC Ep1&2 US v1.02 (Plus)
|
||||
case 0x39: // GC Ep1&2 JP v1.05 (Plus)
|
||||
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
|
||||
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
|
||||
break;
|
||||
case 0x40: // GC Ep3 JP and Trial Edition
|
||||
// sub_version can't be used to tell JP final and Trial Edition apart; we
|
||||
// instead look at header.flag in the 61 command.
|
||||
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
|
||||
this->set_flag(Flag::IS_EPISODE_3);
|
||||
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
case 0x42: // Also GC Ep3 JP?
|
||||
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
|
||||
this->set_flag(Flag::IS_EPISODE_3);
|
||||
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
case 0x41: // GC Ep3 US
|
||||
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
|
||||
this->set_flag(Flag::IS_EPISODE_3);
|
||||
this->set_flag(Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL);
|
||||
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
break;
|
||||
case 0x43: // GC Ep3 EU
|
||||
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
|
||||
this->set_flag(Flag::IS_EPISODE_3);
|
||||
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
|
||||
break;
|
||||
default:
|
||||
throw runtime_error(string_printf("unknown sub_version %" PRIX64, sub_version));
|
||||
}
|
||||
}
|
||||
|
||||
Client::Client(
|
||||
shared_ptr<Server> server,
|
||||
@@ -51,16 +139,12 @@ Client::Client(
|
||||
: server(server),
|
||||
id(next_id++),
|
||||
log(string_printf("[C-%" PRIX64 "] ", this->id), client_log.min_level),
|
||||
bb_game_state(0),
|
||||
flags(flags_for_version(version, -1)),
|
||||
specific_version(default_specific_version_for_version(version, -1)),
|
||||
channel(bev, version, 1, nullptr, nullptr, this, string_printf("C-%" PRIX64, this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN),
|
||||
server_behavior(server_behavior),
|
||||
should_disconnect(false),
|
||||
should_send_to_lobby_server(false),
|
||||
should_send_to_proxy_server(false),
|
||||
proxy_destination_address(0),
|
||||
proxy_destination_port(0),
|
||||
bb_connection_phase(0xFF),
|
||||
x(0.0f),
|
||||
z(0.0f),
|
||||
area(0),
|
||||
@@ -89,6 +173,10 @@ Client::Client(
|
||||
can_chat(true),
|
||||
pending_bb_save_player_index(0),
|
||||
dol_base_addr(0) {
|
||||
|
||||
this->config.set_flags_for_version(version, -1);
|
||||
this->config.specific_version = default_specific_version_for_version(version, -1);
|
||||
|
||||
this->last_switch_enabled_command.header.subcommand = 0;
|
||||
memset(&this->next_connection_addr, 0, sizeof(this->next_connection_addr));
|
||||
|
||||
@@ -122,9 +210,9 @@ void Client::reschedule_ping_and_timeout_events() {
|
||||
QuestScriptVersion Client::quest_version() const {
|
||||
switch (this->version()) {
|
||||
case GameVersion::DC:
|
||||
if (this->flags & Flag::IS_DC_TRIAL_EDITION) {
|
||||
if (this->config.check_flag(Flag::IS_DC_TRIAL_EDITION)) {
|
||||
return QuestScriptVersion::DC_NTE;
|
||||
} else if (this->flags & Flag::IS_DC_V1) {
|
||||
} else if (this->config.check_flag(Flag::IS_DC_V1)) {
|
||||
return QuestScriptVersion::DC_V1;
|
||||
} else {
|
||||
return QuestScriptVersion::DC_V2;
|
||||
@@ -132,9 +220,9 @@ QuestScriptVersion Client::quest_version() const {
|
||||
case GameVersion::PC:
|
||||
return QuestScriptVersion::PC_V2;
|
||||
case GameVersion::GC:
|
||||
if (this->flags & Flag::IS_GC_TRIAL_EDITION) {
|
||||
if (this->config.check_flag(Flag::IS_GC_TRIAL_EDITION)) {
|
||||
return QuestScriptVersion::GC_NTE;
|
||||
} else if (this->flags & Flag::IS_EPISODE_3) {
|
||||
} else if (this->config.check_flag(Flag::IS_EPISODE_3)) {
|
||||
return QuestScriptVersion::GC_EP3;
|
||||
} else {
|
||||
return QuestScriptVersion::GC_V3;
|
||||
@@ -172,42 +260,6 @@ shared_ptr<Lobby> Client::require_lobby() const {
|
||||
return l;
|
||||
}
|
||||
|
||||
ClientConfig Client::export_config() const {
|
||||
ClientConfig cc;
|
||||
cc.magic = CLIENT_CONFIG_MAGIC;
|
||||
cc.flags = this->flags;
|
||||
cc.specific_version = this->specific_version;
|
||||
cc.proxy_destination_address = this->proxy_destination_address;
|
||||
cc.proxy_destination_port = this->proxy_destination_port;
|
||||
cc.unused.clear(0xFF);
|
||||
return cc;
|
||||
}
|
||||
|
||||
ClientConfigBB Client::export_config_bb() const {
|
||||
ClientConfigBB cc;
|
||||
cc.cfg = this->export_config();
|
||||
cc.bb_game_state = this->bb_game_state;
|
||||
cc.bb_player_index = this->game_data.bb_player_index;
|
||||
cc.unused.clear(0xFF);
|
||||
return cc;
|
||||
}
|
||||
|
||||
void Client::import_config(const ClientConfig& cc) {
|
||||
if (cc.magic != CLIENT_CONFIG_MAGIC) {
|
||||
throw invalid_argument("invalid client config");
|
||||
}
|
||||
this->flags = cc.flags;
|
||||
this->specific_version = cc.specific_version;
|
||||
this->proxy_destination_address = cc.proxy_destination_address;
|
||||
this->proxy_destination_port = cc.proxy_destination_port;
|
||||
}
|
||||
|
||||
void Client::import_config(const ClientConfigBB& cc) {
|
||||
this->import_config(cc.cfg);
|
||||
this->bb_game_state = cc.bb_game_state;
|
||||
this->game_data.bb_player_index = cc.bb_player_index;
|
||||
}
|
||||
|
||||
void Client::dispatch_save_game_data(evutil_socket_t, short, void* ctx) {
|
||||
reinterpret_cast<Client*>(ctx)->save_game_data();
|
||||
}
|
||||
|
||||
+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);
|
||||
|
||||
+57
-80
@@ -88,44 +88,6 @@
|
||||
// - - $r: Right arrow
|
||||
// - - $u: Up arrow
|
||||
|
||||
// This is the format of newserv's security data, which we call the client
|
||||
// config. This data is opaque to the client, so this structure is not
|
||||
// technically part of the PSO protocol. Because it is opaque to the client, we
|
||||
// can use the server's native-endian types instead of being explicit as we do
|
||||
// for all the other structs in this file.
|
||||
enum ClientStateBB : uint8_t {
|
||||
// Initial connection; server will redirect client to another port
|
||||
INITIAL_LOGIN = 0x00,
|
||||
// Second connection; server will send client game data and account data
|
||||
DOWNLOAD_DATA = 0x01,
|
||||
// Third connection; client will show the choose character menu
|
||||
CHOOSE_PLAYER = 0x02,
|
||||
// Fourth connection; used for saving characters only. If you do not create a
|
||||
// character, the server sets this state during the third connection so this
|
||||
// connection is effectively skipped.
|
||||
SAVE_PLAYER = 0x03,
|
||||
// Fifth connection; redirects client to login server
|
||||
SHIP_SELECT = 0x04,
|
||||
// All other connections
|
||||
IN_GAME = 0x05,
|
||||
};
|
||||
|
||||
struct ClientConfig {
|
||||
uint64_t magic = 0;
|
||||
uint32_t flags = 0;
|
||||
uint32_t specific_version = 0;
|
||||
uint32_t proxy_destination_address = 0;
|
||||
uint16_t proxy_destination_port = 0;
|
||||
parray<uint8_t, 0x0A> unused;
|
||||
} __packed__;
|
||||
|
||||
struct ClientConfigBB {
|
||||
ClientConfig cfg;
|
||||
uint8_t bb_game_state = 0;
|
||||
uint8_t bb_player_index = 0;
|
||||
parray<uint8_t, 0x06> unused;
|
||||
} __packed__;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// PATCH SERVER COMMANDS ///////////////////////////////////////////////////////
|
||||
@@ -478,8 +440,7 @@ struct C_LegacyLogin_BB_04 {
|
||||
// config set by the E6 command (and returned in the 93 command). In most cases,
|
||||
// E6 should be used for BB clients instead of 04.
|
||||
|
||||
template <typename ClientConfigT>
|
||||
struct S_UpdateClientConfig {
|
||||
struct S_UpdateClientConfig_DC_PC_04 {
|
||||
// Note: What we call player_tag here is actually three fields: two uint8_ts
|
||||
// followed by a le_uint16_t. It's unknown what the uint8_t fields are for
|
||||
// (they seem to always be zero), but the le_uint16_t is likely a boolean
|
||||
@@ -489,16 +450,20 @@ struct S_UpdateClientConfig {
|
||||
// player is present and zero when none is present.
|
||||
le_uint32_t player_tag = 0x00010000;
|
||||
le_uint32_t guild_card_number = 0;
|
||||
// The ClientConfig structure describes how newserv uses this command; other
|
||||
// servers do not use the same format for the following 0x20 or 0x28 bytes (or
|
||||
// may not use it at all). The cfg field is opaque to the client; it will send
|
||||
// back the contents verbatim in its next 9E command (or on request via 9F).
|
||||
ClientConfigT cfg;
|
||||
} __packed__;
|
||||
|
||||
struct S_UpdateClientConfig_DC_PC_V3_04 : S_UpdateClientConfig<ClientConfig> {
|
||||
struct S_UpdateClientConfig_V3_04 {
|
||||
le_uint32_t player_tag = 0x00010000;
|
||||
le_uint32_t guild_card_number = 0;
|
||||
// This field is opaque to the client; it will send back the contents verbatim
|
||||
// in its next 9E command (or on request via 9F).
|
||||
parray<uint8_t, 0x20> client_config;
|
||||
} __packed__;
|
||||
struct S_UpdateClientConfig_BB_04 : S_UpdateClientConfig<ClientConfigBB> {
|
||||
|
||||
struct S_UpdateClientConfig_BB_04 {
|
||||
le_uint32_t player_tag = 0x00010000;
|
||||
le_uint32_t guild_card_number = 0;
|
||||
parray<uint8_t, 0x28> client_config;
|
||||
} __packed__;
|
||||
|
||||
// 05: Disconnect
|
||||
@@ -530,7 +495,7 @@ struct S_UpdateClientConfig_BB_04 : S_UpdateClientConfig<ClientConfigBB> {
|
||||
// the chat string accidentally.) We call this byte private_flags in the places
|
||||
// where newserv uses it.
|
||||
|
||||
// 07 (S->C): Ship select menu
|
||||
// 07 (S->C): Ship or block select menu
|
||||
// Internal name: RcvDirList
|
||||
// This command triggers a general form of blocking menu, which was used for
|
||||
// both the ship select and block select menus by Sega (and all other private
|
||||
@@ -539,6 +504,8 @@ struct S_UpdateClientConfig_BB_04 : S_UpdateClientConfig<ClientConfigBB> {
|
||||
// there was a separate command to send the block list, but it was scrapped.
|
||||
// Perhaps this was used for command A1, which is identical to 07 and A0 in all
|
||||
// versions of PSO (except DC NTE).
|
||||
// The menu is titles "Ship Select" unless the first menu item begins with the
|
||||
// text "BLOCK" (all caps), in which case it is titled "Block Select".
|
||||
|
||||
// Command is a list of these; header.flag is the entry count. The first entry
|
||||
// is not included in the count and does not appear on the client. The text of
|
||||
@@ -860,17 +827,20 @@ struct S_ReconnectSplit_19 {
|
||||
// 21: GameGuard control (old versions of BB)
|
||||
// Format unknown
|
||||
|
||||
// 22: GameGuard check (BB)
|
||||
// 0022: GameGuard check (BB)
|
||||
|
||||
// Command 0022 is a 16-byte challenge (sent in the data field) using the
|
||||
// following structure.
|
||||
|
||||
struct SC_GameCardCheck_BB_0022 {
|
||||
struct SC_GameGuardCheck_BB_0022 {
|
||||
parray<le_uint32_t, 4> data;
|
||||
} __packed__;
|
||||
|
||||
// Command 0122 uses a 4-byte challenge sent in the header.flag field instead.
|
||||
// This version of the command has no other arguments.
|
||||
// 0122 (C->S): Time deviation (BB)
|
||||
// This command is sent when the client executes a quest opcode 5D (gettime) and
|
||||
// the returned timestamp is before the previous timestamp returned, but not by
|
||||
// too much - it seems the game only considers deltas between 3 seconds and 30
|
||||
// minutes suspicious for these purposes.
|
||||
|
||||
// 23 (S->C): Momoka Item Exchange result (BB)
|
||||
// Sent in response to a 6xD9 command from the client.
|
||||
@@ -1693,7 +1663,18 @@ struct C_LoginExtendedV1_DC_93 : C_LoginV1_DC_93 {
|
||||
struct C_Login_BB_93 {
|
||||
le_uint32_t player_tag = 0x00010000;
|
||||
le_uint32_t guild_card_number = 0;
|
||||
pstring<TextEncoding::ASCII, 0x08> unused;
|
||||
le_uint32_t sub_version = 0;
|
||||
uint8_t language;
|
||||
uint8_t character_slot;
|
||||
// Values for connection_phase:
|
||||
// 00 - initial connection (client will request system file, characters, etc.)
|
||||
// 01 - choose character
|
||||
// 02 - create character
|
||||
// 03 - apply updates from dressing room
|
||||
// 04 - login server
|
||||
// 05 - lobby server (and beyond)
|
||||
uint8_t connection_phase;
|
||||
uint8_t client_code;
|
||||
le_uint32_t team_id = 0;
|
||||
pstring<TextEncoding::ASCII, 0x30> username;
|
||||
pstring<TextEncoding::ASCII, 0x30> password;
|
||||
@@ -1711,16 +1692,10 @@ struct C_Login_BB_93 {
|
||||
// the client config starts 8 bytes earlier on those versions and the entire
|
||||
// command is 8 bytes shorter, hence this odd-looking union.
|
||||
union VariableLengthSection {
|
||||
union ClientConfigFields {
|
||||
ClientConfigBB cfg;
|
||||
pstring<TextEncoding::ASCII, 0x28> version_string;
|
||||
parray<le_uint32_t, 10> as_u32;
|
||||
} __packed__;
|
||||
|
||||
ClientConfigFields old_clients_cfg;
|
||||
parray<uint8_t, 0x28> old_client_config;
|
||||
struct NewFormat {
|
||||
parray<le_uint32_t, 2> hardware_info;
|
||||
ClientConfigFields cfg;
|
||||
parray<uint8_t, 0x28> client_config;
|
||||
} __packed__ new_clients;
|
||||
} __packed__ var;
|
||||
} __packed__;
|
||||
@@ -1909,11 +1884,7 @@ struct C_LoginExtended_PC_9D : C_Login_DC_PC_GC_9D {
|
||||
// header.flag is 1 if the client has UDP disabled.
|
||||
|
||||
struct C_Login_GC_9E : C_Login_DC_PC_GC_9D {
|
||||
union ClientConfigFields {
|
||||
ClientConfig cfg;
|
||||
parray<uint8_t, 0x20> data;
|
||||
ClientConfigFields() : data() {}
|
||||
} __packed__ client_config;
|
||||
parray<uint8_t, 0x20> client_config;
|
||||
} __packed__;
|
||||
struct C_LoginExtended_GC_9E : C_Login_GC_9E {
|
||||
SC_MeetUserExtension<TextEncoding::MARKED> extension;
|
||||
@@ -1951,10 +1922,16 @@ struct C_LoginExtended_BB_9E {
|
||||
|
||||
// 9F (C->S): Client config / security data response (V3/BB)
|
||||
// The data is opaque to the client, as described at the top of this file.
|
||||
// If newserv ever sent a 9F command (it currently does not), the response
|
||||
// format here would be ClientConfig (0x20 bytes) on V3, or ClientConfigBB (0x28
|
||||
// bytes) on BB. However, on BB, this returns the client config that was set by
|
||||
// a preceding 04 command, not the config set by a preceding E6 command.
|
||||
// If newserv ever sent a 9F command (it currently does not). On BB, this
|
||||
// command does not work during the data server phase.
|
||||
|
||||
struct C_ClientConfig_V3_9F {
|
||||
parray<uint8_t, 0x20> data;
|
||||
} __packed__;
|
||||
|
||||
struct C_ClientConfig_BB_9F {
|
||||
parray<uint8_t, 0x28> data;
|
||||
} __packed__;
|
||||
|
||||
// A0 (C->S): Change ship
|
||||
// Internal name: SndShipList
|
||||
@@ -2806,7 +2783,7 @@ struct S_TournamentList_GC_Ep3_E0 {
|
||||
parray<Entry, 0x20> entries;
|
||||
} __packed__;
|
||||
|
||||
// E0 (C->S): Request team and key config (BB)
|
||||
// E0 (C->S): Request system file (BB)
|
||||
// No arguments. The server should respond with an E1 or E2 command.
|
||||
|
||||
// E1 (S->C): Game information (Episode 3)
|
||||
@@ -2830,10 +2807,10 @@ struct S_GameInformation_GC_Ep3_E1 {
|
||||
/* 0298 */
|
||||
} __packed__;
|
||||
|
||||
// E1 (S->C): Team and key config missing? (BB)
|
||||
// E1 (S->C): Create system file (BB)
|
||||
// This seems to take the place of 00E2 in certain cases. Perhaps it was used
|
||||
// when a client hadn't logged in before and didn't have a team or key config,
|
||||
// so the client should use appropriate defaults.
|
||||
// when a client hadn't logged in before and didn't have a system file, so the
|
||||
// client should use appropriate defaults.
|
||||
|
||||
struct S_TeamAndKeyConfigMissing_00E1_BB {
|
||||
// If success is not equal to 1, the client shows a message saying "Forced
|
||||
@@ -3033,16 +3010,16 @@ struct C_JoinSpectatorTeam_GC_Ep3_E6_Flag01 {
|
||||
// Same format as 08 command.
|
||||
|
||||
// E6 (S->C): Set guild card number and update client config (BB)
|
||||
// BB clients have multiple client configs. This command sets the client config
|
||||
// that is returned by the 93 commands, but does not affect the client config
|
||||
// set by the 04 command (and returned in the 9E and 9F commands).
|
||||
// This command sets the player's guild card number. During the data server
|
||||
// phase, it also sets the client config and enabled features (these fields are
|
||||
// ignored during the game server phase).
|
||||
|
||||
struct S_ClientInit_BB_00E6 {
|
||||
le_uint32_t error = 0;
|
||||
le_uint32_t error_code = 0;
|
||||
le_uint32_t player_tag = 0x00010000;
|
||||
le_uint32_t guild_card_number = 0;
|
||||
le_uint32_t team_id = 0;
|
||||
ClientConfigBB cfg;
|
||||
parray<uint8_t, 0x28> client_config;
|
||||
uint8_t can_create_team = 1;
|
||||
uint8_t episode_4_unlocked = 1;
|
||||
parray<uint8_t, 2> unused;
|
||||
@@ -3325,7 +3302,7 @@ struct C_PromoteTeamMember_BB_11EA {
|
||||
|
||||
struct S_TeamMembershipInformation_BB_12EA {
|
||||
le_uint32_t unknown_a1 = 0; // Command is ignored unless this is 0
|
||||
le_uint32_t guild_card_number = 0;
|
||||
le_uint32_t guild_card_number = 0; // Team membership ID?
|
||||
le_uint32_t team_id = 0;
|
||||
le_uint32_t unknown_a4 = 0;
|
||||
le_uint32_t privilege_level = 0;
|
||||
|
||||
@@ -2349,7 +2349,7 @@ void Server::send_6xB6x41_to_all_clients() const {
|
||||
}
|
||||
if (map_commands_by_language[c->language()].empty()) {
|
||||
map_commands_by_language[c->language()] = this->prepare_6xB6x41_map_definition(
|
||||
this->last_chosen_map, c->language(), l->flags & Lobby::Flag::IS_EP3_TRIAL);
|
||||
this->last_chosen_map, c->language(), l->check_flag(Lobby::Flag::IS_EP3_TRIAL));
|
||||
}
|
||||
this->log().info("Sending %c version of map %08" PRIX32, char_for_language_code(c->language()), this->last_chosen_map->map_number);
|
||||
send_command(c, 0x6C, 0x00, map_commands_by_language[c->language()]);
|
||||
|
||||
@@ -702,8 +702,8 @@ void Tournament::send_all_state_updates() const {
|
||||
// with this instance of the tournament - an intervening shell command
|
||||
// `reload ep3` could have changed the client's linkage
|
||||
if (c &&
|
||||
(c->flags & Client::Flag::IS_EPISODE_3) &&
|
||||
!(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION) &&
|
||||
c->config.check_flag(Client::Flag::IS_EPISODE_3) &&
|
||||
!c->config.check_flag(Client::Flag::IS_EP3_TRIAL_EDITION) &&
|
||||
(c->ep3_tournament_team.lock() == team)) {
|
||||
send_ep3_confirm_tournament_entry(c, this->shared_from_this());
|
||||
}
|
||||
@@ -716,8 +716,8 @@ void Tournament::send_all_state_updates_on_deletion() const {
|
||||
for (const auto& player : team->players) {
|
||||
auto c = player.client.lock();
|
||||
if (c &&
|
||||
(c->flags & Client::Flag::IS_EPISODE_3) &&
|
||||
!(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION) &&
|
||||
c->config.check_flag(Client::Flag::IS_EPISODE_3) &&
|
||||
!c->config.check_flag(Client::Flag::IS_EP3_TRIAL_EDITION) &&
|
||||
(c->ep3_tournament_team.lock() == team)) {
|
||||
send_ep3_confirm_tournament_entry(c, nullptr);
|
||||
}
|
||||
@@ -916,7 +916,7 @@ shared_ptr<Tournament::Team> TournamentIndex::team_for_serial_number(uint32_t se
|
||||
}
|
||||
|
||||
void TournamentIndex::link_client(shared_ptr<Client> c) {
|
||||
if (!(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (!c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -927,7 +927,7 @@ void TournamentIndex::link_client(shared_ptr<Client> c) {
|
||||
if (player.serial_number == c->license->serial_number) {
|
||||
c->ep3_tournament_team = team;
|
||||
player.client = c;
|
||||
if (!(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)) {
|
||||
if (!c->config.check_flag(Client::Flag::IS_EP3_TRIAL_EDITION)) {
|
||||
send_ep3_confirm_tournament_entry(c, tourn);
|
||||
}
|
||||
return;
|
||||
@@ -936,7 +936,7 @@ void TournamentIndex::link_client(shared_ptr<Client> c) {
|
||||
throw logic_error("tournament team found for player, but player not found on team");
|
||||
} else {
|
||||
c->ep3_tournament_team.reset();
|
||||
if (!(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)) {
|
||||
if (!c->config.check_flag(Client::Flag::IS_EP3_TRIAL_EDITION)) {
|
||||
send_ep3_confirm_tournament_entry(c, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-11
@@ -29,7 +29,7 @@ Lobby::Lobby(shared_ptr<ServerState> s, uint32_t id)
|
||||
block(0),
|
||||
leader_id(0),
|
||||
max_clients(12),
|
||||
flags(0) {
|
||||
enabled_flags(0) {
|
||||
for (size_t x = 0; x < 12; x++) {
|
||||
this->next_item_id[x] = 0x00010000 + 0x00200000 * x;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ void Lobby::create_ep3_server() {
|
||||
this->log.info("Recreating Episode 3 server state");
|
||||
}
|
||||
auto tourn = this->tournament_match ? this->tournament_match->tournament.lock() : nullptr;
|
||||
bool is_trial = (this->flags & Lobby::Flag::IS_EP3_TRIAL);
|
||||
bool is_trial = this->check_flag(Lobby::Flag::IS_EP3_TRIAL);
|
||||
Episode3::Server::Options options = {
|
||||
.card_index = is_trial ? s->ep3_card_index_trial : s->ep3_card_index,
|
||||
.map_index = s->ep3_map_index,
|
||||
@@ -111,10 +111,13 @@ void Lobby::reassign_leader_on_client_departure(size_t leaving_client_index) {
|
||||
|
||||
bool Lobby::any_client_loading() const {
|
||||
for (size_t x = 0; x < this->max_clients; x++) {
|
||||
if (!this->clients[x].get()) {
|
||||
auto lc = this->clients[x];
|
||||
if (!lc.get()) {
|
||||
continue;
|
||||
}
|
||||
if (this->clients[x]->flags & (Client::Flag::LOADING | Client::Flag::LOADING_QUEST | Client::Flag::LOADING_RUNNING_QUEST)) {
|
||||
if (lc->config.check_flag(Client::Flag::LOADING) ||
|
||||
lc->config.check_flag(Client::Flag::LOADING_QUEST) ||
|
||||
lc->config.check_flag(Client::Flag::LOADING_RUNNING_JOINABLE_QUEST)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -133,7 +136,7 @@ size_t Lobby::count_clients() const {
|
||||
|
||||
void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
|
||||
ssize_t index;
|
||||
ssize_t min_client_id = (this->flags & Lobby::Flag::IS_SPECTATOR_TEAM) ? 4 : 0;
|
||||
ssize_t min_client_id = this->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM) ? 4 : 0;
|
||||
|
||||
if (required_client_id >= 0) {
|
||||
if (this->clients[required_client_id].get()) {
|
||||
@@ -142,7 +145,7 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
|
||||
this->clients[required_client_id] = c;
|
||||
index = required_client_id;
|
||||
|
||||
} else if (c->options.debug) {
|
||||
} else if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
for (index = max_clients - 1; index >= min_client_id; index--) {
|
||||
if (!this->clients[index].get()) {
|
||||
this->clients[index] = c;
|
||||
@@ -180,7 +183,7 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
|
||||
|
||||
// If the lobby is a game and item tracking is enabled, assign the inventory's
|
||||
// item IDs
|
||||
if (this->is_game() && (this->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (this->is_game() && this->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto s = this->require_server_state();
|
||||
auto p = c->game_data.player();
|
||||
auto& inv = p->inventory;
|
||||
@@ -196,7 +199,7 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
|
||||
auto p = c->game_data.player();
|
||||
PlayerLobbyDataDCGC lobby_data;
|
||||
lobby_data.player_tag = 0x00010000;
|
||||
lobby_data.guild_card = c->license->serial_number;
|
||||
lobby_data.guild_card_number = c->license->serial_number;
|
||||
lobby_data.name.encode(p->disp.name.decode(c->language()), c->language());
|
||||
this->battle_record->add_player(
|
||||
lobby_data,
|
||||
@@ -207,7 +210,7 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
|
||||
|
||||
// Send spectator count notifications if needed
|
||||
if (this->is_game() && this->is_ep3()) {
|
||||
if (this->flags & Lobby::Flag::IS_SPECTATOR_TEAM) {
|
||||
if (this->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
auto watched_l = this->watched_lobby.lock();
|
||||
if (watched_l) {
|
||||
send_ep3_update_game_metadata(watched_l);
|
||||
@@ -247,7 +250,7 @@ void Lobby::remove_client(shared_ptr<Client> c) {
|
||||
|
||||
// If the lobby is Episode 3, update the appropriate spectator counts
|
||||
if (this->is_game() && this->is_ep3()) {
|
||||
if (this->flags & Lobby::Flag::IS_SPECTATOR_TEAM) {
|
||||
if (this->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
auto watched_l = this->watched_lobby.lock();
|
||||
if (watched_l) {
|
||||
send_ep3_update_game_metadata(watched_l);
|
||||
@@ -271,7 +274,7 @@ void Lobby::move_client_to_lobby(
|
||||
throw out_of_range("required slot is in use");
|
||||
}
|
||||
} else {
|
||||
ssize_t min_client_id = (this->flags & Lobby::Flag::IS_SPECTATOR_TEAM) ? 4 : 0;
|
||||
ssize_t min_client_id = this->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM) ? 4 : 0;
|
||||
size_t available_slots = dest_lobby->max_clients - min_client_id;
|
||||
if (dest_lobby->count_clients() >= available_slots) {
|
||||
throw out_of_range("no space left in lobby");
|
||||
|
||||
+19
-6
@@ -24,7 +24,7 @@
|
||||
struct ServerState;
|
||||
|
||||
struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
enum Flag {
|
||||
enum class Flag {
|
||||
GAME = 0x00000001,
|
||||
PERSISTENT = 0x00000002,
|
||||
|
||||
@@ -113,7 +113,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
uint8_t block;
|
||||
uint8_t leader_id;
|
||||
uint8_t max_clients;
|
||||
uint32_t flags;
|
||||
uint32_t enabled_flags;
|
||||
std::shared_ptr<const Quest> quest;
|
||||
std::array<std::shared_ptr<Client>, 12> clients;
|
||||
// Keys in this map are client_id
|
||||
@@ -125,18 +125,31 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
Lobby& operator=(const Lobby&) = delete;
|
||||
Lobby& operator=(Lobby&&) = delete;
|
||||
|
||||
[[nodiscard]] inline bool check_flag(Flag flag) const {
|
||||
return !!(this->enabled_flags & static_cast<uint32_t>(flag));
|
||||
}
|
||||
inline void set_flag(Flag flag) {
|
||||
this->enabled_flags |= static_cast<uint32_t>(flag);
|
||||
}
|
||||
inline void clear_flag(Flag flag) {
|
||||
this->enabled_flags &= (~static_cast<uint32_t>(flag));
|
||||
}
|
||||
inline void toggle_flag(Flag flag) {
|
||||
this->enabled_flags ^= static_cast<uint32_t>(flag);
|
||||
}
|
||||
|
||||
std::shared_ptr<ServerState> require_server_state() const;
|
||||
void create_item_creator();
|
||||
void create_ep3_server();
|
||||
|
||||
inline bool is_game() const {
|
||||
return this->flags & Flag::GAME;
|
||||
[[nodiscard]] inline bool is_game() const {
|
||||
return this->check_flag(Flag::GAME);
|
||||
}
|
||||
inline bool is_ep3() const {
|
||||
[[nodiscard]] inline bool is_ep3() const {
|
||||
return this->episode == Episode::EP3;
|
||||
}
|
||||
|
||||
inline bool version_is_allowed(QuestScriptVersion v) const {
|
||||
[[nodiscard]] inline bool version_is_allowed(QuestScriptVersion v) const {
|
||||
return this->allowed_versions & (1 << static_cast<size_t>(v));
|
||||
}
|
||||
inline void allow_version(QuestScriptVersion v) {
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ struct SavedAccountDataBB { // .nsa file format
|
||||
parray<le_uint32_t, 0x001E> blocked_senders;
|
||||
PSOBBGuildCardFile guild_card_file;
|
||||
PSOBBSystemFile system_file;
|
||||
le_uint32_t newserv_flags;
|
||||
le_uint32_t unused;
|
||||
le_uint32_t option_flags;
|
||||
parray<uint8_t, 0x0A40> shortcuts;
|
||||
parray<uint8_t, 0x04E0> symbol_chats;
|
||||
|
||||
@@ -237,7 +237,7 @@ void PlayerBank::save(const string& filename, bool save_to_filesystem) const {
|
||||
|
||||
void PlayerLobbyDataPC::clear() {
|
||||
this->player_tag = 0;
|
||||
this->guild_card = 0;
|
||||
this->guild_card_number = 0;
|
||||
this->ip_address = 0;
|
||||
this->client_id = 0;
|
||||
this->name.clear();
|
||||
@@ -245,7 +245,7 @@ void PlayerLobbyDataPC::clear() {
|
||||
|
||||
void PlayerLobbyDataDCGC::clear() {
|
||||
this->player_tag = 0;
|
||||
this->guild_card = 0;
|
||||
this->guild_card_number = 0;
|
||||
this->ip_address = 0;
|
||||
this->client_id = 0;
|
||||
this->name.clear();
|
||||
@@ -263,7 +263,7 @@ void XBNetworkLocation::clear() {
|
||||
|
||||
void PlayerLobbyDataXB::clear() {
|
||||
this->player_tag = 0;
|
||||
this->guild_card = 0;
|
||||
this->guild_card_number = 0;
|
||||
this->netloc.clear();
|
||||
this->client_id = 0;
|
||||
this->name.clear();
|
||||
@@ -271,12 +271,13 @@ void PlayerLobbyDataXB::clear() {
|
||||
|
||||
void PlayerLobbyDataBB::clear() {
|
||||
this->player_tag = 0;
|
||||
this->guild_card = 0;
|
||||
this->ip_address = 0;
|
||||
this->guild_card_number = 0;
|
||||
this->team_guild_card_number = 0;
|
||||
this->team_id = 0;
|
||||
this->unknown_a1.clear(0);
|
||||
this->client_id = 0;
|
||||
this->name.clear();
|
||||
this->unknown_a2 = 0;
|
||||
this->hide_help_prompt = 0;
|
||||
}
|
||||
|
||||
PlayerRecordsBB_Challenge::PlayerRecordsBB_Challenge(const PlayerRecordsDC_Challenge& rec)
|
||||
|
||||
+14
-12
@@ -238,7 +238,7 @@ struct GuildCardBB {
|
||||
|
||||
struct PlayerLobbyDataPC {
|
||||
le_uint32_t player_tag = 0;
|
||||
le_uint32_t guild_card = 0;
|
||||
le_uint32_t guild_card_number = 0;
|
||||
// There's a strange behavior (bug? "feature"?) in Episode 3 where the start
|
||||
// button does nothing in the lobby (hence you can't "quit game") if the
|
||||
// client's IP address is zero. So, we fill it in with a fake nonzero value to
|
||||
@@ -253,7 +253,7 @@ struct PlayerLobbyDataPC {
|
||||
|
||||
struct PlayerLobbyDataDCGC {
|
||||
le_uint32_t player_tag = 0;
|
||||
le_uint32_t guild_card = 0;
|
||||
le_uint32_t guild_card_number = 0;
|
||||
be_uint32_t ip_address = 0x7F000001;
|
||||
le_uint32_t client_id = 0;
|
||||
pstring<TextEncoding::ASCII, 0x10> name;
|
||||
@@ -275,7 +275,7 @@ struct XBNetworkLocation {
|
||||
|
||||
struct PlayerLobbyDataXB {
|
||||
le_uint32_t player_tag = 0;
|
||||
le_uint32_t guild_card = 0;
|
||||
le_uint32_t guild_card_number = 0;
|
||||
XBNetworkLocation netloc;
|
||||
le_uint32_t client_id = 0;
|
||||
pstring<TextEncoding::ASCII, 0x10> name;
|
||||
@@ -284,15 +284,17 @@ struct PlayerLobbyDataXB {
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PlayerLobbyDataBB {
|
||||
le_uint32_t player_tag = 0;
|
||||
le_uint32_t guild_card = 0;
|
||||
// This field is a guess; the official builds didn't use this, but all other
|
||||
// versions have it
|
||||
be_uint32_t ip_address = 0x7F000001;
|
||||
parray<uint8_t, 0x10> unknown_a1;
|
||||
le_uint32_t client_id = 0;
|
||||
pstring<TextEncoding::UTF16, 0x10> name;
|
||||
le_uint32_t unknown_a2 = 0;
|
||||
/* 00 */ le_uint32_t player_tag = 0;
|
||||
/* 04 */ le_uint32_t guild_card_number = 0;
|
||||
/* 08 */ le_uint32_t team_guild_card_number = 0;
|
||||
/* 0C */ le_uint32_t team_id = 0;
|
||||
/* 10 */ parray<uint8_t, 0x0C> unknown_a1;
|
||||
/* 1C */ le_uint32_t client_id = 0;
|
||||
/* 20 */ pstring<TextEncoding::UTF16, 0x10> name;
|
||||
// If this field is zero, the "Press F1 for help" prompt appears in the corner
|
||||
// of the screen in the lobby and on Pioneer 2.
|
||||
/* 40 */ le_uint32_t hide_help_prompt = 1;
|
||||
/* 44 */
|
||||
|
||||
void clear();
|
||||
} __attribute__((packed));
|
||||
|
||||
+101
-103
@@ -122,13 +122,13 @@ static HandlerResult C_05(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
}
|
||||
|
||||
static HandlerResult C_1D(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string&) {
|
||||
return ses->options.suppress_client_pings
|
||||
return ses->config.check_flag(Client::Flag::PROXY_SUPPRESS_CLIENT_PINGS)
|
||||
? HandlerResult::Type::SUPPRESS
|
||||
: HandlerResult::Type::FORWARD;
|
||||
}
|
||||
|
||||
static HandlerResult S_1D(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string&) {
|
||||
if (ses->options.suppress_client_pings) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SUPPRESS_CLIENT_PINGS)) {
|
||||
ses->server_channel.send(0x1D);
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
} else {
|
||||
@@ -139,13 +139,13 @@ static HandlerResult S_1D(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
static HandlerResult S_97(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t flag, string&) {
|
||||
// If the client has already received a 97 command, block this one and
|
||||
// immediately respond with a B1.
|
||||
if (ses->newserv_client_config.cfg.flags & Client::Flag::SAVE_ENABLED) {
|
||||
if (ses->config.check_flag(Client::Flag::SAVE_ENABLED)) {
|
||||
ses->server_channel.send(0xB1, 0x00);
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
} else {
|
||||
// Update the newserv client config so we'll know not to show the Programs
|
||||
// menu if they return to newserv
|
||||
ses->newserv_client_config.cfg.flags |= Client::Flag::SAVE_ENABLED;
|
||||
ses->config.set_flag(Client::Flag::PROXY_SUPPRESS_CLIENT_PINGS);
|
||||
// Trap any 97 command that would have triggered cheat protection, and
|
||||
// always send 97 01 04 00
|
||||
if (flag == 0) {
|
||||
@@ -156,11 +156,14 @@ static HandlerResult S_97(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
}
|
||||
|
||||
static HandlerResult C_G_9E(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string&) {
|
||||
if (ses->options.suppress_remote_login) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SUPPRESS_REMOTE_LOGIN)) {
|
||||
le_uint64_t checksum = random_object<uint64_t>() & 0x0000FFFFFFFFFFFF;
|
||||
ses->server_channel.send(0x96, 0x00, &checksum, sizeof(checksum));
|
||||
|
||||
S_UpdateClientConfig_DC_PC_V3_04 cmd = {{0x00010000, ses->license->serial_number, ClientConfig()}};
|
||||
S_UpdateClientConfig_V3_04 cmd;
|
||||
cmd.player_tag = 0x00010000;
|
||||
cmd.guild_card_number = ses->license->serial_number;
|
||||
cmd.client_config.clear(0xFF);
|
||||
ses->client_channel.send(0x04, 0x00, &cmd, sizeof(cmd));
|
||||
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
@@ -171,7 +174,7 @@ static HandlerResult C_G_9E(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t
|
||||
}
|
||||
|
||||
static HandlerResult S_G_9A(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string&) {
|
||||
if (!ses->license || ses->options.suppress_remote_login) {
|
||||
if (!ses->license || ses->config.check_flag(Client::Flag::PROXY_SUPPRESS_REMOTE_LOGIN)) {
|
||||
return HandlerResult::Type::FORWARD;
|
||||
}
|
||||
|
||||
@@ -192,12 +195,12 @@ static HandlerResult S_G_9A(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t
|
||||
cmd.access_key.encode(ses->license->access_key);
|
||||
cmd.serial_number2 = cmd.serial_number;
|
||||
cmd.access_key2 = cmd.access_key;
|
||||
if (ses->options.blank_name) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED)) {
|
||||
cmd.name.encode(" ", ses->language());
|
||||
} else {
|
||||
cmd.name.encode(ses->character_name, ses->language());
|
||||
}
|
||||
cmd.client_config.data = ses->remote_client_config_data;
|
||||
cmd.client_config = ses->remote_client_config_data;
|
||||
|
||||
// If there's a guild card number, a shorter 9E is sent that ends
|
||||
// right after the client config data
|
||||
@@ -272,7 +275,7 @@ static HandlerResult S_V123P_02_17(
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
|
||||
} else if ((ses->version() == GameVersion::DC) || (ses->version() == GameVersion::PC)) {
|
||||
if (ses->newserv_client_config.cfg.flags & Client::Flag::IS_DC_V1) {
|
||||
if (ses->config.check_flag(Client::Flag::IS_DC_V1)) {
|
||||
if (command == 0x17) {
|
||||
C_LoginV1_DC_PC_V3_90 cmd;
|
||||
cmd.serial_number.encode(string_printf("%08" PRIX32 "", ses->license->serial_number));
|
||||
@@ -342,7 +345,7 @@ static HandlerResult S_V123P_02_17(
|
||||
cmd.access_key.clear_after(8);
|
||||
cmd.serial_number2 = cmd.serial_number;
|
||||
cmd.access_key2 = cmd.access_key;
|
||||
if (ses->options.blank_name) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED)) {
|
||||
cmd.name.encode(" ", ses->language());
|
||||
} else {
|
||||
cmd.name.encode(ses->character_name);
|
||||
@@ -364,7 +367,7 @@ static HandlerResult S_V123P_02_17(
|
||||
ses->server_channel.send(0xDB, 0x00, &cmd, sizeof(cmd));
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
|
||||
} else if (ses->options.suppress_remote_login) {
|
||||
} else if (ses->config.check_flag(Client::Flag::PROXY_SUPPRESS_REMOTE_LOGIN)) {
|
||||
uint32_t guild_card_number;
|
||||
if (ses->remote_guild_card_number >= 0) {
|
||||
guild_card_number = ses->remote_guild_card_number;
|
||||
@@ -393,12 +396,12 @@ static HandlerResult S_V123P_02_17(
|
||||
cmd.access_key.encode(fake_access_key_str);
|
||||
cmd.serial_number2 = cmd.serial_number;
|
||||
cmd.access_key2 = cmd.access_key;
|
||||
if (ses->options.blank_name) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED)) {
|
||||
cmd.name.encode(" ", ses->language());
|
||||
} else {
|
||||
cmd.name.encode(ses->character_name, ses->language());
|
||||
}
|
||||
cmd.client_config.data = ses->remote_client_config_data;
|
||||
cmd.client_config = ses->remote_client_config_data;
|
||||
ses->server_channel.send(0x9E, 0x01, &cmd, sizeof(C_Login_GC_9E));
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
|
||||
@@ -474,7 +477,7 @@ static HandlerResult S_B_03(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t
|
||||
|
||||
static HandlerResult S_V123_04(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
// Suppress extremely short commands from the server instead of disconnecting.
|
||||
if (data.size() < offsetof(S_UpdateClientConfig_DC_PC_V3_04, cfg)) {
|
||||
if (data.size() < offsetof(S_UpdateClientConfig_V3_04, client_config)) {
|
||||
le_uint64_t checksum = random_object<uint64_t>() & 0x0000FFFFFFFFFFFF;
|
||||
ses->server_channel.send(0x96, 0x00, &checksum, sizeof(checksum));
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
@@ -482,9 +485,9 @@ static HandlerResult S_V123_04(shared_ptr<ProxyServer::LinkedSession> ses, uint1
|
||||
|
||||
// Some servers send a short 04 command if they don't use all of the 0x20
|
||||
// bytes available. We should be prepared to handle that.
|
||||
auto& cmd = check_size_t<S_UpdateClientConfig_DC_PC_V3_04>(data,
|
||||
offsetof(S_UpdateClientConfig_DC_PC_V3_04, cfg),
|
||||
sizeof(S_UpdateClientConfig_DC_PC_V3_04));
|
||||
auto& cmd = check_size_t<S_UpdateClientConfig_V3_04>(data,
|
||||
offsetof(S_UpdateClientConfig_V3_04, client_config),
|
||||
sizeof(S_UpdateClientConfig_V3_04));
|
||||
|
||||
// If this is a licensed session, hide the guild card number assigned by the
|
||||
// remote server so the client doesn't see it change. If this is an unlicensed
|
||||
@@ -515,8 +518,8 @@ static HandlerResult S_V123_04(shared_ptr<ProxyServer::LinkedSession> ses, uint1
|
||||
? "t Lobby Server. Copyright SEGA E"
|
||||
: "t Port Map. Copyright SEGA Enter",
|
||||
ses->remote_client_config_data.bytes());
|
||||
memcpy(ses->remote_client_config_data.data(), &cmd.cfg,
|
||||
min<size_t>(data.size() - offsetof(S_UpdateClientConfig_DC_PC_V3_04, cfg),
|
||||
memcpy(ses->remote_client_config_data.data(), &cmd.client_config,
|
||||
min<size_t>(data.size() - offsetof(S_UpdateClientConfig_V3_04, client_config),
|
||||
ses->remote_client_config_data.bytes()));
|
||||
|
||||
// If the guild card number was not set, pretend (to the server) that this is
|
||||
@@ -609,11 +612,11 @@ static HandlerResult S_B1(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
static HandlerResult S_B2(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t flag, string& data) {
|
||||
const auto& cmd = check_size_t<S_ExecuteCode_B2>(data, 0xFFFF);
|
||||
|
||||
if (cmd.code_size && ses->options.save_files) {
|
||||
if (cmd.code_size && ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
uint64_t filename_timestamp = now();
|
||||
string code = data.substr(sizeof(S_ExecuteCode_B2));
|
||||
|
||||
if (ses->newserv_client_config.cfg.flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
|
||||
if (ses->config.check_flag(Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL)) {
|
||||
StringReader r(code);
|
||||
bool is_big_endian = (ses->version() == GameVersion::GC || ses->version() == GameVersion::DC);
|
||||
uint32_t decompressed_size = is_big_endian ? r.get_u32b() : r.get_u32l();
|
||||
@@ -696,11 +699,11 @@ static HandlerResult S_B2(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ses->options.function_call_return_value >= 0) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_BLOCK_FUNCTION_CALLS)) {
|
||||
ses->log.info("Blocking function call from server");
|
||||
C_ExecuteCodeResult_B3 cmd;
|
||||
cmd.return_value = ses->options.function_call_return_value;
|
||||
cmd.checksum = 0;
|
||||
cmd.return_value = 0xFFFFFFFF;
|
||||
cmd.checksum = 0x00000000;
|
||||
ses->server_channel.send(0xB3, flag, &cmd, sizeof(cmd));
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
} else {
|
||||
@@ -727,7 +730,7 @@ static HandlerResult C_B3(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
}
|
||||
|
||||
static HandlerResult S_B_E7(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
if (ses->options.save_files) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
string output_filename = string_printf("player.%" PRId64 ".bin", now());
|
||||
save_file(output_filename, data);
|
||||
ses->log.info("Wrote player data to file %s", output_filename.c_str());
|
||||
@@ -856,7 +859,7 @@ static HandlerResult S_V3_1A_D5(shared_ptr<ProxyServer::LinkedSession> ses, uint
|
||||
// has the no-close-confirmation flag set in its newserv client config, send a
|
||||
// fake confirmation to the remote server immediately.
|
||||
if (((ses->version() == GameVersion::GC) || (ses->version() == GameVersion::XB)) &&
|
||||
(ses->newserv_client_config.cfg.flags & Client::Flag::NO_D6)) {
|
||||
ses->config.check_flag(Client::Flag::NO_D6)) {
|
||||
ses->server_channel.send(0xD6);
|
||||
}
|
||||
return HandlerResult::Type::FORWARD;
|
||||
@@ -864,19 +867,17 @@ static HandlerResult S_V3_1A_D5(shared_ptr<ProxyServer::LinkedSession> ses, uint
|
||||
|
||||
static HandlerResult S_V3_BB_DA(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t flag, string&) {
|
||||
// This command is supported on all V3 versions except Ep1&2 Trial
|
||||
if ((ses->version() == GameVersion::GC) &&
|
||||
(ses->newserv_client_config.cfg.flags & Client::Flag::IS_GC_TRIAL_EDITION)) {
|
||||
if ((ses->version() == GameVersion::GC) && ses->config.check_flag(Client::Flag::IS_GC_TRIAL_EDITION)) {
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
} else if ((ses->options.override_lobby_event >= 0) &&
|
||||
(static_cast<int16_t>(flag) != ses->options.override_lobby_event)) {
|
||||
return HandlerResult(HandlerResult::Type::MODIFIED, 0xDA, ses->options.override_lobby_event);
|
||||
} else if ((ses->config.override_lobby_event != 0xFF) && (flag != ses->config.override_lobby_event)) {
|
||||
return HandlerResult(HandlerResult::Type::MODIFIED, 0xDA, ses->config.override_lobby_event);
|
||||
} else {
|
||||
return HandlerResult::Type::FORWARD;
|
||||
}
|
||||
}
|
||||
|
||||
static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
if (ses->options.save_files) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
if ((ses->version() == GameVersion::GC) && (data.size() >= 0x14)) {
|
||||
if (static_cast<uint8_t>(data[0]) == 0xB6) {
|
||||
const auto& header = check_size_t<G_MapSubsubcommand_GC_Ep3_6xB6>(data, 0xFFFF);
|
||||
@@ -911,7 +912,7 @@ static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (ses->options.ep3_infinite_time && (header.subcommand == 0xB4)) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_EP3_INFINITE_TIME_ENABLED) && (header.subcommand == 0xB4)) {
|
||||
if (header.subsubcommand == 0x3D) {
|
||||
auto& cmd = check_size_t<G_SetTournamentPlayerDecks_GC_Ep3_6xB4x3D>(data);
|
||||
if (cmd.rules.overall_time_limit || cmd.rules.phase_time_limit) {
|
||||
@@ -1003,18 +1004,18 @@ static HandlerResult C_GXB_61(shared_ptr<ProxyServer::LinkedSession> ses, uint16
|
||||
|
||||
if (ses->version() == GameVersion::BB) {
|
||||
auto& pd = check_size_t<C_CharacterData_BB_61_98>(data, 0xFFFF);
|
||||
if (ses->options.enable_chat_filter) {
|
||||
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());
|
||||
}
|
||||
if (ses->options.blank_name) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED)) {
|
||||
pd.disp.name.encode(" ", ses->language());
|
||||
modified = true;
|
||||
}
|
||||
if (ses->options.red_name && pd.disp.visual.name_color != 0xFFFF0000) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_RED_NAME_ENABLED) && pd.disp.visual.name_color != 0xFFFF0000) {
|
||||
pd.disp.visual.name_color = 0xFFFF0000;
|
||||
pd.records.challenge.title_color = 0x7C00;
|
||||
modified = true;
|
||||
} else if (ses->options.blank_name && pd.disp.visual.name_color != 0x00000000) {
|
||||
} else if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED) && pd.disp.visual.name_color != 0x00000000) {
|
||||
pd.disp.visual.name_color = 0x00000000;
|
||||
modified = true;
|
||||
}
|
||||
@@ -1040,18 +1041,18 @@ static HandlerResult C_GXB_61(shared_ptr<ProxyServer::LinkedSession> ses, uint16
|
||||
} else {
|
||||
pd = &check_size_t<C_CharacterData_V3_61_98>(data, 0xFFFF);
|
||||
}
|
||||
if (ses->options.enable_chat_filter) {
|
||||
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());
|
||||
}
|
||||
if (ses->options.blank_name) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED)) {
|
||||
pd->disp.visual.name.encode(" ", ses->language());
|
||||
modified = true;
|
||||
}
|
||||
if (ses->options.red_name && pd->disp.visual.name_color != 0xFFFF0000) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_RED_NAME_ENABLED) && pd->disp.visual.name_color != 0xFFFF0000) {
|
||||
pd->disp.visual.name_color = 0xFFFF0000;
|
||||
pd->records.challenge.stats.title_color = 0x7C00;
|
||||
modified = true;
|
||||
} else if (ses->options.blank_name && pd->disp.visual.name_color != 0x00000000) {
|
||||
} else if (ses->config.check_flag(Client::Flag::PROXY_BLANK_NAME_ENABLED) && pd->disp.visual.name_color != 0x00000000) {
|
||||
pd->disp.visual.name_color = 0x00000000;
|
||||
modified = true;
|
||||
}
|
||||
@@ -1065,7 +1066,7 @@ static HandlerResult C_GXB_61(shared_ptr<ProxyServer::LinkedSession> ses, uint16
|
||||
}
|
||||
|
||||
static HandlerResult C_GX_D9(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
if (ses->options.enable_chat_filter) {
|
||||
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.
|
||||
@@ -1074,7 +1075,7 @@ static HandlerResult C_GX_D9(shared_ptr<ProxyServer::LinkedSession> ses, uint16_
|
||||
}
|
||||
|
||||
static HandlerResult C_B_D9(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
if (ses->options.enable_chat_filter) {
|
||||
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);
|
||||
@@ -1095,13 +1096,13 @@ static HandlerResult S_44_A6(shared_ptr<ProxyServer::LinkedSession> ses, uint16_
|
||||
string filename = cmd.filename.decode();
|
||||
string output_filename;
|
||||
bool is_download = (command == 0xA6);
|
||||
if (ses->options.save_files) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
size_t extension_offset = filename.rfind('.');
|
||||
string basename, extension;
|
||||
if (extension_offset != string::npos) {
|
||||
basename = filename.substr(0, extension_offset);
|
||||
extension = filename.substr(extension_offset);
|
||||
if (extension == ".bin" && (ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (extension == ".bin" && ses->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
extension += ".mnm";
|
||||
}
|
||||
} else {
|
||||
@@ -1124,10 +1125,10 @@ static HandlerResult S_44_A6(shared_ptr<ProxyServer::LinkedSession> ses, uint16_
|
||||
}
|
||||
|
||||
// Episode 3 download quests aren't DLQ-encoded
|
||||
bool decode_dlq = is_download && !(ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3);
|
||||
bool decode_dlq = is_download && !ses->config.check_flag(Client::Flag::IS_EPISODE_3);
|
||||
ProxyServer::LinkedSession::SavingFile sf(filename, output_filename, cmd.file_size, decode_dlq);
|
||||
ses->saving_files.emplace(filename, std::move(sf));
|
||||
if (ses->options.save_files) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
ses->log.info("Saving %s from server to %s", filename.c_str(), output_filename.c_str());
|
||||
} else {
|
||||
ses->log.info("Tracking file %s", filename.c_str());
|
||||
@@ -1167,14 +1168,14 @@ static HandlerResult S_13_A7(shared_ptr<ProxyServer::LinkedSession> ses, uint16_
|
||||
if (!sf->output_filename.empty()) {
|
||||
ses->log.info("Adding %" PRIu32 " bytes to %s => %s",
|
||||
cmd.data_size.load(), sf->basename.c_str(), sf->output_filename.c_str());
|
||||
if (ses->options.save_files) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
sf->blocks.emplace_back(reinterpret_cast<const char*>(cmd.data.data()), cmd.data_size);
|
||||
}
|
||||
}
|
||||
sf->remaining_bytes -= cmd.data_size;
|
||||
|
||||
if (sf->remaining_bytes == 0) {
|
||||
if (ses->options.save_files) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
ses->log.info("Writing file %s => %s", sf->basename.c_str(), sf->output_filename.c_str());
|
||||
sf->write();
|
||||
} else {
|
||||
@@ -1187,8 +1188,8 @@ static HandlerResult S_13_A7(shared_ptr<ProxyServer::LinkedSession> ses, uint16_
|
||||
}
|
||||
|
||||
static HandlerResult S_G_B7(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
if (ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3) {
|
||||
if (ses->options.ep3_infinite_meseta) {
|
||||
if (ses->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_EP3_INFINITE_MESETA_ENABLED)) {
|
||||
auto& cmd = check_size_t<S_RankUpdate_GC_Ep3_B7>(data);
|
||||
if (cmd.current_meseta != 1000000) {
|
||||
cmd.current_meseta = 1000000;
|
||||
@@ -1203,7 +1204,7 @@ static HandlerResult S_G_B7(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t
|
||||
}
|
||||
|
||||
static HandlerResult S_G_B8(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
if (ses->options.save_files) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
if (data.size() < 4) {
|
||||
ses->log.warning("Card list data size is too small; not saving file");
|
||||
return HandlerResult::Type::FORWARD;
|
||||
@@ -1223,16 +1224,15 @@ static HandlerResult S_G_B8(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t
|
||||
|
||||
// Unset the flag specifying that the client has newserv's card definitions,
|
||||
// so the file sill be sent again if the client returns to newserv.
|
||||
ses->newserv_client_config.cfg.flags &= ~Client::Flag::HAS_EP3_CARD_DEFS;
|
||||
ses->config.clear_flag(Client::Flag::HAS_EP3_CARD_DEFS);
|
||||
|
||||
return (ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3)
|
||||
return ses->config.check_flag(Client::Flag::IS_EPISODE_3)
|
||||
? HandlerResult::Type::FORWARD
|
||||
: HandlerResult::Type::SUPPRESS;
|
||||
}
|
||||
|
||||
static HandlerResult S_G_B9(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
|
||||
if (ses->options.save_files) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_SAVE_FILES)) {
|
||||
try {
|
||||
const auto& header = check_size_t<S_UpdateMediaHeader_GC_Ep3_B9>(data, 0xFFFF);
|
||||
|
||||
@@ -1259,14 +1259,14 @@ static HandlerResult S_G_B9(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t
|
||||
}
|
||||
}
|
||||
|
||||
return (ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3)
|
||||
return ses->config.check_flag(Client::Flag::IS_EPISODE_3)
|
||||
? HandlerResult::Type::FORWARD
|
||||
: HandlerResult::Type::SUPPRESS;
|
||||
}
|
||||
|
||||
static HandlerResult S_G_EF(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
if (ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3) {
|
||||
if (ses->options.ep3_infinite_meseta) {
|
||||
if (ses->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_EP3_INFINITE_MESETA_ENABLED)) {
|
||||
auto& cmd = check_size_t<S_StartCardAuction_GC_Ep3_EF>(data,
|
||||
offsetof(S_StartCardAuction_GC_Ep3_EF, unused), 0xFFFF);
|
||||
if (cmd.points_available != 0x7FFF) {
|
||||
@@ -1285,7 +1285,7 @@ static HandlerResult S_B_EF(shared_ptr<ProxyServer::LinkedSession>, uint16_t, ui
|
||||
}
|
||||
|
||||
static HandlerResult S_G_BA(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
if (ses->options.ep3_infinite_meseta) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_EP3_INFINITE_MESETA_ENABLED)) {
|
||||
auto& cmd = check_size_t<S_MesetaTransaction_GC_Ep3_BA>(data);
|
||||
if (cmd.current_meseta != 1000000) {
|
||||
cmd.current_meseta = 1000000;
|
||||
@@ -1299,7 +1299,7 @@ static void update_leader_id(shared_ptr<ProxyServer::LinkedSession> ses, uint8_t
|
||||
if (ses->leader_client_id != leader_id) {
|
||||
ses->leader_client_id = leader_id;
|
||||
ses->log.info("Changed room leader to %zu", ses->leader_client_id);
|
||||
if (ses->options.enable_player_notifications && (ses->leader_client_id == ses->lobby_client_id)) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_PLAYER_NOTIFICATIONS_ENABLED) && (ses->leader_client_id == ses->lobby_client_id)) {
|
||||
send_text_message(ses->client_channel, "$C6You are now the leader");
|
||||
}
|
||||
}
|
||||
@@ -1318,8 +1318,8 @@ static HandlerResult S_65_67_68_EB(shared_ptr<ProxyServer::LinkedSession> ses, u
|
||||
// behavior in the client config, so if it happens during a proxy session,
|
||||
// update the client config that we'll restore if the client uses the change
|
||||
// ship or change block command.
|
||||
if (ses->newserv_client_config.cfg.flags & Client::Flag::NO_D6_AFTER_LOBBY) {
|
||||
ses->newserv_client_config.cfg.flags |= Client::Flag::NO_D6;
|
||||
if (ses->config.check_flag(Client::Flag::NO_D6_AFTER_LOBBY)) {
|
||||
ses->config.set_flag(Client::Flag::NO_D6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1338,16 +1338,17 @@ static HandlerResult S_65_67_68_EB(shared_ptr<ProxyServer::LinkedSession> ses, u
|
||||
} else {
|
||||
string name = entry.disp.visual.name.decode(entry.inventory.language);
|
||||
|
||||
if (ses->license && (entry.lobby_data.guild_card == ses->remote_guild_card_number)) {
|
||||
entry.lobby_data.guild_card = ses->license->serial_number;
|
||||
if (ses->license && (entry.lobby_data.guild_card_number == ses->remote_guild_card_number)) {
|
||||
entry.lobby_data.guild_card_number = ses->license->serial_number;
|
||||
num_replacements++;
|
||||
modified = true;
|
||||
} else if (ses->options.enable_player_notifications && command != 0x67) {
|
||||
} else if (ses->config.check_flag(Client::Flag::PROXY_PLAYER_NOTIFICATIONS_ENABLED) &&
|
||||
(command != 0x67)) {
|
||||
send_text_message_printf(ses->client_channel, "$C6Join: %zu/%" PRIu32 "\n%s",
|
||||
index, entry.lobby_data.guild_card.load(), name.c_str());
|
||||
index, entry.lobby_data.guild_card_number.load(), name.c_str());
|
||||
}
|
||||
auto& p = ses->lobby_players[index];
|
||||
p.guild_card_number = entry.lobby_data.guild_card;
|
||||
p.guild_card_number = entry.lobby_data.guild_card_number;
|
||||
p.name = name;
|
||||
p.language = entry.inventory.language;
|
||||
p.section_id = entry.disp.visual.section_id;
|
||||
@@ -1360,12 +1361,12 @@ static HandlerResult S_65_67_68_EB(shared_ptr<ProxyServer::LinkedSession> ses, u
|
||||
ses->log.warning("Proxied player appears multiple times in lobby");
|
||||
}
|
||||
|
||||
if (ses->options.override_lobby_event >= 0) {
|
||||
cmd.lobby_flags.event = ses->options.override_lobby_event;
|
||||
if (ses->config.override_lobby_event != 0xFF) {
|
||||
cmd.lobby_flags.event = ses->config.override_lobby_event;
|
||||
modified = true;
|
||||
}
|
||||
if (ses->options.override_lobby_number >= 0) {
|
||||
cmd.lobby_flags.lobby_number = ses->options.override_lobby_number;
|
||||
if (ses->config.override_lobby_number != 0x80) {
|
||||
cmd.lobby_flags.lobby_number = ses->config.override_lobby_number;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@@ -1398,12 +1399,12 @@ static HandlerResult S_64(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
ses->lobby_client_id = cmd->client_id;
|
||||
update_leader_id(ses, cmd->leader_id);
|
||||
for (size_t x = 0; x < flag; x++) {
|
||||
if (cmd->lobby_data[x].guild_card == ses->remote_guild_card_number) {
|
||||
cmd->lobby_data[x].guild_card = ses->license->serial_number;
|
||||
if (cmd->lobby_data[x].guild_card_number == ses->remote_guild_card_number) {
|
||||
cmd->lobby_data[x].guild_card_number = ses->license->serial_number;
|
||||
modified = true;
|
||||
}
|
||||
auto& p = ses->lobby_players[x];
|
||||
p.guild_card_number = cmd->lobby_data[x].guild_card;
|
||||
p.guild_card_number = cmd->lobby_data[x].guild_card_number;
|
||||
if (cmd_ep3) {
|
||||
const auto& p_ep3 = cmd_ep3->players_ep3[x];
|
||||
p.language = p_ep3.inventory.language;
|
||||
@@ -1417,16 +1418,16 @@ static HandlerResult S_64(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
x, p.guild_card_number, p.name.c_str());
|
||||
}
|
||||
|
||||
if (ses->options.override_section_id >= 0) {
|
||||
cmd->section_id = ses->options.override_section_id;
|
||||
if (ses->config.override_section_id != 0xFF) {
|
||||
cmd->section_id = ses->config.override_section_id;
|
||||
modified = true;
|
||||
}
|
||||
if (ses->options.override_lobby_event >= 0) {
|
||||
cmd->event = ses->options.override_lobby_event;
|
||||
if (ses->config.override_lobby_event != 0xFF) {
|
||||
cmd->event = ses->config.override_lobby_event;
|
||||
modified = true;
|
||||
}
|
||||
if (ses->options.override_random_seed >= 0) {
|
||||
cmd->rare_seed = ses->options.override_random_seed;
|
||||
if (ses->config.check_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED)) {
|
||||
cmd->rare_seed = ses->config.override_random_seed;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@@ -1456,8 +1457,8 @@ static HandlerResult S_E8(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
auto& player_entry = (x < 4) ? cmd.players[x] : cmd.spectator_players[x - 4];
|
||||
auto& spec_entry = cmd.entries[x];
|
||||
|
||||
if (player_entry.lobby_data.guild_card == ses->remote_guild_card_number) {
|
||||
player_entry.lobby_data.guild_card = ses->license->serial_number;
|
||||
if (player_entry.lobby_data.guild_card_number == ses->remote_guild_card_number) {
|
||||
player_entry.lobby_data.guild_card_number = ses->license->serial_number;
|
||||
modified = true;
|
||||
}
|
||||
if (spec_entry.guild_card_number == ses->remote_guild_card_number) {
|
||||
@@ -1466,25 +1467,24 @@ static HandlerResult S_E8(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
}
|
||||
|
||||
auto& p = ses->lobby_players[x];
|
||||
p.guild_card_number = player_entry.lobby_data.guild_card;
|
||||
p.guild_card_number = player_entry.lobby_data.guild_card_number;
|
||||
p.language = player_entry.inventory.language;
|
||||
p.name = player_entry.disp.visual.name.decode(p.language);
|
||||
p.section_id = player_entry.disp.visual.section_id;
|
||||
p.char_class = player_entry.disp.visual.char_class;
|
||||
ses->log.info("Added lobby player: (%zu) %" PRIu32 " %s",
|
||||
x, p.guild_card_number, p.name.c_str());
|
||||
ses->log.info("Added lobby player: (%zu) %" PRIu32 " %s", x, p.guild_card_number, p.name.c_str());
|
||||
}
|
||||
|
||||
if (ses->options.override_section_id >= 0) {
|
||||
cmd.section_id = ses->options.override_section_id;
|
||||
if (ses->config.override_section_id != 0xFF) {
|
||||
cmd.section_id = ses->config.override_section_id;
|
||||
modified = true;
|
||||
}
|
||||
if (ses->options.override_lobby_event >= 0) {
|
||||
cmd.event = ses->options.override_lobby_event;
|
||||
if (ses->config.override_lobby_event != 0xFF) {
|
||||
cmd.event = ses->config.override_lobby_event;
|
||||
modified = true;
|
||||
}
|
||||
if (ses->options.override_random_seed >= 0) {
|
||||
cmd.rare_seed = ses->options.override_random_seed;
|
||||
if (ses->config.check_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED)) {
|
||||
cmd.rare_seed = ses->config.override_random_seed;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@@ -1507,7 +1507,7 @@ static HandlerResult S_66_69_E9(shared_ptr<ProxyServer::LinkedSession> ses, uint
|
||||
ses->log.warning("Lobby leave command references missing position");
|
||||
} else {
|
||||
auto& p = ses->lobby_players[index];
|
||||
if (ses->options.enable_player_notifications) {
|
||||
if (ses->config.check_flag(Client::Flag::PROXY_PLAYER_NOTIFICATIONS_ENABLED)) {
|
||||
send_text_message_printf(ses->client_channel, "$C4Leave: %zu/%" PRIu32 "\n%s",
|
||||
index, p.guild_card_number, p.name.c_str());
|
||||
}
|
||||
@@ -1545,9 +1545,7 @@ static HandlerResult C_06(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
text.push_back(0);
|
||||
}
|
||||
text = tt_decode_marked(text, ses->language(), true);
|
||||
} else if (!text.empty() &&
|
||||
(text[0] != '\t') &&
|
||||
(ses->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3)) {
|
||||
} else if (!text.empty() && (text[0] != '\t') && ses->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
private_flags = text[0];
|
||||
text = tt_decode_marked(text.substr(1), ses->language(), false);
|
||||
} else {
|
||||
@@ -1560,12 +1558,12 @@ static HandlerResult C_06(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
|
||||
bool is_command = (text[0] == '$') ||
|
||||
(text[0] == '\t' && text[1] != 'C' && text[2] == '$');
|
||||
if (is_command && ses->options.enable_chat_commands) {
|
||||
if (is_command && ses->config.check_flag(Client::Flag::PROXY_CHAT_COMMANDS_ENABLED)) {
|
||||
size_t offset = ((text[0] & 0xF0) == 0x40) ? 1 : 0;
|
||||
offset += (text[offset] == '$') ? 0 : 2;
|
||||
text = text.substr(offset);
|
||||
if (text.size() >= 2 && text[1] == '$') {
|
||||
if (ses->options.enable_chat_filter) {
|
||||
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);
|
||||
@@ -1576,7 +1574,7 @@ static HandlerResult C_06(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
}
|
||||
|
||||
} else if (ses->options.enable_chat_filter) {
|
||||
} 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;
|
||||
|
||||
@@ -1651,7 +1649,7 @@ static HandlerResult C_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t c
|
||||
ses->area = cmd.area;
|
||||
|
||||
} else if (data[0] == 0x2F || data[0] == 0x4B || data[0] == 0x4C) {
|
||||
if (ses->options.infinite_hp) {
|
||||
if (ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
send_player_stats_change(ses->client_channel,
|
||||
ses->lobby_client_id, PlayerStatsChange::ADD_HP, 2550);
|
||||
send_player_stats_change(ses->server_channel,
|
||||
@@ -1666,7 +1664,7 @@ static HandlerResult C_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t c
|
||||
} else if (data[0] == 0x42) {
|
||||
C_6x_movement<G_RunToPosition_6x42>(ses, data);
|
||||
} else if (data[0] == 0x48) {
|
||||
if (ses->options.infinite_tp) {
|
||||
if (ses->config.check_flag(Client::Flag::INFINITE_TP_ENABLED)) {
|
||||
send_player_stats_change(ses->client_channel,
|
||||
ses->lobby_client_id, PlayerStatsChange::ADD_TP, 255);
|
||||
send_player_stats_change(ses->server_channel,
|
||||
@@ -1686,7 +1684,7 @@ template <>
|
||||
HandlerResult C_6x<void>(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, uint32_t, string& data) {
|
||||
check_implemented_subcommand(ses, data);
|
||||
|
||||
if (!data.empty() && (data[0] == 0x05) && ses->options.switch_assist) {
|
||||
if (!data.empty() && (data[0] == 0x05) && ses->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) {
|
||||
auto& cmd = check_size_t<G_SwitchStateChanged_6x05>(data);
|
||||
if (cmd.flags && cmd.header.object_id != 0xFFFF) {
|
||||
if (ses->last_switch_enabled_command.header.subcommand == 0x05) {
|
||||
|
||||
+22
-22
@@ -262,7 +262,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
shared_ptr<License> license;
|
||||
uint32_t sub_version = 0;
|
||||
string character_name;
|
||||
ClientConfigBB client_config;
|
||||
Client::Config config;
|
||||
string login_command_bb;
|
||||
string hardware_id;
|
||||
|
||||
@@ -277,7 +277,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
ses->channel.language = cmd.language;
|
||||
character_name = cmd.name.decode(ses->channel.language);
|
||||
hardware_id = cmd.hardware_id.decode();
|
||||
client_config.cfg.flags |= Client::Flag::IS_DC_V1;
|
||||
config.set_flag(Client::Flag::IS_DC_V1);
|
||||
} else if (command == 0x9D) {
|
||||
const auto& cmd = check_size_t<C_Login_DC_PC_GC_9D>(data, sizeof(C_LoginExtended_DC_GC_9D));
|
||||
license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
@@ -312,7 +312,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
character_name = cmd.name.decode(ses->channel.language);
|
||||
client_config.cfg = cmd.client_config.cfg;
|
||||
config.parse_from(cmd.client_config);
|
||||
|
||||
} else if (ses->version == GameVersion::XB) {
|
||||
throw runtime_error("xbox licenses are not implemented");
|
||||
@@ -373,13 +373,11 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
// If there's no open session for this license, then there must be a valid
|
||||
// destination somewhere - either in the client config or in the unlinked
|
||||
// session
|
||||
if (client_config.cfg.magic == CLIENT_CONFIG_MAGIC) {
|
||||
linked_ses.reset(new LinkedSession(
|
||||
server, ses->local_port, ses->version, license, client_config));
|
||||
if (config.proxy_destination_address != 0) {
|
||||
linked_ses.reset(new LinkedSession(server, ses->local_port, ses->version, license, config));
|
||||
linked_ses->log.info("Opened licensed session for unlinked session based on client config");
|
||||
} else if (ses->next_destination.ss_family == AF_INET) {
|
||||
linked_ses.reset(new LinkedSession(
|
||||
server, ses->local_port, ses->version, license, ses->next_destination));
|
||||
linked_ses.reset(new LinkedSession(server, ses->local_port, ses->version, license, ses->next_destination));
|
||||
linked_ses->log.info("Opened licensed session for unlinked session based on unlinked default destination");
|
||||
} else {
|
||||
ses->log.error("Cannot open linked session: no valid destination in client config or unlinked session");
|
||||
@@ -484,15 +482,15 @@ ProxyServer::LinkedSession::LinkedSession(
|
||||
uint16_t local_port,
|
||||
GameVersion version,
|
||||
shared_ptr<License> license,
|
||||
const ClientConfigBB& newserv_client_config)
|
||||
const Client::Config& config)
|
||||
: LinkedSession(server, license->serial_number, local_port, version) {
|
||||
this->license = license;
|
||||
this->newserv_client_config = newserv_client_config;
|
||||
this->config = config;
|
||||
memset(&this->next_destination, 0, sizeof(this->next_destination));
|
||||
struct sockaddr_in* dest_sin = reinterpret_cast<struct sockaddr_in*>(&this->next_destination);
|
||||
dest_sin->sin_family = AF_INET;
|
||||
dest_sin->sin_port = htons(this->newserv_client_config.cfg.proxy_destination_port);
|
||||
dest_sin->sin_addr.s_addr = htonl(this->newserv_client_config.cfg.proxy_destination_address);
|
||||
dest_sin->sin_port = htons(this->config.proxy_destination_port);
|
||||
dest_sin->sin_addr.s_addr = htonl(this->config.proxy_destination_address);
|
||||
}
|
||||
|
||||
ProxyServer::LinkedSession::LinkedSession(
|
||||
@@ -640,11 +638,11 @@ void ProxyServer::LinkedSession::on_error(Channel& ch, short events) {
|
||||
if (events & BEV_EVENT_CONNECTED) {
|
||||
ses->log.info("%s channel connected", is_server_stream ? "Server" : "Client");
|
||||
|
||||
if (is_server_stream && (ses->options.override_lobby_event >= 0) &&
|
||||
(((ses->version() == GameVersion::GC) && !(ses->newserv_client_config.cfg.flags & Client::Flag::IS_GC_TRIAL_EDITION)) ||
|
||||
if (is_server_stream && (ses->config.override_lobby_event != 0xFF) &&
|
||||
(((ses->version() == GameVersion::GC) && !(ses->config.check_flag(Client::Flag::IS_GC_TRIAL_EDITION))) ||
|
||||
(ses->version() == GameVersion::XB) ||
|
||||
(ses->version() == GameVersion::BB))) {
|
||||
ses->client_channel.send(0xDA, ses->options.override_lobby_event);
|
||||
ses->client_channel.send(0xDA, ses->config.override_lobby_event);
|
||||
}
|
||||
}
|
||||
if (events & BEV_EVENT_ERROR) {
|
||||
@@ -705,11 +703,13 @@ void ProxyServer::LinkedSession::send_to_game_server(const char* error_message)
|
||||
send_ship_info(this->client_channel, string_printf("You\'ve returned to\n\tC6%s$C7\n\n%s", s->name.c_str(), error_message ? error_message : ""));
|
||||
|
||||
// Restore newserv_client_config, so the login server gets the client flags
|
||||
S_UpdateClientConfig_DC_PC_V3_04 update_client_config_cmd;
|
||||
update_client_config_cmd.player_tag = 0x00010000;
|
||||
update_client_config_cmd.guild_card_number = this->license->serial_number;
|
||||
update_client_config_cmd.cfg = this->newserv_client_config.cfg;
|
||||
this->client_channel.send(0x04, 0x00, &update_client_config_cmd, sizeof(update_client_config_cmd));
|
||||
if (this->version() == GameVersion::GC || this->version() == GameVersion::XB) {
|
||||
S_UpdateClientConfig_V3_04 update_client_config_cmd;
|
||||
update_client_config_cmd.player_tag = 0x00010000;
|
||||
update_client_config_cmd.guild_card_number = this->license->serial_number;
|
||||
this->config.serialize_into(update_client_config_cmd.client_config);
|
||||
this->client_channel.send(0x04, 0x00, &update_client_config_cmd, sizeof(update_client_config_cmd));
|
||||
}
|
||||
|
||||
const auto& port_name = version_to_login_port_name.at(static_cast<size_t>(this->version()));
|
||||
|
||||
@@ -817,9 +817,9 @@ shared_ptr<ProxyServer::LinkedSession> ProxyServer::get_session_by_name(
|
||||
|
||||
shared_ptr<ProxyServer::LinkedSession> ProxyServer::create_licensed_session(
|
||||
shared_ptr<License> l, uint16_t local_port, GameVersion version,
|
||||
const ClientConfigBB& newserv_client_config) {
|
||||
const Client::Config& config) {
|
||||
shared_ptr<LinkedSession> session(new LinkedSession(
|
||||
this->shared_from_this(), local_port, version, l, newserv_client_config));
|
||||
this->shared_from_this(), local_port, version, l, config));
|
||||
auto emplace_ret = this->id_to_session.emplace(session->id, session);
|
||||
if (!emplace_ret.second) {
|
||||
throw runtime_error("session already exists for this license");
|
||||
|
||||
+3
-4
@@ -62,12 +62,11 @@ public:
|
||||
std::string hardware_id; // Only used for DC sessions
|
||||
std::string login_command_bb;
|
||||
|
||||
ClientOptions options;
|
||||
uint32_t challenge_rank_color_override;
|
||||
std::string challenge_rank_title_override;
|
||||
int64_t remote_guild_card_number;
|
||||
parray<uint8_t, 0x20> remote_client_config_data;
|
||||
ClientConfigBB newserv_client_config;
|
||||
Client::Config config;
|
||||
// A null handler in here means to forward the response to the remote server
|
||||
std::deque<std::function<void(uint32_t return_value, uint32_t checksum)>> function_call_return_handler_queue;
|
||||
G_SwitchStateChanged_6x05 last_switch_enabled_command;
|
||||
@@ -120,7 +119,7 @@ public:
|
||||
uint16_t local_port,
|
||||
GameVersion version,
|
||||
std::shared_ptr<License> license,
|
||||
const ClientConfigBB& newserv_client_config);
|
||||
const Client::Config& config);
|
||||
LinkedSession(
|
||||
std::shared_ptr<ProxyServer> server,
|
||||
uint16_t local_port,
|
||||
@@ -179,7 +178,7 @@ public:
|
||||
std::shared_ptr<License> l,
|
||||
uint16_t local_port,
|
||||
GameVersion version,
|
||||
const ClientConfigBB& newserv_client_config);
|
||||
const Client::Config& config);
|
||||
void delete_session(uint64_t id);
|
||||
void delete_session(struct bufferevent* bev);
|
||||
|
||||
|
||||
+245
-272
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,6 @@ std::shared_ptr<Lobby> create_game_generic(
|
||||
Episode episode = Episode::EP1,
|
||||
GameMode mode = GameMode::NORMAL,
|
||||
uint8_t difficulty = 0,
|
||||
uint32_t flags = 0,
|
||||
bool allow_v1 = false,
|
||||
std::shared_ptr<Lobby> watched_lobby = nullptr,
|
||||
std::shared_ptr<Episode3::BattleRecordPlayer> battle_player = nullptr);
|
||||
|
||||
+67
-65
@@ -43,7 +43,7 @@ static void forward_subcommand(
|
||||
|
||||
// If the command is an Ep3-only command, make sure an Ep3 client sent it
|
||||
bool command_is_ep3 = (command & 0xF0) == 0xC0;
|
||||
if (command_is_ep3 && !(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (command_is_ep3 && !c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
throw runtime_error("Episode 3 command sent by non-Episode 3 client");
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ static void forward_subcommand(
|
||||
} else {
|
||||
if (command_is_ep3) {
|
||||
for (auto& target : l->clients) {
|
||||
if (!target || (target == c) || !(target->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (!target || (target == c) || !target->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
continue;
|
||||
}
|
||||
send_command(target, command, flag, data, size);
|
||||
@@ -80,7 +80,7 @@ static void forward_subcommand(
|
||||
watcher_subcommands.count(subcommand)) {
|
||||
for (const auto& watcher_lobby : l->watcher_lobbies) {
|
||||
for (auto& target : watcher_lobby->clients) {
|
||||
if (target && (target->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (target && target->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
send_command(target, command, flag, data, size);
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ static void on_unimplemented(shared_ptr<Client> c, uint8_t command, uint8_t flag
|
||||
} else {
|
||||
c->log.warning("Unknown subcommand: %02hhX (public)", cmd.subcommand);
|
||||
}
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5Sub 6x%02hhX missing", cmd.subcommand);
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ static void on_forward_sync_joining_player_state(shared_ptr<Client> c, uint8_t c
|
||||
throw runtime_error("compressed end offset is beyond end of command");
|
||||
}
|
||||
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string decompressed = bc0_decompress(reinterpret_cast<const char*>(data) + sizeof(cmd), cmd.compressed_size);
|
||||
c->log.info("Decompressed sync data (%" PRIX32 " -> %zX bytes; expected %" PRIX32 "):",
|
||||
cmd.compressed_size.load(), decompressed.size(), cmd.decompressed_size.load());
|
||||
@@ -192,7 +192,7 @@ static void on_sync_joining_player_item_state(shared_ptr<Client> c, uint8_t comm
|
||||
}
|
||||
|
||||
string decompressed = bc0_decompress(reinterpret_cast<const char*>(data) + sizeof(cmd), cmd.compressed_size);
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
c->log.info("Decompressed item sync data (%" PRIX32 " -> %zX bytes; expected %" PRIX32 "):",
|
||||
cmd.compressed_size.load(), decompressed.size(), cmd.decompressed_size.load());
|
||||
print_data(stderr, decompressed);
|
||||
@@ -235,7 +235,7 @@ static void on_sync_joining_player_item_state(shared_ptr<Client> c, uint8_t comm
|
||||
out_cmd.decompressed_size = decompressed.size();
|
||||
out_cmd.compressed_size = out_compressed_data.size();
|
||||
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
c->log.info("Byteswapped and recompressed item sync data (%zX bytes)", out_compressed_data.size());
|
||||
}
|
||||
|
||||
@@ -346,16 +346,16 @@ static void on_ep3_sound_chat(shared_ptr<Client> c, uint8_t command, uint8_t fla
|
||||
// forwarded from spectator teams to the primary team. The client only uses
|
||||
// this behavior for the 6xBE command (sound chat), and newserv enforces that
|
||||
// only that command is sent via CB.
|
||||
if (!(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (!c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
throw runtime_error("non-Episode 3 client sent sound chat command");
|
||||
}
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if ((command == 0xCB) && (l->flags & Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
if ((command == 0xCB) && l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
auto watched_lobby = l->watched_lobby.lock();
|
||||
if (watched_lobby) {
|
||||
for (auto& target : watched_lobby->clients) {
|
||||
if (target && (target->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (target && target->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
send_command(target, command, flag, data, size);
|
||||
}
|
||||
}
|
||||
@@ -520,10 +520,10 @@ static void on_set_player_visibility(shared_ptr<Client> c, uint8_t command, uint
|
||||
auto l = c->require_lobby();
|
||||
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
if (!l->is_game() && !(c->flags & Client::Flag::IS_DC_V1)) {
|
||||
if (!l->is_game() && !c->config.check_flag(Client::Flag::IS_DC_V1)) {
|
||||
send_arrow_update(l);
|
||||
}
|
||||
if (!l->is_game() && (l->flags & Lobby::Flag::IS_OVERFLOW)) {
|
||||
if (!l->is_game() && l->check_flag(Lobby::Flag::IS_OVERFLOW)) {
|
||||
send_message_box(c, "$C6All lobbies are full.\n\n$C7You are in a private lobby. You can use the\nteleporter to join other lobbies if there is space\navailable.");
|
||||
send_lobby_message_box(c, "");
|
||||
}
|
||||
@@ -549,7 +549,7 @@ static void on_player_died(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
try {
|
||||
auto& inventory = c->game_data.player()->inventory;
|
||||
size_t mag_index = inventory.find_equipped_mag();
|
||||
@@ -569,7 +569,7 @@ static void on_hit_by_enemy(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() && (cmd.client_id == c->lobby_client_id)) {
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->options.infinite_hp) {
|
||||
if (l->check_flag(Lobby::Flag::CHEATS_ENABLED) && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
send_player_stats_change(c, PlayerStatsChange::ADD_HP, 2550);
|
||||
}
|
||||
}
|
||||
@@ -582,7 +582,7 @@ static void on_cast_technique_finished(shared_ptr<Client> c, uint8_t command, ui
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() && (cmd.header.client_id == c->lobby_client_id)) {
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->options.infinite_tp) {
|
||||
if (l->check_flag(Lobby::Flag::CHEATS_ENABLED) && c->config.check_flag(Client::Flag::INFINITE_TP_ENABLED)) {
|
||||
send_player_stats_change(c, PlayerStatsChange::ADD_TP, 255);
|
||||
}
|
||||
}
|
||||
@@ -629,10 +629,11 @@ static void on_switch_state_changed(shared_ptr<Client> c, uint8_t command, uint8
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
|
||||
if (cmd.flags && cmd.header.object_id != 0xFFFF) {
|
||||
if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->options.switch_assist &&
|
||||
if (l->check_flag(Lobby::Flag::CHEATS_ENABLED) &&
|
||||
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");
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message(c, "$C5Switch assist");
|
||||
}
|
||||
forward_subcommand(c, command, flag, &c->last_switch_enabled_command, sizeof(c->last_switch_enabled_command));
|
||||
@@ -682,7 +683,7 @@ static void on_player_drop_item(shared_ptr<Client> c, uint8_t command, uint8_t f
|
||||
}
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto p = c->game_data.player();
|
||||
auto item = p->remove_item(cmd.item_id, 0, c->version() != GameVersion::BB);
|
||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||
@@ -691,7 +692,7 @@ static void on_player_drop_item(shared_ptr<Client> c, uint8_t command, uint8_t f
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hu dropped item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(), cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
auto name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s",
|
||||
cmd.item_id.load(), name.c_str());
|
||||
@@ -738,7 +739,7 @@ static void on_create_inventory_item_t(shared_ptr<Client> c, uint8_t command, ui
|
||||
}
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto p = c->game_data.player();
|
||||
ItemData item = cmd.item_data;
|
||||
item.decode_if_mag(c->version());
|
||||
@@ -748,7 +749,7 @@ static void on_create_inventory_item_t(shared_ptr<Client> c, uint8_t command, ui
|
||||
auto s = c->require_server_state();
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hu created inventory item %08" PRIX32 " (%s)", c->lobby_client_id, item.id.load(), name.c_str());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5CREATE %08" PRIX32 "\n%s", item.id.load(), name.c_str());
|
||||
}
|
||||
@@ -781,7 +782,7 @@ static void on_drop_partial_stack_t(shared_ptr<Client> c, uint8_t command, uint8
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
// TODO: Should we delete anything from the inventory here? Does the client
|
||||
// send an appropriate 6x29 alongside this?
|
||||
ItemData item = cmd.item_data;
|
||||
@@ -794,7 +795,7 @@ static void on_drop_partial_stack_t(shared_ptr<Client> c, uint8_t command, uint8
|
||||
l->log.info("Player %hu split stack to create floor item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
||||
cmd.header.client_id.load(), item.id.load(), name.c_str(),
|
||||
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s", item.id.load(), name.c_str());
|
||||
}
|
||||
@@ -823,7 +824,7 @@ static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
|
||||
@@ -849,7 +850,7 @@ static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint
|
||||
l->log.info("Player %hu split stack %08" PRIX32 " (removed: %s) at %hu:(%g, %g)",
|
||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(),
|
||||
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
auto name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5SPLIT/BB %08" PRIX32 "\n%s",
|
||||
cmd.item_id.load(), name.c_str());
|
||||
@@ -875,7 +876,7 @@ static void on_buy_shop_item(shared_ptr<Client> c, uint8_t command, uint8_t flag
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto p = c->game_data.player();
|
||||
ItemData item = cmd.item_data;
|
||||
item.decode_if_mag(c->version());
|
||||
@@ -886,7 +887,7 @@ static void on_buy_shop_item(shared_ptr<Client> c, uint8_t command, uint8_t flag
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hu bought item %08" PRIX32 " (%s) from shop (%zu Meseta)",
|
||||
cmd.header.client_id.load(), item.id.load(), name.c_str(), price);
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
auto name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5BUY %08" PRIX32 "\n%s", item.id.load(), name.c_str());
|
||||
}
|
||||
@@ -915,7 +916,7 @@ static void on_box_or_enemy_item_drop_t(shared_ptr<Client> c, uint8_t command, u
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
ItemData item = cmd.item.item;
|
||||
item.decode_if_mag(c->version());
|
||||
l->on_item_id_generated_externally(c->lobby_client_id, item.id);
|
||||
@@ -925,7 +926,7 @@ static void on_box_or_enemy_item_drop_t(shared_ptr<Client> c, uint8_t command, u
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hhu (leader) created floor item %08" PRIX32 " (%s) at %hhu:(%g, %g)",
|
||||
l->leader_id, item.id.load(), name.c_str(), cmd.item.area, cmd.item.x.load(), cmd.item.z.load());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s", item.id.load(), name.c_str());
|
||||
}
|
||||
@@ -971,7 +972,7 @@ static void on_pick_up_item(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto effective_p = effective_c->game_data.player();
|
||||
auto item = l->remove_item(cmd.item_id);
|
||||
effective_p->add_item(item);
|
||||
@@ -980,7 +981,7 @@ static void on_pick_up_item(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hu picked up %08" PRIX32 " (%s)",
|
||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
auto name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5PICK %08" PRIX32 "\n%s", cmd.item_id.load(), name.c_str());
|
||||
}
|
||||
@@ -1004,7 +1005,7 @@ static void on_pick_up_item_request(shared_ptr<Client> c, uint8_t command, uint8
|
||||
// picked up. To account for this, we discard requests to pick up items that
|
||||
// don't exist instead of disconnecting the client.
|
||||
if (l->base_version == GameVersion::BB) {
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
|
||||
@@ -1016,7 +1017,7 @@ static void on_pick_up_item_request(shared_ptr<Client> c, uint8_t command, uint8
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hu picked up (BB) %08" PRIX32 " (%s)",
|
||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
auto name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5PICK/BB %08" PRIX32 "\n%s",
|
||||
cmd.item_id.load(), name.c_str());
|
||||
@@ -1025,7 +1026,7 @@ static void on_pick_up_item_request(shared_ptr<Client> c, uint8_t command, uint8
|
||||
|
||||
send_pick_up_item(c, cmd.item_id, cmd.area);
|
||||
|
||||
} else if ((l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) && !l->item_exists(cmd.item_id)) {
|
||||
} else if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED) && !l->item_exists(cmd.item_id)) {
|
||||
l->log.warning("Player %hu requests to pick up %08" PRIX32 ", but the item does not exist; dropping command",
|
||||
cmd.header.client_id.load(), cmd.item_id.load());
|
||||
|
||||
@@ -1042,7 +1043,7 @@ static void on_equip_unequip_item(shared_ptr<Client> c, uint8_t command, uint8_t
|
||||
}
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto p = c->game_data.player();
|
||||
size_t index = p->inventory.find_item(cmd.item_id);
|
||||
if (cmd.header.subcommand == 0x25) { // Equip
|
||||
@@ -1069,7 +1070,7 @@ static void on_use_item(
|
||||
}
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto s = c->require_server_state();
|
||||
auto p = c->game_data.player();
|
||||
size_t index = p->inventory.find_item(cmd.item_id);
|
||||
@@ -1085,7 +1086,7 @@ static void on_use_item(
|
||||
|
||||
l->log.info("Player %hhu used item %hu:%08" PRIX32 " (%s)",
|
||||
c->lobby_client_id, cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5USE %08" PRIX32 "\n%s",
|
||||
cmd.item_id.load(), colored_name.c_str());
|
||||
}
|
||||
@@ -1107,7 +1108,7 @@ static void on_feed_mag(
|
||||
}
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto s = c->require_server_state();
|
||||
auto p = c->game_data.player();
|
||||
|
||||
@@ -1137,7 +1138,7 @@ static void on_feed_mag(
|
||||
l->log.info("Player %hhu fed item %hu:%08" PRIX32 " (%s) to mag %hu:%08" PRIX32 " (%s)",
|
||||
c->lobby_client_id, cmd.header.client_id.load(), cmd.fed_item_id.load(), fed_name.c_str(),
|
||||
cmd.header.client_id.load(), cmd.mag_item_id.load(), mag_name.c_str());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5FEED %08" PRIX32 "\n%s\n...TO %08" PRIX32 "\n%s",
|
||||
cmd.fed_item_id.load(), fed_colored_name.c_str(),
|
||||
cmd.mag_item_id.load(), mag_colored_name.c_str());
|
||||
@@ -1206,7 +1207,7 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
|
||||
@@ -1244,7 +1245,7 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
|
||||
}
|
||||
}
|
||||
|
||||
} else if ((c->version() == GameVersion::GC) && (c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
} else if ((c->version() == GameVersion::GC) && c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
}
|
||||
@@ -1254,7 +1255,7 @@ static void on_sort_inventory_bb(shared_ptr<Client> c, uint8_t, uint8_t, const v
|
||||
if (l->base_version == GameVersion::BB) {
|
||||
const auto& cmd = check_size_t<G_SortInventory_BB_6xC4>(data, size);
|
||||
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
|
||||
@@ -1307,7 +1308,7 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(l->flags & Lobby::Flag::DROPS_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::DROPS_ENABLED)) {
|
||||
return;
|
||||
}
|
||||
if (!l->item_creator) {
|
||||
@@ -1354,7 +1355,7 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
||||
}
|
||||
item.id = l->generate_item_id(0xFF);
|
||||
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||
}
|
||||
send_drop_item(l, item, cmd.rt_index != 0x30, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||
@@ -1548,7 +1549,7 @@ static void on_steal_exp_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void*
|
||||
// types, so we don't check for that here.
|
||||
uint32_t percent = special.amount + (char_class_is_android(p->disp.visual.char_class) ? 30 : 0);
|
||||
uint32_t stolen_exp = min<uint32_t>((enemy_exp * percent) / 100, (l->difficulty + 1) * 20);
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5+%" PRIu32 " E-%hX %s",
|
||||
stolen_exp, cmd.enemy_id.load(), name_for_enum(enemy.type));
|
||||
}
|
||||
@@ -1582,7 +1583,7 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
||||
string e_str = e.str();
|
||||
c->log.info("Enemy killed: E-%hX => %s", cmd.enemy_id.load(), e_str.c_str());
|
||||
if (e.flags & Map::Enemy::Flag::DEFEATED) {
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5E-%hX __DEFEATED__", cmd.enemy_id.load());
|
||||
}
|
||||
return;
|
||||
@@ -1594,7 +1595,7 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
||||
uint32_t bp_index = battle_param_index_for_enemy_type(l->episode, e.type);
|
||||
experience = bp_table.stats[l->difficulty][bp_index].experience * l->exp_multiplier;
|
||||
} catch (const exception& e) {
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5E-%hX __MISSING__\n%s", cmd.enemy_id.load(), e.what());
|
||||
} else {
|
||||
send_text_message_printf(c, "$C4Unknown enemy type killed:\n%s", e.what());
|
||||
@@ -1619,7 +1620,7 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
||||
// Killer gets full experience, others get 77%
|
||||
bool is_killer = (e.last_hit_by_client_id == other_c->lobby_client_id);
|
||||
uint32_t player_exp = is_killer ? experience : ((experience * 77) / 100);
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5+%" PRIu32 " E-%hX %s",
|
||||
player_exp, cmd.enemy_id.load(), name_for_enum(e.type));
|
||||
}
|
||||
@@ -1682,14 +1683,14 @@ static void on_destroy_inventory_item(shared_ptr<Client> c, uint8_t command, uin
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto s = c->require_server_state();
|
||||
auto p = c->game_data.player();
|
||||
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB);
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hhu destroyed inventory item %hu:%08" PRIX32 " (%s)",
|
||||
c->lobby_client_id, cmd.header.client_id.load(), cmd.item_id.load(), name.c_str());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5DESTROY %08" PRIX32 "\n%s",
|
||||
cmd.item_id.load(), name.c_str());
|
||||
@@ -1707,13 +1708,13 @@ static void on_destroy_ground_item(shared_ptr<Client> c, uint8_t command, uint8_
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) {
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto s = c->require_server_state();
|
||||
auto item = l->remove_item(cmd.item_id);
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hhu destroyed floor item %08" PRIX32 " (%s)",
|
||||
c->lobby_client_id, cmd.item_id.load(), name.c_str());
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5DESTROY/GND %08" PRIX32 "\n%s",
|
||||
cmd.item_id.load(), name.c_str());
|
||||
@@ -1729,7 +1730,7 @@ static void on_identify_item_bb(shared_ptr<Client> c, uint8_t command, uint8_t f
|
||||
if (!l->is_game()) {
|
||||
return;
|
||||
}
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
if (!l->item_creator.get()) {
|
||||
@@ -1758,7 +1759,7 @@ static void on_accept_identify_item_bb(shared_ptr<Client> c, uint8_t command, ui
|
||||
if (l->base_version == GameVersion::BB) {
|
||||
const auto& cmd = check_size_t<G_AcceptItemIdentification_BB_6xBA>(data, size);
|
||||
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
|
||||
@@ -1783,7 +1784,7 @@ static void on_sell_item_at_shop_bb(shared_ptr<Client> c, uint8_t command, uint8
|
||||
if (l->base_version == GameVersion::BB) {
|
||||
const auto& cmd = check_size_t<G_SellItemAtShop_BB_6xC0>(data, size);
|
||||
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
|
||||
@@ -1797,7 +1798,7 @@ static void on_sell_item_at_shop_bb(shared_ptr<Client> c, uint8_t command, uint8
|
||||
l->log.info("Player %hhu sold inventory item %08" PRIX32 " (%s) for %zu Meseta",
|
||||
c->lobby_client_id, cmd.item_id.load(), name.c_str(), price);
|
||||
p->print_inventory(stderr, c->version(), s->item_name_index);
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5DESTROY/SELL %08" PRIX32 "\n+%zu Meseta\n%s",
|
||||
cmd.item_id.load(), price, name.c_str());
|
||||
@@ -1811,7 +1812,7 @@ static void on_buy_shop_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, const vo
|
||||
auto l = c->require_lobby();
|
||||
if (l->base_version == GameVersion::BB) {
|
||||
const auto& cmd = check_size_t<G_BuyShopItem_BB_6xB7>(data, size);
|
||||
if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
if (!l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
throw logic_error("item tracking not enabled in BB game");
|
||||
}
|
||||
|
||||
@@ -1837,7 +1838,7 @@ static void on_buy_shop_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, const vo
|
||||
l->log.info("Player %hhu purchased item %08" PRIX32 " (%s) for %zu meseta",
|
||||
c->lobby_client_id, cmd.inventory_item_id.load(), name.c_str(), price);
|
||||
p->print_inventory(stderr, c->version(), s->item_name_index);
|
||||
if (c->options.debug) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5CREATE/BUY %08" PRIX32 "\n-%zu Meseta\n%s",
|
||||
cmd.inventory_item_id.load(), price, name.c_str());
|
||||
@@ -1858,7 +1859,7 @@ static void on_battle_restart_bb(shared_ptr<Client> c, uint8_t, uint8_t, const v
|
||||
if (l->is_game() &&
|
||||
(l->base_version == GameVersion::BB) &&
|
||||
(l->mode == GameMode::BATTLE) &&
|
||||
(l->flags & Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
const auto& cmd = check_size_t<G_RestartBattle_BB_6xCF>(data, size);
|
||||
|
||||
shared_ptr<BattleRules> new_rules(new BattleRules(cmd.rules));
|
||||
@@ -1884,7 +1885,7 @@ static void on_battle_level_up_bb(shared_ptr<Client> c, uint8_t, uint8_t, const
|
||||
if (l->is_game() &&
|
||||
(l->base_version == GameVersion::BB) &&
|
||||
(l->mode == GameMode::BATTLE) &&
|
||||
(l->flags & Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
const auto& cmd = check_size_t<G_BattleModeLevelUp_BB_6xD0>(data, size);
|
||||
auto lc = l->clients[cmd.header.client_id];
|
||||
if (lc) {
|
||||
@@ -1903,7 +1904,7 @@ static void on_quest_exchange_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, co
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() &&
|
||||
(l->base_version == GameVersion::BB) &&
|
||||
(l->flags & Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
const auto& cmd = check_size_t<G_ExchangeItemInQuest_BB_6xD5>(data, size);
|
||||
|
||||
try {
|
||||
@@ -1973,7 +1974,7 @@ static void on_photon_drop_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t, c
|
||||
|
||||
static void on_photon_crystal_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() && (l->base_version == GameVersion::BB) && (l->flags & Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
if (l->is_game() && (l->base_version == GameVersion::BB) && l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
check_size_t<G_BlackPaperDealPhotonCrystalExchange_BB_6xDF>(data, size);
|
||||
auto p = c->game_data.player();
|
||||
size_t index = p->inventory.find_item_by_primary_identifier(0x031002);
|
||||
@@ -1985,7 +1986,7 @@ static void on_photon_crystal_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t
|
||||
|
||||
static void on_momoka_item_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() && (l->base_version == GameVersion::BB) && (l->flags & Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
if (l->is_game() && (l->base_version == GameVersion::BB) && l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
const auto& cmd = check_size_t<G_MomokaItemExchange_BB_6xD9>(data, size);
|
||||
auto p = c->game_data.player();
|
||||
try {
|
||||
@@ -2014,7 +2015,7 @@ static void on_momoka_item_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t, c
|
||||
|
||||
static void on_upgrade_weapon_attribute_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() && (l->base_version == GameVersion::BB) && (l->flags & Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
if (l->is_game() && (l->base_version == GameVersion::BB) && l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
const auto& cmd = check_size_t<G_UpgradeWeaponAttribute_BB_6xDA>(data, size);
|
||||
auto p = c->game_data.player();
|
||||
try {
|
||||
@@ -2338,7 +2339,8 @@ void on_subcommand_multi(shared_ptr<Client> c, uint8_t command, uint8_t flag, co
|
||||
if (data.empty()) {
|
||||
throw runtime_error("game command is empty");
|
||||
}
|
||||
if (c->version() == GameVersion::DC && (c->flags & (Client::Flag::IS_DC_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE))) {
|
||||
if (c->version() == GameVersion::DC &&
|
||||
(c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION) || c->config.check_flag(Client::Flag::IS_DC_V1_PROTOTYPE))) {
|
||||
// TODO: We should convert these to non-trial formats and vice versa
|
||||
forward_subcommand(c, command, flag, data.data(), data.size());
|
||||
} else {
|
||||
|
||||
+94
-75
@@ -84,7 +84,7 @@ void send_command_excluding_client(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
||||
void send_command_if_not_loading(shared_ptr<Lobby> l,
|
||||
uint16_t command, uint32_t flag, const void* data, size_t size) {
|
||||
for (auto& client : l->clients) {
|
||||
if (!client || (client->flags & Client::Flag::LOADING)) {
|
||||
if (!client || client->config.check_flag(Client::Flag::LOADING)) {
|
||||
continue;
|
||||
}
|
||||
send_command(client, command, flag, data, size);
|
||||
@@ -246,11 +246,31 @@ void send_server_init(shared_ptr<Client> c, uint8_t flags) {
|
||||
}
|
||||
|
||||
void send_update_client_config(shared_ptr<Client> c) {
|
||||
S_UpdateClientConfig_DC_PC_V3_04 cmd;
|
||||
cmd.player_tag = 0x00010000;
|
||||
cmd.guild_card_number = c->license->serial_number;
|
||||
cmd.cfg = c->export_config();
|
||||
send_command_t(c, 0x04, 0x00, cmd);
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
case GameVersion::PC: {
|
||||
if (!c->config.check_flag(Client::Flag::HAS_GUILD_CARD_NUMBER)) {
|
||||
c->config.set_flag(Client::Flag::HAS_GUILD_CARD_NUMBER);
|
||||
S_UpdateClientConfig_DC_PC_04 cmd;
|
||||
cmd.player_tag = 0x00010000;
|
||||
cmd.guild_card_number = c->license->serial_number;
|
||||
send_command_t(c, 0x04, 0x00, cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GameVersion::GC:
|
||||
case GameVersion::XB: {
|
||||
c->config.set_flag(Client::Flag::HAS_GUILD_CARD_NUMBER);
|
||||
S_UpdateClientConfig_V3_04 cmd;
|
||||
cmd.player_tag = 0x00010000;
|
||||
cmd.guild_card_number = c->license->serial_number;
|
||||
c->config.serialize_into(cmd.client_config);
|
||||
send_command_t(c, 0x04, 0x00, cmd);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw logic_error("send_update_client_config called on incorrect game version");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CommandT>
|
||||
@@ -325,15 +345,15 @@ void prepare_client_for_patches(shared_ptr<Client> c, std::function<void()> on_c
|
||||
return;
|
||||
}
|
||||
if (c->version() == GameVersion::GC &&
|
||||
c->specific_version == default_specific_version_for_version(GameVersion::GC, -1)) {
|
||||
c->config.specific_version == default_specific_version_for_version(GameVersion::GC, -1)) {
|
||||
send_function_call(c, s->function_code_index->name_to_function.at("VersionDetect"));
|
||||
c->function_call_response_queue.emplace_back([wc = weak_ptr<Client>(c), on_complete](uint32_t specific_version, uint32_t) -> void {
|
||||
auto c = wc.lock();
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
c->specific_version = specific_version;
|
||||
c->log.info("Version detected as %08" PRIX32, c->specific_version);
|
||||
c->config.specific_version = specific_version;
|
||||
c->log.info("Version detected as %08" PRIX32, c->config.specific_version);
|
||||
on_complete();
|
||||
});
|
||||
} else {
|
||||
@@ -341,7 +361,7 @@ void prepare_client_for_patches(shared_ptr<Client> c, std::function<void()> on_c
|
||||
}
|
||||
};
|
||||
|
||||
if (!(c->flags & Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH)) {
|
||||
if (!c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH)) {
|
||||
send_function_call(c, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0x80000000, 8, 0x7F2734EC);
|
||||
c->function_call_response_queue.emplace_back([s, wc = weak_ptr<Client>(c), send_version_detect](uint32_t, uint32_t header_checksum) -> void {
|
||||
auto c = wc.lock();
|
||||
@@ -349,8 +369,8 @@ void prepare_client_for_patches(shared_ptr<Client> c, std::function<void()> on_c
|
||||
return;
|
||||
}
|
||||
try {
|
||||
c->specific_version = specific_version_for_gc_header_checksum(header_checksum);
|
||||
c->log.info("Version detected as %08" PRIX32 " from header checksum %08" PRIX32, c->specific_version, header_checksum);
|
||||
c->config.specific_version = specific_version_for_gc_header_checksum(header_checksum);
|
||||
c->log.info("Version detected as %08" PRIX32 " from header checksum %08" PRIX32, c->config.specific_version, header_checksum);
|
||||
} catch (const out_of_range&) {
|
||||
c->log.info("Could not detect specific version from header checksum %08" PRIX32, header_checksum);
|
||||
}
|
||||
@@ -361,7 +381,7 @@ void prepare_client_for_patches(shared_ptr<Client> c, std::function<void()> on_c
|
||||
return;
|
||||
}
|
||||
c->log.info("Client cache behavior patched");
|
||||
c->flags |= Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
c->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
|
||||
send_update_client_config(c);
|
||||
send_version_detect();
|
||||
});
|
||||
@@ -381,7 +401,7 @@ void send_function_call(
|
||||
uint32_t override_relocations_offset) {
|
||||
return send_function_call(
|
||||
c->channel,
|
||||
c->flags,
|
||||
c->config,
|
||||
code,
|
||||
label_writes,
|
||||
suffix,
|
||||
@@ -392,17 +412,17 @@ void send_function_call(
|
||||
|
||||
void send_function_call(
|
||||
Channel& ch,
|
||||
uint64_t client_flags,
|
||||
const Client::Config& client_config,
|
||||
shared_ptr<CompiledFunctionCode> code,
|
||||
const unordered_map<string, uint32_t>& label_writes,
|
||||
const string& suffix,
|
||||
uint32_t checksum_addr,
|
||||
uint32_t checksum_size,
|
||||
uint32_t override_relocations_offset) {
|
||||
if (client_flags & Client::Flag::NO_SEND_FUNCTION_CALL) {
|
||||
if (client_config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
|
||||
throw logic_error("client does not support function calls");
|
||||
}
|
||||
if (code.get() && (client_flags & Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
|
||||
if (code.get() && client_config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
|
||||
throw logic_error("client only supports checksums in send_function_call");
|
||||
}
|
||||
|
||||
@@ -412,7 +432,7 @@ void send_function_call(
|
||||
data = code->generate_client_command(label_writes, suffix, override_relocations_offset);
|
||||
index = code->index;
|
||||
|
||||
if (client_flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
|
||||
if (client_config.check_flag(Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL)) {
|
||||
uint32_t key = random_object<uint32_t>();
|
||||
|
||||
// This format was probably never used on any little-endian system, but we
|
||||
@@ -469,13 +489,13 @@ void send_pc_console_split_reconnect(shared_ptr<Client> c, uint32_t address,
|
||||
send_command_t(c, 0x19, 0x00, cmd);
|
||||
}
|
||||
|
||||
void send_client_init_bb(shared_ptr<Client> c, uint32_t error) {
|
||||
void send_client_init_bb(shared_ptr<Client> c, uint32_t error_code) {
|
||||
S_ClientInit_BB_00E6 cmd;
|
||||
cmd.error = error;
|
||||
cmd.error_code = error_code;
|
||||
cmd.player_tag = 0x00010000;
|
||||
cmd.guild_card_number = c->license->serial_number;
|
||||
cmd.team_id = static_cast<uint32_t>(random_object<uint32_t>());
|
||||
cmd.cfg = c->export_config_bb();
|
||||
c->config.serialize_into(cmd.client_config);
|
||||
cmd.can_create_team = 1;
|
||||
cmd.episode_4_unlocked = 1;
|
||||
send_command_t(c, 0x00E6, 0x00000000, cmd);
|
||||
@@ -744,11 +764,11 @@ void send_message_box(shared_ptr<Client> c, const string& text) {
|
||||
default:
|
||||
throw logic_error("invalid game version");
|
||||
}
|
||||
send_text(c->channel, command, text, (c->flags & Client::Flag::IS_DC_TRIAL_EDITION) ? ColorMode::STRIP : ColorMode::ADD);
|
||||
send_text(c->channel, command, text, c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION) ? ColorMode::STRIP : ColorMode::ADD);
|
||||
}
|
||||
|
||||
void send_ep3_timed_message_box(Channel& ch, uint32_t frames, const string& message) {
|
||||
string encoded = tt_encode_marked(message, ch.language, false);
|
||||
string encoded = tt_encode_marked(add_color(message), ch.language, false);
|
||||
StringWriter w;
|
||||
w.put<S_TimedMessageBoxHeader_GC_Ep3_EA>({frames});
|
||||
w.write(encoded);
|
||||
@@ -766,15 +786,15 @@ void send_lobby_name(shared_ptr<Client> c, const string& text) {
|
||||
void send_quest_info(shared_ptr<Client> c, const string& text, bool is_download_quest) {
|
||||
send_text(
|
||||
c->channel, is_download_quest ? 0xA5 : 0xA3, text,
|
||||
(c->flags & Client::Flag::IS_DC_TRIAL_EDITION) ? ColorMode::STRIP : ColorMode::ADD);
|
||||
c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION) ? ColorMode::STRIP : ColorMode::ADD);
|
||||
}
|
||||
|
||||
void send_lobby_message_box(shared_ptr<Client> c, const string& text) {
|
||||
send_header_text(c->channel, 0x01, 0, text, (c->flags & Client::Flag::IS_DC_TRIAL_EDITION) ? ColorMode::STRIP : ColorMode::ADD);
|
||||
send_header_text(c->channel, 0x01, 0, text, c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION) ? ColorMode::STRIP : ColorMode::ADD);
|
||||
}
|
||||
|
||||
void send_ship_info(shared_ptr<Client> c, const string& text) {
|
||||
send_header_text(c->channel, 0x11, 0, text, (c->flags & Client::Flag::IS_DC_TRIAL_EDITION) ? ColorMode::STRIP : ColorMode::ADD);
|
||||
send_header_text(c->channel, 0x11, 0, text, c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION) ? ColorMode::STRIP : ColorMode::ADD);
|
||||
}
|
||||
|
||||
void send_ship_info(Channel& ch, const string& text) {
|
||||
@@ -786,7 +806,7 @@ void send_text_message(Channel& ch, const string& text) {
|
||||
}
|
||||
|
||||
void send_text_message(shared_ptr<Client> c, const string& text) {
|
||||
if (!(c->flags & Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
if (!c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
send_header_text(c->channel, 0xB0, 0, text, ColorMode::ADD);
|
||||
}
|
||||
}
|
||||
@@ -814,7 +834,7 @@ __attribute__((format(printf, 2, 3))) void send_ep3_text_message_printf(shared_p
|
||||
va_end(va);
|
||||
for (auto& it : s->id_to_lobby) {
|
||||
for (auto& c : it.second->clients) {
|
||||
if (c && (c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (c && c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
send_text_message(c, buf);
|
||||
}
|
||||
}
|
||||
@@ -896,7 +916,7 @@ void send_prepared_chat_message(shared_ptr<Lobby> l, uint32_t from_guild_card_nu
|
||||
void send_chat_message(shared_ptr<Client> c, uint32_t from_guild_card_number, const string& from_name, const string& text, char private_flags) {
|
||||
string prepared_data = prepare_chat_data(
|
||||
c->version(),
|
||||
c->flags & Client::Flag::IS_DC_TRIAL_EDITION,
|
||||
c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION),
|
||||
c->language(),
|
||||
c->lobby_client_id,
|
||||
from_name,
|
||||
@@ -1143,7 +1163,7 @@ void send_menu_t(shared_ptr<Client> c, shared_ptr<const Menu> menu, bool is_info
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_DC);
|
||||
if (c->flags & Client::Flag::IS_DC_TRIAL_EDITION) {
|
||||
if (c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_DCNTE);
|
||||
}
|
||||
break;
|
||||
@@ -1152,7 +1172,7 @@ void send_menu_t(shared_ptr<Client> c, shared_ptr<const Menu> menu, bool is_info
|
||||
break;
|
||||
case GameVersion::GC:
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_GC);
|
||||
if (c->flags & Client::Flag::IS_GC_TRIAL_EDITION) {
|
||||
if (c->config.check_flag(Client::Flag::IS_GC_TRIAL_EDITION)) {
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_GC_TRIAL_EDITION);
|
||||
}
|
||||
break;
|
||||
@@ -1166,13 +1186,13 @@ void send_menu_t(shared_ptr<Client> c, shared_ptr<const Menu> menu, bool is_info
|
||||
throw runtime_error("menus not supported for this game version");
|
||||
}
|
||||
if (item.flags & MenuItem::Flag::REQUIRES_MESSAGE_BOXES) {
|
||||
is_visible &= !(c->flags & Client::Flag::NO_D6);
|
||||
is_visible &= !c->config.check_flag(Client::Flag::NO_D6);
|
||||
}
|
||||
if (item.flags & MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL) {
|
||||
is_visible &= !(c->flags & Client::Flag::NO_SEND_FUNCTION_CALL);
|
||||
is_visible &= !c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL);
|
||||
}
|
||||
if (item.flags & MenuItem::Flag::REQUIRES_SAVE_DISABLED) {
|
||||
is_visible &= !(c->flags & Client::Flag::SAVE_ENABLED);
|
||||
is_visible &= !c->config.check_flag(Client::Flag::SAVE_ENABLED);
|
||||
}
|
||||
if (item.flags & MenuItem::Flag::INVISIBLE_IN_INFO_MENU) {
|
||||
is_visible &= !is_info_menu;
|
||||
@@ -1227,8 +1247,7 @@ void send_game_menu_t(
|
||||
if (!l->version_is_allowed(c->quest_version())) {
|
||||
continue;
|
||||
}
|
||||
bool l_is_spectator_team = !!(l->flags & Lobby::Flag::IS_SPECTATOR_TEAM);
|
||||
if (l_is_spectator_team != is_spectator_team_list) {
|
||||
if (l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM) != is_spectator_team_list) {
|
||||
continue;
|
||||
}
|
||||
if (show_tournaments_only && !l->tournament_match) {
|
||||
@@ -1264,7 +1283,7 @@ void send_game_menu_t(
|
||||
e.episode = ((c->version() == GameVersion::BB) ? (l->max_clients << 4) : 0) | episode_num;
|
||||
}
|
||||
if (l->is_ep3()) {
|
||||
e.flags = (l->password.empty() ? 0 : 2) | ((l->flags & Lobby::Flag::BATTLE_IN_PROGRESS) ? 4 : 0);
|
||||
e.flags = (l->password.empty() ? 0 : 2) | (l->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS) ? 4 : 0);
|
||||
} else {
|
||||
e.flags = ((episode_num << 6) | (l->password.empty() ? 0 : 2));
|
||||
switch (l->mode) {
|
||||
@@ -1396,13 +1415,13 @@ void send_lobby_list(shared_ptr<Client> c) {
|
||||
auto s = c->require_server_state();
|
||||
vector<S_LobbyListEntry_83> entries;
|
||||
for (shared_ptr<Lobby> l : s->all_lobbies()) {
|
||||
if (!(l->flags & Lobby::Flag::DEFAULT)) {
|
||||
if (!l->check_flag(Lobby::Flag::DEFAULT)) {
|
||||
continue;
|
||||
}
|
||||
if ((l->flags & Lobby::Flag::V2_AND_LATER) && (c->flags & Client::Flag::IS_DC_V1)) {
|
||||
if (l->check_flag(Lobby::Flag::V2_AND_LATER) && c->config.check_flag(Client::Flag::IS_DC_V1)) {
|
||||
continue;
|
||||
}
|
||||
if (l->is_ep3() && !(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (l->is_ep3() && !c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
continue;
|
||||
}
|
||||
auto& e = entries.emplace_back();
|
||||
@@ -1442,13 +1461,13 @@ void send_player_records_t(shared_ptr<Client> c, shared_ptr<Lobby> l, shared_ptr
|
||||
}
|
||||
|
||||
static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
if (!(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (!c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
throw runtime_error("lobby is not Episode 3");
|
||||
}
|
||||
if (!l->is_ep3()) {
|
||||
throw runtime_error("lobby is not Episode 3");
|
||||
}
|
||||
if (!(l->flags & Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
if (!l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
throw runtime_error("lobby is not a spectator team");
|
||||
}
|
||||
|
||||
@@ -1474,7 +1493,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
|
||||
auto wc_p = wc->game_data.player();
|
||||
auto& p = cmd.players[z];
|
||||
p.lobby_data.player_tag = 0x00010000;
|
||||
p.lobby_data.guild_card = wc->license->serial_number;
|
||||
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());
|
||||
p.inventory = wc_p->inventory;
|
||||
@@ -1522,7 +1541,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
|
||||
|
||||
auto& e = cmd.entries[client_id];
|
||||
e.player_tag = 0x00010000;
|
||||
e.guild_card_number = entry.lobby_data.guild_card;
|
||||
e.guild_card_number = entry.lobby_data.guild_card_number;
|
||||
e.name = entry.disp.visual.name;
|
||||
e.present = 1;
|
||||
e.level = entry.level.load();
|
||||
@@ -1542,7 +1561,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
|
||||
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 = other_c->license->serial_number;
|
||||
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());
|
||||
cmd_p.inventory = other_p->inventory;
|
||||
@@ -1567,7 +1586,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
|
||||
}
|
||||
|
||||
void send_join_game(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
if (l->flags & Lobby::Flag::IS_SPECTATOR_TEAM) {
|
||||
if (l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
send_join_spectator_team(c, l);
|
||||
return;
|
||||
}
|
||||
@@ -1578,7 +1597,7 @@ void send_join_game(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
auto lc = l->clients[x];
|
||||
if (lc) {
|
||||
cmd.lobby_data[x].player_tag = 0x00010000;
|
||||
cmd.lobby_data[x].guild_card = lc->license->serial_number;
|
||||
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->game_data.player()->disp.name.decode(lc->language()), c->language());
|
||||
player_count++;
|
||||
@@ -1623,7 +1642,7 @@ void send_join_game(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC: {
|
||||
if (c->flags & Client::Flag::IS_DC_TRIAL_EDITION) {
|
||||
if (c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
S_JoinGame_DCNTE_64 cmd;
|
||||
cmd.client_id = c->lobby_client_id;
|
||||
cmd.leader_id = l->leader_id;
|
||||
@@ -1645,7 +1664,7 @@ void send_join_game(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
break;
|
||||
}
|
||||
case GameVersion::GC: {
|
||||
if (c->flags & Client::Flag::IS_EPISODE_3) {
|
||||
if (c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
S_JoinGame_GC_Ep3_64 cmd;
|
||||
size_t player_count = populate_v3_cmd(cmd);
|
||||
for (size_t x = 0; x < 4; x++) {
|
||||
@@ -1692,7 +1711,7 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l, shared_ptr<Cli
|
||||
uint8_t command;
|
||||
if (l->is_game()) {
|
||||
if (joining_client) {
|
||||
command = (l->flags & Lobby::Flag::IS_SPECTATOR_TEAM) ? 0xEB : 0x65;
|
||||
command = l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM) ? 0xEB : 0x65;
|
||||
} else {
|
||||
throw logic_error("send_join_lobby_t should not be used for primary game join command");
|
||||
}
|
||||
@@ -1700,15 +1719,15 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l, shared_ptr<Cli
|
||||
command = joining_client ? 0x68 : 0x67;
|
||||
}
|
||||
|
||||
if ((c->version() != GameVersion::DC) || !(c->flags & Client::Flag::IS_DC_V1)) {
|
||||
if ((c->version() != GameVersion::DC) || !c->config.check_flag(Client::Flag::IS_DC_V1)) {
|
||||
send_player_records_t<RecordsT>(c, l, joining_client);
|
||||
}
|
||||
|
||||
uint8_t lobby_type;
|
||||
if (c->options.override_lobby_number >= 0) {
|
||||
lobby_type = c->options.override_lobby_number;
|
||||
} else if (l->flags & Lobby::Flag::IS_OVERFLOW) {
|
||||
lobby_type = (c->flags & Client::Flag::IS_EPISODE_3) ? 15 : 0;
|
||||
if (c->config.override_lobby_number != 0x80) {
|
||||
lobby_type = c->config.override_lobby_number;
|
||||
} else if (l->check_flag(Lobby::Flag::IS_OVERFLOW)) {
|
||||
lobby_type = c->config.check_flag(Client::Flag::IS_EPISODE_3) ? 15 : 0;
|
||||
} else {
|
||||
lobby_type = l->block - 1;
|
||||
}
|
||||
@@ -1716,7 +1735,7 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l, shared_ptr<Cli
|
||||
// Allow non-canonical lobby types on GC. They may work on other versions too,
|
||||
// but I haven't verified which values don't crash on each version.
|
||||
if (c->version() == GameVersion::GC) {
|
||||
if (c->flags & Client::Flag::IS_EPISODE_3) {
|
||||
if (c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
if ((lobby_type > 0x14) && (lobby_type < 0xE9)) {
|
||||
lobby_type = l->block - 1;
|
||||
}
|
||||
@@ -1758,7 +1777,7 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l, shared_ptr<Cli
|
||||
auto lp = lc->game_data.player();
|
||||
auto& e = cmd.entries[used_entries++];
|
||||
e.lobby_data.player_tag = 0x00010000;
|
||||
e.lobby_data.guild_card = lc->license->serial_number;
|
||||
e.lobby_data.guild_card_number = lc->license->serial_number;
|
||||
e.lobby_data.client_id = lc->lobby_client_id;
|
||||
if (UseLanguageMarkerInName) {
|
||||
e.lobby_data.name.encode("\tJ" + lp->disp.name.decode(lp->inventory.language), c->language());
|
||||
@@ -1808,7 +1827,7 @@ void send_join_lobby_dc_nte(shared_ptr<Client> c, shared_ptr<Lobby> l,
|
||||
auto lp = lc->game_data.player();
|
||||
auto& e = cmd.entries[used_entries++];
|
||||
e.lobby_data.player_tag = 0x00010000;
|
||||
e.lobby_data.guild_card = lc->license->serial_number;
|
||||
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());
|
||||
e.inventory = lp->inventory;
|
||||
@@ -1826,7 +1845,7 @@ void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
} else {
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
if (c->flags & (Client::Flag::IS_DC_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
|
||||
if (c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION) || c->config.check_flag(Client::Flag::IS_DC_V1_PROTOTYPE)) {
|
||||
send_join_lobby_dc_nte(c, l);
|
||||
} else {
|
||||
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3, PlayerRecordsEntry_DC, false>(c, l);
|
||||
@@ -1851,8 +1870,8 @@ void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
|
||||
// If the client will stop sending message box close confirmations after
|
||||
// joining any lobby, set the appropriate flag and update the client config
|
||||
if ((c->flags & (Client::Flag::NO_D6_AFTER_LOBBY | Client::Flag::NO_D6)) == Client::Flag::NO_D6_AFTER_LOBBY) {
|
||||
c->flags |= Client::Flag::NO_D6;
|
||||
if (c->config.check_flag(Client::Flag::NO_D6_AFTER_LOBBY) && !c->config.check_flag(Client::Flag::NO_D6)) {
|
||||
c->config.set_flag(Client::Flag::NO_D6);
|
||||
send_update_client_config(c);
|
||||
}
|
||||
}
|
||||
@@ -1861,7 +1880,7 @@ void send_player_join_notification(shared_ptr<Client> c,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> joining_client) {
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
if (c->flags & (Client::Flag::IS_DC_TRIAL_EDITION | Client::Flag::IS_DC_V1_PROTOTYPE)) {
|
||||
if (c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION) || c->config.check_flag(Client::Flag::IS_DC_V1_PROTOTYPE)) {
|
||||
send_join_lobby_dc_nte(c, l, joining_client);
|
||||
} else {
|
||||
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3, PlayerRecordsEntry_DC, false>(c, l, joining_client);
|
||||
@@ -1888,7 +1907,7 @@ void send_player_leave_notification(shared_ptr<Lobby> l, uint8_t leaving_client_
|
||||
S_LeaveLobby_66_69_Ep3_E9 cmd = {leaving_client_id, l->leader_id, 1, 0};
|
||||
uint8_t cmd_num;
|
||||
if (l->is_game()) {
|
||||
cmd_num = (l->flags & Lobby::Flag::IS_SPECTATOR_TEAM) ? 0xE9 : 0x66;
|
||||
cmd_num = l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM) ? 0xE9 : 0x66;
|
||||
} else {
|
||||
cmd_num = 0x69;
|
||||
}
|
||||
@@ -1905,7 +1924,7 @@ void send_self_leave_notification(shared_ptr<Client> c) {
|
||||
|
||||
void send_get_player_info(shared_ptr<Client> c) {
|
||||
if ((c->version() == GameVersion::DC) &&
|
||||
(c->flags & Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
c->config.check_flag(Client::Flag::IS_DC_TRIAL_EDITION)) {
|
||||
send_command(c, 0x8D, 0x00);
|
||||
} else {
|
||||
send_command(c, 0x95, 0x00);
|
||||
@@ -1931,7 +1950,7 @@ void send_execute_item_trade(shared_ptr<Client> c, const vector<ItemData>& items
|
||||
|
||||
void send_execute_card_trade(shared_ptr<Client> c,
|
||||
const vector<pair<uint32_t, uint32_t>>& card_to_count) {
|
||||
if (!(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (!c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
throw logic_error("cannot send trade cards command to non-Ep3 client");
|
||||
}
|
||||
|
||||
@@ -1972,7 +1991,7 @@ void send_arrow_update(shared_ptr<Lobby> l) {
|
||||
}
|
||||
|
||||
for (size_t x = 0; x < l->max_clients; x++) {
|
||||
if (!l->clients[x] || (l->clients[x]->flags & Client::Flag::IS_DC_V1)) {
|
||||
if (!l->clients[x] || l->clients[x]->config.check_flag(Client::Flag::IS_DC_V1)) {
|
||||
continue;
|
||||
}
|
||||
send_command_vt(l->clients[x], 0x88, entries.size(), entries);
|
||||
@@ -2233,9 +2252,9 @@ void send_quest_function_call(shared_ptr<Client> c, uint16_t function_id) {
|
||||
// ep3 only commands
|
||||
|
||||
void send_ep3_card_list_update(shared_ptr<Client> c) {
|
||||
if (!(c->flags & Client::Flag::HAS_EP3_CARD_DEFS)) {
|
||||
if (!c->config.check_flag(Client::Flag::HAS_EP3_CARD_DEFS)) {
|
||||
auto s = c->require_server_state();
|
||||
const auto& data = (c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)
|
||||
const auto& data = c->config.check_flag(Client::Flag::IS_EP3_TRIAL_EDITION)
|
||||
? s->ep3_card_index_trial->get_compressed_definitions()
|
||||
: s->ep3_card_index->get_compressed_definitions();
|
||||
|
||||
@@ -2309,7 +2328,7 @@ void send_ep3_set_context_token(shared_ptr<Client> c, uint32_t context_token) {
|
||||
void send_ep3_confirm_tournament_entry(
|
||||
shared_ptr<Client> c,
|
||||
shared_ptr<const Episode3::Tournament> tourn) {
|
||||
if (c->flags & Client::Flag::IS_EP3_TRIAL_EDITION) {
|
||||
if (c->config.check_flag(Client::Flag::IS_EP3_TRIAL_EDITION)) {
|
||||
throw runtime_error("cannot send tournament entry command to Episode 3 Trial Edition client");
|
||||
}
|
||||
|
||||
@@ -2426,7 +2445,7 @@ void send_ep3_tournament_details(
|
||||
}
|
||||
|
||||
string ep3_description_for_client(shared_ptr<Client> c) {
|
||||
if (!(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
if (!c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
throw runtime_error("client is not Episode 3");
|
||||
}
|
||||
auto player = c->game_data.player();
|
||||
@@ -2440,7 +2459,7 @@ string ep3_description_for_client(shared_ptr<Client> c) {
|
||||
void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
|
||||
shared_ptr<Lobby> primary_lobby;
|
||||
if (l->flags & Lobby::Flag::IS_SPECTATOR_TEAM) {
|
||||
if (l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
primary_lobby = l->watched_lobby.lock();
|
||||
} else {
|
||||
primary_lobby = l;
|
||||
@@ -2852,7 +2871,7 @@ bool send_quest_barrier_if_all_clients_ready(shared_ptr<Lobby> l) {
|
||||
if (!l->clients[x]) {
|
||||
continue;
|
||||
}
|
||||
if (l->clients[x]->flags & Client::Flag::LOADING_QUEST) {
|
||||
if (l->clients[x]->config.check_flag(Client::Flag::LOADING_QUEST)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2886,7 +2905,7 @@ bool send_ep3_start_tournament_deck_select_if_all_clients_ready(shared_ptr<Lobby
|
||||
if (!l->clients[x]) {
|
||||
continue;
|
||||
}
|
||||
if (l->clients[x]->flags & Client::Flag::LOADING_TOURNAMENT) {
|
||||
if (l->clients[x]->config.check_flag(Client::Flag::LOADING_TOURNAMENT)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2930,7 +2949,7 @@ void send_ep3_card_auction(shared_ptr<Lobby> l) {
|
||||
distribution_size += e.probability;
|
||||
}
|
||||
|
||||
auto card_index = (l->flags & Lobby::Flag::IS_EP3_TRIAL)
|
||||
auto card_index = l->check_flag(Lobby::Flag::IS_EP3_TRIAL)
|
||||
? s->ep3_card_index_trial
|
||||
: s->ep3_card_index;
|
||||
|
||||
@@ -2983,7 +3002,7 @@ void send_change_event(shared_ptr<Client> c, uint8_t new_event) {
|
||||
// This command isn't supported on versions before V3, nor on Trial Edition.
|
||||
if ((c->version() == GameVersion::DC) ||
|
||||
(c->version() == GameVersion::PC) ||
|
||||
(c->flags & Client::Flag::IS_GC_TRIAL_EDITION)) {
|
||||
c->config.check_flag(Client::Flag::IS_GC_TRIAL_EDITION)) {
|
||||
return;
|
||||
}
|
||||
send_command(c, 0xDA, new_event);
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ void send_quest_buffer_overflow(std::shared_ptr<Client> c);
|
||||
void prepare_client_for_patches(std::shared_ptr<Client> c, std::function<void()> on_complete);
|
||||
void send_function_call(
|
||||
Channel& ch,
|
||||
uint64_t client_flags,
|
||||
const Client::Config& client_config,
|
||||
std::shared_ptr<CompiledFunctionCode> code,
|
||||
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
||||
const std::string& suffix = "",
|
||||
|
||||
+29
-22
@@ -45,6 +45,16 @@ static void set_boolean(bool* target, const string& args) {
|
||||
}
|
||||
}
|
||||
|
||||
static void set_flag(Client::Config& config, Client::Flag flag, const string& args) {
|
||||
if (args == "on") {
|
||||
config.set_flag(flag);
|
||||
} else if (args == "off") {
|
||||
config.clear_flag(flag);
|
||||
} else {
|
||||
throw invalid_argument("argument must be \"on\" or \"off\"");
|
||||
}
|
||||
}
|
||||
|
||||
static string get_quoted_string(string& s) {
|
||||
string ret;
|
||||
char end_char = (s.at(0) == '\"') ? '\"' : ' ';
|
||||
@@ -346,7 +356,7 @@ Proxy session commands:\n\
|
||||
} else if (mask == "root") {
|
||||
l->flags = License::Flag::ROOT;
|
||||
} else {
|
||||
l->flags = stoul(mask);
|
||||
l->flags = stoul(mask, nullptr, 16);
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -403,7 +413,7 @@ Proxy session commands:\n\
|
||||
l->serial_number = stoul(token.substr(7));
|
||||
|
||||
} else if (starts_with(token, "flags=")) {
|
||||
string mask = token.substr(11);
|
||||
string mask = token.substr(6);
|
||||
if (mask == "normal") {
|
||||
l->flags = 0;
|
||||
} else if (mask == "mod") {
|
||||
@@ -413,7 +423,7 @@ Proxy session commands:\n\
|
||||
} else if (mask == "root") {
|
||||
l->flags = License::Flag::ROOT;
|
||||
} else {
|
||||
l->flags = stoul(mask);
|
||||
l->flags = stoul(mask, nullptr, 16);
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -695,7 +705,7 @@ Proxy session commands:\n\
|
||||
} else if ((command_name == "wc") || (command_name == "wchat")) {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
if ((session->version() != GameVersion::GC) ||
|
||||
!(session->newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3)) {
|
||||
!session->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
throw runtime_error("wchat can only be used on Episode 3");
|
||||
}
|
||||
string data(8, '\0');
|
||||
@@ -741,29 +751,30 @@ Proxy session commands:\n\
|
||||
} else if (command_name == "set-override-section-id") {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
if (command_args.empty()) {
|
||||
session->options.override_section_id = -1;
|
||||
session->config.override_section_id = 0xFF;
|
||||
} else {
|
||||
session->options.override_section_id = section_id_for_name(command_args);
|
||||
session->config.override_section_id = section_id_for_name(command_args);
|
||||
}
|
||||
|
||||
} else if (command_name == "set-override-event") {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
if (command_args.empty()) {
|
||||
session->options.override_lobby_event = -1;
|
||||
session->config.override_lobby_event = 0xFF;
|
||||
} else {
|
||||
session->options.override_lobby_event = event_for_name(command_args);
|
||||
session->config.override_lobby_event = event_for_name(command_args);
|
||||
if ((session->version() != GameVersion::DC) &&
|
||||
(session->version() != GameVersion::PC) && (!((session->version() == GameVersion::GC) && (session->newserv_client_config.cfg.flags & Client::Flag::IS_GC_TRIAL_EDITION)))) {
|
||||
session->client_channel.send(0xDA, session->options.override_lobby_event);
|
||||
(session->version() != GameVersion::PC) &&
|
||||
!((session->version() == GameVersion::GC) && session->config.check_flag(Client::Flag::IS_GC_TRIAL_EDITION))) {
|
||||
session->client_channel.send(0xDA, session->config.override_lobby_event);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (command_name == "set-override-lobby-number") {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
if (command_args.empty()) {
|
||||
session->options.override_lobby_number = -1;
|
||||
session->config.override_lobby_number = 0x80;
|
||||
} else {
|
||||
session->options.override_lobby_number = lobby_type_for_name(command_args);
|
||||
session->config.override_lobby_number = lobby_type_for_name(command_args);
|
||||
}
|
||||
|
||||
} else if (command_name == "set-challenge-rank-title") {
|
||||
@@ -776,31 +787,27 @@ Proxy session commands:\n\
|
||||
|
||||
} else if (command_name == "set-chat-filter") {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
set_boolean(&session->options.enable_chat_filter, command_args);
|
||||
set_flag(session->config, Client::Flag::PROXY_CHAT_FILTER_ENABLED, command_args);
|
||||
|
||||
} else if (command_name == "set-infinite-hp") {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
set_boolean(&session->options.infinite_hp, command_args);
|
||||
set_flag(session->config, Client::Flag::INFINITE_HP_ENABLED, command_args);
|
||||
|
||||
} else if (command_name == "set-infinite-tp") {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
set_boolean(&session->options.infinite_tp, command_args);
|
||||
set_flag(session->config, Client::Flag::INFINITE_TP_ENABLED, command_args);
|
||||
|
||||
} else if (command_name == "set-switch-assist") {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
set_boolean(&session->options.switch_assist, command_args);
|
||||
set_flag(session->config, Client::Flag::SWITCH_ASSIST_ENABLED, command_args);
|
||||
|
||||
} else if (command_name == "set-save-files" && this->state->proxy_allow_save_files) {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
set_boolean(&session->options.save_files, command_args);
|
||||
set_flag(session->config, Client::Flag::PROXY_SAVE_FILES, command_args);
|
||||
|
||||
} else if (command_name == "set-block-function-calls") {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
if (command_args.empty()) {
|
||||
session->options.function_call_return_value = -1;
|
||||
} else {
|
||||
session->options.function_call_return_value = stoul(command_args);
|
||||
}
|
||||
set_flag(session->config, Client::Flag::PROXY_BLOCK_FUNCTION_CALLS, command_args);
|
||||
|
||||
} else if ((command_name == "create-item") || (command_name == "set-next-item")) {
|
||||
auto session = this->get_proxy_session(session_name);
|
||||
|
||||
+16
-14
@@ -60,11 +60,12 @@ void ServerState::init() {
|
||||
bool is_ep3_only = (x > 14);
|
||||
|
||||
shared_ptr<Lobby> l = this->create_lobby();
|
||||
l->flags |=
|
||||
Lobby::Flag::PUBLIC |
|
||||
Lobby::Flag::DEFAULT |
|
||||
Lobby::Flag::PERSISTENT |
|
||||
(v2_and_later_only ? Lobby::Flag::V2_AND_LATER : 0);
|
||||
l->set_flag(Lobby::Flag::PUBLIC);
|
||||
l->set_flag(Lobby::Flag::DEFAULT);
|
||||
l->set_flag(Lobby::Flag::PERSISTENT);
|
||||
if (v2_and_later_only) {
|
||||
l->set_flag(Lobby::Flag::V2_AND_LATER);
|
||||
}
|
||||
l->block = x + 1;
|
||||
l->name = lobby_name;
|
||||
l->max_clients = 12;
|
||||
@@ -122,8 +123,8 @@ void ServerState::add_client_to_available_lobby(shared_ptr<Client> c) {
|
||||
auto l = this->find_lobby(c->preferred_lobby_id);
|
||||
if (l &&
|
||||
!l->is_game() &&
|
||||
(l->flags & Lobby::Flag::PUBLIC) &&
|
||||
((c->flags & Client::Flag::IS_EPISODE_3) || (l->episode != Episode::EP3))) {
|
||||
l->check_flag(Lobby::Flag::PUBLIC) &&
|
||||
(c->config.check_flag(Client::Flag::IS_EPISODE_3) || (l->episode != Episode::EP3))) {
|
||||
l->add_client(c);
|
||||
added_to_lobby = l;
|
||||
}
|
||||
@@ -133,9 +134,9 @@ void ServerState::add_client_to_available_lobby(shared_ptr<Client> c) {
|
||||
|
||||
if (!added_to_lobby.get()) {
|
||||
const auto* search_order = &this->public_lobby_search_order_non_v1;
|
||||
if (c->flags & Client::Flag::IS_DC_V1) {
|
||||
if (c->config.check_flag(Client::Flag::IS_DC_V1)) {
|
||||
search_order = &this->public_lobby_search_order_v1;
|
||||
} else if (c->flags & Client::Flag::IS_EPISODE_3) {
|
||||
} else if (c->config.check_flag(Client::Flag::IS_EPISODE_3)) {
|
||||
search_order = &this->public_lobby_search_order_ep3;
|
||||
}
|
||||
for (const auto& l : *search_order) {
|
||||
@@ -150,7 +151,8 @@ void ServerState::add_client_to_available_lobby(shared_ptr<Client> c) {
|
||||
|
||||
if (!added_to_lobby) {
|
||||
added_to_lobby = this->create_lobby();
|
||||
added_to_lobby->flags |= Lobby::Flag::PUBLIC | Lobby::Flag::IS_OVERFLOW;
|
||||
added_to_lobby->set_flag(Lobby::Flag::PUBLIC);
|
||||
added_to_lobby->set_flag(Lobby::Flag::IS_OVERFLOW);
|
||||
added_to_lobby->block = 100;
|
||||
added_to_lobby->name = "Overflow";
|
||||
added_to_lobby->max_clients = 12;
|
||||
@@ -166,7 +168,7 @@ void ServerState::remove_client_from_lobby(shared_ptr<Client> c) {
|
||||
auto l = c->lobby.lock();
|
||||
if (l) {
|
||||
l->remove_client(c);
|
||||
if (!(l->flags & Lobby::Flag::PERSISTENT) && (l->count_clients() == 0)) {
|
||||
if (!l->check_flag(Lobby::Flag::PERSISTENT) && (l->count_clients() == 0)) {
|
||||
this->remove_lobby(l->lobby_id);
|
||||
} else {
|
||||
send_player_leave_notification(l, c->lobby_client_id);
|
||||
@@ -193,7 +195,7 @@ bool ServerState::change_client_lobby(
|
||||
}
|
||||
|
||||
if (current_lobby) {
|
||||
if (!(current_lobby->flags & Lobby::Flag::PERSISTENT) && (current_lobby->count_clients() == 0)) {
|
||||
if (!current_lobby->check_flag(Lobby::Flag::PERSISTENT) && (current_lobby->count_clients() == 0)) {
|
||||
this->remove_lobby(current_lobby->lobby_id);
|
||||
} else {
|
||||
send_player_leave_notification(current_lobby, old_lobby_client_id);
|
||||
@@ -263,7 +265,7 @@ void ServerState::remove_lobby(uint32_t lobby_id) {
|
||||
throw logic_error("attempted to delete lobby with clients in it");
|
||||
}
|
||||
|
||||
if (l->flags & Lobby::Flag::IS_SPECTATOR_TEAM) {
|
||||
if (l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM)) {
|
||||
auto primary_l = l->watched_lobby.lock();
|
||||
if (primary_l) {
|
||||
primary_l->log.info("Unlinking watcher lobby %" PRIX32, l->lobby_id);
|
||||
@@ -1153,7 +1155,7 @@ shared_ptr<const vector<string>> ServerState::information_contents_for_client(sh
|
||||
}
|
||||
|
||||
shared_ptr<const QuestIndex> ServerState::quest_index_for_client(shared_ptr<const Client> c) const {
|
||||
return (c->flags & Client::Flag::IS_EPISODE_3)
|
||||
return c->config.check_flag(Client::Flag::IS_EPISODE_3)
|
||||
? this->ep3_download_quest_index
|
||||
: this->default_quest_index;
|
||||
}
|
||||
|
||||
@@ -15,98 +15,6 @@ const vector<string> version_to_lobby_port_name = {
|
||||
const vector<string> version_to_proxy_port_name = {
|
||||
"", "dc-proxy", "pc-proxy", "gc-proxy", "xb-proxy", "bb-proxy"};
|
||||
|
||||
uint32_t flags_for_version(GameVersion version, int64_t sub_version) {
|
||||
switch (sub_version) {
|
||||
case -1: // Initial check (before sub_version recognition)
|
||||
switch (version) {
|
||||
case GameVersion::DC:
|
||||
return Client::Flag::NO_D6 |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
case GameVersion::GC:
|
||||
return 0;
|
||||
case GameVersion::XB:
|
||||
return Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
case GameVersion::PC:
|
||||
return Client::Flag::NO_D6 |
|
||||
Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
case GameVersion::PATCH:
|
||||
return Client::Flag::NO_D6 |
|
||||
Client::Flag::NO_SEND_FUNCTION_CALL;
|
||||
case GameVersion::BB:
|
||||
return Client::Flag::NO_D6 |
|
||||
Client::Flag::SAVE_ENABLED |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x20: // DCNTE, possibly also DCv1 JP
|
||||
case 0x21: // DCv1 US
|
||||
// In the case of DCNTE, the IS_DC_TRIAL_EDITION flag is already set when
|
||||
// we get here, so the remaining flags are the same as DCv1
|
||||
return Client::Flag::IS_DC_V1 |
|
||||
Client::Flag::NO_D6 |
|
||||
Client::Flag::NO_SEND_FUNCTION_CALL;
|
||||
case 0x23: // DCv1 EU
|
||||
return Client::Flag::IS_DC_V1 |
|
||||
Client::Flag::NO_D6 |
|
||||
Client::Flag::NO_SEND_FUNCTION_CALL;
|
||||
|
||||
case 0x25: // DCv2 JP
|
||||
case 0x26: // DCv2 US
|
||||
case 0x28: // DCv2 EU
|
||||
return Client::Flag::NO_D6 |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
|
||||
case 0x29: // PC
|
||||
return Client::Flag::NO_D6 |
|
||||
Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
|
||||
case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 JP v1.02, at least one version of PSO XB
|
||||
case 0x31: // GC Ep1&2 US v1.00, GC US v1.01, GC EU v1.00, GC JP v1.00
|
||||
case 0x34: // GC Ep1&2 JP v1.03
|
||||
// In the case of GC Trial Edition, the IS_GC_TRIAL_EDITION flag is
|
||||
// already set when we get here (because the client has used V2 encryption
|
||||
// instead of V3)
|
||||
return 0;
|
||||
case 0x32: // GC Ep1&2 EU 50Hz
|
||||
case 0x33: // GC Ep1&2 EU 60Hz
|
||||
return Client::Flag::NO_D6_AFTER_LOBBY;
|
||||
case 0x35: // GC Ep1&2 JP v1.04 (Plus)
|
||||
return Client::Flag::NO_D6_AFTER_LOBBY |
|
||||
Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
case 0x36: // GC Ep1&2 US v1.02 (Plus)
|
||||
case 0x39: // GC Ep1&2 JP v1.05 (Plus)
|
||||
return Client::Flag::NO_D6_AFTER_LOBBY |
|
||||
Client::Flag::NO_SEND_FUNCTION_CALL;
|
||||
|
||||
case 0x40: // GC Ep3 JP and Trial Edition
|
||||
// sub_version can't be used to tell JP final and Trial Edition apart; we
|
||||
// instead look at header.flag in the 61 command.
|
||||
return Client::Flag::NO_D6_AFTER_LOBBY |
|
||||
Client::Flag::IS_EPISODE_3 |
|
||||
Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
case 0x42: // Also GC Ep3 JP?
|
||||
return Client::Flag::NO_D6_AFTER_LOBBY |
|
||||
Client::Flag::IS_EPISODE_3 |
|
||||
Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
case 0x41: // GC Ep3 US
|
||||
return Client::Flag::NO_D6_AFTER_LOBBY |
|
||||
Client::Flag::IS_EPISODE_3 |
|
||||
Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
case 0x43: // GC Ep3 EU
|
||||
return Client::Flag::NO_D6_AFTER_LOBBY |
|
||||
Client::Flag::IS_EPISODE_3 |
|
||||
Client::Flag::NO_SEND_FUNCTION_CALL;
|
||||
}
|
||||
throw runtime_error(string_printf("unknown sub_version %" PRIX64, sub_version));
|
||||
}
|
||||
|
||||
const char* name_for_version(GameVersion version) {
|
||||
switch (version) {
|
||||
case GameVersion::GC:
|
||||
|
||||
@@ -28,7 +28,6 @@ extern const std::vector<std::string> version_to_login_port_name;
|
||||
extern const std::vector<std::string> version_to_lobby_port_name;
|
||||
extern const std::vector<std::string> version_to_proxy_port_name;
|
||||
|
||||
uint32_t flags_for_version(GameVersion version, int64_t sub_version);
|
||||
const char* name_for_version(GameVersion version);
|
||||
GameVersion version_for_name(const char* name);
|
||||
|
||||
|
||||
@@ -58,9 +58,7 @@ I 40469 2023-05-26 10:40:55 - [Commands] Received from C-1 (version=DC command=9
|
||||
0110 | 00 00 00 00 |
|
||||
I 40469 2023-05-26 10:40:55 - [C-1] Game version changed to DC
|
||||
I 40469 2023-05-26 10:40:55 - [Commands] Sending to C-1 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 14 02 0A 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 0C 00 00 00 01 00 77 77 77 77 | , wwww
|
||||
I 40469 2023-05-26 10:40:55 - [Commands] Sending to C-1 (version=DC command=07 flag=05)
|
||||
0000 | 07 04 90 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al
|
||||
0010 | 65 78 61 6E 64 72 69 61 00 00 00 00 00 00 00 00 | exandria
|
||||
@@ -82,10 +80,6 @@ I 40469 2023-05-26 10:40:58 - [Commands] Received from C-1 (version=DC command=1
|
||||
0000 | 10 00 0C 00 11 00 00 11 11 22 22 11 | ""
|
||||
I 40469 2023-05-26 10:40:58 - [Commands] Sending to C-1 (version=DC command=97 flag=01)
|
||||
0000 | 97 01 04 00 |
|
||||
I 40469 2023-05-26 10:40:58 - [Commands] Sending to C-1 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 14 06 0A 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
I 40469 2023-05-26 10:40:58 - [Commands] Received from C-1 (version=DC command=B1 flag=00)
|
||||
0000 | B1 00 04 00 |
|
||||
I 40469 2023-05-26 10:40:58 - [Commands] Sending to C-1 (version=DC command=B1 flag=00)
|
||||
@@ -130,9 +124,7 @@ I 40469 2023-05-26 10:40:59 - [Commands] Received from C-2 (version=GC command=9
|
||||
00A0 | 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | li
|
||||
I 40469 2023-05-26 10:40:59 - [C-2] Game version changed to DC
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 14 02 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 0C 00 00 00 01 00 77 77 77 77 | , wwww
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (version=DC command=04 flag=00)
|
||||
0000 | 90 01 04 00 |
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Received from C-2 (version=DC command=61 flag=01)
|
||||
@@ -161,10 +153,6 @@ I 40469 2023-05-26 10:40:59 - [Commands] Received from C-2 (version=GC command=9
|
||||
0080 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0090 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 54 61 | Ta
|
||||
00A0 | 6C 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | li
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 14 02 08 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
I 40469 2023-05-26 10:40:59 - [Commands] Sending to C-2 (version=DC command=83 flag=0A)
|
||||
0000 | 83 0A 7C 00 33 00 00 33 01 00 00 00 00 00 00 00 | | 3 3
|
||||
0010 | 33 00 00 33 02 00 00 00 00 00 00 00 33 00 00 33 | 3 3 3 3
|
||||
|
||||
@@ -57,9 +57,7 @@ I 40992 2023-05-26 10:52:51 - [Commands] Received from C-1 (version=DC command=9
|
||||
0120 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
I 40992 2023-05-26 10:52:51 - [C-1] Game version changed to DC
|
||||
I 40992 2023-05-26 10:52:51 - [Commands] Sending to C-1 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 04 00 02 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 0C 00 00 00 01 00 77 77 77 77 | , wwww
|
||||
I 40992 2023-05-26 10:52:51 - [Commands] Sending to C-1 (version=DC command=07 flag=05)
|
||||
0000 | 07 04 90 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al
|
||||
0010 | 65 78 61 6E 64 72 69 61 00 00 00 00 00 00 00 00 | exandria
|
||||
@@ -81,10 +79,6 @@ I 40992 2023-05-26 10:52:56 - [Commands] Received from C-1 (version=DC command=1
|
||||
0000 | 10 00 0C 00 11 00 00 11 11 22 22 11 | ""
|
||||
I 40992 2023-05-26 10:52:56 - [Commands] Sending to C-1 (version=DC command=97 flag=01)
|
||||
0000 | 97 01 04 00 |
|
||||
I 40992 2023-05-26 10:52:56 - [Commands] Sending to C-1 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 04 04 02 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
I 40992 2023-05-26 10:52:57 - [Commands] Received from C-1 (version=DC command=B1 flag=00)
|
||||
0000 | B1 00 04 00 |
|
||||
I 40992 2023-05-26 10:52:57 - [Commands] Sending to C-1 (version=DC command=B1 flag=00)
|
||||
@@ -131,9 +125,7 @@ I 40992 2023-05-26 10:52:57 - [Commands] Received from C-2 (version=GC command=9
|
||||
00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
I 40992 2023-05-26 10:52:57 - [C-2] Game version changed to DC
|
||||
I 40992 2023-05-26 10:52:57 - [Commands] Sending to C-2 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 04 00 02 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 0C 00 00 00 01 00 77 77 77 77 | , wwww
|
||||
I 40992 2023-05-26 10:52:57 - [Commands] Sending to C-2 (version=DC command=83 flag=0F)
|
||||
0000 | 83 0F B8 00 33 00 00 33 01 00 00 00 00 00 00 00 | 3 3
|
||||
0010 | 33 00 00 33 02 00 00 00 00 00 00 00 33 00 00 33 | 3 3 3 3
|
||||
@@ -1603,9 +1595,7 @@ I 40992 2023-05-26 10:55:37 - [Commands] Received from C-3 (version=DC command=9
|
||||
0120 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
I 40992 2023-05-26 10:55:37 - [C-3] Game version changed to DC
|
||||
I 40992 2023-05-26 10:55:37 - [Commands] Sending to C-3 (version=DC command=04 flag=00)
|
||||
0000 | 04 00 2C 00 00 00 01 00 77 77 77 77 39 98 AC 82 | , wwww9
|
||||
0010 | 0E 89 2A 49 04 00 02 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 0C 00 00 00 01 00 77 77 77 77 | , wwww
|
||||
I 40992 2023-05-26 10:55:37 - [Commands] Sending to C-3 (version=DC command=07 flag=05)
|
||||
0000 | 07 04 90 00 11 00 00 11 FF FF FF FF 04 00 41 6C | Al
|
||||
0010 | 65 78 61 6E 64 72 69 61 00 00 00 00 00 00 00 00 | exandria
|
||||
|
||||
@@ -57,9 +57,9 @@ I 16332 2023-09-17 10:14:34 - [Commands] Received from C-1 (version=GC command=9
|
||||
0130 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 03 02 00 30 45 53 33 00 00 00 00 | *I 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 42 60 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 |
|
||||
0010 | 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF |
|
||||
@@ -2618,17 +2618,17 @@ I 16332 2023-09-17 10:14:34 - [Commands] Sending to C-1 (version=GC command=CC f
|
||||
04F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 43 02 00 30 45 53 33 00 00 00 00 | *I C 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 4A 60 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] Received from C-1 (Tali) (version=GC command=99 flag=00)
|
||||
0000 | 99 00 04 00 |
|
||||
I 16332 2023-09-17 10:14:34 - [Commands] Received from C-1 (Tali) (version=GC command=D6 flag=00)
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 42 02 00 30 45 53 33 00 00 00 00 | *I B 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 48 60 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 (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
|
||||
0010 | 65 78 61 6E 64 72 69 61 00 00 00 00 00 00 00 00 | exandria
|
||||
@@ -2650,9 +2650,9 @@ I 16332 2023-09-17 10:14:35 - [Commands] Received from C-1 (Tali) (version=GC co
|
||||
I 16332 2023-09-17 10:14:35 - [Commands] Sending to C-1 (Tali) (version=GC command=97 flag=01)
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 46 02 00 30 45 53 33 00 00 00 00 | *I F 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 4C 60 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 |
|
||||
I 16332 2023-09-17 10:14:35 - [Commands] Sending to C-1 (Tali) (version=GC command=B1 flag=00)
|
||||
@@ -2701,13 +2701,13 @@ I 16332 2023-09-17 10:14:37 - [Commands] Received from C-2 (version=GC command=9
|
||||
0090 | 31 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 | 11111111
|
||||
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 39 98 AC 82 | 9
|
||||
00D0 | 0E 89 2A 49 0A 46 02 00 30 45 53 33 00 00 00 00 | *I F 0ES3
|
||||
00E0 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , 2
|
||||
00D0 | 30 45 53 33 40 91 00 4C 60 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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 46 02 00 30 45 53 33 00 00 00 00 | *I F 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 4C 60 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 |
|
||||
0010 | 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF |
|
||||
@@ -3583,9 +3583,9 @@ I 16332 2023-09-17 10:14:37 - [Commands] Sending to C-2 (Tali) (version=GC comma
|
||||
0430 | 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0E 46 02 00 30 45 53 33 00 00 00 00 | *I F 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 93 00 4C 60 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 | ` ?
|
||||
0010 | B2 84 1C BF 00 00 00 00 53 88 BA C2 | S
|
||||
|
||||
@@ -57,9 +57,9 @@ I 17097 2023-09-19 21:52:47 - [Commands] Received from C-1 (version=GC command=9
|
||||
0130 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 03 02 00 30 45 53 33 00 00 00 00 | *I 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 42 60 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 |
|
||||
0010 | 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF |
|
||||
@@ -2618,17 +2618,17 @@ I 17097 2023-09-19 21:52:47 - [Commands] Sending to C-1 (version=GC command=CC f
|
||||
04F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 43 02 00 30 45 53 33 00 00 00 00 | *I C 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 4A 60 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] Received from C-1 (Tali) (version=GC command=99 flag=00)
|
||||
0000 | 99 00 04 00 |
|
||||
I 17097 2023-09-19 21:52:47 - [Commands] Received from C-1 (Tali) (version=GC command=D6 flag=00)
|
||||
0000 | D6 00 04 00 |
|
||||
I 17097 2023-09-19 21:52:47 - [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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 42 02 00 30 45 53 33 00 00 00 00 | *I B 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 48 60 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 (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
|
||||
0010 | 65 78 61 6E 64 72 69 61 00 00 00 00 00 00 00 00 | exandria
|
||||
@@ -2650,9 +2650,9 @@ I 17097 2023-09-19 21:52:48 - [Commands] Received from C-1 (Tali) (version=GC co
|
||||
I 17097 2023-09-19 21:52:48 - [Commands] Sending to C-1 (Tali) (version=GC command=97 flag=01)
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 46 02 00 30 45 53 33 00 00 00 00 | *I F 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 4C 60 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 |
|
||||
I 17097 2023-09-19 21:52:49 - [Commands] Sending to C-1 (Tali) (version=GC command=B1 flag=00)
|
||||
@@ -2701,13 +2701,13 @@ I 17097 2023-09-19 21:52:50 - [Commands] Received from C-2 (version=GC command=9
|
||||
0090 | 31 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 | 11111111
|
||||
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 39 98 AC 82 | 9
|
||||
00D0 | 0E 89 2A 49 0A 46 02 00 30 45 53 33 00 00 00 00 | *I F 0ES3
|
||||
00E0 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , 2
|
||||
00D0 | 30 45 53 33 40 91 00 4C 60 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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 46 02 00 30 45 53 33 00 00 00 00 | *I F 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 91 00 4C 60 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 |
|
||||
0010 | 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF |
|
||||
@@ -3583,9 +3583,9 @@ I 17097 2023-09-19 21:52:50 - [Commands] Sending to C-2 (Tali) (version=GC comma
|
||||
0430 | 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0E 46 02 00 30 45 53 33 00 00 00 00 | *I F 0ES3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 30 45 53 33 40 93 00 4C 60 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 | ` ?
|
||||
0010 | B2 84 1C BF 00 00 00 00 53 88 BA C2 | S
|
||||
@@ -5864,9 +5864,9 @@ I 17097 2023-09-19 21:53:53 - [Commands] Received from C-3 (version=GC command=9
|
||||
0130 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 09 02 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2
|
||||
0010 | 00 00 00 33 40 A1 00 42 60 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 |
|
||||
0010 | 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF |
|
||||
@@ -8425,17 +8425,17 @@ I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (version=GC command=CC f
|
||||
04F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 49 02 00 00 00 00 33 00 00 00 00 | *I I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2
|
||||
0010 | 00 00 00 33 40 A1 00 4A 60 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=99 flag=00)
|
||||
0000 | 99 00 04 00 |
|
||||
I 17097 2023-09-19 21:53:53 - [Commands] Received from C-3 (Tali) (version=GC command=D6 flag=00)
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 48 02 00 00 00 00 33 00 00 00 00 | *I H 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2
|
||||
0010 | 00 00 00 33 40 A1 00 48 60 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 (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
|
||||
0010 | 65 78 61 6E 64 72 69 61 00 00 00 00 00 00 00 00 | exandria
|
||||
@@ -8457,9 +8457,9 @@ I 17097 2023-09-19 21:53:53 - [Commands] Received from C-3 (Tali) (version=GC co
|
||||
I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (Tali) (version=GC command=97 flag=01)
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 4C 02 00 00 00 00 33 00 00 00 00 | *I L 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2
|
||||
0010 | 00 00 00 33 40 A1 00 4C 60 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 |
|
||||
I 17097 2023-09-19 21:53:53 - [Commands] Sending to C-3 (Tali) (version=GC command=B1 flag=00)
|
||||
@@ -8508,13 +8508,13 @@ I 17097 2023-09-19 21:53:54 - [Commands] Received from C-4 (version=GC command=9
|
||||
0090 | 32 32 32 32 32 32 32 32 00 00 00 00 00 00 00 00 | 22222222
|
||||
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 39 98 AC 82 | 9
|
||||
00D0 | 0E 89 2A 49 0A 4C 02 00 00 00 00 33 00 00 00 00 | *I L 3
|
||||
00E0 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , """"2
|
||||
00D0 | 00 00 00 33 40 A1 00 4C 60 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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0A 4C 02 00 00 00 00 33 00 00 00 00 | *I L 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2
|
||||
0010 | 00 00 00 33 40 A1 00 4C 60 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 |
|
||||
0010 | 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF |
|
||||
@@ -9390,9 +9390,9 @@ I 17097 2023-09-19 21:53:54 - [Commands] Sending to C-4 (Tali) (version=GC comma
|
||||
0430 | 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 0E 4C 02 00 00 00 00 33 00 00 00 00 | *I L 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 22 22 22 22 32 AC 99 83 | , """"2
|
||||
0010 | 00 00 00 33 40 A3 00 4C 60 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 | ` ?
|
||||
0010 | B2 84 1C BF 00 00 00 00 53 88 BA C2 | S
|
||||
|
||||
@@ -57,9 +57,9 @@ I 49108 2023-05-26 16:18:01 - [Commands] Received from C-1 (version=GC command=9
|
||||
0130 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 02 01 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 00 00 00 33 00 01 00 42 60 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
|
||||
0010 | 65 63 74 65 64 20 74 6F 20 09 43 36 41 6C 65 78 | ected to C6Alex
|
||||
@@ -74,9 +74,9 @@ I 49108 2023-05-26 16:18:01 - [Commands] Received from C-1 (version=GC command=9
|
||||
I 49108 2023-05-26 16:18:02 - [Commands] Received from C-1 (version=GC command=D6 flag=00)
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 02 00 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 00 00 00 33 00 01 00 40 60 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: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
|
||||
0010 | 65 78 61 6E 64 72 69 61 00 00 00 00 00 00 00 00 | exandria
|
||||
@@ -96,9 +96,9 @@ I 49108 2023-05-26 16:18:06 - [Commands] Received from C-1 (version=GC command=1
|
||||
I 49108 2023-05-26 16:18:06 - [Commands] Sending to C-1 (version=GC command=97 flag=01)
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 02 04 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 00 00 00 33 00 01 00 44 60 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 |
|
||||
I 49108 2023-05-26 16:18:06 - [Commands] Sending to C-1 (version=GC command=B1 flag=00)
|
||||
@@ -147,13 +147,13 @@ I 49108 2023-05-26 16:18:08 - [Commands] Received from C-2 (version=GC command=9
|
||||
0090 | 31 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 | 11111111
|
||||
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 39 98 AC 82 | 9
|
||||
00D0 | 0E 89 2A 49 02 04 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
00E0 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , 2
|
||||
00D0 | 00 00 00 33 00 01 00 44 60 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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 02 04 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 00 00 00 33 00 01 00 44 60 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
|
||||
0010 | 33 00 00 33 02 00 00 00 00 00 00 00 33 00 00 33 | 3 3 3 3
|
||||
@@ -364,9 +364,9 @@ I 49108 2023-05-26 16:18:08 - [Commands] Sending to C-2 (Jess) (version=GC comma
|
||||
0430 | 00 05 00 00 00 00 01 00 05 0E 00 04 03 00 0E 02 |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 06 04 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 00 00 00 33 00 03 00 44 60 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 | ` ?
|
||||
0010 | 00 00 00 00 00 00 A0 41 00 00 07 43 | A C
|
||||
@@ -10117,9 +10117,9 @@ I 49108 2023-05-26 16:28:29 - [Commands] Received from C-3 (version=GC command=9
|
||||
0090 | 31 31 31 31 31 31 31 31 00 00 00 00 00 00 00 00 | 11111111
|
||||
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 39 98 AC 82 | 9
|
||||
00D0 | 0E 89 2A 49 06 04 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
00E0 | 00 00 FF FF FF FF FF FF FF FF FF FF 00 00 00 00 |
|
||||
00C0 | 00 00 00 00 00 00 00 00 00 00 00 00 32 AC 99 83 | , 2
|
||||
00D0 | 00 00 00 33 00 03 00 44 60 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 |
|
||||
0110 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
@@ -10127,9 +10127,9 @@ I 49108 2023-05-26 16:28:29 - [Commands] Received from C-3 (version=GC command=9
|
||||
0130 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
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 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 06 04 00 00 00 00 00 33 00 00 00 00 | *I 3
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 04 00 2C 00 00 00 01 00 11 11 11 11 32 AC 99 83 | , 2
|
||||
0010 | 00 00 00 33 00 03 00 44 60 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
|
||||
0010 | 65 78 61 6E 64 72 69 61 00 00 00 00 00 00 00 00 | exandria
|
||||
|
||||
@@ -57,9 +57,7 @@ I 49484 2023-05-26 16:35:10 - [Commands] Received from C-2 (version=PC command=9
|
||||
0130 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0140 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
I 49484 2023-05-26 16:35:10 - [Commands] Sending to C-2 (version=PC command=04 flag=00)
|
||||
0000 | 2C 00 04 00 00 00 01 00 11 11 11 11 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 04 10 02 00 00 00 00 32 00 00 00 00 | *I 2
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 0C 00 04 00 00 00 01 00 11 11 11 11 |
|
||||
I 49484 2023-05-26 16:35:10 - [Commands] Sending to C-2 (version=PC command=07 flag=05)
|
||||
0000 | 0C 01 07 05 11 00 00 11 FF FF FF FF 04 00 41 00 | A
|
||||
0010 | 6C 00 65 00 78 00 61 00 6E 00 64 00 72 00 69 00 | l e x a n d r i
|
||||
@@ -89,10 +87,6 @@ I 49484 2023-05-26 16:35:14 - [Commands] Received from C-2 (version=PC command=1
|
||||
0000 | 0C 00 10 00 11 00 00 11 11 22 22 11 | ""
|
||||
I 49484 2023-05-26 16:35:14 - [Commands] Sending to C-2 (version=PC command=97 flag=01)
|
||||
0000 | 04 00 97 01 |
|
||||
I 49484 2023-05-26 16:35:14 - [Commands] Sending to C-2 (version=PC command=04 flag=00)
|
||||
0000 | 2C 00 04 00 00 00 01 00 11 11 11 11 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 04 14 02 00 00 00 00 32 00 00 00 00 | *I 2
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
I 49484 2023-05-26 16:35:14 - [Commands] Received from C-2 (version=PC command=B1 flag=00)
|
||||
0000 | 04 00 B1 00 |
|
||||
I 49484 2023-05-26 16:35:14 - [Commands] Sending to C-2 (version=PC command=B1 flag=00)
|
||||
@@ -138,9 +132,7 @@ I 49484 2023-05-26 16:35:14 - [Commands] Received from C-3 (version=PC command=9
|
||||
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 |
|
||||
I 49484 2023-05-26 16:35:14 - [Commands] Sending to C-3 (version=PC command=04 flag=00)
|
||||
0000 | 2C 00 04 00 00 00 01 00 11 11 11 11 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 04 10 02 00 00 00 00 32 00 00 00 00 | *I 2
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 0C 00 04 00 00 00 01 00 11 11 11 11 |
|
||||
I 49484 2023-05-26 16:35:14 - [Commands] Sending to C-3 (version=PC command=83 flag=0F)
|
||||
0000 | B8 00 83 0F 33 00 00 33 01 00 00 00 00 00 00 00 | 3 3
|
||||
0010 | 33 00 00 33 02 00 00 00 00 00 00 00 33 00 00 33 | 3 3 3 3
|
||||
@@ -1168,9 +1160,7 @@ I 49484 2023-05-26 16:37:03 - [Commands] Received from C-4 (version=PC command=9
|
||||
0130 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
0140 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
||||
I 49484 2023-05-26 16:37:03 - [Commands] Sending to C-4 (version=PC command=04 flag=00)
|
||||
0000 | 2C 00 04 00 00 00 01 00 11 11 11 11 39 98 AC 82 | , 9
|
||||
0010 | 0E 89 2A 49 04 10 02 00 00 00 00 32 00 00 00 00 | *I 2
|
||||
0020 | 00 00 FF FF FF FF FF FF FF FF FF FF |
|
||||
0000 | 0C 00 04 00 00 00 01 00 11 11 11 11 |
|
||||
I 49484 2023-05-26 16:37:03 - [Commands] Sending to C-4 (version=PC command=07 flag=05)
|
||||
0000 | 0C 01 07 05 11 00 00 11 FF FF FF FF 04 00 41 00 | A
|
||||
0010 | 6C 00 65 00 78 00 61 00 6E 00 64 00 72 00 69 00 | l e x a n d r i
|
||||
|
||||
Reference in New Issue
Block a user