[+] Metaserver client code rewritten: now using class CMetaClient available through Lua

This commit is contained in:
cybermind 2013-12-05 21:03:34 +06:00
parent 45441a848a
commit bc16a6c6eb
11 changed files with 282 additions and 201 deletions

View file

@ -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

View file

@ -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)) {

View file

@ -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;

View file

@ -32,22 +32,53 @@
//@{
#include <list>
#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 <CClientLog*> 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);
//@}

View file

@ -35,7 +35,7 @@
-- Includes
----------------------------------------------------------------------------*/
#include "network/udpsocket.h"
#include "network/netsockets.h"
/*----------------------------------------------------------------------------
-- Declarations

View file

@ -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 <string>
@ -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

View file

@ -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);
MetaClient.SetMetaServer(LuaToString(l, 1), LuaToNumber(l, 2));
MasterHost = LuaToString(l, 1);
MasterPort = 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<CClientLog*>::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<Socket>(-1)) {
if (NetConnectTCP(MetaServerFildes, NetResolveHost(MasterHost), MasterPort) != -1) {
break;
}
MetaServerFildes = static_cast<Socket>(-1);
}
}
if (MetaServerFildes == static_cast<Socket>(-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
// <Stratagus> if for Magnant Compatibility, it may be removed
// Player Name, Game Name, VERSION, Command, **Paramaters**
sprintf(s, "<Stratagus>\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;
}

View file

@ -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));

View file

@ -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 <stdio.h>
@ -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();
}
//@}

22
src/tolua/master.pkg Normal file
View file

@ -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;

View file

@ -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"