From d0bf4ff3d786c9f0d6ae35384ae472baf3743303 Mon Sep 17 00:00:00 2001 From: Ion Agorria Date: Tue, 2 Jan 2024 01:10:41 +0100 Subject: [PATCH] net: Add initial relay support --- Source/Network/NetConnection.cpp | 15 +++- Source/Network/NetConnection.h | 16 +++- Source/Network/NetConnectionHandler.cpp | 74 ++++++++++++++----- Source/Network/P2P_interface.h | 9 ++- Source/Network/P2P_interface1Th.cpp | 17 ++++- Source/Network/P2P_interface2Th.cpp | 5 +- Source/Network/P2P_interface2Th_NetConn.cpp | 14 ++-- Source/UserInterface/Menu/MultiplayerHost.cpp | 7 +- Source/UserInterface/Menu/MultiplayerList.cpp | 2 +- 9 files changed, 118 insertions(+), 41 deletions(-) diff --git a/Source/Network/NetConnection.cpp b/Source/Network/NetConnection.cpp index cb71af80b..f99d6a512 100644 --- a/Source/Network/NetConnection.cpp +++ b/Source/Network/NetConnection.cpp @@ -147,11 +147,11 @@ int NetConnection::send_raw(const void* buffer, uint32_t len) { return sent; } -int NetConnection::send(const XBuffer& data) { - return send(reinterpret_cast(data.buf), data.length()); +int NetConnection::send(const XBuffer& data, NETID destination) { + return send(reinterpret_cast(data.buf), data.length(), destination); } -int NetConnection::send(const uint8_t* buffer, uint32_t len) { +int NetConnection::send(const uint8_t* buffer, uint32_t len, NETID destination) { if (buffer == nullptr) { ErrH.Abort("Got null buffer in send"); } @@ -169,6 +169,12 @@ int NetConnection::send(const uint8_t* buffer, uint32_t len) { } } + //Destination NETID info + if (destination != NETID_NONE) { + flags |= PERIMETER_MESSAGE_FLAG_NETID; + len += sizeof(NETID); + } + //Calculate message size int msg_size = static_cast(len + sizeof(NC_HEADER_MAGIC)); if (msg_size > PERIMETER_MESSAGE_MAX_SIZE) { @@ -185,6 +191,9 @@ int NetConnection::send(const uint8_t* buffer, uint32_t len) { xbuf.set(3); xbuf < len; xbuf.set(8); + if (flags & PERIMETER_MESSAGE_FLAG_NETID) { + xbuf < destination; + } xbuf.write(sending_buffer, len); int sent = send_raw(xbuf.buf, msg_size); diff --git a/Source/Network/NetConnection.h b/Source/Network/NetConnection.h index 305be4022..565a36cb0 100644 --- a/Source/Network/NetConnection.h +++ b/Source/Network/NetConnection.h @@ -7,6 +7,8 @@ const uint32_t PERIMETER_MESSAGE_MAX_SIZE = 32 * 1024 * 1024; const uint32_t PERIMETER_MESSAGE_COMPRESSION_SIZE = 128*1024; ///Specifies this message contains compressed payload const uint16_t PERIMETER_MESSAGE_FLAG_COMPRESSED = 1 << 0; +///Specifies this message contains NETID that is intended for +const uint16_t PERIMETER_MESSAGE_FLAG_NETID = 1 << 1; ///How many milliseconds extra to wait for the data part once getting header const int RECV_DATA_AFTER_HEADER_TIMEOUT = 10000; ///How many milliseconds to wait for handshake to be sent/recv @@ -17,6 +19,7 @@ const int CONNECTION_HANDSHAKE_TIMEOUT = 10000; //Special IDs const uint64_t NETID_NONE = 0; const uint64_t NETID_HOST = 1; +const uint64_t NETID_RELAY = -2; const uint64_t NETID_ALL = -1; //Used to identify player connection @@ -139,19 +142,21 @@ class NetConnection { * Closes connection upon error * * @param buffer data to send into connection + * @param netid destination NETID to send this data, can be NETID_NONE * @return amount of bytes sent, <0 if error or closed */ - int send(const XBuffer& data); + int send(const XBuffer& data, NETID destination); /** * Writes data to connection * Closes connection upon error * * @param buffer pointer of data to send into connection + * @param netid destination NETID to send this data, can be NETID_NONE * @param len amount of data to send * @return amount of bytes sent, <0 if error or closed */ - int send(const uint8_t* buffer, uint32_t len); + int send(const uint8_t* buffer, uint32_t len, NETID destination); /** * Receives data from connection if any @@ -177,10 +182,14 @@ class NetConnectionHandler { PNetCenter* net_center = nullptr; TCPsocket accept_socket = nullptr; std::unordered_map connections; + //Special connection dedicated to relay + NetConnection* relayConnection; void stopListening(); + void stopRelay(); void stopConnections(); - NetConnection* newConnectionFromSocket(TCPsocket socket, bool host); + + NetConnection* newConnectionFromSocket(TCPsocket socket, NETID netid); public: explicit NetConnectionHandler(PNetCenter* center); @@ -209,6 +218,7 @@ class NetConnectionHandler { //Host related functions bool startListening(uint16_t port); + bool startRelay(); //Single client connection NetConnection* startConnection(NetAddress* address); diff --git a/Source/Network/NetConnectionHandler.cpp b/Source/Network/NetConnectionHandler.cpp index 7cc465ae4..6674b0968 100644 --- a/Source/Network/NetConnectionHandler.cpp +++ b/Source/Network/NetConnectionHandler.cpp @@ -15,6 +15,7 @@ NetConnectionHandler::~NetConnectionHandler() { void NetConnectionHandler::reset() { stopListening(); + stopRelay(); stopConnections(); } @@ -28,19 +29,20 @@ NETID NetConnectionHandler::acceptConnection() { NetConnection* incoming = nullptr; //Find any closed connection in array - bool reused = false; for (auto& entry : connections) { if (entry.second->is_closed()) { incoming = entry.second; incoming->set_socket(incoming_socket); - reused = true; break; } } - //Check if we can add it - if (!reused) { - incoming = newConnectionFromSocket(incoming_socket, false); + //Couldn't find any connection to reuse, create new + if (incoming == nullptr) { + incoming = newConnectionFromSocket( + incoming_socket, + NETID_HOST + connections.size() + 1 + ); } //incoming may be deallocated if index is not available, so get it before @@ -137,6 +139,49 @@ void NetConnectionHandler::stopListening() { } } +bool NetConnectionHandler::startRelay() { + reset(); + + +#if defined(PERIMETER_DEBUG) && 1 + const char* relay_address = "127.0.0.1:8877"; +#else + const char* relay_address = nullptr; //"relay.kdlab.com"; +#endif + + if (!relay_address) { + fprintf(stderr, "No relay available!\n"); + return false; + } + + NetAddress relay_addr; + bool resolveFailed = !NetAddress::resolve(relay_addr, relay_address); + if (!resolveFailed) { + return false; + } + + TCPsocket socket = SDLNet_TCP_Open(&relay_addr.addr); + if (!socket) { + fprintf(stderr, "TCP socket open failed address %s error %s\n", relay_addr.getString().c_str(), SDLNet_GetError()); + return false; + } + + max_connections = 0; + relayConnection = newConnectionFromSocket(socket, NETID_RELAY); + + //TODO do presentation to relay telling the game type and own data? + + return true; +} + +void NetConnectionHandler::stopRelay() { + if (relayConnection) { + LogMsg("Relay connection closed\n"); + relayConnection->close(); + relayConnection = nullptr; + } +} + NetConnection* NetConnectionHandler::startConnection(NetAddress* address) { reset(); @@ -159,29 +204,22 @@ NetConnection* NetConnectionHandler::startConnection(NetAddress* address) { } } -NetConnection* NetConnectionHandler::newConnectionFromSocket(TCPsocket socket, bool host) { +NetConnection* NetConnectionHandler::newConnectionFromSocket(TCPsocket socket, NETID netid) { NetConnection* connection = new NetConnection(socket); if (connections.size() < max_connections) { - NETID netid; - if (host) { - netid = NETID_HOST; - } else { - netid = NETID_HOST + connections.size() + 1; - } connection->netid = netid; connections.insert_or_assign(netid, connection); } return connection; } - -size_t SendBufferToConnection(const uint8_t* buffer, size_t size, NetConnection* connection) { +size_t SendBufferToConnection(const uint8_t* buffer, size_t size, NetConnection* connection, NETID destination) { int retries = 5; if (!connection || !connection->is_active()) { return 0; } while (0 < retries) { - int sent = connection->send(buffer, size); + int sent = connection->send(buffer, size, destination); if (0 < sent) { return size; } else if (!connection->is_active()) { @@ -199,12 +237,14 @@ size_t NetConnectionHandler::sendToNETID(const uint8_t* buffer, size_t size, NET size_t sent = 0; if (destination == NETID_NONE) { fprintf(stderr, "Discarding sending to NETID_NONE\n"); + } else if (relayConnection) { + sent = SendBufferToConnection(buffer, size, relayConnection, destination); } else if (destination == NETID_ALL) { for (auto& conn : connections) { - sent += SendBufferToConnection(buffer, size, conn.second); + sent += SendBufferToConnection(buffer, size, conn.second, NETID_NONE); } } else { - sent = SendBufferToConnection(buffer, size, getConnection(destination)); + sent = SendBufferToConnection(buffer, size, getConnection(destination), NETID_NONE); } return sent; diff --git a/Source/Network/P2P_interface.h b/Source/Network/P2P_interface.h index bcc73118a..7825a23d0 100644 --- a/Source/Network/P2P_interface.h +++ b/Source/Network/P2P_interface.h @@ -243,7 +243,8 @@ struct sPNCInterfaceCommand { }; enum e_PNCInternalCommand{ - PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST, + PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST, + PNC_COMMAND__CONNECT_RELAY_AND_CREATE_GAME_AND_STOP_FIND_HOST, //PNCC_START_FIND_HOST, PNC_COMMAND__CONNECT_2_HOST_AND_STOP_FIND_HOST, @@ -436,7 +437,9 @@ class PNetCenter { bool ExecuteInternalCommand(e_PNCInternalCommand ic, bool waitExecution); bool ExecuteInterfaceCommand(e_PNCInterfaceCommands ic, std::unique_ptr text = nullptr); - void CreateGame(const NetAddress& connection, const std::string& gameName, MissionDescription* mission, const std::string& playerName, const std::string& password=""); + void CreateGame(bool isPublicGame, const NetAddress& connection, + const std::string& gameName, MissionDescription* mission, + const std::string& playerName, const std::string& password); void JoinGame(const NetAddress& connection, const std::string& playerName, const std::string& password=""); @@ -523,7 +526,7 @@ class PNetCenter { void StartFindHost(); bool Init(); - bool ServerStart(); + bool ServerStart(bool); void SetConnectionTimeout(int ms); void RemovePlayer(NETID netid); diff --git a/Source/Network/P2P_interface1Th.cpp b/Source/Network/P2P_interface1Th.cpp index 9cd020512..9cf3315e8 100644 --- a/Source/Network/P2P_interface1Th.cpp +++ b/Source/Network/P2P_interface1Th.cpp @@ -238,8 +238,11 @@ int PNetCenter::getHostPort() { ////////////////////////////////////////////// /// New network game creation or joining -void PNetCenter::CreateGame(const NetAddress& connection, const std::string& gameName, MissionDescription* mission, const std::string& playerName, const std::string& password) -{ +void PNetCenter::CreateGame( + bool isPublicGame, const NetAddress& connection, + const std::string& gameName, MissionDescription* mission, + const std::string& playerName, const std::string& password +) { LogMsg("Create Game\n"); clientPause=false; clientInPacketPause=false; @@ -252,11 +255,17 @@ void PNetCenter::CreateGame(const NetAddress& connection, const std::string& gam xassert(mission); hostMissionDescription = mission; - //Argument PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST + //Arguments for: + // PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST + // PNC_COMMAND__CONNECT_RELAY_AND_CREATE_GAME_AND_STOP_FIND_HOST m_GameName = gameName; m_PlayerName = playerName; - ExecuteInternalCommand(PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST, true); + ExecuteInternalCommand( + isPublicGame ? PNC_COMMAND__CONNECT_RELAY_AND_CREATE_GAME_AND_STOP_FIND_HOST + : PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST, + true + ); gameShell->callBack_CreateGameReturnCode(isConnected() ? GameShell::CG_RC_OK : GameShell::CG_RC_CREATE_HOST_ERR); } diff --git a/Source/Network/P2P_interface2Th.cpp b/Source/Network/P2P_interface2Th.cpp index 294a5a191..9576ff1d4 100644 --- a/Source/Network/P2P_interface2Th.cpp +++ b/Source/Network/P2P_interface2Th.cpp @@ -135,6 +135,7 @@ bool PNetCenter::SecondThread() } break; case PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST: + case PNC_COMMAND__CONNECT_RELAY_AND_CREATE_GAME_AND_STOP_FIND_HOST: { flag_LockIputPacket=0; flag_SkipProcessingGameCommand=0; @@ -148,8 +149,8 @@ bool PNetCenter::SecondThread() LogMsg("starting server...\n"); if(!isConnected()) { - //m_pConnection->Init(); - if (ServerStart()) { + bool isPrivate = curInternalCommand == PNC_COMMAND__START_HOST_AND_CREATE_GAME_AND_STOP_FIND_HOST; + if (ServerStart(isPrivate)) { LogMsg("...started OK\n"); } } diff --git a/Source/Network/P2P_interface2Th_NetConn.cpp b/Source/Network/P2P_interface2Th_NetConn.cpp index e6812c218..36980c2cb 100644 --- a/Source/Network/P2P_interface2Th_NetConn.cpp +++ b/Source/Network/P2P_interface2Th_NetConn.cpp @@ -38,10 +38,14 @@ bool PNetCenter::Init() return true; } -bool PNetCenter::ServerStart() -{ +bool PNetCenter::ServerStart(bool publicServer) +{ m_hostNETID = m_localNETID = NETID_NONE; - flag_connected = connectionHandler.startListening(hostConnection.port()); + if (publicServer) { + flag_connected = connectionHandler.startRelay(); + } else { + flag_connected = connectionHandler.startListening(hostConnection.port()); + } if (!flag_connected) { fprintf(stderr, "Error listening on port %d\n", hostConnection.port()); } else { @@ -89,7 +93,7 @@ bool PNetCenter::Connect() { connectInfo.set(currentShortVersion, gamePassword.c_str(), terGameContentSelect, m_PlayerName.c_str()); XBuffer buffer(256, true); connectInfo.write(buffer); - connection->send(buffer); + connection->send(buffer, NETID_HOST); //Get the response and pass it to interface command int ret = connection->receive(buffer, CONNECTION_HANDSHAKE_TIMEOUT); @@ -267,7 +271,7 @@ void PNetCenter::handleIncomingClientConnection(NetConnection* connection) { if (response.connectResult != NetConnectionInfoResponse::CR_NONE) { XBuffer responseBuffer(256, true); response.write(responseBuffer); - ret = connection->send(responseBuffer); + ret = connection->send(responseBuffer, connection->netid); } //Close connection if result was not OK diff --git a/Source/UserInterface/Menu/MultiplayerHost.cpp b/Source/UserInterface/Menu/MultiplayerHost.cpp index efad2fc73..099b7a7f8 100644 --- a/Source/UserInterface/Menu/MultiplayerHost.cpp +++ b/Source/UserInterface/Menu/MultiplayerHost.cpp @@ -70,6 +70,8 @@ int creatingHostDialogQuant(float, float ) { std::string gameName = gameNameInput->getText(); CEditWindow* passwordInput = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_HOST_PASSWORD_INPUT)); std::string password = passwordInput->getText(); + CComboWindow* hostTypeCombo = dynamic_cast(_shellIconManager.GetWnd(SQSH_MM_MULTIPLAYER_HOST_TYPE_COMBO)); + bool isPublic = hostTypeCombo->pos != 0; putStringSettings("HostName", gameName); putStringSettings("HostPort", std::to_string(conn.port())); @@ -87,7 +89,7 @@ int creatingHostDialogQuant(float, float ) { mission = new MissionDescription(missionName.c_str(), GT_MULTI_PLAYER_CREATE); } - gameShell->getNetClient()->CreateGame(conn, gameName, mission, playerName, password); + gameShell->getNetClient()->CreateGame(isPublic, conn, gameName, mission, playerName, password); } return 0; } @@ -100,8 +102,7 @@ void onMMMultiplayerHostTypeCombo(CShellWindow* pWnd, InterfaceEventCode code, i CComboWindow *pCombo = (CComboWindow*) pWnd; if (code == EVENT_CREATEWND) { pCombo->Array.emplace_back(getItemTextFromBase("Private Server").c_str() ); - //TODO enable this once implemented the public listing - //pCombo->Array.emplace_back(getItemTextFromBase("Public Server").c_str() ); + pCombo->Array.emplace_back(getItemTextFromBase("Public Server").c_str() ); pCombo->size = pCombo->Array.size(); pCombo->pos = 0; } diff --git a/Source/UserInterface/Menu/MultiplayerList.cpp b/Source/UserInterface/Menu/MultiplayerList.cpp index 578aaa49e..4e4427f0e 100644 --- a/Source/UserInterface/Menu/MultiplayerList.cpp +++ b/Source/UserInterface/Menu/MultiplayerList.cpp @@ -51,7 +51,7 @@ int startCmdlineQuant(float, float ) { mission->loadIntoMemory(); } _shellIconManager.initialMenu = SQSH_MM_MULTIPLAYER_LOBBY_SCR; - gameShell->getNetClient()->CreateGame(conn, gameName, mission, cmdLineData.playerName, cmdLineData.password); + gameShell->getNetClient()->CreateGame(cmdLineData.publicHost, conn, gameName, mission, cmdLineData.playerName, cmdLineData.password); showMessageBoxButtons(); } } else {