hide all non-BB redirects on proxy server
This commit is contained in:
+1
-1
@@ -124,7 +124,7 @@ static void server_command_lobby_info(shared_ptr<ServerState>, shared_ptr<Lobby>
|
|||||||
|
|
||||||
static void proxy_command_lobby_info(shared_ptr<ServerState>,
|
static void proxy_command_lobby_info(shared_ptr<ServerState>,
|
||||||
ProxyServer::LinkedSession& session, const std::u16string&) {
|
ProxyServer::LinkedSession& session, const std::u16string&) {
|
||||||
string msg = string_printf("$C7GC: $C6%" PRIu32 "\n$C7Client ID: $C6%zu%s",
|
string msg = string_printf("$C7GC: $C6%" PRId64 "\n$C7Client ID: $C6%zu%s",
|
||||||
session.remote_guild_card_number,
|
session.remote_guild_card_number,
|
||||||
session.lobby_client_id,
|
session.lobby_client_id,
|
||||||
(session.leader_client_id == session.lobby_client_id) ? " (L)" : "");
|
(session.leader_client_id == session.lobby_client_id) ? " (L)" : "");
|
||||||
|
|||||||
+12
-6
@@ -259,7 +259,17 @@ struct S_StartFileDownloads_Patch_11 {
|
|||||||
// size of this command is 0x2004 bytes, including the header.
|
// size of this command is 0x2004 bytes, including the header.
|
||||||
|
|
||||||
// 14 (S->C): Reconnect
|
// 14 (S->C): Reconnect
|
||||||
// Same format and usage as command 19 on the game server (described below).
|
// Same format and usage as command 19 on the game server (described below),
|
||||||
|
// except the port field is big-endian for some reason.
|
||||||
|
|
||||||
|
template <typename PortT>
|
||||||
|
struct S_Reconnect {
|
||||||
|
be_uint32_t address;
|
||||||
|
PortT port;
|
||||||
|
le_uint16_t unused;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S_Reconnect_Patch_14 : S_Reconnect<be_uint16_t> { };
|
||||||
|
|
||||||
// 15 (S->C): Login failure
|
// 15 (S->C): Login failure
|
||||||
// No arguments
|
// No arguments
|
||||||
@@ -618,11 +628,7 @@ struct C_WriteFileConfirmation_V3_BB_13_A7 {
|
|||||||
// Note: PSO XB seems to ignore the address field, which makes sense given its
|
// Note: PSO XB seems to ignore the address field, which makes sense given its
|
||||||
// networking architecture.
|
// networking architecture.
|
||||||
|
|
||||||
struct S_Reconnect_19 {
|
struct S_Reconnect_19 : S_Reconnect<le_uint16_t> { };
|
||||||
be_uint32_t address;
|
|
||||||
le_uint16_t port;
|
|
||||||
le_uint16_t unused;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Because PSO PC and some versions of PSO DC/GC use the same port but different
|
// Because PSO PC and some versions of PSO DC/GC use the same port but different
|
||||||
// protocols, we use a specially-crafted 19 command to send them to two
|
// protocols, we use a specially-crafted 19 command to send them to two
|
||||||
|
|||||||
+111
-126
@@ -132,7 +132,7 @@ static HandlerResult process_server_gc_9A(shared_ptr<ServerState>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
C_LoginExtended_GC_9E cmd;
|
C_LoginExtended_GC_9E cmd;
|
||||||
if (session.remote_guild_card_number == 0) {
|
if (session.remote_guild_card_number < 0) {
|
||||||
cmd.player_tag = 0xFFFF0000;
|
cmd.player_tag = 0xFFFF0000;
|
||||||
cmd.guild_card_number = 0xFFFFFFFF;
|
cmd.guild_card_number = 0xFFFFFFFF;
|
||||||
} else {
|
} else {
|
||||||
@@ -141,7 +141,7 @@ static HandlerResult process_server_gc_9A(shared_ptr<ServerState>,
|
|||||||
}
|
}
|
||||||
cmd.unused = 0;
|
cmd.unused = 0;
|
||||||
cmd.sub_version = session.sub_version;
|
cmd.sub_version = session.sub_version;
|
||||||
cmd.is_extended = session.remote_guild_card_number ? 0 : 1;
|
cmd.is_extended = (session.remote_guild_card_number < 0) ? 0 : 1;
|
||||||
cmd.language = session.language;
|
cmd.language = session.language;
|
||||||
cmd.serial_number = string_printf("%08" PRIX32 "", session.license->serial_number);
|
cmd.serial_number = string_printf("%08" PRIX32 "", session.license->serial_number);
|
||||||
cmd.access_key = session.license->access_key;
|
cmd.access_key = session.license->access_key;
|
||||||
@@ -227,47 +227,82 @@ static HandlerResult process_server_dc_pc_v3_patch_02_17(
|
|||||||
} else if ((session.version == GameVersion::DC) ||
|
} else if ((session.version == GameVersion::DC) ||
|
||||||
(session.version == GameVersion::PC)) {
|
(session.version == GameVersion::PC)) {
|
||||||
if (session.newserv_client_config.cfg.flags & Client::Flag::DCV1) {
|
if (session.newserv_client_config.cfg.flags & Client::Flag::DCV1) {
|
||||||
C_LoginV1_DC_93 cmd;
|
if (command == 0x17) {
|
||||||
if (session.remote_guild_card_number == 0) {
|
C_LoginV1_DC_PC_V3_90 cmd;
|
||||||
cmd.player_tag = 0xFFFF0000;
|
cmd.serial_number = string_printf("%08" PRIX32 "",
|
||||||
cmd.guild_card_number = 0xFFFFFFFF;
|
session.license->serial_number);
|
||||||
|
cmd.access_key = session.license->access_key;
|
||||||
|
cmd.access_key.clear_after(8);
|
||||||
|
session.server_channel.send(0x90, 0x00, &cmd, sizeof(cmd));
|
||||||
|
return HandlerResult::Type::SUPPRESS;
|
||||||
} else {
|
} else {
|
||||||
cmd.player_tag = 0x00010000;
|
C_LoginV1_DC_93 cmd;
|
||||||
cmd.guild_card_number = session.remote_guild_card_number;
|
if (session.remote_guild_card_number < 0) {
|
||||||
|
cmd.player_tag = 0xFFFF0000;
|
||||||
|
cmd.guild_card_number = 0xFFFFFFFF;
|
||||||
|
} else {
|
||||||
|
cmd.player_tag = 0x00010000;
|
||||||
|
cmd.guild_card_number = session.remote_guild_card_number;
|
||||||
|
}
|
||||||
|
cmd.unknown_a1 = 0;
|
||||||
|
cmd.unknown_a2 = 0;
|
||||||
|
cmd.sub_version = session.sub_version;
|
||||||
|
cmd.is_extended = 0;
|
||||||
|
cmd.language = session.language;
|
||||||
|
cmd.serial_number = string_printf("%08" PRIX32 "",
|
||||||
|
session.license->serial_number);
|
||||||
|
cmd.access_key = session.license->access_key;
|
||||||
|
cmd.access_key.clear_after(8);
|
||||||
|
cmd.hardware_id = session.hardware_id;
|
||||||
|
cmd.name = session.character_name;
|
||||||
|
session.server_channel.send(0x93, 0x00, &cmd, sizeof(cmd));
|
||||||
|
return HandlerResult::Type::SUPPRESS;
|
||||||
}
|
}
|
||||||
cmd.unknown_a1 = 0;
|
} else { // DCv2 or PC
|
||||||
cmd.unknown_a2 = 0;
|
if (command == 0x17) {
|
||||||
cmd.sub_version = session.sub_version;
|
C_Login_DC_PC_V3_9A cmd;
|
||||||
cmd.is_extended = 0;
|
if (session.remote_guild_card_number < 0) {
|
||||||
cmd.language = session.language;
|
cmd.player_tag = 0xFFFF0000;
|
||||||
cmd.serial_number = string_printf("%08" PRIX32 "",
|
cmd.guild_card_number = 0xFFFFFFFF;
|
||||||
session.license->serial_number);
|
} else {
|
||||||
cmd.access_key = session.license->access_key;
|
cmd.player_tag = 0x00010000;
|
||||||
cmd.hardware_id = session.hardware_id;
|
cmd.guild_card_number = session.remote_guild_card_number;
|
||||||
cmd.name = session.character_name;
|
}
|
||||||
session.server_channel.send(0x93, 0x00, &cmd, sizeof(cmd));
|
cmd.sub_version = session.sub_version;
|
||||||
return HandlerResult::Type::SUPPRESS;
|
cmd.serial_number = string_printf("%08" PRIX32 "",
|
||||||
} else {
|
session.license->serial_number);
|
||||||
C_Login_DC_PC_GC_9D cmd;
|
cmd.access_key = session.license->access_key;
|
||||||
if (session.remote_guild_card_number == 0) {
|
cmd.access_key.clear_after(8);
|
||||||
cmd.player_tag = 0xFFFF0000;
|
cmd.serial_number2 = cmd.serial_number;
|
||||||
cmd.guild_card_number = 0xFFFFFFFF;
|
cmd.access_key2 = cmd.access_key;
|
||||||
|
// TODO: We probably should set email_address, but we currently don't
|
||||||
|
// keep that value anywhere in the session object, nor is it saved in
|
||||||
|
// the License object.
|
||||||
|
session.server_channel.send(0x9A, 0x00, &cmd, sizeof(cmd));
|
||||||
|
return HandlerResult::Type::SUPPRESS;
|
||||||
} else {
|
} else {
|
||||||
cmd.player_tag = 0x00010000;
|
C_Login_DC_PC_GC_9D cmd;
|
||||||
cmd.guild_card_number = session.remote_guild_card_number;
|
if (session.remote_guild_card_number < 0) {
|
||||||
|
cmd.player_tag = 0xFFFF0000;
|
||||||
|
cmd.guild_card_number = 0xFFFFFFFF;
|
||||||
|
} else {
|
||||||
|
cmd.player_tag = 0x00010000;
|
||||||
|
cmd.guild_card_number = session.remote_guild_card_number;
|
||||||
|
}
|
||||||
|
cmd.unused = 0;
|
||||||
|
cmd.sub_version = session.sub_version;
|
||||||
|
cmd.is_extended = 0;
|
||||||
|
cmd.language = session.language;
|
||||||
|
cmd.serial_number = string_printf("%08" PRIX32 "",
|
||||||
|
session.license->serial_number);
|
||||||
|
cmd.access_key = session.license->access_key;
|
||||||
|
cmd.access_key.clear_after(8);
|
||||||
|
cmd.serial_number2 = cmd.serial_number;
|
||||||
|
cmd.access_key2 = cmd.access_key;
|
||||||
|
cmd.name = session.character_name;
|
||||||
|
session.server_channel.send(0x9D, 0x00, &cmd, sizeof(cmd));
|
||||||
|
return HandlerResult::Type::SUPPRESS;
|
||||||
}
|
}
|
||||||
cmd.unused = 0xFFFFFFFFFFFF0000;
|
|
||||||
cmd.sub_version = session.sub_version;
|
|
||||||
cmd.is_extended = 0;
|
|
||||||
cmd.language = session.language;
|
|
||||||
cmd.serial_number = string_printf("%08" PRIX32 "",
|
|
||||||
session.license->serial_number);
|
|
||||||
cmd.access_key = session.license->access_key;
|
|
||||||
cmd.serial_number2 = cmd.serial_number;
|
|
||||||
cmd.access_key2 = cmd.access_key;
|
|
||||||
cmd.name = session.character_name;
|
|
||||||
session.server_channel.send(0x9D, 0x00, &cmd, sizeof(cmd));
|
|
||||||
return HandlerResult::Type::SUPPRESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (session.version == GameVersion::GC) {
|
} else if (session.version == GameVersion::GC) {
|
||||||
@@ -365,13 +400,13 @@ static HandlerResult process_server_dc_pc_v3_04(shared_ptr<ServerState>,
|
|||||||
// remote server so the client doesn't see it change. If this is an unlicensed
|
// remote server so the client doesn't see it change. If this is an unlicensed
|
||||||
// session, then the client never received a guild card number from newserv
|
// session, then the client never received a guild card number from newserv
|
||||||
// anyway, so we can let the client see the number from the remote server.
|
// anyway, so we can let the client see the number from the remote server.
|
||||||
bool had_guild_card_number = (session.remote_guild_card_number != 0);
|
bool had_guild_card_number = (session.remote_guild_card_number >= 0);
|
||||||
if (session.remote_guild_card_number != cmd.guild_card_number) {
|
if (session.remote_guild_card_number != cmd.guild_card_number) {
|
||||||
session.remote_guild_card_number = cmd.guild_card_number;
|
session.remote_guild_card_number = cmd.guild_card_number;
|
||||||
session.log.info("Remote guild card number set to %" PRIu32,
|
session.log.info("Remote guild card number set to %" PRId64,
|
||||||
session.remote_guild_card_number);
|
session.remote_guild_card_number);
|
||||||
send_text_message_to_client(session, 0x11, string_printf(
|
send_text_message_to_client(session, 0x11, string_printf(
|
||||||
"The remote server\nhas assigned your\nGuild Card number as\n\tC6%" PRIu32,
|
"The remote server\nhas assigned your\nGuild Card number:\n\tC6%" PRId64,
|
||||||
session.remote_guild_card_number));
|
session.remote_guild_card_number));
|
||||||
}
|
}
|
||||||
if (session.license) {
|
if (session.license) {
|
||||||
@@ -636,7 +671,7 @@ static HandlerResult process_server_bb_22(shared_ptr<ServerState>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static HandlerResult process_server_game_19_patch_14(shared_ptr<ServerState>,
|
static HandlerResult process_server_game_19_patch_14(shared_ptr<ServerState>,
|
||||||
ProxyServer::LinkedSession& session, uint16_t command, uint32_t, string& data) {
|
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) {
|
||||||
// If the command is shorter than 6 bytes, use the previous server command to
|
// If the command is shorter than 6 bytes, use the previous server command to
|
||||||
// fill it in. This simulates a behavior used by some private servers where a
|
// fill it in. This simulates a behavior used by some private servers where a
|
||||||
// longer previous command is used to fill part of the client's receive buffer
|
// longer previous command is used to fill part of the client's receive buffer
|
||||||
@@ -657,52 +692,47 @@ static HandlerResult process_server_game_19_patch_14(shared_ptr<ServerState>,
|
|||||||
session.remote_ip_crc = crc32(data.data(), 4);
|
session.remote_ip_crc = crc32(data.data(), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This weird maximum size is here to properly handle the version-split
|
// Set the destination netloc appropriately
|
||||||
// command that some servers (including newserv) use on port 9100
|
|
||||||
auto& cmd = check_size_t<S_Reconnect_19>(data, sizeof(S_Reconnect_19), 0xB0);
|
|
||||||
memset(&session.next_destination, 0, sizeof(session.next_destination));
|
memset(&session.next_destination, 0, sizeof(session.next_destination));
|
||||||
struct sockaddr_in* sin = reinterpret_cast<struct sockaddr_in*>(
|
struct sockaddr_in* sin = reinterpret_cast<struct sockaddr_in*>(
|
||||||
&session.next_destination);
|
&session.next_destination);
|
||||||
sin->sin_family = AF_INET;
|
sin->sin_family = AF_INET;
|
||||||
sin->sin_addr.s_addr = cmd.address.load_raw();
|
if (session.version == GameVersion::PATCH) {
|
||||||
sin->sin_port = htons(cmd.port);
|
auto& cmd = check_size_t<S_Reconnect_Patch_14>(data);
|
||||||
|
sin->sin_addr.s_addr = cmd.address.load_raw(); // Already big-endian
|
||||||
|
sin->sin_port = htons(cmd.port);
|
||||||
|
} else {
|
||||||
|
// This weird maximum size is here to properly handle the version-split
|
||||||
|
// command that some servers (including newserv) use on port 9100
|
||||||
|
auto& cmd = check_size_t<S_Reconnect_19>(data, sizeof(S_Reconnect_19), 0xFFFF);
|
||||||
|
sin->sin_addr.s_addr = cmd.address.load_raw(); // Already big-endian
|
||||||
|
sin->sin_port = htons(cmd.port);
|
||||||
|
}
|
||||||
|
|
||||||
if (!session.client_channel.connected()) {
|
if (!session.client_channel.connected()) {
|
||||||
session.log.warning("Received reconnect command with no destination present");
|
session.log.warning("Received reconnect command with no destination present");
|
||||||
return HandlerResult::Type::SUPPRESS;
|
return HandlerResult::Type::SUPPRESS;
|
||||||
|
|
||||||
} else if (command == 0x14) {
|
} else if (session.version != GameVersion::BB) {
|
||||||
// On the patch server, hide redirects from the client completely. The new
|
// Hide redirects from the client completely. The new destination server
|
||||||
// destination server will presumably send a new 02 command to start
|
// will presumably send a new encryption init command, which the handlers
|
||||||
// encryption; it appears that PSOBB doesn't fail if this happens, and
|
// will appropriately respond to.
|
||||||
// simply re-initializes its encryption appropriately.
|
|
||||||
session.server_channel.crypt_in.reset();
|
session.server_channel.crypt_in.reset();
|
||||||
session.server_channel.crypt_out.reset();
|
session.server_channel.crypt_out.reset();
|
||||||
|
|
||||||
struct sockaddr_in* dest_sin = reinterpret_cast<sockaddr_in*>(
|
// We already modified next_destination, so start the connection process
|
||||||
&session.next_destination);
|
|
||||||
dest_sin->sin_family = AF_INET;
|
|
||||||
dest_sin->sin_addr.s_addr = cmd.address.load_raw();
|
|
||||||
dest_sin->sin_port = cmd.port;
|
|
||||||
session.connect();
|
session.connect();
|
||||||
return HandlerResult::Type::SUPPRESS;
|
return HandlerResult::Type::SUPPRESS;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// If the client is on a virtual connection (fd < 0), only change
|
const struct sockaddr_in* sin = reinterpret_cast<const struct sockaddr_in*>(
|
||||||
// the port (so we'll know which version to treat the next
|
&session.client_channel.local_addr);
|
||||||
// connection as). It's better to leave the address as-is so we
|
if (sin->sin_family != AF_INET) {
|
||||||
// can circumvent the Plus/Ep3 same-network-server check.
|
throw logic_error("existing connection is not ipv4");
|
||||||
if (session.client_channel.is_virtual_connection) {
|
|
||||||
cmd.port = session.local_port;
|
|
||||||
} else {
|
|
||||||
const struct sockaddr_in* sin = reinterpret_cast<const struct sockaddr_in*>(
|
|
||||||
&session.client_channel.local_addr);
|
|
||||||
if (sin->sin_family != AF_INET) {
|
|
||||||
throw logic_error("existing connection is not ipv4");
|
|
||||||
}
|
|
||||||
cmd.address.store_raw(sin->sin_addr.s_addr);
|
|
||||||
cmd.port = ntohs(sin->sin_port);
|
|
||||||
}
|
}
|
||||||
|
auto& cmd = check_size_t<S_Reconnect_19>(data, sizeof(S_Reconnect_19), 0xFFFF);
|
||||||
|
cmd.address.store_raw(sin->sin_addr.s_addr);
|
||||||
|
cmd.port = ntohs(sin->sin_port);
|
||||||
return HandlerResult::Type::MODIFIED;
|
return HandlerResult::Type::MODIFIED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1135,7 +1165,7 @@ HandlerResult process_client_60_62_6C_6D_C9_CB<void>(shared_ptr<ServerState>,
|
|||||||
return HandlerResult::Type::FORWARD;
|
return HandlerResult::Type::FORWARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HandlerResult process_client_dc_pc_v3_A0_A1(shared_ptr<ServerState> s,
|
static HandlerResult process_client_dc_pc_v3_A0_A1(shared_ptr<ServerState>,
|
||||||
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string&) {
|
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string&) {
|
||||||
if (!session.license) {
|
if (!session.license) {
|
||||||
return HandlerResult::Type::FORWARD;
|
return HandlerResult::Type::FORWARD;
|
||||||
@@ -1143,58 +1173,7 @@ static HandlerResult process_client_dc_pc_v3_A0_A1(shared_ptr<ServerState> s,
|
|||||||
|
|
||||||
// For licensed sessions, send them back to newserv's main menu instead of
|
// For licensed sessions, send them back to newserv's main menu instead of
|
||||||
// going to the remote server's ship/block select menu
|
// going to the remote server's ship/block select menu
|
||||||
|
session.send_to_game_server();
|
||||||
// Delete all the other players
|
|
||||||
for (size_t x = 0; x < session.lobby_players.size(); x++) {
|
|
||||||
if (session.lobby_players[x].guild_card_number == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
uint8_t leaving_id = x;
|
|
||||||
uint8_t leader_id = session.lobby_client_id;
|
|
||||||
S_LeaveLobby_66_69_Ep3_E9 cmd = {leaving_id, leader_id, 0};
|
|
||||||
session.client_channel.send(0x69, leaving_id, &cmd, sizeof(cmd));
|
|
||||||
}
|
|
||||||
|
|
||||||
string encoded_name = encode_sjis(s->name);
|
|
||||||
send_text_message_to_client(session, 0x11, string_printf(
|
|
||||||
"You\'ve returned to\n\tC6%s", encoded_name.c_str()));
|
|
||||||
|
|
||||||
// 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 = session.license->serial_number;
|
|
||||||
update_client_config_cmd.cfg = session.newserv_client_config.cfg;
|
|
||||||
session.client_channel.send(0x04, 0x00, &update_client_config_cmd, sizeof(update_client_config_cmd));
|
|
||||||
|
|
||||||
static const vector<string> version_to_port_name({
|
|
||||||
"console-login", "pc-login", "bb-patch", "console-login", "console-login", "bb-login"});
|
|
||||||
const auto& port_name = version_to_port_name.at(static_cast<size_t>(
|
|
||||||
session.version));
|
|
||||||
|
|
||||||
S_Reconnect_19 reconnect_cmd = {
|
|
||||||
0, s->name_to_port_config.at(port_name)->port, 0};
|
|
||||||
|
|
||||||
// If the client is on a virtual connection, we can use any address
|
|
||||||
// here and they should be able to connect back to the game server. If
|
|
||||||
// the client is on a real connection, we'll use the sockname of the
|
|
||||||
// existing connection (like we do in the server 19 command handler).
|
|
||||||
if (session.client_channel.is_virtual_connection) {
|
|
||||||
struct sockaddr_in* dest_sin = reinterpret_cast<struct sockaddr_in*>(&session.next_destination);
|
|
||||||
if (dest_sin->sin_family != AF_INET) {
|
|
||||||
throw logic_error("ss not AF_INET");
|
|
||||||
}
|
|
||||||
reconnect_cmd.address.store_raw(dest_sin->sin_addr.s_addr);
|
|
||||||
} else {
|
|
||||||
const struct sockaddr_in* sin = reinterpret_cast<const struct sockaddr_in*>(
|
|
||||||
&session.client_channel.local_addr);
|
|
||||||
if (sin->sin_family != AF_INET) {
|
|
||||||
throw logic_error("existing connection is not ipv4");
|
|
||||||
}
|
|
||||||
reconnect_cmd.address.store_raw(sin->sin_addr.s_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
session.client_channel.send(0x19, 0x00, &reconnect_cmd, sizeof(reconnect_cmd));
|
|
||||||
|
|
||||||
return HandlerResult::Type::SUPPRESS;
|
return HandlerResult::Type::SUPPRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1486,6 +1465,12 @@ void process_proxy_command(
|
|||||||
}
|
}
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
session.log.error("Failed to process command: %s", e.what());
|
session.log.error("Failed to process command: %s", e.what());
|
||||||
session.disconnect();
|
if (from_server) {
|
||||||
|
string error_str = "Error: ";
|
||||||
|
error_str += e.what();
|
||||||
|
session.send_to_game_server(error_str.c_str());
|
||||||
|
} else {
|
||||||
|
session.disconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+60
-1
@@ -477,7 +477,7 @@ ProxyServer::LinkedSession::LinkedSession(
|
|||||||
version(version),
|
version(version),
|
||||||
sub_version(0), // This is set during resume()
|
sub_version(0), // This is set during resume()
|
||||||
language(1), // Default = English. This is also set during resume()
|
language(1), // Default = English. This is also set during resume()
|
||||||
remote_guild_card_number(0),
|
remote_guild_card_number(-1),
|
||||||
enable_chat_filter(true),
|
enable_chat_filter(true),
|
||||||
switch_assist(false),
|
switch_assist(false),
|
||||||
infinite_hp(false),
|
infinite_hp(false),
|
||||||
@@ -655,10 +655,69 @@ void ProxyServer::LinkedSession::on_error(Channel& ch, short events) {
|
|||||||
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
|
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
|
||||||
session->log.info("%s has disconnected",
|
session->log.info("%s has disconnected",
|
||||||
is_server_stream ? "Server" : "Client");
|
is_server_stream ? "Server" : "Client");
|
||||||
|
// If the server disconnected, send the client back to the game server so
|
||||||
|
// they're not disconnected completely.
|
||||||
|
if (is_server_stream) {
|
||||||
|
session->send_to_game_server("The server has\ndisconnected.");
|
||||||
|
}
|
||||||
session->disconnect();
|
session->disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProxyServer::LinkedSession::send_to_game_server(const char* error_message) {
|
||||||
|
// Delete all the other players
|
||||||
|
for (size_t x = 0; x < this->lobby_players.size(); x++) {
|
||||||
|
if (this->lobby_players[x].guild_card_number == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint8_t leaving_id = x;
|
||||||
|
uint8_t leader_id = this->lobby_client_id;
|
||||||
|
S_LeaveLobby_66_69_Ep3_E9 cmd = {leaving_id, leader_id, 0};
|
||||||
|
this->client_channel.send(0x69, leaving_id, &cmd, sizeof(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
string encoded_name = encode_sjis(this->server->state->name);
|
||||||
|
send_ship_info(this->client_channel, decode_sjis(string_printf(
|
||||||
|
"You\'ve returned to\n\tC6%s$C7\n\n%s", encoded_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));
|
||||||
|
|
||||||
|
static const vector<string> version_to_port_name({
|
||||||
|
"console-login", "pc-login", "bb-patch", "console-login", "console-login", "bb-login"});
|
||||||
|
const auto& port_name = version_to_port_name.at(static_cast<size_t>(
|
||||||
|
this->version));
|
||||||
|
|
||||||
|
S_Reconnect_19 reconnect_cmd = {
|
||||||
|
0, this->server->state->name_to_port_config.at(port_name)->port, 0};
|
||||||
|
|
||||||
|
// If the client is on a virtual connection, we can use any address
|
||||||
|
// here and they should be able to connect back to the game server. If
|
||||||
|
// the client is on a real connection, we'll use the sockname of the
|
||||||
|
// existing connection (like we do in the server 19 command handler).
|
||||||
|
if (this->client_channel.is_virtual_connection) {
|
||||||
|
struct sockaddr_in* dest_sin = reinterpret_cast<struct sockaddr_in*>(&this->next_destination);
|
||||||
|
if (dest_sin->sin_family != AF_INET) {
|
||||||
|
throw logic_error("ss not AF_INET");
|
||||||
|
}
|
||||||
|
reconnect_cmd.address.store_raw(dest_sin->sin_addr.s_addr);
|
||||||
|
} else {
|
||||||
|
const struct sockaddr_in* sin = reinterpret_cast<const struct sockaddr_in*>(
|
||||||
|
&this->client_channel.local_addr);
|
||||||
|
if (sin->sin_family != AF_INET) {
|
||||||
|
throw logic_error("existing connection is not ipv4");
|
||||||
|
}
|
||||||
|
reconnect_cmd.address.store_raw(sin->sin_addr.s_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->client_channel.send(0x19, 0x00, &reconnect_cmd, sizeof(reconnect_cmd));
|
||||||
|
}
|
||||||
|
|
||||||
void ProxyServer::LinkedSession::disconnect() {
|
void ProxyServer::LinkedSession::disconnect() {
|
||||||
// Forward the disconnection to the other end
|
// Forward the disconnection to the other end
|
||||||
this->client_channel.disconnect();
|
this->client_channel.disconnect();
|
||||||
|
|||||||
+2
-1
@@ -57,7 +57,7 @@ public:
|
|||||||
std::string hardware_id; // Only used for DC sessions
|
std::string hardware_id; // Only used for DC sessions
|
||||||
std::string login_command_bb;
|
std::string login_command_bb;
|
||||||
|
|
||||||
uint32_t remote_guild_card_number;
|
int64_t remote_guild_card_number;
|
||||||
parray<uint8_t, 0x20> remote_client_config_data;
|
parray<uint8_t, 0x20> remote_client_config_data;
|
||||||
ClientConfigBB newserv_client_config;
|
ClientConfigBB newserv_client_config;
|
||||||
bool enable_chat_filter;
|
bool enable_chat_filter;
|
||||||
@@ -145,6 +145,7 @@ public:
|
|||||||
static void on_error(Channel& ch, short events);
|
static void on_error(Channel& ch, short events);
|
||||||
void on_timeout();
|
void on_timeout();
|
||||||
|
|
||||||
|
void send_to_game_server(const char* error_message = nullptr);
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
||||||
bool is_connected() const;
|
bool is_connected() const;
|
||||||
|
|||||||
@@ -568,6 +568,10 @@ void send_ship_info(shared_ptr<Client> c, const u16string& text) {
|
|||||||
send_header_text(c->channel, 0x11, 0, text, true);
|
send_header_text(c->channel, 0x11, 0, text, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void send_ship_info(Channel& ch, const u16string& text) {
|
||||||
|
send_header_text(ch, 0x11, 0, text, true);
|
||||||
|
}
|
||||||
|
|
||||||
void send_text_message(Channel& ch, const std::u16string& text) {
|
void send_text_message(Channel& ch, const std::u16string& text) {
|
||||||
send_header_text(ch, 0xB0, 0, text, true);
|
send_header_text(ch, 0xB0, 0, text, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ void send_quest_info(std::shared_ptr<Client> c, const std::u16string& text,
|
|||||||
bool is_download_quest);
|
bool is_download_quest);
|
||||||
void send_lobby_message_box(std::shared_ptr<Client> c, const std::u16string& text);
|
void send_lobby_message_box(std::shared_ptr<Client> c, const std::u16string& text);
|
||||||
void send_ship_info(std::shared_ptr<Client> c, const std::u16string& text);
|
void send_ship_info(std::shared_ptr<Client> c, const std::u16string& text);
|
||||||
|
void send_ship_info(Channel& ch, const std::u16string& text);
|
||||||
void send_text_message(Channel& ch, const std::u16string& text);
|
void send_text_message(Channel& ch, const std::u16string& text);
|
||||||
void send_text_message(std::shared_ptr<Client> c, const std::u16string& text);
|
void send_text_message(std::shared_ptr<Client> c, const std::u16string& text);
|
||||||
void send_text_message(std::shared_ptr<Lobby> l, const std::u16string& text);
|
void send_text_message(std::shared_ptr<Lobby> l, const std::u16string& text);
|
||||||
|
|||||||
Reference in New Issue
Block a user