// all server side masterserver and pinging functionality #include "cube.h" #ifdef STANDALONE 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 httpgetsend(ENetAddress &remoteaddress, char *hostname, char *req, char *ref, char *agent, ENetAddress *localaddress = NULL) { if(remoteaddress.host==ENET_HOST_ANY) { #ifdef STANDALONE printf("looking up %s...\n", hostname); #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) { #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); #endif enet_socket_send(sock, NULL, &buf, 1); return sock; } bool httpgetreceive(ENetSocket sock, ENetBuffer &buf, int timeout = 0) { if(sock==ENET_SOCKET_NULL) return false; enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; if(enet_socket_wait(sock, &events, timeout) >= 0 && events) { 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; } return true; } uchar *stripheader(uchar *b) { char *s = strstr((char *)b, "\n\r\n"); if(!s) s = strstr((char *)b, "\n\n"); 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; string masterpath; uchar masterrep[MAXTRANS]; ENetBuffer masterb; void updatemasterserver(int seconds) { if(seconds>updmaster) // send alive signal to masterserver every hour of uptime { s_sprintfd(path)("%sregister.do?action=add", masterpath); s_sprintfd(agent)("AssaultCube Server %d", AC_VERSION); mssock = httpgetsend(masterserver, masterbase, path, "assaultcubeserver", agent, &msaddress); masterrep[0] = 0; masterb.data = masterrep; masterb.dataLength = MAXTRANS-1; updmaster = seconds+60*60; } } void checkmasterreply() { if(mssock!=ENET_SOCKET_NULL && !httpgetreceive(mssock, masterb)) { mssock = ENET_SOCKET_NULL; printf("masterserver reply: %s\n", stripheader(masterrep)); } } #ifndef STANDALONE #define RETRIEVELIMIT 20000 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); 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... (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), 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; string serverdesc; void serverms(int mode, int numplayers, int minremain, char *smapname, int seconds) { checkmasterreply(); updatemasterserver(seconds); // reply all server info requests ENetBuffer buf; ENetAddress addr; uchar pong[MAXTRANS]; int len; enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; buf.data = pong; while(enet_socket_wait(pongsock, &events, 0) >= 0 && events) { buf.dataLength = sizeof(pong); len = enet_socket_receive(pongsock, &addr, &buf, 1); if(len < 0) return; ucharbuf p(&pong[len], sizeof(pong)-len); putint(p, PROTOCOL_VERSION); putint(p, mode); putint(p, numplayers); putint(p, minremain); sendstring(smapname, p); sendstring(serverdesc, p); putint(p, maxclients); buf.dataLength = len + p.length(); enet_socket_send(pongsock, &addr, &buf, 1); } } void servermsinit(const char *master, char *ip, char *sdesc, bool listen) { const char *mid = strstr(master, "/"); if(!mid) mid = master; s_strcpy(masterpath, mid); s_strncpy(masterbase, master, mid-master+1); s_strcpy(serverdesc, sdesc); if(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"); } }