diff --git a/CMakeLists.txt b/CMakeLists.txt index 97eee7bc2c..59ec399bc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -224,6 +224,7 @@ set(network_SRCS src/network/net_lowlevel.cpp src/network/net_message.cpp src/network/master.cpp + src/network/net_connection_handler.cpp src/network/netconnect.cpp src/network/network.cpp src/network/netsockets.cpp @@ -539,6 +540,7 @@ set(stratagus_generic_HDRS src/include/net_message.h src/include/netconnect.h src/include/network.h + src/include/net_connection_handler.h src/include/network/netsockets.h src/include/parameters.h src/include/particle.h diff --git a/src/include/net_connection_handler.h b/src/include/net_connection_handler.h new file mode 100644 index 0000000000..72accad453 --- /dev/null +++ b/src/include/net_connection_handler.h @@ -0,0 +1,176 @@ +// _________ __ __ +// / _____// |_____________ _/ |______ ____ __ __ ______ +// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/ +// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ | +// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ > +// \/ \/ \//_____/ \/ +// ______________________ ______________________ +// T H E W A R B E G I N S +// Stratagus - A free fantasy real time strategy game engine +// +/**@name master.cpp - The master server. */ +// +// (c) Copyright 2003-2007 by Tom Zickel and Jimmy Salmon +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; only version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#ifndef __NET_CONNECTION_HANDLER_H__ +#define __NET_CONNECTION_HANDLER_H__ + +#include "network/netsockets.h" + +#include +#include +#include + +class IConnectionHandler +{ +public: + virtual ~IConnectionHandler() = default; + virtual void Open(const CHost &host) = 0; + virtual bool IsValid() = 0; + virtual int HasDataToRead(int timeout) = 0; + virtual int Recv(unsigned char *buf, int len, CHost *hostFrom) = 0; + virtual void Close() = 0; +}; + +class IServerConnectionHandler : public IConnectionHandler +{ +public: + virtual ~IServerConnectionHandler() = default; + virtual void SendToAllClients(std::vector hosts, const unsigned char *buf, unsigned int len) = 0; + virtual void SendToClient(CHost host, const unsigned char *buf, unsigned int len) = 0; +}; + +class IClientConnectionHandler : public IConnectionHandler +{ +public: + virtual ~IClientConnectionHandler() = default; + virtual void SendToServer(const unsigned char *buf, unsigned int len) = 0; +}; + +class CTCPConnectionHandler +{ +public: + CTCPConnectionHandler() = default; + CTCPConnectionHandler(CTCPSocket socket) : _socket(socket) {} + bool Open(const CHost& host); + int Listen(); + std::shared_ptr Accept(); + void Close(); + bool Connect(const CHost& host); + int Send(const unsigned char* buf, unsigned int len); + int Recv(unsigned char* buf, int len); + void SetBlocking(); + void SetNonBlocking(); + // + int HasDataToRead(int timeout); + bool IsValid() const; + CHost GetHost() const; + +private: + CTCPSocket _socket; +}; + +class CUDPConnectionHandler +{ +public: + bool Open(const CHost& host); + void Close(); + void Send(const CHost& host, const unsigned char* buf, unsigned int len); + int Recv(unsigned char* buf, int len, CHost* hostFrom); + void SetNonBlocking(); + // + int HasDataToRead(int timeout); + bool IsValid() const; + +private: + CUDPSocket _socket; +}; + +class CTCPServerConnectionHandler : public IServerConnectionHandler +{ +public: + virtual ~CTCPServerConnectionHandler() = default; + void Open(const CHost& host) override; + bool IsValid() override { return _connectionHandler.IsValid(); } + int HasDataToRead(int timeout) override; + void SendToAllClients(std::vector hosts, const unsigned char* buf, unsigned int len) override; + void SendToClient(CHost host, const unsigned char* buf, unsigned int len) override; + int Recv(unsigned char* buf, int len, CHost* hostFrom) override; + void Close() override; + +private: + int _skipClient = 0; + CTCPConnectionHandler _connectionHandler; + std::map> _clientConnections; +}; + +class CUDPServerConnectionHandler : public IServerConnectionHandler +{ +public: + virtual ~CUDPServerConnectionHandler() = default; + void Open(const CHost& host) override; + int HasDataToRead(int timeout) override { return _connectionHandler.HasDataToRead(timeout); } + void SendToAllClients(std::vector hosts, const unsigned char* buf, unsigned int len) override; + void SendToClient(CHost host, const unsigned char* buf, unsigned int len) override; + int Recv(unsigned char* buf, int len, CHost* hostFrom) override; + bool IsValid() override { return _connectionHandler.IsValid(); } + void Close() override { _connectionHandler.Close(); } + +private: + CUDPConnectionHandler _connectionHandler; +}; + +class CTCPClientConnectionHandler : public IClientConnectionHandler +{ +public: + CTCPClientConnectionHandler(CHost serverHost) + : _serverHost(serverHost) {} + + virtual ~CTCPClientConnectionHandler() = default; + void Open(const CHost &host) override; + int HasDataToRead(int timeout) override { return _connectionHandler.HasDataToRead(timeout); } + void SendToServer(const unsigned char *buf, unsigned int len) override; + int Recv(unsigned char *buf, int len, CHost *hostFrom) override; + bool IsValid() override { return _connectionHandler.IsValid(); } + void Close() override { _connectionHandler.Close(); } + +private: + CHost _serverHost; + CTCPConnectionHandler _connectionHandler; +}; + +class CUDPClientConnectionHandler : public IClientConnectionHandler +{ +public: + CUDPClientConnectionHandler(CHost serverHost) + : _serverHost(serverHost) {} + + virtual ~CUDPClientConnectionHandler() = default; + void Open(const CHost &host) override; + int HasDataToRead(int timeout) override { return _connectionHandler.HasDataToRead(timeout); } + void SendToServer(const unsigned char *buf, unsigned int len) override; + int Recv(unsigned char *buf, int len, CHost *hostFrom) override; + bool IsValid() override { return _connectionHandler.IsValid(); } + void Close() override { _connectionHandler.Close(); } + +private: + CHost _serverHost; + CUDPConnectionHandler _connectionHandler; +}; + +#endif // !__NET_CONNECTION_HANDLER_H__ \ No newline at end of file diff --git a/src/include/net_lowlevel.h b/src/include/net_lowlevel.h index 42611a323e..45ca7078a7 100644 --- a/src/include/net_lowlevel.h +++ b/src/include/net_lowlevel.h @@ -141,6 +141,8 @@ extern int NetListenTCP(Socket sockfd); extern Socket NetAcceptTCP(Socket sockfd, unsigned long *clientHost, int *clientPort); +/// Set socket to blocking +extern int NetSetBlocking(Socket sockfd); /// Set socket to non-blocking extern int NetSetNonBlocking(Socket sockfd); /// Wait for socket ready. diff --git a/src/include/netconnect.h b/src/include/netconnect.h index 9baf8e3caf..9d0911cea1 100644 --- a/src/include/netconnect.h +++ b/src/include/netconnect.h @@ -31,7 +31,12 @@ //@{ +/*---------------------------------------------------------------------------- +-- Includes +----------------------------------------------------------------------------*/ + #include "net_message.h" +#include "net_connection_handler.h" class CHost; @@ -121,4 +126,154 @@ extern void NetworkDetachFromServer(); /// Menu Loop: Client: Send GoodBye //@} +/** +** Connect state information of network systems active in current game. +*/ +struct NetworkState { + void Clear() + { + State = ccs_unused; + MsgCnt = 0; + LastFrame = 0; + } + + unsigned char State; /// Menu: ConnectState + unsigned short MsgCnt; /// Menu: Counter for state msg of same type (detect unreachable) + unsigned long LastFrame; /// Last message received + // Fill in here... +}; + +class CServer +{ +public: + void Init(const std::string &name, CServerSetup *serverSetup); + + void Open(const CHost &host, bool udp); + + bool IsValid() const; + + int HasDataToRead(int timeout) const; + + void SendToAllClients(CNetworkHost hosts[], int hostCount, const unsigned char *buf, unsigned int len); + + template + void SendMessageToSpecificClient(const CHost &host, const T &msg); + + void SendMessageToSpecificClient(const CHost &host, const CInitMessage_Header &msg); + + int Recv(unsigned char *buf, int len, CHost *hostFrom) const; + + void Close(); + + void Update(unsigned long frameCounter); + void Parse(unsigned long frameCounter, const unsigned char *buf, const CHost &host); + + void MarkClientsAsResync(); + void KickClient(int c); + +private: + int Parse_Hello(int h, const CInitMessage_Hello &msg, const CHost &host); + void Parse_Resync(const int h); + void Parse_Waiting(const int h); + void Parse_Map(const int h); + void Parse_State(const int h, const CInitMessage_State &msg); + void Parse_GoodBye(const int h); + void Parse_SeeYou(const int h); + + void Send_AreYouThere(const CNetworkHost &host); + void Send_GameFull(const CHost &host); + void Send_Welcome(const CNetworkHost &host, int hostIndex); + void Send_Resync(const CNetworkHost &host, int hostIndex); + void Send_Map(const CNetworkHost &host); + void Send_State(const CNetworkHost &host); + void Send_GoodBye(const CNetworkHost &host); + +private: + std::string name; + NetworkState networkStates[PlayerMax]; /// Client Host states + + IServerConnectionHandler* _serverConnectionHandler = nullptr; + + CServerSetup *serverSetup; +}; + +class CClient +{ +public: + void Init(const std::string &name, CServerSetup *serverSetup, CServerSetup *localSetup, unsigned long tick); + void SetServerHost(const CHost &host) { serverHost = host; } + + void Open(bool udp); + + bool IsValid() const; + + int HasDataToRead(int timeout); + + void SendToServer(const unsigned char *buf, unsigned int len); + + int Recv(unsigned char *buf, int len, CHost *hostFrom); + + void Close(); + + bool Parse(const unsigned char *buf); + bool Update(unsigned long tick); + + void DetachFromServer(); + + int GetNetworkState() const { return networkState.State; } + +private: + bool Update_disconnected(); + bool Update_detaching(unsigned long tick); + bool Update_connecting(unsigned long tick); + bool Update_connected(unsigned long tick); + bool Update_synced(unsigned long tick); + bool Update_changed(unsigned long tick); + bool Update_async(unsigned long tick); + bool Update_mapinfo(unsigned long tick); + bool Update_badmap(unsigned long tick); + bool Update_goahead(unsigned long tick); + bool Update_started(unsigned long tick); + + void Send_Go(unsigned long tick); + void Send_Config(unsigned long tick); + void Send_MapUidMismatch(unsigned long tick); + void Send_Map(unsigned long tick); + void Send_Resync(unsigned long tick); + void Send_State(unsigned long tick); + void Send_Waiting(unsigned long tick, unsigned long msec); + void Send_Hello(unsigned long tick); + void Send_GoodBye(unsigned long tick); + + template + void SendRateLimited(const T &msg, unsigned long tick, unsigned long msecs); + + void SetConfig(const CInitMessage_Config &msg); + + void Parse_GameFull(); + void Parse_LuaMismatch(const unsigned char *buf); + void Parse_EngineMismatch(const unsigned char *buf); + void Parse_Resync(const unsigned char *buf); + void Parse_Config(const unsigned char *buf); + void Parse_State(const unsigned char *buf); + void Parse_Welcome(const unsigned char *buf); + void Parse_Map(const unsigned char *buf); + void Parse_AreYouThere(); + + template + void SendToServer(const T & msg); + void SendToServer(const CInitMessage_Header &msg); + +private: + std::string name; + CHost serverHost; /// IP:port of server to join + NetworkState networkState; + unsigned char lastMsgTypeSent; /// Subtype of last InitConfig message sent + + IClientConnectionHandler* _clientConnectionHandler = nullptr; + + CServerSetup *serverSetup; + CServerSetup *localSetup; +}; + #endif // !__NETCONNECT_H__ diff --git a/src/include/network.h b/src/include/network.h index 78710e0ece..26e1707efb 100644 --- a/src/include/network.h +++ b/src/include/network.h @@ -66,16 +66,18 @@ class CNetworkParameter -- Variables ----------------------------------------------------------------------------*/ -extern CUDPSocket NetworkFildes; /// Network file descriptor extern bool NetworkInSync; /// Network is in sync +extern bool NetworkGame; /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ -extern inline bool IsNetworkGame() { return NetworkFildes.IsValid(); } +extern inline bool IsNetworkGame() { return NetworkGame; } + extern void InitNetwork1(); /// Initialise network extern void ExitNetwork1(); /// Cleanup network (port) +extern bool NetworkHasDataToRead(); extern void NetworkOnStartGame(); /// Initialise network data for ingame communication extern void NetworkEvent(); /// Handle network events extern void NetworkSync(); /// Hold in sync diff --git a/src/include/network/netsockets.h b/src/include/network/netsockets.h index b2701cd437..46424a5bcc 100644 --- a/src/include/network/netsockets.h +++ b/src/include/network/netsockets.h @@ -46,6 +46,7 @@ class CHost bool operator == (const CHost &rhs) const { return ip == rhs.ip && port == rhs.port; } bool operator != (const CHost &rhs) const { return !(*this == rhs); } + bool operator < (const CHost &rhs) const { return ip < rhs.ip || ip == rhs.ip && port < rhs.port; } private: unsigned long ip; int port; @@ -61,8 +62,8 @@ class CUDPSocket ~CUDPSocket(); bool Open(const CHost &host); void Close(); - void Send(const CHost &host, const void *buf, unsigned int len); - int Recv(void *buf, int len, CHost *hostFrom); + void Send(const CHost &host, const unsigned char *buf, unsigned int len); + int Recv(unsigned char *buf, int len, CHost *hostFrom); void SetNonBlocking(); // int HasDataToRead(int timeout); @@ -103,18 +104,22 @@ class CTCPSocket { public: CTCPSocket(); - ~CTCPSocket(); bool Open(const CHost &host); + int Listen(); + CTCPSocket* Accept(); void Close(); bool Connect(const CHost &host); - int Send(const void *buf, unsigned int len); - int Recv(void *buf, int len); + int Send(const unsigned char *buf, unsigned int len); + int Recv(unsigned char *buf, int len); + void SetBlocking(); void SetNonBlocking(); // int HasDataToRead(int timeout); bool IsValid() const; + CHost GetHost() const; private: - CTCPSocket_Impl *m_impl; + CTCPSocket(CTCPSocket_Impl* impl) : m_impl(impl) {}; + CTCPSocket_Impl* m_impl; }; //@} diff --git a/src/include/parameters.h b/src/include/parameters.h index e6c404b780..f02e46b9b0 100644 --- a/src/include/parameters.h +++ b/src/include/parameters.h @@ -48,6 +48,8 @@ class Parameters std::string luaEditorStartFilename; std::string luaScriptArguments; std::string LocalPlayerName; /// Name of local player + bool UseUDP; + private: std::string userDirectory; /// Directory containing user settings and data public: diff --git a/src/network/master.cpp b/src/network/master.cpp index 6c8bc4d07f..771e81697d 100644 --- a/src/network/master.cpp +++ b/src/network/master.cpp @@ -160,7 +160,7 @@ int CMetaClient::Send(const std::string cmd) if (metaSocket.IsValid()) { std::string mes(cmd); mes.append("\n"); - ret = metaSocket.Send(mes.c_str(), mes.size()); + ret = metaSocket.Send((unsigned char*)mes.c_str(), mes.size()); } return ret; } @@ -178,7 +178,7 @@ int CMetaClient::Recv() char buf[1024]; memset(&buf, 0, sizeof(buf)); - int n = metaSocket.Recv(&buf, sizeof(buf)); + int n = metaSocket.Recv((unsigned char*)buf, sizeof(buf)); if (n == -1) { return n; } @@ -197,48 +197,52 @@ int CMetaClient::Recv() //@} int CMetaClient::CreateGame(std::string desc, std::string map, std::string players) { - if (metaSocket.IsValid() == false) { - return -1; - } - if (NetworkFildes.IsValid() == false) { - return -1; - } - CHost metaServerHost(metaHost.c_str(), metaPort); - - // Advertise an external IP address if we can - unsigned long ips[1]; - int networkNumInterfaces = NetworkFildes.GetSocketAddresses(ips, 1); - std::string ipport = ""; - if (!networkNumInterfaces || CNetworkParameter::Instance.localHost.compare("127.0.0.1")) { - ipport += CNetworkParameter::Instance.localHost.c_str(); - } else { - ipport += inet_ntoa(((struct in_addr *)ips)[0]); - } - ipport += " "; - ipport += std::to_string(CNetworkParameter::Instance.localPort); - - std::string cmd("CREATEGAME \""); - cmd += desc; - cmd += "\" \""; - cmd += map; - cmd += "\" "; - cmd += players; - cmd += " "; - cmd += ipport; - - if (this->Send(cmd.c_str()) == -1) { // not sent - return -1; - } - if (this->Recv() == -1) { // not received - return -1; - } - CClientLog &log = *GetLastMessage(); - if (log.entry.find("CREATEGAME_OK") != std::string::npos) { - // Everything is OK, let's inform metaserver of our UDP info - NetworkFildes.Send(metaServerHost, ipport.c_str(), ipport.size()); - return 0; - } else { - fprintf(stderr, "METACLIENT: failed to create game: %s\n", log.entry.c_str()); - return -1; - } + //TODO: decide where to publish newly created games from + + return -1; + + //if (metaSocket.IsValid() == false) { + // return -1; + //} + //if (Server.IsValid() == false) { + // return -1; + //} + //CHost metaServerHost(metaHost.c_str(), metaPort); + + //// Advertise an external IP address if we can + //unsigned long ips[1]; + //int networkNumInterfaces = Server.GetSocketAddresses(ips, 1); + //std::string ipport = ""; + //if (!networkNumInterfaces || CNetworkParameter::Instance.localHost.compare("127.0.0.1")) { + // ipport += CNetworkParameter::Instance.localHost.c_str(); + //} else { + // ipport += inet_ntoa(((struct in_addr *)ips)[0]); + //} + //ipport += " "; + //ipport += std::to_string(CNetworkParameter::Instance.localPort); + + //std::string cmd("CREATEGAME \""); + //cmd += desc; + //cmd += "\" \""; + //cmd += map; + //cmd += "\" "; + //cmd += players; + //cmd += " "; + //cmd += ipport; + + //if (this->Send(cmd.c_str()) == -1) { // not sent + // return -1; + //} + //if (this->Recv() == -1) { // not received + // return -1; + //} + //CClientLog &log = *GetLastMessage(); + //if (log.entry.find("CREATEGAME_OK") != std::string::npos) { + // // Everything is OK, let's inform metaserver of our UDP info + // NetworkFildes.Send(metaServerHost, ipport.c_str(), ipport.size()); + // return 0; + //} else { + // fprintf(stderr, "METACLIENT: failed to create game: %s\n", log.entry.c_str()); + // return -1; + //} } diff --git a/src/network/net_connection_handler.cpp b/src/network/net_connection_handler.cpp new file mode 100644 index 0000000000..080c4092f6 --- /dev/null +++ b/src/network/net_connection_handler.cpp @@ -0,0 +1,264 @@ +// _________ __ __ +// / _____// |_____________ _/ |______ ____ __ __ ______ +// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/ +// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ | +// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ > +// \/ \/ \//_____/ \/ +// ______________________ ______________________ +// T H E W A R B E G I N S +// Stratagus - A free fantasy real time strategy game engine +// +/**@name master.cpp - The master server. */ +// +// (c) Copyright 2003-2007 by Tom Zickel and Jimmy Salmon +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; only version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#include "net_connection_handler.h" + +using namespace std; + +bool CTCPConnectionHandler::Open(const CHost& host) { + return _socket.Open(host); +} + +int CTCPConnectionHandler::Listen() { + return _socket.Listen(); +} + +shared_ptr CTCPConnectionHandler::Accept() { + CTCPSocket* newSocket = _socket.Accept(); + + if(newSocket == nullptr) { + return nullptr; + } + + auto newConnectionHandler = make_shared(*newSocket); + delete newSocket; + + return newConnectionHandler; +} + +void CTCPConnectionHandler::Close() { + _socket.Close(); +} + +bool CTCPConnectionHandler::Connect(const CHost& host) { + return _socket.Connect(host); +} + +int CTCPConnectionHandler::Send(const unsigned char* buf, unsigned int len) { + const auto bufLength = new unsigned char[2]; + + bufLength[0] = len >> 8 & 0xFF; + bufLength[1] = len & 0xFF; + + _socket.Send(bufLength, 2); + return _socket.Send(buf, len); +} + +int CTCPConnectionHandler::Recv(unsigned char* buf, int len) { + auto* bufLength = new unsigned char[2]; + + if(_socket.HasDataToRead(0) <= 0) { + return 0; + } + + const int resLen = _socket.Recv(bufLength, 2); + if (resLen < 2) + { + _socket.Close(); + return resLen; + } + + int actualLen = bufLength[0]; + actualLen <<= 8; + actualLen |= bufLength[1]; + + return _socket.Recv(buf, actualLen); +} + +void CTCPConnectionHandler::SetBlocking() { + _socket.SetBlocking(); +} + +void CTCPConnectionHandler::SetNonBlocking() { + _socket.SetNonBlocking(); +} + +int CTCPConnectionHandler::HasDataToRead(int timeout) { + return _socket.HasDataToRead(timeout); +} + +bool CTCPConnectionHandler::IsValid() const { + return _socket.IsValid(); +} + +CHost CTCPConnectionHandler::GetHost() const { + return _socket.GetHost(); +} + +bool CUDPConnectionHandler::Open(const CHost& host) { + return _socket.Open(host); +} + +void CUDPConnectionHandler::Close() { + _socket.Close(); +} + +void CUDPConnectionHandler::Send(const CHost& host, const unsigned char* buf, unsigned int len) { + _socket.Send(host, buf, len); +} + +int CUDPConnectionHandler::Recv(unsigned char* buf, int len, CHost* hostFrom) { + return _socket.Recv(buf, len, hostFrom); +} + +void CUDPConnectionHandler::SetNonBlocking() { + _socket.SetNonBlocking(); +} + +int CUDPConnectionHandler::HasDataToRead(int timeout) { + return _socket.HasDataToRead(timeout); +} + +bool CUDPConnectionHandler::IsValid() const { + return _socket.IsValid(); +} + +void CTCPServerConnectionHandler::Open(const CHost& host) { + _connectionHandler.Open(host); + _connectionHandler.SetNonBlocking(); + _connectionHandler.Listen(); +} + +int CTCPServerConnectionHandler::HasDataToRead(int timeout) { + const auto newConnectionHandler = _connectionHandler.Accept(); + if (newConnectionHandler) + { + newConnectionHandler->SetBlocking(); + // TODO make sure new connection does not override legitimate host + _clientConnections[newConnectionHandler->GetHost()] = newConnectionHandler; + } + + for (auto& it : _clientConnections) { + const int read = it.second->HasDataToRead(timeout); + if (read > 0) + { + return read; + } + } + + return 0; +} + +void CTCPServerConnectionHandler::SendToAllClients(vector hosts, const unsigned char* buf, unsigned int len) { + for (auto& host : hosts) { + _clientConnections[host]->Send(buf, len); + } +} + +void CTCPServerConnectionHandler::SendToClient(CHost host, const unsigned char* buf, unsigned int len) { + _clientConnections[host]->Send(buf, len); +} + +int CTCPServerConnectionHandler::Recv(unsigned char* buf, int len, CHost* hostFrom) { + // round robin across _clientSockets to avoid chatty client flooding client/server comm + + int skip = _skipClient; + int take = _clientConnections.size(); + + _skipClient = (_skipClient + 1) % _clientConnections.size(); + + for (auto& it : _clientConnections) { + if (skip-- > 0) continue; + take--; + + const int read = it.second->HasDataToRead(0); + if (read > 0) + { + *hostFrom = it.first; + return it.second->Recv(buf, len); + } + } + + for (auto& it : _clientConnections) { + if (take-- == 0) break; + + const int read = it.second->HasDataToRead(0); + if (read > 0) + { + *hostFrom = it.first; + return it.second->Recv(buf, len); + } + } + + return 0; +} + +void CTCPServerConnectionHandler::Close() { + for (auto& it : _clientConnections) { + it.second->Close(); + } + + _clientConnections.clear(); + _connectionHandler.Close(); +} + +void CUDPServerConnectionHandler::Open(const CHost& host) { + _connectionHandler.Open(host); +} + +void CUDPServerConnectionHandler::SendToAllClients(std::vector hosts, const unsigned char* buf, unsigned int len) { + for (auto& host : hosts) { + _connectionHandler.Send(host, buf, len); + } +} + +void CUDPServerConnectionHandler::SendToClient(CHost host, const unsigned char* buf, unsigned int len) { + _connectionHandler.Send(host, buf, len); +} + +int CUDPServerConnectionHandler::Recv(unsigned char* buf, int len, CHost* hostFrom) { + return _connectionHandler.Recv(buf, len, hostFrom); +} + +void CTCPClientConnectionHandler::Open(const CHost& host) { + _connectionHandler.Open(host); + _connectionHandler.SetBlocking(); + _connectionHandler.Connect(_serverHost); +} + +void CTCPClientConnectionHandler::SendToServer(const unsigned char* buf, unsigned int len) { + _connectionHandler.Send(buf, len); +} + +int CTCPClientConnectionHandler::Recv(unsigned char* buf, int len, CHost* hostFrom) { + *hostFrom = _serverHost; + return _connectionHandler.Recv(buf, len); +} + +void CUDPClientConnectionHandler::Open(const CHost& host) { + _connectionHandler.Open(CHost("localhost", 0)); +} + +void CUDPClientConnectionHandler::SendToServer(const unsigned char* buf, unsigned int len) { + _connectionHandler.Send(_serverHost, buf, len); +} + +int CUDPClientConnectionHandler::Recv(unsigned char* buf, int len, CHost* hostFrom) { + return _connectionHandler.Recv(buf, len, hostFrom); +} diff --git a/src/network/net_lowlevel.cpp b/src/network/net_lowlevel.cpp index a182e8e5e1..73bbd7bf0b 100644 --- a/src/network/net_lowlevel.cpp +++ b/src/network/net_lowlevel.cpp @@ -171,6 +171,27 @@ void NetCloseTCP(Socket sockfd) #endif // } !USE_WINSOCK +/** +** Set socket to blocking. +** +** @param sockfd Socket +** +** @return 0 for success, -1 for error +*/ +#ifdef USE_WINSOCK +int NetSetBlocking(Socket sockfd) +{ + unsigned long opt = 0; + return ioctlsocket(sockfd, FIONBIO, &opt); +} +#else +int NetSetBlocking(Socket sockfd) +{ + int flags = fcntl(sockfd, F_GETFL, 0); + return fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK); +} +#endif + /** ** Set socket to non-blocking. ** diff --git a/src/network/netconnect.cpp b/src/network/netconnect.cpp index aceda3af4b..e1d6635c24 100644 --- a/src/network/netconnect.cpp +++ b/src/network/netconnect.cpp @@ -47,6 +47,9 @@ #include "settings.h" #include "version.h" #include "video.h" +#include "net_lowlevel.h" + +#include //---------------------------------------------------------------------------- // Declaration @@ -56,23 +59,6 @@ #define CLIENT_LIVE_BEAT 60 #define CLIENT_IS_DEAD 300 -/** -** Connect state information of network systems active in current game. -*/ -struct NetworkState { - void Clear() - { - State = ccs_unused; - MsgCnt = 0; - LastFrame = 0; - } - - unsigned char State; /// Menu: ConnectState - unsigned short MsgCnt; /// Menu: Counter for state msg of same type (detect unreachable) - unsigned long LastFrame; /// Last message received - // Fill in here... -}; - //---------------------------------------------------------------------------- // Variables //---------------------------------------------------------------------------- @@ -89,133 +75,16 @@ int NetPlayers; /// How many network players std::string NetworkMapName; /// Name of the map received with ICMMap static int NoRandomPlacementMultiplayer = 0; /// Disable the random placement of players in muliplayer mode +CServer Server; +CClient Client; + CServerSetup ServerSetupState; // Server selection state for Multiplayer clients CServerSetup LocalSetupState; // Local selection state for Multiplayer clients -class CServer -{ -public: - void Init(const std::string &name, CUDPSocket *socket, CServerSetup *serverSetup); - - void Update(unsigned long frameCounter); - void Parse(unsigned long frameCounter, const unsigned char *buf, const CHost &host); - - void MarkClientsAsResync(); - void KickClient(int c); -private: - int Parse_Hello(int h, const CInitMessage_Hello &msg, const CHost &host); - void Parse_Resync(const int h); - void Parse_Waiting(const int h); - void Parse_Map(const int h); - void Parse_State(const int h, const CInitMessage_State &msg); - void Parse_GoodBye(const int h); - void Parse_SeeYou(const int h); - - void Send_AreYouThere(const CNetworkHost &host); - void Send_GameFull(const CHost &host); - void Send_Welcome(const CNetworkHost &host, int hostIndex); - void Send_Resync(const CNetworkHost &host, int hostIndex); - void Send_Map(const CNetworkHost &host); - void Send_State(const CNetworkHost &host); - void Send_GoodBye(const CNetworkHost &host); -private: - std::string name; - NetworkState networkStates[PlayerMax]; /// Client Host states - CUDPSocket *socket; - CServerSetup *serverSetup; -}; - -class CClient -{ -public: - void Init(const std::string &name, CUDPSocket *socket, CServerSetup *serverSetup, CServerSetup *localSetup, unsigned long tick); - void SetServerHost(const CHost &host) { serverHost = host; } - - bool Parse(const unsigned char *buf, const CHost &host); - bool Update(unsigned long tick); - - void DetachFromServer(); - - int GetNetworkState() const { return networkState.State; } - -private: - bool Update_disconnected(); - bool Update_detaching(unsigned long tick); - bool Update_connecting(unsigned long tick); - bool Update_connected(unsigned long tick); - bool Update_synced(unsigned long tick); - bool Update_changed(unsigned long tick); - bool Update_async(unsigned long tick); - bool Update_mapinfo(unsigned long tick); - bool Update_badmap(unsigned long tick); - bool Update_goahead(unsigned long tick); - bool Update_started(unsigned long tick); - - void Send_Go(unsigned long tick); - void Send_Config(unsigned long tick); - void Send_MapUidMismatch(unsigned long tick); - void Send_Map(unsigned long tick); - void Send_Resync(unsigned long tick); - void Send_State(unsigned long tick); - void Send_Waiting(unsigned long tick, unsigned long msec); - void Send_Hello(unsigned long tick); - void Send_GoodBye(unsigned long tick); - - template - void SendRateLimited(const T &msg, unsigned long tick, unsigned long msecs); - - void SetConfig(const CInitMessage_Config &msg); - - void Parse_GameFull(); - void Parse_LuaMismatch(const unsigned char *buf); - void Parse_EngineMismatch(const unsigned char *buf); - void Parse_Resync(const unsigned char *buf); - void Parse_Config(const unsigned char *buf); - void Parse_State(const unsigned char *buf); - void Parse_Welcome(const unsigned char *buf); - void Parse_Map(const unsigned char *buf); - void Parse_AreYouThere(); - -private: - std::string name; - CHost serverHost; /// IP:port of server to join - NetworkState networkState; - unsigned char lastMsgTypeSent; /// Subtype of last InitConfig message sent - CUDPSocket *socket; - CServerSetup *serverSetup; - CServerSetup *localSetup; -}; - -static CServer Server; -static CClient Client; - // // CClient // -/** -** Send an InitConfig message across the Network -** -** @param host Host to send to (network byte order). -** @param port Port of host to send to (network byte order). -** @param msg The message to send -*/ -template -static void NetworkSendICMessage(CUDPSocket &socket, const CHost &host, const T &msg) -{ - const unsigned char *buf = msg.Serialize(); - socket.Send(host, buf, msg.Size()); - delete[] buf; -} - -void NetworkSendICMessage(CUDPSocket &socket, const CHost &host, const CInitMessage_Header &msg) -{ - unsigned char *buf = new unsigned char [msg.Size()]; - msg.Serialize(buf); - socket.Send(host, buf, msg.Size()); - delete[] buf; -} - static const char *ncconstatenames[] = { "ccs_unused", "ccs_connecting", // new client @@ -263,29 +132,6 @@ static const char *icmsgsubtypenames[] = { "IAmHere", // Client answers I am here }; -template -static void NetworkSendICMessage_Log(CUDPSocket &socket, const CHost &host, const T &msg) -{ - NetworkSendICMessage(socket, host, msg); - -#ifdef DEBUG - const std::string hostStr = host.toString(); - DebugPrint("Sending to %s -> %s\n" _C_ hostStr.c_str() - _C_ icmsgsubtypenames[msg.GetHeader().GetSubType()]); -#endif -} - -static void NetworkSendICMessage_Log(CUDPSocket &socket, const CHost &host, const CInitMessage_Header &msg) -{ - NetworkSendICMessage(socket, host, msg); - -#ifdef DEBUG - const std::string hostStr = host.toString(); - DebugPrint("Sending to %s -> %s\n" _C_ hostStr.c_str() - _C_ icmsgsubtypenames[msg.GetSubType()]); -#endif -} - /** ** Send a message to the server, but only if the last packet was a while ago ** @@ -308,7 +154,7 @@ void CClient::SendRateLimited(const T &msg, unsigned long tick, unsigned long ms networkState.MsgCnt = 0; lastMsgTypeSent = subtype; } - NetworkSendICMessage(*socket, serverHost, msg); + SendToServer(msg); DebugPrint("[%s] Sending (%s:#%d)\n" _C_ ncconstatenames[networkState.State] _C_ icmsgsubtypenames[subtype] _C_ networkState.MsgCnt); @@ -329,13 +175,13 @@ void CClient::SendRateLimited(const CInitMessage_Header &ms networkState.MsgCnt = 0; lastMsgTypeSent = subtype; } - NetworkSendICMessage(*socket, serverHost, msg); + SendToServer(msg); DebugPrint("[%s] Sending (%s:#%d)\n" _C_ ncconstatenames[networkState.State] _C_ icmsgsubtypenames[subtype] _C_ networkState.MsgCnt); } -void CClient::Init(const std::string &name, CUDPSocket *socket, CServerSetup *serverSetup, CServerSetup *localSetup, unsigned long tick) +void CClient::Init(const std::string &name, CServerSetup *serverSetup, CServerSetup *localSetup, unsigned long tick) { networkState.LastFrame = tick; networkState.State = ccs_connecting; @@ -343,8 +189,52 @@ void CClient::Init(const std::string &name, CUDPSocket *socket, CServerSetup *se lastMsgTypeSent = ICMServerQuit; this->serverSetup = serverSetup; this->localSetup = localSetup; - this->name = name; - this->socket = socket; + this->name = name; +} + +void CClient::Open(bool udp) { + if (_clientConnectionHandler) { + Close(); + } + + if (udp) { + _clientConnectionHandler = new CUDPClientConnectionHandler(this->serverHost); + } + else { + _clientConnectionHandler = new CTCPClientConnectionHandler(this->serverHost); + } + + _clientConnectionHandler->Open(CHost("localhost", 0)); +} + +bool CClient::IsValid() const { + if (_clientConnectionHandler == nullptr) { + return false; + } + + return _clientConnectionHandler->IsValid(); +} + +int CClient::HasDataToRead(int timeout) { + if (!IsValid()) return -1; + return _clientConnectionHandler->HasDataToRead(timeout); +} + +void CClient::SendToServer(const unsigned char *buf, unsigned int len) { + if (!IsValid()) return; + _clientConnectionHandler->SendToServer(buf, len); +} + +int CClient::Recv(unsigned char *buf, int len, CHost *hostFrom) { + if (!IsValid()) return -1; + return _clientConnectionHandler->Recv(buf, len, hostFrom); +} + +void CClient::Close() { + if (_clientConnectionHandler == nullptr) return; + _clientConnectionHandler->Close(); + delete _clientConnectionHandler; + _clientConnectionHandler = nullptr; } void CClient::DetachFromServer() @@ -360,7 +250,7 @@ bool CClient::Update_disconnected() // Spew out 5 and trust in God that they arrive for (int i = 0; i < 5; ++i) { - NetworkSendICMessage(*socket, serverHost, message); + SendToServer(message); } networkState.State = ccs_usercanceled; return false; @@ -633,7 +523,7 @@ void CClient::SetConfig(const CInitMessage_Config &msg) #endif } -bool CClient::Parse(const unsigned char *buf, const CHost &host) +bool CClient::Parse(const unsigned char *buf) { CInitMessage_Header header; header.Deserialize(buf); @@ -897,8 +787,23 @@ void CClient::Parse_EngineMismatch(const unsigned char *buf) void CClient::Parse_AreYouThere() { const CInitMessage_Header message(MessageInit_FromClient, ICMIAH); // IAmHere + SendToServer(message); +} - NetworkSendICMessage(*socket, serverHost, message); +template +void CClient::SendToServer(const T &msg) +{ + const unsigned char *buf = msg.Serialize(); + SendToServer(buf, msg.Size()); + delete[] buf; +} + +void CClient::SendToServer(const CInitMessage_Header &msg) +{ + unsigned char *buf = new unsigned char [msg.Size()]; + msg.Serialize(buf); + SendToServer(buf, msg.Size()); + delete[] buf; } // @@ -920,7 +825,7 @@ void CServer::KickClient(int c) } } -void CServer::Init(const std::string &name, CUDPSocket *socket, CServerSetup *serverSetup) +void CServer::Init(const std::string &name, CServerSetup *serverSetup) { for (int i = 0; i < PlayerMax; ++i) { networkStates[i].Clear(); @@ -928,21 +833,77 @@ void CServer::Init(const std::string &name, CUDPSocket *socket, CServerSetup *se } this->serverSetup = serverSetup; this->name = name; - this->socket = socket; +} + +void CServer::Open(const CHost &host, bool udp) { + if (udp) { + _serverConnectionHandler = new CUDPServerConnectionHandler(); + } + else { + _serverConnectionHandler = new CTCPServerConnectionHandler(); + } + + _serverConnectionHandler->Open(host); +} + +bool CServer::IsValid() const { + if (_serverConnectionHandler == nullptr) { + return false; + } + + return _serverConnectionHandler->IsValid(); +} + +int CServer::HasDataToRead(int timeout) const { + return _serverConnectionHandler->HasDataToRead(timeout); +} + +void CServer::SendToAllClients(CNetworkHost hosts[], int hostCount, const unsigned char *buf, unsigned int len) { + std::vector hostVector; + + for (int i = 0; i < HostsCount; ++i) { + const CHost host(Hosts[i].Host, Hosts[i].Port); + hostVector.emplace_back(host); + } + _serverConnectionHandler->SendToAllClients(hostVector, buf, len); +} + +template +void CServer::SendMessageToSpecificClient(const CHost &host, const T &msg) { + const unsigned char *buf = msg.Serialize(); + _serverConnectionHandler->SendToClient(host, buf, msg.Size()); + delete[] buf; +} + +void CServer::SendMessageToSpecificClient(const CHost &host, const CInitMessage_Header &msg) +{ + auto buf = new unsigned char [msg.Size()]; + msg.Serialize(buf); + + _serverConnectionHandler->SendToClient(host, buf, msg.Size()); + delete[] buf; +} + +int CServer::Recv(unsigned char *buf, int len, CHost *hostFrom) const { + return _serverConnectionHandler->Recv(buf, len, hostFrom); +} + +void CServer::Close() { + _serverConnectionHandler->Close(); + delete _serverConnectionHandler; + _serverConnectionHandler = nullptr; } void CServer::Send_AreYouThere(const CNetworkHost &host) { const CInitMessage_Header message(MessageInit_FromServer, ICMAYT); // AreYouThere - - NetworkSendICMessage(*socket, CHost(host.Host, host.Port), message); + SendMessageToSpecificClient(CHost(host.Host, host.Port), message); } void CServer::Send_GameFull(const CHost &host) { const CInitMessage_Header message(MessageInit_FromServer, ICMGameFull); - - NetworkSendICMessage_Log(*socket, host, message); + SendMessageToSpecificClient(host, message); } void CServer::Send_Welcome(const CNetworkHost &host, int index) @@ -956,7 +917,7 @@ void CServer::Send_Welcome(const CNetworkHost &host, int index) message.hosts[i] = Hosts[i]; } } - NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message); + SendMessageToSpecificClient(CHost(host.Host, host.Port), message); } void CServer::Send_Resync(const CNetworkHost &host, int hostIndex) @@ -968,28 +929,25 @@ void CServer::Send_Resync(const CNetworkHost &host, int hostIndex) message.hosts[i] = Hosts[i]; } } - NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message); + SendMessageToSpecificClient(CHost(host.Host, host.Port), message); } void CServer::Send_Map(const CNetworkHost &host) { const CInitMessage_Map message(NetworkMapName.c_str(), Map.Info.MapUID); - - NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message); + SendMessageToSpecificClient(CHost(host.Host, host.Port), message); } void CServer::Send_State(const CNetworkHost &host) { const CInitMessage_State message(MessageInit_FromServer, *serverSetup); - - NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message); + SendMessageToSpecificClient(CHost(host.Host, host.Port), message); } void CServer::Send_GoodBye(const CNetworkHost &host) { const CInitMessage_Header message(MessageInit_FromServer, ICMGoodBye); - - NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message); + SendMessageToSpecificClient(CHost(host.Host, host.Port), message); } void CServer::Update(unsigned long frameCounter) @@ -1288,15 +1246,13 @@ void CServer::Parse_SeeYou(const int h) ** ** @return 0 if the versions match, -1 otherwise */ -static int CheckVersions(const CInitMessage_Hello &msg, CUDPSocket &socket, const CHost &host) +static int CheckVersions(const CInitMessage_Hello &msg, const CHost &host) { if (msg.Stratagus != StratagusVersion) { const std::string hostStr = host.toString(); fprintf(stderr, "Incompatible Stratagus version %d <-> %d from %s\n", StratagusVersion, msg.Stratagus, hostStr.c_str()); - const CInitMessage_EngineMismatch message; - NetworkSendICMessage_Log(socket, host, message); return -1; } @@ -1307,9 +1263,7 @@ static int CheckVersions(const CInitMessage_Hello &msg, CUDPSocket &socket, cons msg.Version, hostStr.c_str()); - const CInitMessage_LuaFilesMismatch message; - NetworkSendICMessage_Log(socket, host, message); - return -1; + return -2; } return 0; } @@ -1324,7 +1278,15 @@ void CServer::Parse(unsigned long frameCounter, const unsigned char *buf, const CInitMessage_Hello msg; msg.Deserialize(buf); - if (CheckVersions(msg, *socket, host)) { + int versionCheck = CheckVersions(msg, host); + if (versionCheck == -1) { + const CInitMessage_EngineMismatch message; + Server.SendMessageToSpecificClient(host, message); + return; + } + if (versionCheck == -2) { + const CInitMessage_LuaFilesMismatch message; + Server.SendMessageToSpecificClient(host, message); return; } // Special case: a new client has arrived @@ -1402,7 +1364,7 @@ int NetworkParseSetupEvent(const unsigned char *buf, int size, const CHost &host hostStr.c_str()); #endif if (NetConnectRunning == 2) { // client - if (Client.Parse(buf, host) == false) { + if (Client.Parse(buf) == false) { NetConnectRunning = 0; } } else if (NetConnectRunning == 1) { // server @@ -1461,7 +1423,16 @@ void NetworkInitClientConnect() } ServerSetupState.Clear(); LocalSetupState.Clear(); - Client.Init(Parameters::Instance.LocalPlayerName, &NetworkFildes, &ServerSetupState, &LocalSetupState, GetTicks()); + Client.Init(Parameters::Instance.LocalPlayerName, &ServerSetupState, &LocalSetupState, GetTicks()); + + Client.Open(Parameters::Instance.UseUDP); + if (Client.IsValid() == false) { + fprintf(stderr, "Unable to open socket for client\n"); + NetExit(); // machine dependent network exit + return; + } + + NetworkGame = true; } /** @@ -1641,17 +1612,17 @@ void NetworkServerStartGame() if (num[Hosts[i].PlyNr] == 1) { // not acknowledged yet message.clientIndex = i; - NetworkSendICMessage_Log(NetworkFildes, host, message); + Server.SendMessageToSpecificClient(host, message); } else if (num[Hosts[i].PlyNr] == 2) { - NetworkSendICMessage_Log(NetworkFildes, host, statemsg); + Server.SendMessageToSpecificClient(host, statemsg); } } // Wait for acknowledge unsigned char buf[1024]; - while (j && NetworkFildes.HasDataToRead(1000)) { + while (j && Server.HasDataToRead(1000)) { CHost host; - const int len = NetworkFildes.Recv(buf, sizeof(buf), &host); + const int len = Server.Recv(buf, sizeof(buf), &host); if (len < 0) { #ifdef DEBUG const std::string hostStr = host.toString(); @@ -1710,7 +1681,7 @@ void NetworkServerStartGame() const CInitMessage_Header message_go(MessageInit_FromServer, ICMGo); for (int i = 0; i < HostsCount; ++i) { const CHost host(Hosts[i].Host, Hosts[i].Port); - NetworkSendICMessage_Log(NetworkFildes, host, message_go); + Server.SendMessageToSpecificClient(host, message_go); } } @@ -1759,7 +1730,7 @@ void NetworkInitServerConnect(int openslots) } ServerSetupState.Clear(); LocalSetupState.Clear(); // Unused when we are server - Server.Init(Parameters::Instance.LocalPlayerName, &NetworkFildes, &ServerSetupState); + Server.Init(Parameters::Instance.LocalPlayerName, &ServerSetupState); // preset the server (initially always slot 0) Hosts[0].SetName(Parameters::Instance.LocalPlayerName.c_str()); @@ -1767,6 +1738,24 @@ void NetworkInitServerConnect(int openslots) for (int i = openslots; i < PlayerMax - 1; ++i) { ServerSetupState.CompOpt[i] = 1; } + + // Our communication port + const int port = CNetworkParameter::Instance.localPort; + const char *NetworkAddr = NULL; // FIXME : bad use + const CHost host(NetworkAddr, port); + Server.Open(host, Parameters::Instance.UseUDP); + + if (Server.IsValid() == false) { + fprintf(stderr, "NETWORK: No free port %d available, aborting\n", port); + NetExit(); // machine dependent network exit + return; + } +#ifdef DEBUG + const std::string hostStr = host.toString(); + DebugPrint("My host:port %s\n" _C_ hostStr.c_str()); +#endif + + NetworkGame = true; } /** diff --git a/src/network/netsockets.cpp b/src/network/netsockets.cpp index 265147bf53..4c94772d76 100644 --- a/src/network/netsockets.cpp +++ b/src/network/netsockets.cpp @@ -135,7 +135,7 @@ void CUDPSocket::Close() m_impl->Close(); } -void CUDPSocket::Send(const CHost &host, const void *buf, unsigned int len) +void CUDPSocket::Send(const CHost &host, const unsigned char *buf, unsigned int len) { #ifdef DEBUG ++m_statistic.sentPacketsCount; @@ -145,7 +145,7 @@ void CUDPSocket::Send(const CHost &host, const void *buf, unsigned int len) m_impl->Send(host, buf, len); } -int CUDPSocket::Recv(void *buf, int len, CHost *hostFrom) +int CUDPSocket::Recv(unsigned char *buf, int len, CHost *hostFrom) { const int res = m_impl->Recv(buf, len, hostFrom); #ifdef DEBUG @@ -192,21 +192,31 @@ class CTCPSocket_Impl bool Open(const CHost &host); void Close() { NetCloseTCP(socket); socket = Socket(-1); } bool Connect(const CHost &host) { return NetConnectTCP(socket, host.getIp(), host.getPort()) != -1; } + int Listen() { return NetListenTCP(socket); } + CTCPSocket_Impl* Accept(); int Send(const void *buf, unsigned int len) { return NetSendTCP(socket, buf, len); } int Recv(void *buf, int len) { int res = NetRecvTCP(socket, buf, len); return res; } + void SetBlocking() { NetSetBlocking(socket); } void SetNonBlocking() { NetSetNonBlocking(socket); } int HasDataToRead(int timeout) { return NetSocketReady(socket, timeout); } bool IsValid() const { return socket != Socket(-1); } + unsigned long ip; + int port; +private: + CTCPSocket_Impl(Socket socket, unsigned long ip, int port) : socket(socket), ip(ip), port(port) {} private: Socket socket; }; bool CTCPSocket_Impl::Open(const CHost &host) { + this->ip = host.getIp(); + this->port = host.getPort(); + char ip[24]; // 127.255.255.255:65555 memset(&ip, 0, sizeof(ip)); sprintf(ip, "%d.%d.%d.%d", NIPQUAD(ntohl(host.getIp()))); @@ -214,60 +224,109 @@ bool CTCPSocket_Impl::Open(const CHost &host) return this->socket != INVALID_SOCKET; } +CTCPSocket_Impl* CTCPSocket_Impl::Accept() +{ + unsigned long clientIp; + int clientPort; + auto clientSocket = NetAcceptTCP(socket, &clientIp, &clientPort); + if(clientSocket == INVALID_SOCKET) + { + return nullptr; + } + + return new CTCPSocket_Impl(clientSocket, clientIp, clientPort); +} + // // CTCPSocket // -CTCPSocket::CTCPSocket() +CTCPSocket::CTCPSocket() : m_impl(nullptr) { - m_impl = new CTCPSocket_Impl(); } -CTCPSocket::~CTCPSocket() +bool CTCPSocket::Open(const CHost &host) { - delete m_impl; + if(!m_impl) { + m_impl = new CTCPSocket_Impl(); + } + + return m_impl->Open(host); } -bool CTCPSocket::Open(const CHost &host) +int CTCPSocket::Listen() { - return m_impl->Open(host); + if (!IsValid()) return -1; + return m_impl->Listen(); } void CTCPSocket::Close() { + if (!IsValid()) return; + m_impl->Close(); + delete m_impl; + m_impl = nullptr; } - bool CTCPSocket::Connect(const CHost &host) { + if (!IsValid()) return false; return m_impl->Connect(host); } -int CTCPSocket::Send(const void *buf, unsigned int len) +CTCPSocket* CTCPSocket::Accept() { + if (!IsValid()) return nullptr; + + auto impl = m_impl->Accept(); + if(impl == nullptr) + { + return nullptr; + } + + return new CTCPSocket(impl); +} + +int CTCPSocket::Send(const unsigned char *buf, unsigned int len) +{ + if (!IsValid()) return -1; return m_impl->Send(buf, len); } -int CTCPSocket::Recv(void *buf, int len) +int CTCPSocket::Recv(unsigned char *buf, int len) { - const int res = m_impl->Recv(buf, len); - return res; + if (!IsValid()) return -1; + return m_impl->Recv(buf, len); +} + +void CTCPSocket::SetBlocking() +{ + if (!IsValid()) return; + m_impl->SetBlocking(); } void CTCPSocket::SetNonBlocking() { + if (!IsValid()) return; m_impl->SetNonBlocking(); } int CTCPSocket::HasDataToRead(int timeout) { + if (!IsValid()) return -1; return m_impl->HasDataToRead(timeout); } bool CTCPSocket::IsValid() const { - return m_impl->IsValid(); + return m_impl && m_impl->IsValid(); } +CHost CTCPSocket::GetHost() const +{ + return CHost(m_impl->ip, m_impl->port); +} + + //@} diff --git a/src/network/network.cpp b/src/network/network.cpp index eabc20fdd8..7a279fd32b 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -196,9 +196,6 @@ ** ::NetworkCommands() ** Network Updates : exec current command, and send commands to other players ** -** ::NetworkFildes -** UDP Socket for communication. -** ** ::NetworkInSync ** false when commands of the next gameNetCycle of the other player are not ready. ** @@ -291,8 +288,7 @@ void CNetworkParameter::FixValues() } bool NetworkInSync = true; /// Network is in sync - -CUDPSocket NetworkFildes; /// Network file descriptor +bool NetworkGame = false; static unsigned long NetworkLastFrame[PlayerMax]; /// Last frame received packet static unsigned long NetworkLastCycle[PlayerMax]; /// Last cycle received packet @@ -303,6 +299,8 @@ static CNetworkCommandQueue NetworkIn[256][PlayerMax][MaxNetworkCommands]; /// P static std::deque CommandsIn; /// Network command input queue static std::deque MsgCommandsIn; /// Network message input queue +extern CServer Server; +extern CClient Client; #ifdef DEBUG class CNetworkStat @@ -324,11 +322,11 @@ class CNetworkStat static void printStatistic(const CUDPSocket::CStatistic &statistic) { DebugPrint("Sent: %d packets %d bytes (max %d bytes).\n" - _C_ statistic.sentPacketsCount _C_ statistic.sentBytesCount - _C_ statistic.biggestSentPacketSize); + _C_ statistic.sentPacketsCount _C_ statistic.sentBytesCount + _C_ statistic.biggestSentPacketSize); DebugPrint("Received: %d packets %d bytes (max %d bytes).\n" _C_ - statistic.receivedPacketsCount _C_ statistic.receivedBytesCount - _C_ statistic.biggestReceivedPacketSize); + statistic.receivedPacketsCount _C_ statistic.receivedBytesCount + _C_ statistic.biggestReceivedPacketSize); DebugPrint("Received: %d error(s).\n" _C_ statistic.receivedErrorCount); } @@ -355,16 +353,10 @@ static void NetworkBroadcast(const CNetworkPacket &packet, int numcommands, int // Send to all clients. if (NetConnectType == 1) { // server - for (int i = 0; i < HostsCount; ++i) { - const CHost host(Hosts[i].Host, Hosts[i].Port); - if (Hosts[i].PlyNr == player) { - continue; - } - NetworkFildes.Send(host, buf, size); - } - } else { // client - const CHost host(Hosts[HostsCount - 1].Host, Hosts[HostsCount - 1].Port); - NetworkFildes.Send(host, buf, size); + Server.SendToAllClients(Hosts, HostsCount, buf, size); + } + else { // client + Client.SendToServer(buf, size); } delete[] buf; } @@ -404,35 +396,12 @@ static void NetworkSendPacket(const CNetworkCommandQueue(&ncq)[MaxNetworkCommand void InitNetwork1() { CNetworkParameter::Instance.FixValues(); - NetInit(); // machine dependent setup +} - // Our communication port - const int port = CNetworkParameter::Instance.localPort; - const char *NetworkAddr = NULL; // FIXME : bad use - const CHost host(NetworkAddr, port); - NetworkFildes.Open(host); - if (NetworkFildes.IsValid() == false) { - fprintf(stderr, "NETWORK: No free port %d available, aborting\n", port); - NetExit(); // machine dependent network exit - return; - } -#ifdef DEBUG - const std::string hostStr = host.toString(); - DebugPrint("My host:port %s\n" _C_ hostStr.c_str()); -#endif - - unsigned long ips[10]; - int networkNumInterfaces = NetworkFildes.GetSocketAddresses(ips, 10); - if (networkNumInterfaces) { - DebugPrint("Num IP: %d\n" _C_ networkNumInterfaces); - for (int i = 0; i < networkNumInterfaces; ++i) { - DebugPrint("IP: %d.%d.%d.%d\n" _C_ NIPQUAD(ntohl(ips[i]))); - } - } else { - fprintf(stderr, "WARNING: Not connected to any external IPV4-network!\n"); - return; - } +bool NetworkHasDataToRead() +{ + return NetConnectType == 1 ? Server.HasDataToRead(0) > 0 : Client.HasDataToRead(0) > 0; } /** @@ -444,16 +413,17 @@ void ExitNetwork1() return; } -#ifdef DEBUG - printStatistic(NetworkFildes.getStatistic()); - NetworkFildes.clearStatistic(); - NetworkStat.print(); -#endif + if (NetConnectType == 1) { // server + Server.Close(); + } + else { // client + Client.Close(); + } - NetworkFildes.Close(); NetExit(); // machine dependent setup NetworkInSync = true; + NetworkGame = false; NetPlayers = 0; HostsCount = 0; } @@ -468,8 +438,8 @@ void NetworkOnStartGame() Players[Hosts[i].PlyNr].SetName(Hosts[i].PlyName); } DebugPrint("Updates %d, Lag %d, Hosts %d\n" _C_ - CNetworkParameter::Instance.gameCyclesPerUpdate _C_ - CNetworkParameter::Instance.NetworkLag _C_ HostsCount); + CNetworkParameter::Instance.gameCyclesPerUpdate _C_ + CNetworkParameter::Instance.NetworkLag _C_ HostsCount); NetworkInSync = true; CommandsIn.clear(); @@ -525,7 +495,7 @@ void NetworkOnStartGame() ** @warning Destination and unit-type shares the same network slot. */ void NetworkSendCommand(int command, const CUnit &unit, int x, int y, - const CUnit *dest, const CUnitType *type, int status) + const CUnit *dest, const CUnitType *type, int status) { CNetworkCommandQueue ncq; @@ -568,7 +538,7 @@ void NetworkSendCommand(int command, const CUnit &unit, int x, int y, ** @param status Append command or flush old commands. */ void NetworkSendExtendedCommand(int command, int arg1, int arg2, int arg3, - int arg4, int status) + int arg4, int status) { CNetworkCommandQueue ncq; @@ -682,7 +652,7 @@ static bool IsNetworkCommandReady(unsigned long gameNetCycle) return false; } } - + return true; } @@ -729,7 +699,7 @@ static bool IsAValidCommand_Command(const CNetworkPacket &packet, int index, con const CUnit *unit = slot < UnitManager.GetUsedSlotCount() ? &UnitManager.GetSlotUnit(slot) : NULL; if (unit && (unit->Player->Index == player - || Players[player].IsTeamed(*unit) || unit->Player->Type == PlayerNeutral)) { + || Players[player].IsTeamed(*unit) || unit->Player->Type == PlayerNeutral)) { return true; } else { return false; @@ -752,15 +722,15 @@ static bool IsAValidCommand_Dismiss(const CNetworkPacket &packet, int index, con static bool IsAValidCommand(const CNetworkPacket &packet, int index, const int player) { switch (packet.Header.Type[index] & 0x7F) { - case MessageExtendedCommand: // FIXME: ensure the sender is part of the command - case MessageSync: // Sync does not matter - case MessageSelection: // FIXME: ensure it's from the right player - case MessageQuit: // FIXME: ensure it's from the right player - case MessageResend: // FIXME: ensure it's from the right player - case MessageChat: // FIXME: ensure it's from the right player - return true; - case MessageCommandDismiss: return IsAValidCommand_Dismiss(packet, index, player); - default: return IsAValidCommand_Command(packet, index, player); + case MessageExtendedCommand: // FIXME: ensure the sender is part of the command + case MessageSync: // Sync does not matter + case MessageSelection: // FIXME: ensure it's from the right player + case MessageQuit: // FIXME: ensure it's from the right player + case MessageResend: // FIXME: ensure it's from the right player + case MessageChat: // FIXME: ensure it's from the right player + return true; + case MessageCommandDismiss: return IsAValidCommand_Dismiss(packet, index, player); + default: return IsAValidCommand_Command(packet, index, player); } // FIXME: not all values in nc have been validated } @@ -826,7 +796,7 @@ static void NetworkParseInGameEvent(const unsigned char *buf, int len, const CHo } else { SetMessage(_("%s sent bad command"), Players[player].Name.c_str()); DebugPrint("%s sent bad command: 0x%x\n" _C_ Players[player].Name.c_str() - _C_ packet.Header.Type[i] & 0x7F); + _C_ packet.Header.Type[i] & 0x7F); } } for (int i = commands; i != MaxNetworkCommands; ++i) { @@ -855,7 +825,18 @@ void NetworkEvent() // Read the packet. unsigned char buf[1024]; CHost host; - int len = NetworkFildes.Recv(&buf, sizeof(buf), &host); + int len; + + if (NetConnectType == 1) { // server + len = Server.Recv(buf, sizeof(buf), &host); + } else { // client + len = Client.Recv(buf, sizeof(buf), &host); + } + + if (len == 0) { + return; + } + if (len < 0) { DebugPrint("Server/Client gone?\n"); // just hope for an automatic recover right now.. @@ -915,8 +896,8 @@ static void NetworkExecCommand_Sync(const CNetworkCommandQueue &ncq) || syncHash != NetworkSyncHashs[gameNetCycle & 0xFF]) { SetMessage("%s", _("Network out of sync")); DebugPrint("\nNetwork out of sync %x!=%x! %d!=%d! Cycle %lu\n\n" _C_ - syncSeed _C_ NetworkSyncSeeds[gameNetCycle & 0xFF] _C_ - syncHash _C_ NetworkSyncHashs[gameNetCycle & 0xFF] _C_ GameCycle); + syncSeed _C_ NetworkSyncSeeds[gameNetCycle & 0xFF] _C_ + syncHash _C_ NetworkSyncHashs[gameNetCycle & 0xFF] _C_ GameCycle); } } @@ -968,7 +949,7 @@ static void NetworkExecCommand_ExtendedCommand(const CNetworkCommandQueue &ncq) nec.Deserialize(&ncq.Data[0]); ExecExtendedCommand(nec.ExtendedType, (ncq.Type & 0x80) >> 7, - nec.Arg1, nec.Arg2, nec.Arg3, nec.Arg4); + nec.Arg1, nec.Arg2, nec.Arg3, nec.Arg4); } static void NetworkExecCommand_Command(const CNetworkCommandQueue &ncq) @@ -987,16 +968,16 @@ static void NetworkExecCommand_Command(const CNetworkCommandQueue &ncq) static void NetworkExecCommand(const CNetworkCommandQueue &ncq) { switch (ncq.Type & 0x7F) { - case MessageSync: NetworkExecCommand_Sync(ncq); break; - case MessageSelection: NetworkExecCommand_Selection(ncq); break; - case MessageChat: NetworkExecCommand_Chat(ncq); break; - case MessageQuit: NetworkExecCommand_Quit(ncq); break; - case MessageExtendedCommand: NetworkExecCommand_ExtendedCommand(ncq); break; - case MessageNone: - // Nothing to Do, This Message Should Never be Executed - Assert(0); - break; - default: NetworkExecCommand_Command(ncq); break; + case MessageSync: NetworkExecCommand_Sync(ncq); break; + case MessageSelection: NetworkExecCommand_Selection(ncq); break; + case MessageChat: NetworkExecCommand_Chat(ncq); break; + case MessageQuit: NetworkExecCommand_Quit(ncq); break; + case MessageExtendedCommand: NetworkExecCommand_ExtendedCommand(ncq); break; + case MessageNone: + // Nothing to Do, This Message Should Never be Executed + Assert(0); + break; + default: NetworkExecCommand_Command(ncq); break; } } @@ -1030,8 +1011,8 @@ static void NetworkSendCommands(unsigned long gameNetCycle) // FIXME: we can send destoyed units over network :( if (unit.Destroyed) { DebugPrint("Sending destroyed unit %d over network!!!!!!\n" _C_ nc.Unit); - } - } + } +} #endif ncq[numcommands] = incommand; ncq[numcommands].Time = gameNetCycle; @@ -1105,7 +1086,7 @@ static void CheckPlayerThatTimeOut(int hostIndex) const int timeoutInS = CNetworkParameter::Instance.timeoutInS; if (3 <= secs && secs < timeoutInS && FrameCounter % framesPerSecond == 0) { SetMessage(_("Waiting for player \"%s\": %d:%02d"), Hosts[hostIndex].PlyName, - (timeoutInS - secs) / 60, (timeoutInS - secs) % 60); + (timeoutInS - secs) / 60, (timeoutInS - secs) % 60); } if (secs >= timeoutInS) { const unsigned int nextGameNetCycle = GameCycle / CNetworkParameter::Instance.gameCyclesPerUpdate + 1; diff --git a/src/stratagus/main.cpp b/src/stratagus/main.cpp index 99d607c8a4..0b9865717d 100644 --- a/src/stratagus/main.cpp +++ b/src/stratagus/main.cpp @@ -32,6 +32,16 @@ #include "stratagus.h" #include "SDL.h" +#ifdef WIN32 +#include +#include + +int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char*, int nShowCmd) +{ + return main(__argc, __argv); +} +#endif + int main(int argc, char **argv) { return stratagusMain(argc, argv); diff --git a/src/stratagus/parameters.cpp b/src/stratagus/parameters.cpp index a705ba21db..28bb31bb7e 100644 --- a/src/stratagus/parameters.cpp +++ b/src/stratagus/parameters.cpp @@ -44,6 +44,7 @@ void Parameters::SetDefaultValues() luaStartFilename = "scripts/stratagus.lua"; luaEditorStartFilename = "scripts/editor.lua"; SetDefaultUserDirectory(); + UseUDP = true; } void Parameters::SetDefaultUserDirectory() diff --git a/src/stratagus/stratagus.cpp b/src/stratagus/stratagus.cpp index 750ab92889..aab9959fd7 100644 --- a/src/stratagus/stratagus.cpp +++ b/src/stratagus/stratagus.cpp @@ -523,7 +523,7 @@ void ParseCommandLine(int argc, char **argv, Parameters ¶meters) { char *sep; for (;;) { - switch (getopt(argc, argv, "ac:d:D:eE:FG:hiI:lN:oOP:ps:S:u:v:Wx:Z:?-")) { + switch (getopt(argc, argv, "ac:d:D:eE:FG:hiI:lN:oOP:ps:S:tu:v:Wx:Z:?-")) { case 'a': EnableAssert = true; continue; @@ -597,6 +597,9 @@ void ParseCommandLine(int argc, char **argv, Parameters ¶meters) case 'S': VideoSyncSpeed = atoi(optarg); continue; + case 't': + Parameters::Instance.UseUDP = false; + continue; case 'u': Parameters::Instance.SetUserDirectory(optarg); continue; diff --git a/src/video/sdl.cpp b/src/video/sdl.cpp index 5e1983b0b2..ce6fc6f1f7 100644 --- a/src/video/sdl.cpp +++ b/src/video/sdl.cpp @@ -75,6 +75,7 @@ #ifdef USE_WIN32 #include +#include #endif #include "editor.h" @@ -981,15 +982,15 @@ void WaitEventsOneFrame() } // Network - int s = 0; + bool networkHasDataToRead = false; if (IsNetworkGame()) { - s = NetworkFildes.HasDataToRead(0); - if (s > 0) { + networkHasDataToRead = NetworkHasDataToRead(); + if (networkHasDataToRead) { GetCallbacks()->NetworkEvent(); } } // No more input and time for frame over: return - if (!i && s <= 0 && interrupts) { + if (!i && !networkHasDataToRead && interrupts) { break; } }