diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index 788d3dba..94c1f443 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -902,6 +902,7 @@ static HandlerResult on_server_65_67_68(shared_ptr, session.lobby_players.clear(); session.lobby_players.resize(12); session.log.info("Cleared lobby players"); + session.is_in_game = false; // This command can cause the client to no longer send D6 responses when // 1A/D5 large message boxes are closed. newserv keeps track of this @@ -957,6 +958,7 @@ static HandlerResult on_server_64(shared_ptr, // overwrite all 4 entries for this command session.lobby_players.resize(4); session.log.info("Cleared lobby players"); + session.is_in_game = true; CmdT* cmd; S_JoinGame_GC_Ep3_64* cmd_ep3 = nullptr; @@ -1020,6 +1022,12 @@ static HandlerResult on_server_66_69(shared_ptr, return HandlerResult::Type::FORWARD; } +static HandlerResult on_client_98(shared_ptr, + ProxyServer::LinkedSession& session, uint16_t, uint32_t, string&) { + session.is_in_game = false; + return HandlerResult::Type::FORWARD; +} + static HandlerResult on_client_06(shared_ptr s, ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) { if (data.size() >= 12) { @@ -1611,7 +1619,7 @@ static on_command_t handlers[6][0x100][2] = { /* 95 */ {nullptr, nullptr}, /* 96 */ {nullptr, nullptr}, /* 97 */ {on_server_97, nullptr}, - /* 98 */ {nullptr, nullptr}, + /* 98 */ {nullptr, on_client_98}, /* 99 */ {nullptr, nullptr}, /* 9A */ {nullptr, nullptr}, /* 9B */ {nullptr, nullptr}, @@ -1877,7 +1885,7 @@ static on_command_t handlers[6][0x100][2] = { /* 95 */ {nullptr, nullptr}, /* 96 */ {nullptr, nullptr}, /* 97 */ {on_server_97, nullptr}, - /* 98 */ {nullptr, nullptr}, + /* 98 */ {nullptr, on_client_98}, /* 99 */ {nullptr, nullptr}, /* 9A */ {nullptr, nullptr}, /* 9B */ {nullptr, nullptr}, @@ -2143,7 +2151,7 @@ static on_command_t handlers[6][0x100][2] = { /* 95 */ {nullptr, nullptr}, /* 96 */ {nullptr, nullptr}, /* 97 */ {on_server_97, nullptr}, - /* 98 */ {nullptr, nullptr}, + /* 98 */ {nullptr, on_client_98}, /* 99 */ {nullptr, nullptr}, /* 9A */ {on_server_gc_9A, nullptr}, /* 9B */ {nullptr, nullptr}, @@ -2409,7 +2417,7 @@ static on_command_t handlers[6][0x100][2] = { /* 95 */ {nullptr, nullptr}, /* 96 */ {nullptr, nullptr}, /* 97 */ {on_server_97, nullptr}, - /* 98 */ {nullptr, nullptr}, + /* 98 */ {nullptr, on_client_98}, /* 99 */ {nullptr, nullptr}, /* 9A */ {nullptr, nullptr}, /* 9B */ {nullptr, nullptr}, @@ -2675,7 +2683,7 @@ static on_command_t handlers[6][0x100][2] = { /* 95 */ {nullptr, nullptr}, /* 96 */ {nullptr, nullptr}, /* 97 */ {nullptr, nullptr}, - /* 98 */ {nullptr, nullptr}, + /* 98 */ {nullptr, on_client_98}, /* 99 */ {nullptr, nullptr}, /* 9A */ {nullptr, nullptr}, /* 9B */ {nullptr, nullptr}, diff --git a/src/ProxyServer.cc b/src/ProxyServer.cc index da76a7b8..bfc3998a 100644 --- a/src/ProxyServer.cc +++ b/src/ProxyServer.cc @@ -487,7 +487,8 @@ ProxyServer::LinkedSession::LinkedSession( override_lobby_number(-1), lobby_players(12), lobby_client_id(0), - leader_client_id(0) { + leader_client_id(0), + is_in_game(false) { this->last_switch_enabled_command.subcommand = 0; memset(this->prev_server_command_bytes, 0, sizeof(this->prev_server_command_bytes)); } @@ -670,49 +671,57 @@ void ProxyServer::LinkedSession::send_to_game_server(const char* error_message) uint8_t leaving_id = x; uint8_t leader_id = this->lobby_client_id; S_LeaveLobby_66_69_Ep3_E9 cmd = {leaving_id, leader_id, 1, 0}; - this->client_channel.send(0x69, leaving_id, &cmd, sizeof(cmd)); + this->client_channel.send(this->is_in_game ? 0x66 : 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 : ""))); + if (this->is_in_game) { + send_ship_info(this->client_channel, decode_sjis(string_printf( + "You cannot return\nto $C6%s$C7\nwhile in a game.\n\n%s", + encoded_name.c_str(), error_message ? error_message : ""))); + this->disconnect(); - // 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 version_to_port_name({ - "bb-patch", "console-login", "pc-login", "console-login", "console-login", "bb-init"}); - const auto& port_name = version_to_port_name.at(static_cast( - 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(&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( - &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); - } + 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 : ""))); - this->client_channel.send(0x19, 0x00, &reconnect_cmd, sizeof(reconnect_cmd)); + // 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 version_to_port_name({ + "bb-patch", "console-login", "pc-login", "console-login", "console-login", "bb-init"}); + const auto& port_name = version_to_port_name.at(static_cast( + 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(&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( + &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() { diff --git a/src/ProxyServer.hh b/src/ProxyServer.hh index 2ea0f082..c036eea5 100644 --- a/src/ProxyServer.hh +++ b/src/ProxyServer.hh @@ -82,6 +82,7 @@ public: std::vector lobby_players; size_t lobby_client_id; size_t leader_client_id; + bool is_in_game; std::shared_ptr detector_crypt;