diff --git a/source/enet/include/enet/enet.h b/source/enet/include/enet/enet.h index 03a8f29e..cfbd01f5 100755 --- a/source/enet/include/enet/enet.h +++ b/source/enet/include/enet/enet.h @@ -44,7 +44,9 @@ typedef enum enum { ENET_HOST_ANY = 0, /**< specifies the default server host */ - ENET_HOST_BROADCAST = 0xFFFFFFFF /**< specifies a subnet-wide broadcast */ + ENET_HOST_BROADCAST = 0xFFFFFFFF, /**< specifies a subnet-wide broadcast */ + + ENET_PORT_ANY = 0 /**< specifies that a port should be automatically chosen */ }; /** diff --git a/source/enet/packet.c b/source/enet/packet.c index db6f2d47..e1ae9c44 100755 --- a/source/enet/packet.c +++ b/source/enet/packet.c @@ -30,11 +30,11 @@ enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags) { enet_free (packet); return NULL; - }; + } if (data != NULL) memcpy (packet -> data, data, dataLength); - }; + } packet -> referenceCount = 0; packet -> flags = flags; diff --git a/source/enet/unix.c b/source/enet/unix.c index b0e6ee28..8f157b26 100755 --- a/source/enet/unix.c +++ b/source/enet/unix.c @@ -193,6 +193,7 @@ enet_socket_create (ENetSocketType type, const ENetAddress * address) (struct sockaddr *) & sin, sizeof (struct sockaddr_in)) == -1 || (type == ENET_SOCKET_TYPE_STREAM && + address -> port != ENET_PORT_ANY && listen (newSocket, SOMAXCONN) == -1)) { close (newSocket); diff --git a/source/enet/win32.c b/source/enet/win32.c index 5fde10d3..95591540 100755 --- a/source/enet/win32.c +++ b/source/enet/win32.c @@ -142,6 +142,7 @@ enet_socket_create (ENetSocketType type, const ENetAddress * address) sizeof (struct sockaddr_in)) == SOCKET_ERROR || (type == ENET_SOCKET_TYPE_STREAM && address != NULL && + address -> port != ENET_PORT_ANY && listen (newSocket, SOMAXCONN) == SOCKET_ERROR)) { closesocket (newSocket); diff --git a/source/src/protos.h b/source/src/protos.h index deed590f..d40fcda4 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -40,6 +40,7 @@ extern void drawmenubg(int x1, int y1, int x2, int y2, bool border); extern void addserver(char *servername); extern char *getservername(int n); extern bool resolverwait(const char *name, ENetAddress *address); +extern int connectwithtimeout(ENetSocket sock, char *hostname, ENetAddress &remoteaddress); extern void writeservercfg(); extern void refreshservers(void *menu, bool init); @@ -293,7 +294,7 @@ extern void restoreserverstate(vector &ents); extern uchar *retrieveservers(uchar *buf, int buflen); extern char msgsizelookup(int msg); extern void serverms(int mode, int numplayers, int minremain, char *smapname, int seconds); -extern void servermsinit(const char *master, char *sdesc, bool listen); +extern void servermsinit(const char *master, char *ip, char *sdesc, bool listen); extern bool serverpickup(uint i, int sec, int sender); // weapon diff --git a/source/src/server.cpp b/source/src/server.cpp index d3f829ca..881a8b99 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -1181,7 +1181,7 @@ void initserver(bool dedicated, int uprate, char *sdesc, char *ip, char *master, { if(passwd) s_strcpy(serverpassword, passwd); maxclients = maxcl > 0 ? min(maxcl, MAXCLIENTS) : DEFAULTCLIENTS; - servermsinit(master ? master : "masterserver.cubers.net/cgi-bin/actioncube.pl/", sdesc, dedicated); + servermsinit(master ? master : "masterserver.cubers.net/cgi-bin/actioncube.pl/", ip, sdesc, dedicated); if(isdedicated = dedicated) { diff --git a/source/src/serverbrowser.cpp b/source/src/serverbrowser.cpp index 3cba379b..8fceb849 100644 --- a/source/src/serverbrowser.cpp +++ b/source/src/serverbrowser.cpp @@ -190,6 +190,86 @@ bool resolverwait(const char *name, ENetAddress *address) return resolved; } +SDL_Thread *connthread = NULL; +SDL_mutex *connmutex = NULL; +SDL_cond *conncond = NULL; + +struct connectdata +{ + ENetSocket sock; + ENetAddress address; + int result; +}; + +// do this in a thread to prevent timeouts +// could set timeouts on sockets, but this is more reliable and gives more control +int connectthread(void *data) +{ + SDL_LockMutex(connmutex); + if(!connthread || SDL_GetThreadID(connthread) != SDL_ThreadID()) + { + SDL_UnlockMutex(connmutex); + return 0; + } + connectdata cd = *(connectdata *)data; + SDL_UnlockMutex(connmutex); + + int result = enet_socket_connect(cd.sock, &cd.address); + + SDL_LockMutex(connmutex); + if(!connthread || SDL_GetThreadID(connthread) != SDL_ThreadID()) + { + enet_socket_destroy(cd.sock); + SDL_UnlockMutex(connmutex); + return 0; + } + ((connectdata *)data)->result = result; + SDL_CondSignal(conncond); + SDL_UnlockMutex(connmutex); + + return 0; +} + +#define CONNLIMIT 20000 + +int connectwithtimeout(ENetSocket sock, char *hostname, ENetAddress &address) +{ + s_sprintfd(text)("connecting to %s... (esc to abort)", hostname); + show_out_of_renderloop_progress(0, text); + + if(!connmutex) connmutex = SDL_CreateMutex(); + if(!conncond) conncond = SDL_CreateCond(); + SDL_LockMutex(connmutex); + connectdata cd = { sock, address, -1 }; + connthread = SDL_CreateThread(connectthread, &cd); + + int starttime = SDL_GetTicks(), timeout = 0; + for(;;) + { + if(!SDL_CondWaitTimeout(conncond, connmutex, 250)) + { + if(cd.result<0) enet_socket_destroy(sock); + break; + } + timeout = SDL_GetTicks() - starttime; + show_out_of_renderloop_progress(min(float(timeout)/CONNLIMIT, 1), text); + SDL_Event event; + while(SDL_PollEvent(&event)) + { + if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) timeout = CONNLIMIT + 1; + } + if(timeout > CONNLIMIT) break; + } + + /* thread will actually timeout eventually if its still trying to connect + * so just leave it (and let it destroy socket) instead of causing problems on some platforms by killing it + */ + connthread = NULL; + SDL_UnlockMutex(connmutex); + + return cd.result; +} + struct serverinfo { char *name; @@ -358,9 +438,8 @@ void refreshservers(void *menu, bool init) void updatefrommaster() { - const int MAXUPD = 32000; - uchar buf[MAXUPD]; - uchar *reply = retrieveservers(buf, MAXUPD); + uchar buf[32000]; + uchar *reply = retrieveservers(buf, sizeof(buf)); if(!*reply || strstr((char *)reply, "") || strstr((char *)reply, "")) conoutf("master server not replying"); else { diff --git a/source/src/serverms.cpp b/source/src/serverms.cpp index 5c6fd176..08460ca1 100644 --- a/source/src/serverms.cpp +++ b/source/src/serverms.cpp @@ -7,57 +7,61 @@ bool resolverwait(const char *name, ENetAddress *address) { return enet_address_set_host(address, name) >= 0; } + +int connectwithtimeout(ENetSocket sock, char *hostname, ENetAddress &remoteaddress) +{ + int result = enet_socket_connect(sock, &remoteaddress); + if(result<0) enet_socket_destroy(sock); + return result; +} #endif -ENetSocket mssock = ENET_SOCKET_NULL; - -void httpgetsend(ENetAddress &ad, char *hostname, char *req, char *ref, char *agent) +ENetSocket httpgetsend(ENetAddress &remoteaddress, char *hostname, char *req, char *ref, char *agent, ENetAddress *localaddress = NULL) { - if(mssock!=ENET_SOCKET_NULL) - { - enet_socket_destroy(mssock); - mssock = ENET_SOCKET_NULL; - } - if(ad.host==ENET_HOST_ANY) + if(remoteaddress.host==ENET_HOST_ANY) { +#ifdef STANDALONE printf("looking up %s...\n", hostname); - if(!resolverwait(hostname, &ad)) return; +#endif + if(!resolverwait(hostname, &remoteaddress)) return ENET_SOCKET_NULL; } - mssock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, NULL); - if(mssock==ENET_SOCKET_NULL) { printf("could not open socket\n"); return; } - if(enet_socket_connect(mssock, &ad)<0) - { - printf("could not connect\n"); - enet_socket_destroy(mssock); - mssock = ENET_SOCKET_NULL; - return; + ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, localaddress); + if(sock==ENET_SOCKET_NULL || 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; } 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.dataLength = strlen((char *)buf.data); +#ifdef STANDALONE printf("sending request to %s...\n", hostname); - enet_socket_send(mssock, NULL, &buf, 1); -} +#endif + enet_socket_send(sock, NULL, &buf, 1); + return sock; +} -void httpgetrecieve(ENetBuffer &buf) -{ - if(mssock==ENET_SOCKET_NULL) return; +bool httpgetreceive(ENetSocket sock, ENetBuffer &buf) +{ + if(sock==ENET_SOCKET_NULL) return false; enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; - if(enet_socket_wait(mssock, &events, 0) >= 0 && events) + if(enet_socket_wait(sock, &events, 0) >= 0 && events) { - int len = enet_socket_receive(mssock, NULL, &buf, 1); + int len = enet_socket_receive(sock, NULL, &buf, 1); if(len<=0) { - enet_socket_destroy(mssock); - mssock = ENET_SOCKET_NULL; - return; + enet_socket_destroy(sock); + return false; } buf.data = ((char *)buf.data)+len; ((char*)buf.data)[0] = 0; buf.dataLength -= len; } -} + return true; +} uchar *stripheader(uchar *b) { @@ -66,6 +70,8 @@ uchar *stripheader(uchar *b) return s ? (uchar *)s : b; } +ENetSocket mssock = ENET_SOCKET_NULL; +ENetAddress msaddress = { ENET_HOST_ANY, ENET_PORT_ANY }; ENetAddress masterserver = { ENET_HOST_ANY, 80 }; int updmaster = 0; string masterbase; @@ -79,7 +85,7 @@ void updatemasterserver(int seconds) { s_sprintfd(path)("%sregister.do?action=add", masterpath); s_sprintfd(agent)("AssaultCube Server %d", AC_VERSION); - httpgetsend(masterserver, masterbase, path, "assaultcubeserver", agent); + mssock = httpgetsend(masterserver, masterbase, path, "assaultcubeserver", agent, &msaddress); masterrep[0] = 0; masterb.data = masterrep; masterb.dataLength = MAXTRANS-1; @@ -89,23 +95,36 @@ void updatemasterserver(int seconds) void checkmasterreply() { - bool busy = mssock!=ENET_SOCKET_NULL; - httpgetrecieve(masterb); - if(busy && mssock==ENET_SOCKET_NULL) printf("masterserver reply: %s\n", stripheader(masterrep)); -} + if(mssock!=ENET_SOCKET_NULL && !httpgetreceive(mssock, masterb)) + { + mssock = ENET_SOCKET_NULL; + printf("masterserver reply: %s\n", stripheader(masterrep)); + } +} +#ifndef STANDALONE uchar *retrieveservers(uchar *buf, int buflen) { + buf[0] = '\0'; + s_sprintfd(path)("%sretrieve.do?item=list", masterpath); s_sprintfd(agent)("AssaultCube Client %d", AC_VERSION); - httpgetsend(masterserver, masterbase, path, "assaultcubeclient", agent); + 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; + + s_sprintfd(text)("retrieving servers from %s...", masterbase); + show_out_of_renderloop_progress(0, text); + ENetBuffer eb; - buf[0] = 0; eb.data = buf; eb.dataLength = buflen-1; - while(mssock!=ENET_SOCKET_NULL) httpgetrecieve(eb); + while(httpgetreceive(sock, eb)); return stripheader(buf); } +#endif ENetSocket pongsock = ENET_SOCKET_NULL; string serverdesc; @@ -140,7 +159,7 @@ void serverms(int mode, int numplayers, int minremain, char *smapname, int secon } } -void servermsinit(const char *master, char *sdesc, bool listen) +void servermsinit(const char *master, char *ip, char *sdesc, bool listen) { const char *mid = strstr(master, "/"); if(!mid) mid = master; @@ -153,5 +172,6 @@ void servermsinit(const char *master, char *sdesc, bool listen) ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, &address); if(pongsock == ENET_SOCKET_NULL) fatal("could not create server info socket\n"); + if(*ip && enet_address_set_host(&msaddress, ip)<0) printf("WARNING: server ip not resolved"); } }