diff --git a/src/IPStackSimulator.cc b/src/IPStackSimulator.cc index 46156f5d..775a1050 100644 --- a/src/IPStackSimulator.cc +++ b/src/IPStackSimulator.cc @@ -77,10 +77,8 @@ string IPStackSimulator::str_for_tcp_connection(shared_ptr c, IPStackSimulator::IPStackSimulator( std::shared_ptr base, - std::shared_ptr game_server, std::shared_ptr state) : base(base), - game_server(game_server), state(state), pcap_text_log_file(state->ip_stack_debug ? fopen("IPStackSimulator-Log.txt", "wt") : nullptr) { memset(this->host_mac_address_bytes, 0x90, 6); @@ -779,9 +777,9 @@ void IPStackSimulator::open_server_connection( this->log(INFO, "Connected TCP connection %s to proxy server", conn_str.c_str()); } - } else if (this->game_server.get()) { - this->game_server->connect_client(bevs[1], c->ipv4_addr, conn.client_port, - port_config->version, port_config->behavior); + } else if (this->state->game_server.get()) { + this->state->game_server->connect_client(bevs[1], c->ipv4_addr, + conn.client_port, port_config->version, port_config->behavior); this->log(INFO, "Connected TCP connection %s to game server", conn_str.c_str()); } else { diff --git a/src/IPStackSimulator.hh b/src/IPStackSimulator.hh index b02cf42a..75d1c1ce 100644 --- a/src/IPStackSimulator.hh +++ b/src/IPStackSimulator.hh @@ -17,7 +17,6 @@ class IPStackSimulator { public: IPStackSimulator( std::shared_ptr base, - std::shared_ptr game_server, std::shared_ptr state); ~IPStackSimulator(); @@ -31,7 +30,6 @@ public: private: static PrefixedLogger log; std::shared_ptr base; - std::shared_ptr game_server; std::shared_ptr state; using unique_listener = std::unique_ptr; diff --git a/src/Main.cc b/src/Main.cc index fef4ca24..20b49d6b 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -444,8 +444,6 @@ int main(int argc, char** argv) { log(INFO, "DNS server is disabled"); } - shared_ptr game_server; - log(INFO, "Opening sockets"); for (const auto& it : state->name_to_port_config) { const auto& pc = it.second; @@ -475,22 +473,21 @@ int main(int argc, char** argv) { } } } else { - if (!game_server.get()) { + if (!state->game_server.get()) { log(INFO, "Starting game server"); - game_server.reset(new Server(base, state)); + state->game_server.reset(new Server(base, state)); } string name = string_printf("%s (%s, %s) on port %hu", pc->name.c_str(), name_for_version(pc->version), name_for_server_behavior(pc->behavior), pc->port); - game_server->listen(name, "", pc->port, pc->version, pc->behavior); + state->game_server->listen(name, "", pc->port, pc->version, pc->behavior); } } shared_ptr ip_stack_simulator; if (!state->ip_stack_addresses.empty()) { log(INFO, "Starting IP stack simulator"); - ip_stack_simulator.reset(new IPStackSimulator( - base, game_server, state)); + ip_stack_simulator.reset(new IPStackSimulator(base, state)); for (const auto& it : state->ip_stack_addresses) { auto netloc = parse_netloc(it); ip_stack_simulator->listen(netloc.first, netloc.second); diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 64e829be..79250c86 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -153,6 +153,33 @@ void send_command(shared_ptr s, uint16_t command, uint32_t flag, } } +template +void send_command_with_header_t(shared_ptr c, const void* data, + size_t size) { + const HeaderT* header = reinterpret_cast(data); + send_command(c, header->command, header->flag, header + 1, size - sizeof(HeaderT)); +} + +void send_command_with_header(shared_ptr c, const void* data, + size_t size) { + switch (c->version) { + case GameVersion::GC: + case GameVersion::DC: + send_command_with_header_t(c, data, size); + break; + case GameVersion::PC: + case GameVersion::PATCH: + send_command_with_header_t(c, data, size); + break; + case GameVersion::BB: + send_command_with_header_t(c, data, size); + break; + default: + throw logic_error("unimplemented game version in send_command_with_header"); + } +} + + // specific command sending functions follow. in general, they're written in @@ -540,7 +567,7 @@ void send_chat_message(shared_ptr c, uint32_t from_guild_card_number, send_header_text(c, 0x06, from_guild_card_number, data); } -void send_simple_mail_gc(std::shared_ptr c, uint32_t from_guild_card_number, +void send_simple_mail_gc(shared_ptr c, uint32_t from_guild_card_number, const u16string& from_name, const u16string& text) { SC_SimpleMail_GC_81 cmd; cmd.player_tag = 0x00010000; @@ -551,7 +578,7 @@ void send_simple_mail_gc(std::shared_ptr c, uint32_t from_guild_card_num send_command_t(c, 0x81, 0x00, cmd); } -void send_simple_mail(std::shared_ptr c, uint32_t from_guild_card_number, +void send_simple_mail(shared_ptr c, uint32_t from_guild_card_number, const u16string& from_name, const u16string& text) { if (c->version == GameVersion::GC) { send_simple_mail_gc(c, from_guild_card_number, from_name, text); @@ -852,7 +879,7 @@ void send_quest_menu(shared_ptr c, uint32_t menu_id, } void send_quest_menu(shared_ptr c, uint32_t menu_id, - const std::vector& items, bool is_download_menu) { + const vector& items, bool is_download_menu) { if (c->version == GameVersion::PC) { send_quest_menu_t(c, menu_id, items, is_download_menu); } else if (c->version == GameVersion::GC) { diff --git a/src/SendCommands.hh b/src/SendCommands.hh index e161447e..56f4d397 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -22,8 +22,8 @@ // command to one client, sometimes to everyone in a lobby, etc.) // - For the const void* versions, the data and size arguments should not be // independently optional - this can lead to bugs where a non-null data -// pointer is given but size is accidentally not given zero (e.g. if the type -// of data in the calling function is changed from string to void*). +// pointer is given but size is accidentally not given (e.g. if the type of +// data in the calling function is changed from string to void*). void send_command(struct bufferevent* bev, GameVersion version, PSOEncryption* crypt, uint16_t command, uint32_t flag, const void* data, @@ -88,6 +88,9 @@ void send_command_t_vt(std::shared_ptr c, uint16_t command, send_command(c, command, flag, all_data.data(), all_data.size()); } +void send_command_with_header(std::shared_ptr c, const void* data, + size_t size); + S_ServerInit_DC_PC_GC_02_17 prepare_server_init_contents_dc_pc_gc( diff --git a/src/Server.cc b/src/Server.cc index 48372a69..e7654670 100644 --- a/src/Server.cc +++ b/src/Server.cc @@ -273,3 +273,13 @@ void Server::add_socket( this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(this, name, fd, version, behavior)); } + +shared_ptr Server::get_client() const { + if (this->bev_to_client.empty()) { + throw runtime_error("no clients on game server"); + } + if (this->bev_to_client.size() > 1) { + throw runtime_error("multiple clients on game server"); + } + return this->bev_to_client.begin()->second; +} \ No newline at end of file diff --git a/src/Server.hh b/src/Server.hh index 1f7c7ee7..249fb6a9 100644 --- a/src/Server.hh +++ b/src/Server.hh @@ -29,6 +29,8 @@ public: void connect_client(struct bufferevent* bev, uint32_t address, uint16_t port, GameVersion version, ServerBehavior initial_state); + std::shared_ptr get_client() const; + private: PrefixedLogger log; std::shared_ptr base; diff --git a/src/ServerShell.cc b/src/ServerShell.cc index e7e729bd..ef56baeb 100644 --- a/src/ServerShell.cc +++ b/src/ServerShell.cc @@ -280,9 +280,6 @@ Proxy commands (these will only work when exactly one client is connected):\n\ // PROXY COMMANDS } else if ((command_name == "sc") || (command_name == "ss")) { - auto session = this->get_proxy_session(); - - bool to_server = (command_name[1] == 's'); string data = parse_data_string(command_args); if (data.size() & 3) { throw invalid_argument("data size is not a multiple of 4"); @@ -291,7 +288,23 @@ Proxy commands (these will only work when exactly one client is connected):\n\ throw invalid_argument("no data given"); } - session->send_to_end_with_header(to_server, data); + shared_ptr proxy_session; + try { + proxy_session = this->get_proxy_session(); + } catch (const exception&) { } + + if (proxy_session.get()) { + bool to_server = (command_name[1] == 's'); + proxy_session->send_to_end_with_header(to_server, data); + + } else { + if (command_name [1] == 's') { + throw runtime_error("cannot send to server in non-proxy session"); + } + + auto c = this->state->game_server->get_client(); + send_command_with_header(c, data.data(), data.size()); + } } else if ((command_name == "chat") || (command_name == "dchat")) { auto session = this->get_proxy_session(); diff --git a/src/ServerState.hh b/src/ServerState.hh index d24d3473..f8583b71 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -18,8 +18,9 @@ -// Forwawrd declaration due to reference cycle +// Forwawrd declarations due to reference cycles class ProxyServer; +class Server; struct PortConfiguration { std::string name; @@ -76,9 +77,8 @@ struct ServerState { uint32_t local_address; uint32_t external_address; - // TODO: This is only here because the menu selection handler has to call - // delete_session on it. Find a cleaner way to do this. std::shared_ptr proxy_server; + std::shared_ptr game_server; ServerState();