From 97db8da273fcc094f4909bf67857e98a8abe034f Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Mon, 28 Mar 2022 22:28:37 -0700 Subject: [PATCH] keep track of lobby player slots on proxy server --- src/ProxyServer.cc | 114 +++++++++++++++++++++++++++++++++++++++++++- src/ProxyServer.hh | 8 ++++ src/SendCommands.cc | 19 +------- src/SendCommands.hh | 19 ++++++++ 4 files changed, 141 insertions(+), 19 deletions(-) diff --git a/src/ProxyServer.cc b/src/ProxyServer.cc index a7ee11b3..6b2d1ec2 100644 --- a/src/ProxyServer.cc +++ b/src/ProxyServer.cc @@ -291,7 +291,9 @@ ProxyServer::LinkedSession::LinkedSession( local_port(local_port), version(version), sub_version(0), // This is set during resume() - guild_card_number(0) { + guild_card_number(0), + lobby_players(12), + lobby_client_id(0) { memset(this->client_config_data, 0, 0x20); memset(&this->next_destination, 0, sizeof(this->next_destination)); struct sockaddr_in* dest_sin = reinterpret_cast(&this->next_destination); @@ -792,6 +794,116 @@ void ProxyServer::LinkedSession::on_server_input() { this->license->serial_number, size, output_filename.c_str()); break; } + + case 0x67: // join lobby + this->lobby_players.clear(); + this->lobby_players.resize(12); + log(WARNING, "[ProxyServer/%08" PRIX32 "] Cleared lobby players", + this->license->serial_number); + [[fallthrough]]; + + case 0x65: // other player joined game + case 0x68: { // other player joined lobby + struct Command { + uint8_t client_id; + uint8_t leader_id; + uint8_t disable_udp; + uint8_t lobby_number; + uint16_t block_number; + uint16_t event; + uint32_t unused; + struct Entry { + PlayerLobbyDataGC lobby_data; + PlayerLobbyJoinDataPCGC data; + } __attribute__((packed)); + Entry entries[0]; + } __attribute__((packed)); + + size_t expected_size = sizeof(Command) + sizeof(Command::Entry) * flag; + if (data.size() < expected_size) { + log(WARNING, "[ProxyServer/%08" PRIX32 "] Lobby join command is incorrect size (expected 0x%zX bytes, received 0x%zX bytes)", + this->license->serial_number, expected_size, data.size()); + } else { + const auto* cmd = reinterpret_cast(data.data()); + + this->lobby_client_id = cmd->client_id; + + for (size_t x = 0; x < flag; x++) { + size_t index = cmd->entries[x].lobby_data.client_id; + if (index >= this->lobby_players.size()) { + log(WARNING, "[ProxyServer/%08" PRIX32 "] Ignoring invalid player index %zu at position %zu", + this->license->serial_number, index, x); + } else { + this->lobby_players[index].guild_card_number = cmd->entries[x].lobby_data.guild_card; + this->lobby_players[index].name = cmd->entries[x].data.disp.name; + log(INFO, "[ProxyServer/%08" PRIX32 "] Added lobby player: (%zu) %" PRIu32 " %s", + this->license->serial_number, index, + this->lobby_players[index].guild_card_number, + this->lobby_players[index].name.c_str()); + } + } + } + break; + } + + case 0x64: { // join game + // We don't need to clear lobby_players here because we always + // overwrite all 4 entries in this case + this->lobby_players.resize(4); + log(WARNING, "[ProxyServer/%08" PRIX32 "] Cleared lobby players", + this->license->serial_number); + + const size_t expected_size = offsetof(JoinGameCommand_GC_64, player); + const size_t ep3_expected_size = sizeof(JoinGameCommand_GC_64); + if (data.size() < expected_size) { + log(WARNING, "[ProxyServer/%08" PRIX32 "] Game join command is incorrect size (expected 0x%zX bytes, received 0x%zX bytes)", + this->license->serial_number, expected_size, data.size()); + } else { + const auto* cmd = reinterpret_cast(data.data()); + + this->lobby_client_id = cmd->client_id; + + for (size_t x = 0; x < flag; x++) { + this->lobby_players[x].guild_card_number = cmd->lobby_data[x].guild_card; + if (data.size() >= ep3_expected_size) { + this->lobby_players[x].name = cmd->player[x].disp.name; + } else { + this->lobby_players[x].name.clear(); + } + log(INFO, "[ProxyServer/%08" PRIX32 "] Added lobby player: (%zu) %" PRIu32 " %s", + this->license->serial_number, x, + this->lobby_players[x].guild_card_number, + this->lobby_players[x].name.c_str()); + } + } + break; + } + + case 0x66: + case 0x69: { + struct Command { + uint8_t client_id; + uint8_t leader_id; + uint16_t unused; + } __attribute__((packed)); + if (data.size() < sizeof(Command)) { + log(WARNING, "[ProxyServer/%08" PRIX32 "] Lobby leave command is incorrect size", + this->license->serial_number); + } else { + const auto* cmd = reinterpret_cast(data.data()); + size_t index = cmd->client_id; + if (index >= this->lobby_players.size()) { + log(WARNING, "[ProxyServer/%08" PRIX32 "] Lobby leave command references missing position", + this->license->serial_number); + } else { + this->lobby_players[index].guild_card_number = 0; + this->lobby_players[index].name.clear(); + log(INFO, "[ProxyServer/%08" PRIX32 "] Removed lobby player (%zu)", + this->license->serial_number, index); + } + } + break; + } } if (should_forward) { diff --git a/src/ProxyServer.hh b/src/ProxyServer.hh index 13197680..538e6557 100644 --- a/src/ProxyServer.hh +++ b/src/ProxyServer.hh @@ -46,6 +46,14 @@ public: uint32_t guild_card_number; uint8_t client_config_data[0x20]; + struct LobbyPlayer { + uint32_t guild_card_number; + std::string name; + LobbyPlayer() : guild_card_number(0) { } + }; + std::vector lobby_players; + size_t lobby_client_id; + std::shared_ptr client_input_crypt; std::shared_ptr client_output_crypt; std::shared_ptr server_input_crypt; diff --git a/src/SendCommands.cc b/src/SendCommands.cc index e85fa31e..88181f81 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -1399,24 +1399,7 @@ static void send_join_game_pc(shared_ptr c, shared_ptr l) { } static void send_join_game_gc(shared_ptr c, shared_ptr l) { - struct { - uint32_t variations[0x20]; - PlayerLobbyDataGC lobby_data[4]; - uint8_t client_id; - uint8_t leader_id; - uint8_t disable_udp; // guess; putting 0 here causes no movement messages to be sent - uint8_t difficulty; - uint8_t battle_mode; - uint8_t event; - uint8_t section_id; - uint8_t challenge_mode; - uint32_t rare_seed; - uint32_t episode; // for PSOPC, this must be 0x00000100 - struct { - PlayerInventory inventory; - PlayerDispDataPCGC disp; - } player[4]; // only used on ep3 - } cmd; + JoinGameCommand_GC_64 cmd; size_t player_count = 0; memcpy(cmd.variations, l->variations, sizeof(cmd.variations)); diff --git a/src/SendCommands.hh b/src/SendCommands.hh index 319ed5f4..a9859af0 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -131,6 +131,25 @@ void send_quest_menu(std::shared_ptr c, uint32_t menu_id, const std::vector& items, bool is_download_menu); void send_lobby_list(std::shared_ptr c, std::shared_ptr s); +struct JoinGameCommand_GC_64 { + uint32_t variations[0x20]; + PlayerLobbyDataGC lobby_data[4]; + uint8_t client_id; + uint8_t leader_id; + uint8_t disable_udp; // guess; putting 0 here causes no movement messages to be sent + uint8_t difficulty; + uint8_t battle_mode; + uint8_t event; + uint8_t section_id; + uint8_t challenge_mode; + uint32_t rare_seed; + uint32_t episode; // for PSOPC, this must be 0x00000100 + struct { + PlayerInventory inventory; + PlayerDispDataPCGC disp; + } player[4]; // only used on ep3 +} __attribute__((packed)); + void send_join_lobby(std::shared_ptr c, std::shared_ptr l); void send_player_join_notification(std::shared_ptr c, std::shared_ptr l, std::shared_ptr joining_client);