From bc16a6c6eb9819f5e11f7ffc26ae31aceca79b6b Mon Sep 17 00:00:00 2001 From: cybermind Date: Thu, 5 Dec 2013 21:03:34 +0600 Subject: [PATCH] [+] Metaserver client code rewritten: now using class CMetaClient available through Lua --- CMakeLists.txt | 5 +- metaserver/cmd.cpp | 13 +- metaserver/main.cpp | 10 +- src/include/master.h | 45 ++- src/include/network.h | 2 +- .../network/{udpsocket.h => netsockets.h} | 30 +- src/network/master.cpp | 256 ++++++------------ src/network/net_lowlevel.cpp | 2 +- src/network/{udpsocket.cpp => netsockets.cpp} | 97 ++++++- src/tolua/master.pkg | 22 ++ src/tolua/stratagus.pkg | 1 + 11 files changed, 282 insertions(+), 201 deletions(-) rename src/include/network/{udpsocket.h => netsockets.h} (82%) rename src/network/{udpsocket.cpp => netsockets.cpp} (69%) create mode 100644 src/tolua/master.pkg diff --git a/CMakeLists.txt b/CMakeLists.txt index 8352e76b2..eed462c6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -231,7 +231,7 @@ set(network_SRCS src/network/master.cpp src/network/netconnect.cpp src/network/network.cpp - src/network/udpsocket.cpp + src/network/netsockets.cpp ) source_group(network FILES ${network_SRCS}) @@ -363,6 +363,7 @@ set(tolua_FILES src/tolua/font.pkg src/tolua/game.pkg src/tolua/map.pkg + src/tolua/master.pkg src/tolua/minimap.pkg src/tolua/network.pkg src/tolua/particle.pkg @@ -540,7 +541,7 @@ set(stratagus_generic_HDRS src/include/net_message.h src/include/netconnect.h src/include/network.h - src/include/network/udpsocket.h + src/include/network/netsockets.h src/include/parameters.h src/include/particle.h src/include/pathfinder.h diff --git a/metaserver/cmd.cpp b/metaserver/cmd.cpp index 1f7bbc589..298fdbd63 100644 --- a/metaserver/cmd.cpp +++ b/metaserver/cmd.cpp @@ -203,6 +203,14 @@ static int Parse5or6Args(char *buf, char **arg1, char **arg2, char **arg3, return 0; } +/** +** Parse PING +*/ +static void ParsePing(Session *session) +{ + Send(session, "PING_OK\n"); +} + /** ** Parse USER */ @@ -480,8 +488,9 @@ static void ParseBuffer(Session *session) } buf = session->Buffer; - - if (!session->UserData.LoggedIn) { + if (!strncmp(buf, "PING", 4)) { + ParsePing(session); + } else if (!session->UserData.LoggedIn) { if (!strncmp(buf, "USER ", 5)) { ParseUser(session, buf + 5); } else if (!strncmp(buf, "REGISTER ", 9)) { diff --git a/metaserver/main.cpp b/metaserver/main.cpp index 6a31794a8..33a5d51e2 100644 --- a/metaserver/main.cpp +++ b/metaserver/main.cpp @@ -223,14 +223,20 @@ int main(int argc, char **argv) // // Parse the command line. // - while ((i = getopt(argc, argv, ":p:m:i:d:")) != -1) { + while ((i = getopt(argc, argv, "aP:pm:i:d:")) != -1) { switch (i) { - case 'p': + case 'a': + EnableAssert = true; + continue; + case 'P': Server.Port = atoi(optarg); if (Server.Port <= 0) { Server.Port = DEFAULT_PORT; } break; + case 'p': + EnableDebugPrint = true; + continue; case 'm': Server.MaxConnections = atoi(optarg); break; diff --git a/src/include/master.h b/src/include/master.h index 1dc9505d3..b08505b4a 100644 --- a/src/include/master.h +++ b/src/include/master.h @@ -32,22 +32,53 @@ //@{ +#include +#include "network/netsockets.h" + /*---------------------------------------------------------------------------- -- Declarations ----------------------------------------------------------------------------*/ struct lua_State; +// Log data used in metaserver client +struct CClientLog +{ + std::string entry; // command itself +}; + + +// Class representing meta server client structure +class CMetaClient +{ +public: + CMetaClient() : metaSocket(), metaPort(-1), lastRecvState(-1) {} + ~CMetaClient(); + void SetMetaServer(const std::string host, const int port); + int Init(); + void Close(); + int Send(const std::string cmd); + int Recv(); + int GetLastRecvState() { return lastRecvState; } + int GetLogSize() { return events.size(); } + CClientLog* GetLastMessage() { return events.back(); } + +private: + CTCPSocket metaSocket; /// This is a TCP socket + std::string metaHost; /// Address of metaserver + int metaPort; /// Port of metaserver + std::list events; /// All commands received from metaserver + int lastRecvState; /// Now many bytes have been received in last reply +}; + + + /*---------------------------------------------------------------------------- -- Variables ----------------------------------------------------------------------------*/ - -extern int MetaInit(); -extern int MetaClose(); -extern int MetaServerOK(const char *reply); -extern int SendMetaCommand(const char *command, const char *format, ...); -extern int RecvMetaReply(char **reply); -extern int GetMetaParameter(char *reply, int pos, char **value); +// Metaserver itself +extern CMetaClient MetaClient; +// Warning: deprecated function extern int CclSetMetaServer(lua_State *l); //@} diff --git a/src/include/network.h b/src/include/network.h index 3c260976d..78710e0ec 100644 --- a/src/include/network.h +++ b/src/include/network.h @@ -35,7 +35,7 @@ -- Includes ----------------------------------------------------------------------------*/ -#include "network/udpsocket.h" +#include "network/netsockets.h" /*---------------------------------------------------------------------------- -- Declarations diff --git a/src/include/network/udpsocket.h b/src/include/network/netsockets.h similarity index 82% rename from src/include/network/udpsocket.h rename to src/include/network/netsockets.h index c57b65d7c..a0739ae9e 100644 --- a/src/include/network/udpsocket.h +++ b/src/include/network/netsockets.h @@ -8,9 +8,9 @@ // T H E W A R B E G I N S // Stratagus - A free fantasy real time strategy game engine // -/**@name udpsocket.h - The udp socket header file. */ +/**@name netsockets.h - TCP and UDP sockets. */ // -// (c) Copyright 2013 by Joris Dauphin +// (c) Copyright 2013 by Joris Dauphin and cybermind // // 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 @@ -26,8 +26,8 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 02111-1307, USA. -#ifndef UDPSOCKET_H -#define UDPSOCKET_H +#ifndef NETSOCKETS_H +#define NETSOCKETS_H #include @@ -52,6 +52,7 @@ private: }; class CUDPSocket_Impl; +class CTCPSocket_Impl; class CUDPSocket { @@ -96,6 +97,25 @@ private: CUDPSocket_Impl *m_impl; }; +// Class representing TCP socket used in communication +class CTCPSocket +{ +public: + CTCPSocket(); + ~CTCPSocket(); + bool Open(const CHost &host); + void Close(); + bool Connect(const CHost &host); + int Send(const void *buf, unsigned int len); + int Recv(void *buf, int len); + void SetNonBlocking(); + // + int HasDataToRead(int timeout); + bool IsValid() const; +private: + CTCPSocket_Impl *m_impl; +}; + //@} -#endif // !UDPSOCKET_H +#endif // !NETSOCKETS_H diff --git a/src/network/master.cpp b/src/network/master.cpp index a6618a7fd..13c2d6a74 100644 --- a/src/network/master.cpp +++ b/src/network/master.cpp @@ -43,6 +43,7 @@ #include "master.h" #include "game.h" +#include "network/netsockets.h" #include "network.h" #include "net_lowlevel.h" #include "parameters.h" @@ -54,10 +55,7 @@ -- Variables ----------------------------------------------------------------------------*/ -static Socket MetaServerFildes; // This is a TCP socket. - -static std::string MasterHost; /// Metaserver Address -static int MasterPort; /// Metaserver Port +CMetaClient MetaClient; /*---------------------------------------------------------------------------- -- Functions @@ -67,53 +65,79 @@ static int MasterPort; /// Metaserver Port ** Set the metaserver to use for internet play. ** ** @param l Lua state. +** @warning Deprecated function, need to be removed soon */ -int CclSetMetaServer(lua_State *l) -{ - LuaCheckArgs(l, 2); - - MasterHost = LuaToString(l, 1); - MasterPort = LuaToNumber(l, 2); - return 0; +int CclSetMetaServer(lua_State *l) +{ + LuaCheckArgs(l, 2); + MetaClient.SetMetaServer(LuaToString(l, 1), LuaToNumber(l, 2)); + + return 0; } /** -** Initialize the TCP connection to the Meta Server. +** Set the metaserver to use for internet play. +** +** @param host Host to connect +** @param port Port to use to connect +*/ +void CMetaClient::SetMetaServer(const std::string host, const int port) +{ + metaHost = host; + metaPort = port; +} + +CMetaClient::~CMetaClient() +{ + for (std::list::iterator it = events.begin(); it != events.end(); ++it) { + CClientLog *log = *it; + delete log; + } + events.clear(); + this->Close(); +} + +/** +** Initialize the TCP connection to the Meta Server and send test ping to it. ** ** @return -1 fail, 0 success. -** @todo Make a dynamic port allocation. */ -int MetaInit() +int CMetaClient::Init() { - const int port_range_min = 1234; - const int port_range_max = 1244; - - for (int i = port_range_min; i < port_range_max; ++i) { - MetaServerFildes = NetOpenTCP(CNetworkParameter::Instance.localHost.c_str(), i); - if (MetaServerFildes != static_cast(-1)) { - if (NetConnectTCP(MetaServerFildes, NetResolveHost(MasterHost), MasterPort) != -1) { - break; - } - MetaServerFildes = static_cast(-1); - } - } - if (MetaServerFildes == static_cast(-1)) { + if (metaPort == -1) { return -1; } - if (SendMetaCommand("Login", "") == -1) { + // Server socket + CHost metaServerHost(metaHost.c_str(), metaPort); + // Client socket + CHost metaClientHost(CNetworkParameter::Instance.localHost.c_str(), CNetworkParameter::Instance.localPort); + metaSocket.Open(metaClientHost); + if (metaSocket.IsValid() == false) { + fprintf(stderr, "METACLIENT: No free port %d available, aborting\n", metaServerHost.getPort()); + return -1; + } + if (metaSocket.Connect(metaServerHost) == false) { + fprintf(stderr, "METACLIENT: Unable to connect to host %s\n", metaServerHost.toString().c_str()); + MetaClient.Close(); return -1; } - char *reply = NULL; - if (RecvMetaReply(&reply) == -1) { + if (this->Send("PING") == -1) { // not sent + MetaClient.Close(); return -1; } - if (MetaServerOK(reply)) { - delete[] reply; + if (this->Recv() == -1) { // not received + MetaClient.Close(); + return -1; + } + CClientLog &log = *GetLastMessage(); + if (log.entry.find("PING_OK") != std::string::npos) { + // Everything is OK return 0; } else { - delete[] reply; + fprintf(stderr, "METACLIENT: inappropriate message received from %s\n", metaServerHost.toString().c_str()); + MetaClient.Close(); return -1; } } @@ -123,178 +147,56 @@ int MetaInit() ** ** @return nothing */ -int MetaClose() +void CMetaClient::Close() { - NetCloseTCP(MetaServerFildes); - return 0; -} - -/** -** Checks if a Message was OK or ERR -** -** @return 1 OK, 0 Error. -*/ -int MetaServerOK(const char *reply) -{ - return !strcmp("OK\r\n", reply) || !strcmp("OK\n", reply); -} - -/** -** Retrieves the value of the parameter at position paramNumber -** -** @param reply The reply from the metaserver -** @param pos The parameter number -** @param value The returned value -** -** @returns -1 if error. -*/ -int GetMetaParameter(char *reply, int pos, char **value) -{ - // Take Care for OK/ERR - *value = strchr(reply, '\n'); - (*value)++; - - while (pos-- && *value) { - *value = strchr(*value, '\n'); - if (*value) { - (*value)++; - } + if (metaSocket.IsValid()) { + metaSocket.Close(); } - - if (!*value) { - // Parameter our of bounds - return -1; - } - - if (*value[0] == '\n') { - (*value)++; - } - - char *endline = strchr(*value, '\n'); - - if (!endline) { - return -1; - } - - *endline = '\0'; - *value = new_strdup(*value); - *endline = '\n'; - return 0; } /** ** Send a command to the meta server ** -** @param command command to send -** @param format format of parameters -** @param ... parameters +** @param cmd command to send ** -** @returns -1 fail, length of command +** @returns -1 if failed, otherwise length of command */ -int SendMetaCommand(const char *command, const char *format, ...) +int CMetaClient::Send(const std::string cmd) { - int size = GameName.size() + Parameters::Instance.LocalPlayerName.size() + strlen(command) + 100; int ret = -1; - char *p = new char[size]; - if (p == NULL) { - return -1; - } - char *s = new char[size]; - if (s == NULL) { - delete[] p; - return -1; - } - - // Message Structure - // if for Magnant Compatibility, it may be removed - // Player Name, Game Name, VERSION, Command, **Paramaters** - sprintf(s, "\n%s\n%s\n%s\n%s\n", - Parameters::Instance.LocalPlayerName.c_str(), GameName.c_str(), VERSION, command); - - // Commands - // Login - password - // Logout - 0 - // AddGame - IP,Port,Description,Map,Players,FreeSpots - // JoinGame - Nick of Hoster - // ChangeGame - Description,Map,Players,FreeSpots - // GameList - 0 - // NextGameInList - 0 - // StartGame - 0 - // PlayerScore - Player,Score,Win (Add razings...) - // EndGame - Called after PlayerScore. - // AbandonGame - 0 - char *newp; - va_list ap; - while (1) { - /* Try to print in the allocated space. */ - va_start(ap, format); - int n = vsnprintf(p, size, format, ap); - va_end(ap); - /* If that worked, string was processed. */ - if (n > -1 && n < size) { - break; - } - /* Else try again with more space. */ - if (n > -1) { /* glibc 2.1 */ - size = n + 1; /* precisely what is needed */ - } else { /* glibc 2.0 */ - size *= 2; /* twice the old size */ - } - if ((newp = new char[size + 1]) == NULL) { - delete[] p; - delete[] s; - return -1; - } - memcpy(newp, p, size * sizeof(char)); - delete[] p; - p = newp; - } - if ((newp = new char[strlen(s) + size + 2]) == NULL) { - delete[] s; - delete[] p; - return -1; - } - strcpy(newp, s); - delete[] s; - s = newp; - strcat(s, p); - strcat(s, "\n"); - size = strlen(s); - ret = NetSendTCP(MetaServerFildes, s, size); - delete[] p; - delete[] s; + std::string mes(cmd); + mes.append("\n"); + ret = metaSocket.Send(mes.c_str(), mes.size()); return ret; } /** ** Receive reply from Meta Server ** -** @param reply Text of the reply -** ** @return error or number of bytes */ -int RecvMetaReply(char **reply) +int CMetaClient::Recv() { - if (NetSocketReady(MetaServerFildes, 5000) == -1) { + if (metaSocket.HasDataToRead(5000) == -1) { return -1; } - // FIXME: Allow for large packets char buf[1024]; - int n = NetRecvTCP(MetaServerFildes, &buf, 1024); - char *p = new char[n + 1]; - if (!p) { - return -1; + memset(&buf, 0, sizeof(buf)); + int n = metaSocket.Recv(&buf, sizeof(buf)); + if (n == -1) { + return n; } - // We know we now have the whole command. // Convert to standard notation - buf[n - 1] = '\0'; - buf[n - 2] = '\n'; - strcpy(p, buf); - - *reply = p; + std::string cmd(buf, strlen(buf)); + cmd += '\n'; + cmd += '\0'; + CClientLog *log = new CClientLog; + log->entry = cmd; + events.push_back(log); + lastRecvState = n; return n; } diff --git a/src/network/net_lowlevel.cpp b/src/network/net_lowlevel.cpp index e6fb3b1a8..87fbfebe4 100644 --- a/src/network/net_lowlevel.cpp +++ b/src/network/net_lowlevel.cpp @@ -407,7 +407,7 @@ Socket NetOpenTCP(const char *addr, int port) } else { sock_addr.sin_addr.s_addr = INADDR_ANY; } - sock_addr.sin_port = htons(port); + sock_addr.sin_port = port; int opt = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (setsockopttype)&opt, sizeof(opt)); diff --git a/src/network/udpsocket.cpp b/src/network/netsockets.cpp similarity index 69% rename from src/network/udpsocket.cpp rename to src/network/netsockets.cpp index 10171bc43..ec15e987d 100644 --- a/src/network/udpsocket.cpp +++ b/src/network/netsockets.cpp @@ -8,9 +8,9 @@ // T H E W A R B E G I N S // Stratagus - A free fantasy real time strategy game engine // -/**@name udpsocket.cpp - The udpsocket class. */ +/**@name netsockets.cpp - TCP and UDP sockets. */ // -// (c) Copyright 2013 by Joris Dauphin +// (c) Copyright 2013 by Joris Dauphin and cybermind // // 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 @@ -35,7 +35,7 @@ #include "stratagus.h" -#include "network/udpsocket.h" +#include "network/netsockets.h" #include "net_lowlevel.h" #include @@ -174,4 +174,93 @@ bool CUDPSocket::IsValid() const return m_impl->IsValid(); } -//@} +// +// CTCPSocket_Impl +// + +class CTCPSocket_Impl +{ +public: + CTCPSocket_Impl() : socket(Socket(-1)) {} + ~CTCPSocket_Impl() { if (IsValid()) { Close(); } } + 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 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 SetNonBlocking() { NetSetNonBlocking(socket); } + int HasDataToRead(int timeout) { return NetSocketReady(socket, timeout); } + bool IsValid() const { return socket != Socket(-1); } +private: + Socket socket; +}; + +bool CTCPSocket_Impl::Open(const CHost &host) +{ + char ip[24]; // 127.255.255.255:65555 + memset(&ip, 0, sizeof(ip)); + sprintf(ip, "%d.%d.%d.%d", NIPQUAD(ntohl(host.getIp()))); + this->socket = NetOpenTCP(ip, host.getPort()); + return this->socket != INVALID_SOCKET; +} + +// +// CTCPSocket +// + +CTCPSocket::CTCPSocket() +{ + m_impl = new CTCPSocket_Impl(); +} + +CTCPSocket::~CTCPSocket() +{ + delete m_impl; +} + +bool CTCPSocket::Open(const CHost &host) +{ + return m_impl->Open(host); +} + +void CTCPSocket::Close() +{ + m_impl->Close(); +} + + +bool CTCPSocket::Connect(const CHost &host) +{ + return m_impl->Connect(host); +} + +int CTCPSocket::Send(const void *buf, unsigned int len) +{ + return m_impl->Send(buf, len); +} + +int CTCPSocket::Recv(void *buf, int len) +{ + const int res = m_impl->Recv(buf, len); + return res; +} + +void CTCPSocket::SetNonBlocking() +{ + m_impl->SetNonBlocking(); +} + +int CTCPSocket::HasDataToRead(int timeout) +{ + return m_impl->HasDataToRead(timeout); +} + +bool CTCPSocket::IsValid() const +{ + return m_impl->IsValid(); +} + +//@} \ No newline at end of file diff --git a/src/tolua/master.pkg b/src/tolua/master.pkg new file mode 100644 index 000000000..52044bef4 --- /dev/null +++ b/src/tolua/master.pkg @@ -0,0 +1,22 @@ +$#include "master.h" + +struct CClientLog +{ + std::string entry; +}; + +class CMetaClient +{ +public: + CMetaClient(); + void SetMetaServer(const std::string host, const int port); + int Init(); + void Close(); + int Send(const std::string cmd); + int Recv(); + int GetLastRecvState() { return lastRecvState; } + int GetLogSize() { return events.size(); } + CClientLog* GetLastMessage() { return events.back(); } +}; + +CMetaClient MetaClient; \ No newline at end of file diff --git a/src/tolua/stratagus.pkg b/src/tolua/stratagus.pkg index fbf4c34d5..0db37745a 100644 --- a/src/tolua/stratagus.pkg +++ b/src/tolua/stratagus.pkg @@ -70,6 +70,7 @@ $pfile "editor.pkg" $pfile "font.pkg" $pfile "game.pkg" $pfile "map.pkg" +$pfile "master.pkg" $pfile "minimap.pkg" $pfile "network.pkg" $pfile "particle.pkg"