From aafc2e790d80947157d94eb1cca8433b2c382963 Mon Sep 17 00:00:00 2001 From: driAn Date: Mon, 15 Sep 2008 16:10:28 +0000 Subject: [PATCH] find all AC servers in your LAN git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@2631 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/src/protocol.h | 2 +- source/src/protos.h | 31 ++++++- source/src/server.cpp | 5 +- source/src/serverbrowser.cpp | 30 ++++--- source/src/serverms.cpp | 162 +++++++++++++++++++++++++++-------- 5 files changed, 175 insertions(+), 55 deletions(-) diff --git a/source/src/protocol.h b/source/src/protocol.h index 5ac02ee5..4599afa9 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -2,8 +2,8 @@ #define DEFAULTCLIENTS 6 #define MAXTRANS 5000 // max amount of data to swallow in 1 go #define CUBE_DEFAULT_SERVER_PORT 28763 +#define CUBE_SERVINFO_PORT_LAN 28762 #define CUBE_SERVINFO_PORT(serverport) (serverport+1) -#define CUBE_SERVINFO_TO_SERV_PORT(servinfoport) (servinfoport-1) #define PROTOCOL_VERSION 1126 // bump when protocol changes #define DEMO_VERSION 1 // bump when demo format changes #define DEMO_MAGIC "ASSAULTCUBE_DEMO" diff --git a/source/src/protos.h b/source/src/protos.h index d8c773bc..f1a035a6 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -609,9 +609,9 @@ extern void getstring(char *t, ucharbuf &p, int len = MAXTRANS); extern void filtertext(char *dst, const char *src, bool whitespace = true, int len = sizeof(string)-1); extern void startintermission(); extern void restoreserverstate(vector &ents); -extern uchar *retrieveservers(uchar *buf, int buflen); +extern uchar *retrieveservers(uchar *buf, int buflen, ENetAddress &masterserver, char *masterpath); extern char msgsizelookup(int msg); -extern void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, int serverport); +extern void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, ENetAddress &localaddr); extern void servermsdesc(const char *sdesc); extern void servermsinit(const char *master, const char *ip, int serverport, const char *sdesc, bool listen); extern bool serverpickup(int i, int sender); @@ -621,6 +621,33 @@ extern void extinfo_statsbuf(ucharbuf &p, int pid, int bpos, ENetSocket &pongsoc extern void extinfo_teamscorebuf(ucharbuf &p); extern char *votestring(int type, char *arg1, char *arg2); +// local masterserver +struct masterserver +{ + ENetAddress address; + ENetSocket socket; + bool created; + int lastpurge; + + struct server + { + ENetAddress addr; + int addmillis; + }; + vector servers; + + masterserver(); + void update(); + + void processrequests(); + void purgeserverlist(); + char *getserverlist(); + void create(ENetAddress &addr); + bool addserver(ENetAddress &addr); +}; + +extern masterserver localmasterserver; + // demo struct demoheader { diff --git a/source/src/server.cpp b/source/src/server.cpp index a1534f4f..cdd7079c 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -484,6 +484,7 @@ static string servdesc_full, servdesc_pre, servdesc_suf; bool isdedicated; ENetHost *serverhost = NULL; +masterserver localmasterserver; void process(ENetPacket *packet, int sender, int chan); void welcomepacket(ucharbuf &p, int n); @@ -2962,7 +2963,8 @@ void serverslice(uint timeout) // main server update, called from cube main lo if(!isdedicated) return; // below is network only - serverms(smode, numclients(), minremain, smapname, servmillis, serverhost->address.port); + serverms(smode, numclients(), minremain, smapname, servmillis, serverhost->address); + localmasterserver.update(); if(autoteam && m_teammode && !m_arena && !interm && servmillis - lastfillup > 5000 && refillteams()) lastfillup = servmillis; @@ -3233,6 +3235,7 @@ void initserver(bool dedicated, int uprate, const char *sdesc, const char *sdesc serverhost = enet_host_create(&address, maxclients+1, 0, uprate); if(!serverhost) fatal("could not create server host"); loopi(maxclients) serverhost->peers[i].data = (void *)-1; + readscfg(maprot && maprot[0] ? maprot : "config/maprot.cfg"); if(adminpwd && adminpwd[0]) adminpasswd = adminpwd; if(srvmsg && srvmsg[0]) motd = srvmsg; diff --git a/source/src/serverbrowser.cpp b/source/src/serverbrowser.cpp index bb95cf8a..b0708ba4 100644 --- a/source/src/serverbrowser.cpp +++ b/source/src/serverbrowser.cpp @@ -333,8 +333,6 @@ void addserver(char *servername, char *serverport) newserver(servername, ENET_HOST_ANY, port); } -VAR(searchlan, 0, 0, 1); - void pingservers() { if(pingsock == ENET_SOCKET_NULL) @@ -360,15 +358,6 @@ void pingservers() buf.dataLength = p.length(); enet_socket_send(pingsock, &si.address, &buf, 1); } - if(searchlan) - { - ENetAddress address; - address.host = ENET_HOST_BROADCAST; - address.port = CUBE_SERVINFO_PORT(CUBE_DEFAULT_SERVER_PORT); - buf.data = ping; - buf.dataLength = p.length(); - enet_socket_send(pingsock, &address, &buf, 1); - } lastinfo = totalmillis; } @@ -425,7 +414,6 @@ void checkpings() si = servers[i]; break; } - if(!si && searchlan) si = newserver(NULL, addr.host, CUBE_SERVINFO_TO_SERV_PORT(addr.port)); if(!si) continue; ucharbuf p(ping, len); @@ -501,10 +489,10 @@ void clearservers() servers.deletecontentsp(); } -void updatefrommaster() +void getserversfrommaster(ENetAddress &masterserver, char *masterpath) { uchar buf[32000]; - uchar *reply = retrieveservers(buf, sizeof(buf)); + uchar *reply = retrieveservers(buf, sizeof(buf), masterserver, masterpath); if(!*reply || strstr((char *)reply, "") || strstr((char *)reply, "")) conoutf("master server not replying"); else { @@ -524,9 +512,23 @@ void updatefrommaster() } } +void updatefrommaster() +{ + extern string masterpath; + extern ENetAddress masterserver; + getserversfrommaster(masterserver, masterpath); +} + +void updatefromlanmasters() +{ + ENetAddress bcast = { ENET_HOST_BROADCAST, CUBE_SERVINFO_PORT_LAN }; + getserversfrommaster(bcast, "/"); +} + COMMAND(addserver, ARG_2STR); COMMAND(clearservers, ARG_NONE); COMMAND(updatefrommaster, ARG_NONE); +COMMAND(updatefromlanmasters, ARG_NONE); void writeservercfg() { diff --git a/source/src/serverms.cpp b/source/src/serverms.cpp index 8ab80c7e..e8d6cd93 100644 --- a/source/src/serverms.cpp +++ b/source/src/serverms.cpp @@ -17,7 +17,14 @@ 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) +char *httpgetrequest(const char *hostname, const char *uripart, const char *ref, const char *agent) +{ + char *s = newstring(_MAXDEFSTR); + s_sprintf(s)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", uripart, hostname, ref, agent); + return s; +} + +ENetSocket httpgetsend(ENetAddress &remoteaddress, const char *hostname, char *request, ENetAddress *localaddress = NULL, ENetSocketType sockettype = ENET_SOCKET_TYPE_STREAM) { if(remoteaddress.host==ENET_HOST_ANY) { @@ -26,22 +33,22 @@ ENetSocket httpgetsend(ENetAddress &remoteaddress, const char *hostname, const c #endif if(!resolverwait(hostname, &remoteaddress)) return ENET_SOCKET_NULL; } - ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, localaddress); - if(sock==ENET_SOCKET_NULL || connectwithtimeout(sock, hostname, remoteaddress)<0) + ENetSocket sock = enet_socket_create(sockettype, localaddress); + if(sock==ENET_SOCKET_NULL || (sockettype==ENET_SOCKET_TYPE_STREAM && connectwithtimeout(sock, hostname, remoteaddress)<0)) { #ifdef STANDALONE printf(sock==ENET_SOCKET_NULL ? "could not open socket\n" : "could not connect\n"); #endif return ENET_SOCKET_NULL; } + enet_socket_set_option(sock, ENET_SOCKOPT_BROADCAST, 1); ENetBuffer buf; - s_sprintfd(httpget)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req, hostname, ref, agent); - buf.data = httpget; + buf.data = request; buf.dataLength = strlen((char *)buf.data); #ifdef STANDALONE printf("sending request to %s...\n", hostname); #endif - enet_socket_send(sock, NULL, &buf, 1); + enet_socket_send(sock, sockettype==ENET_SOCKET_TYPE_STREAM ? NULL : &remoteaddress, &buf, 1); return sock; } @@ -72,24 +79,45 @@ uchar *stripheader(uchar *b) } ENetSocket mssock = ENET_SOCKET_NULL; -ENetAddress msaddress = { ENET_HOST_ANY, ENET_PORT_ANY }; ENetAddress masterserver = { ENET_HOST_ANY, 80 }; +ENetAddress msaddress = { ENET_HOST_ANY, ENET_PORT_ANY }; +ENetAddress machinemasterserver = { ENET_HOST_ANY, CUBE_SERVINFO_PORT_LAN }; int lastupdatemaster = 0; string masterbase; string masterpath; uchar masterrep[MAXTRANS]; ENetBuffer masterb; -void updatemasterserver(int millis, int serverport) +void updatemasterserver(int millis, ENetAddress &localaddr) { if(millis/(60*60*1000)!=lastupdatemaster) // send alive signal to masterserver every hour of uptime { - s_sprintfd(path)("%sregister.do?action=add&port=%d", masterpath, serverport); + char *request; + s_sprintfd(path)("%sregister.do?action=add&port=%d", masterpath, localaddr.port); s_sprintfd(agent)("AssaultCube Server %d", AC_VERSION); - mssock = httpgetsend(masterserver, masterbase, path, "assaultcubeserver", agent, &msaddress); + s_sprintfd(ref)("assaultcubeserver"); + + // register on public masterserver + request = httpgetrequest(masterbase, path, ref, agent); + mssock = httpgetsend(masterserver, masterbase, request, &msaddress); + delete[] request; masterrep[0] = 0; masterb.data = masterrep; masterb.dataLength = MAXTRANS-1; + + // register on local machine masterserver + localmasterserver.create(machinemasterserver); // try creating an instance, maybe we are the first or the only local server + string localhost; + if(machinemasterserver.host==ENET_HOST_ANY) + { + s_strcpy(localhost, "localhost"); + } + else enet_address_get_host_ip(&machinemasterserver, localhost, _MAXDEFSTR); + request = httpgetrequest(localhost, path, ref, agent); + ENetSocket lansock = httpgetsend(machinemasterserver, localhost, request, &msaddress, ENET_SOCKET_TYPE_DATAGRAM); + delete[] request; + enet_socket_destroy(lansock); + lastupdatemaster = millis/(60*60*1000); } } @@ -106,55 +134,114 @@ void checkmasterreply() #ifndef STANDALONE #define RETRIEVELIMIT 20000 +#define BROADCASTLIMIT 5000 -uchar *retrieveservers(uchar *buf, int buflen) +uchar *retrieveservers(uchar *buf, int buflen, ENetAddress &masterserver, char *masterpath) { + bool lansearch = (masterserver.host==ENET_HOST_BROADCAST); buf[0] = '\0'; s_sprintfd(path)("%sretrieve.do?item=list", masterpath); s_sprintfd(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; + ENetSocket sock = ENET_SOCKET_NULL; - s_sprintfd(text)("retrieving servers from %s... (esc to abort)", masterbase); - show_out_of_renderloop_progress(0, text); + if(lansearch) + { + char *request = httpgetrequest("", path, "assaultcubeclient", agent); + sock = httpgetsend(masterserver, "localhost", request, NULL, ENET_SOCKET_TYPE_DATAGRAM); + } + else + { + char *request = httpgetrequest(masterbase, path, "assaultcubeclient", agent); + sock = httpgetsend(address, masterbase, request); + } + + if(sock==ENET_SOCKET_NULL) return buf; + // only cache this if connection succeeds + masterserver = address; 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); + if(lansearch) // broadcast lan + { + s_sprintfd(text)("searching servers in your LAN... (esc to abort)"); + + while(timeout < BROADCASTLIMIT) + { + 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; + } + + // collect responses + enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; + uchar lanbuf[32000] = { 0 }; + ENetBuffer laneb; + laneb.data = lanbuf; + laneb.dataLength = sizeof(lanbuf); + + if(enet_socket_wait(sock, &events, 250) >= 0 && events) + { + // strip response + int len = enet_socket_receive(sock, NULL, &laneb, 1); + if(len<=0) continue; + uchar *bodybegin = stripheader(lanbuf); + size_t headersize = bodybegin-lanbuf; + + // collect responses into a single buffer + memcpy(eb.data, lanbuf+headersize, len-headersize); + eb.data = ((char *)eb.data)+len-headersize; + ((char*)eb.data)[0] = 0; + eb.dataLength -= len-headersize; + } + } + + enet_socket_destroy(sock); + return buf; + } + else // contact public masterserver + { + s_sprintfd(text)("retrieving servers from %s... (esc to abort)", masterbase); + show_out_of_renderloop_progress(0, text); + + // reconstruct multipacket reponse + 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; + } + } + + enet_socket_destroy(sock); + return stripheader(buf); + } } #endif ENetSocket pongsock = ENET_SOCKET_NULL; string serverdesc; -void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, int serverport) +void serverms(int mode, int numplayers, int minremain, char *smapname, int millis, ENetAddress &localaddr) { checkmasterreply(); - updatemasterserver(millis, serverport); + updatemasterserver(millis, localaddr); // reply all server info requests ENetBuffer buf; @@ -246,6 +333,7 @@ void servermsinit(const char *master, const char *ip, int infoport, const char * ENetAddress address = { ENET_HOST_ANY, infoport }; if(*ip && enet_address_set_host(&address, ip)<0) printf("WARNING: server ip not resolved"); if(*ip && enet_address_set_host(&msaddress, ip)<0) printf("WARNING: server ip not resolved"); + if(*ip && enet_address_set_host(&machinemasterserver, ip)<0) printf("WARNING: server ip not resolved"); pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, &address); if(pongsock == ENET_SOCKET_NULL) fatal("could not create server info socket\n"); else enet_socket_set_option(pongsock, ENET_SOCKOPT_NONBLOCK, 1);