WIP port of sauer master server

git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@4803 e016b1c5-ed68-44aa-a2f7-31cac88cffde
This commit is contained in:
Lee Salzman
2010-05-04 19:42:40 +00:00
parent a35ca97f08
commit 7d90c91175
10 changed files with 886 additions and 164 deletions
+130 -116
View File
@@ -17,158 +17,172 @@ int connectwithtimeout(ENetSocket sock, const char *hostname, ENetAddress &remot
}
#endif
ENetSocket httpgetsend(ENetAddress &remoteaddress, const char *hostname, const char *req, const char *ref, const char *agent, ENetAddress *localaddress = NULL)
ENetSocket mastersock = ENET_SOCKET_NULL;
ENetAddress masteraddress = { ENET_HOST_ANY, ENET_PORT_ANY }, serveraddress = { ENET_HOST_ANY, ENET_PORT_ANY };
string mastername = AC_MASTER_URI;
int lastupdatemaster = 0;
vector<char> masterout, masterin;
int masteroutpos = 0, masterinpos = 0;
void disconnectmaster()
{
if(remoteaddress.host==ENET_HOST_ANY)
if(mastersock == ENET_SOCKET_NULL) return;
enet_socket_destroy(mastersock);
mastersock = ENET_SOCKET_NULL;
masterout.setsize(0);
masterin.setsize(0);
masteroutpos = masterinpos = 0;
masteraddress.host = ENET_HOST_ANY;
masteraddress.port = ENET_PORT_ANY;
lastupdatemaster = 0;
}
ENetSocket connectmaster()
{
if(!mastername[0]) return ENET_SOCKET_NULL;
if(masteraddress.host == ENET_HOST_ANY)
{
logline(ACLOG_INFO, "looking up %s...", hostname);
if(!resolverwait(hostname, &remoteaddress)) return ENET_SOCKET_NULL;
logline(ACLOG_INFO, "looking up %s...", mastername);
masteraddress.port = AC_MASTER_PORT;
if(!resolverwait(mastername, &masteraddress)) return ENET_SOCKET_NULL;
}
ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
if(sock!=ENET_SOCKET_NULL && localaddress && enet_socket_bind(sock, localaddress) < 0)
if(sock != ENET_SOCKET_NULL && serveraddress.host != ENET_HOST_ANY && enet_socket_bind(sock, &serveraddress) < 0)
{
enet_socket_destroy(sock);
sock = ENET_SOCKET_NULL;
}
if(sock==ENET_SOCKET_NULL || connectwithtimeout(sock, hostname, remoteaddress)<0)
if(sock == ENET_SOCKET_NULL || connectwithtimeout(sock, mastername, masteraddress) < 0)
{
logline(ACLOG_WARNING, sock==ENET_SOCKET_NULL ? "could not open socket" : "could not connect");
return ENET_SOCKET_NULL;
}
ENetBuffer buf;
defformatstring(httpget)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req, hostname, ref, agent);
buf.data = httpget;
buf.dataLength = strlen((char *)buf.data);
logline(ACLOG_INFO, "sending request to %s...", hostname);
enet_socket_send(sock, NULL, &buf, 1);
enet_socket_set_option(sock, ENET_SOCKOPT_NONBLOCK, 1);
return sock;
}
bool httpgetreceive(ENetSocket sock, ENetBuffer &buf, int timeout = 0)
bool requestmaster(const char *req)
{
if(sock==ENET_SOCKET_NULL) return false;
enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
if(enet_socket_wait(sock, &events, timeout) >= 0 && events)
if(mastersock == ENET_SOCKET_NULL)
{
int len = enet_socket_receive(sock, NULL, &buf, 1);
if(len<=0)
{
enet_socket_destroy(sock);
return false;
}
buf.data = ((char *)buf.data)+len;
((char*)buf.data)[0] = 0;
buf.dataLength -= len;
mastersock = connectmaster();
if(mastersock == ENET_SOCKET_NULL) return false;
}
masterout.put(req, strlen(req));
return true;
}
uchar *stripheader(uchar *b)
bool requestmasterf(const char *fmt, ...)
{
char *s = strstr((char *)b, "\n\r\n");
if(!s) s = strstr((char *)b, "\n\n");
return s ? (uchar *)s : b;
defvformatstring(req, fmt, fmt);
return requestmaster(req);
}
ENetSocket mssock = ENET_SOCKET_NULL;
ENetAddress msaddress = { ENET_HOST_ANY, ENET_PORT_ANY };
ENetAddress masterserver = { ENET_HOST_ANY, 80 };
//int lastupdatemaster = 0;
string masterbase;
string masterpath;
uchar masterrep[MAXTRANS];
ENetBuffer masterb;
extern void processmasterinput(const char *cmd, int cmdlen, const char *args);
extern void update_cmod_and_ms(int millis, int type);
//extern int updatecmod(int millis, int type);
void processmasterinput()
{
if(masterinpos >= masterin.length()) return;
char *input = &masterin[masterinpos], *end = (char *)memchr(input, '\n', masterin.length() - masterinpos);
while(end)
{
*end++ = '\0';
const char *args = input;
while(args < end && !isspace(*args)) args++;
int cmdlen = args - input;
while(args < end && isspace(*args)) args++;
if(!strncmp(input, "failreg", cmdlen))
logline(ACLOG_WARNING, "master server registration failed: %s", args);
else if(!strncmp(input, "succreg", cmdlen))
logline(ACLOG_INFO, "master server registration succeeded");
else processmasterinput(input, cmdlen, args);
masterinpos = end - masterin.getbuf();
input = end;
end = (char *)memchr(input, '\n', masterin.length() - masterinpos);
}
if(masterinpos >= masterin.length())
{
masterin.setsize(0);
masterinpos = 0;
}
}
void flushmasteroutput()
{
if(masterout.empty()) return;
ENetBuffer buf;
buf.data = &masterout[masteroutpos];
buf.dataLength = masterout.length() - masteroutpos;
int sent = enet_socket_send(mastersock, NULL, &buf, 1);
if(sent >= 0)
{
masteroutpos += sent;
if(masteroutpos >= masterout.length())
{
masterout.setsize(0);
masteroutpos = 0;
}
}
else disconnectmaster();
}
void flushmasterinput()
{
if(masterin.length() >= masterin.capacity())
masterin.reserve(4096);
ENetBuffer buf;
buf.data = &masterin[masterin.length()];
buf.dataLength = masterin.capacity() - masterin.length();
int recv = enet_socket_receive(mastersock, NULL, &buf, 1);
if(recv > 0)
{
masterin.advance(recv);
processmasterinput();
}
else disconnectmaster();
}
// send alive signal to masterserver every hour of uptime
void updatemasterserver(int millis, const ENetAddress &localaddr)
static inline void updatemasterserver(int millis, int port)
{
update_cmod_and_ms(millis, UT_SERVER);
/* if( !millis || millis/(60*60*1000)!=lastupdatemaster )
if(!lastupdatemaster || millis-lastupdatemaster>60*60*1000)
{
if ( updatecmod(millis, UT_SERVER) ){
defformatstring(path)("%sregister.do?action=add&port=%d", masterpath, localaddr.port);
defformatstring(agent)("AssaultCube Server %d", AC_VERSION);
mssock = httpgetsend(masterserver, masterbase, path, "assaultcubeserver", agent, &msaddress);
masterrep[0] = 0;
masterb.data = masterrep;
masterb.dataLength = MAXTRANS-1;
}
lastupdatemaster = millis/(60*60*1000);
}*/
}
void checkmasterreply()
{
if(mssock!=ENET_SOCKET_NULL && !httpgetreceive(mssock, masterb))
{
mssock = ENET_SOCKET_NULL;
string text;
filtertext(text, (const char *) stripheader(masterrep));
logline(ACLOG_INFO, "masterserver reply: %s", text);
if(mastername[0]) requestmasterf("regserv %d\n", port);
lastupdatemaster = millis ? millis : 1;
}
}
#ifndef STANDALONE
#define RETRIEVELIMIT 20000
uchar *retrieveservers(uchar *buf, int buflen)
{
buf[0] = '\0';
defformatstring(path)("%sretrieve.do?item=list", masterpath);
defformatstring(agent)("AssaultCube Client %d", AC_VERSION);
ENetAddress address = masterserver;
ENetSocket sock = httpgetsend(address, masterbase, path, "assaultcubeclient", agent);
if(sock==ENET_SOCKET_NULL) return buf;
/* only cache this if connection succeeds */
masterserver = address;
defformatstring(text)("retrieving servers from %s... (esc to abort)", masterbase);
show_out_of_renderloop_progress(0, text);
ENetBuffer eb;
eb.data = buf;
eb.dataLength = buflen-1;
int starttime = SDL_GetTicks(), timeout = 0;
while(httpgetreceive(sock, eb, 250))
{
timeout = SDL_GetTicks() - starttime;
show_out_of_renderloop_progress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
SDL_Event event;
while(SDL_PollEvent(&event))
{
if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) timeout = RETRIEVELIMIT + 1;
}
if(timeout > RETRIEVELIMIT)
{
buf[0] = '\0';
enet_socket_destroy(sock);
return buf;
}
}
return stripheader(buf);
}
#endif
ENetSocket pongsock = ENET_SOCKET_NULL, lansock = ENET_SOCKET_NULL;
extern int getpongflags(enet_uint32 ip);
void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, const ENetAddress &localaddr, int *mnum, int *msend, int *mrec, int *cnum, int *csend, int *crec, int protocol_version)
{
checkmasterreply();
if(protocol_version > 0 || strncmp(masterpath, AC_MASTER_URI, 23)) updatemasterserver(millis, localaddr);
flushmasteroutput();
updatemasterserver(millis, localaddr.port);
static ENetSocketSet sockset;
ENET_SOCKETSET_EMPTY(sockset);
ENetSocket maxsock = pongsock;
ENET_SOCKETSET_ADD(sockset, pongsock);
if(mastersock != ENET_SOCKET_NULL)
{
maxsock = max(maxsock, mastersock);
ENET_SOCKETSET_ADD(sockset, mastersock);
}
if(lansock != ENET_SOCKET_NULL)
{
maxsock = max(maxsock, lansock);
@@ -177,9 +191,9 @@ void serverms(int mode, int numplayers, int minremain, char *smapname, int milli
if(enet_socketset_select(maxsock, &sockset, NULL, 0) <= 0) return;
// reply all server info requests
static uchar data[MAXTRANS];
ENetBuffer buf;
ENetAddress addr;
uchar data[MAXTRANS];
buf.data = data;
int len;
@@ -300,15 +314,15 @@ void serverms(int mode, int numplayers, int minremain, char *smapname, int milli
if(std) *msend += (int)buf.dataLength;
else *csend += (int)buf.dataLength;
}
if(mastersock != ENET_SOCKET_NULL && ENET_SOCKETSET_CHECK(sockset, mastersock)) flushmasterinput();
}
// this function should be made better, because it is used just ONCE (no need of so much parameters)
void servermsinit(const char *master, const char *ip, int infoport, bool listen)
{
const char *mid = strstr(master, "/");
if(mid) copystring(masterbase, master, mid-master+1);
else copystring(masterbase, (mid = master));
copystring(masterpath, mid);
copystring(mastername, master);
disconnectmaster();
if(listen)
{
@@ -316,7 +330,7 @@ void servermsinit(const char *master, const char *ip, int infoport, bool listen)
if(*ip)
{
if(enet_address_set_host(&address, ip)<0) logline(ACLOG_WARNING, "server ip not resolved");
else msaddress.host = address.host;
else serveraddress.host = address.host;
}
pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
if(pongsock != ENET_SOCKET_NULL && enet_socket_bind(pongsock, &address) < 0)