// all server side masterserver and pinging functionality #include "cube.h" static ENetSocket mssock = ENET_SOCKET_NULL; static void httpgetsend(ENetAddress *ad, OFString *hostname, OFString *req, OFString *ref, OFString *agent) { if (ad->host == ENET_HOST_ANY) { [OFStdOut writeFormat: @"looking up %@...\n", hostname]; enet_address_set_host(ad, hostname.UTF8String); if (ad->host == ENET_HOST_ANY) return; } if (mssock != ENET_SOCKET_NULL) enet_socket_destroy(mssock); mssock = enet_socket_create(ENET_SOCKET_TYPE_STREAM); if (mssock == ENET_SOCKET_NULL) { printf("could not open socket\n"); return; } if (enet_socket_connect(mssock, ad) < 0) { printf("could not connect\n"); return; } ENetBuffer buf; OFString *httpget = [OFString stringWithFormat: @"GET %@ HTTP/1.0\n" @"Host: %@\n" @"Referer: %@\n" @"User-Agent: %@\n\n", req, hostname, ref, agent]; buf.data = (void *)httpget.UTF8String; buf.dataLength = httpget.UTF8StringLength; [OFStdOut writeFormat: @"sending request to %@...\n", hostname]; enet_socket_send(mssock, NULL, &buf, 1); } static void httpgetrecieve(ENetBuffer *buf) { if (mssock == ENET_SOCKET_NULL) return; enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; if (enet_socket_wait(mssock, &events, 0) >= 0 && events) { int len = enet_socket_receive(mssock, NULL, buf, 1); if (len <= 0) { enet_socket_destroy(mssock); mssock = ENET_SOCKET_NULL; return; } buf->data = ((char *)buf->data) + len; ((char *)buf->data)[0] = 0; buf->dataLength -= len; } } static unsigned char * stripheader(unsigned char *b) { char *s = strstr((char *)b, "\n\r\n"); if (!s) s = strstr((char *)b, "\n\n"); return s ? (unsigned char *)s : b; } static ENetAddress masterserver = { ENET_HOST_ANY, 80 }; static int updmaster = 0; static OFString *masterbase; static OFString *masterpath; static unsigned char masterrep[MAXTRANS]; static ENetBuffer masterb; static void updatemasterserver(int seconds) { // send alive signal to masterserver every hour of uptime if (seconds > updmaster) { OFString *path = [OFString stringWithFormat: @"%@register.do?action=add", masterpath]; httpgetsend(&masterserver, masterbase, path, @"cubeserver", @"Cube Server"); masterrep[0] = 0; masterb.data = masterrep; masterb.dataLength = MAXTRANS - 1; updmaster = seconds + 60 * 60; } } static void checkmasterreply() { bool busy = mssock != ENET_SOCKET_NULL; httpgetrecieve(&masterb); if (busy && mssock == ENET_SOCKET_NULL) printf("masterserver reply: %s\n", stripheader(masterrep)); } unsigned char * retrieveservers(unsigned char *buf, int buflen) { OFString *path = [OFString stringWithFormat: @"%@retrieve.do?item=list", masterpath]; httpgetsend( &masterserver, masterbase, path, @"cubeserver", @"Cube Server"); ENetBuffer eb; buf[0] = 0; eb.data = buf; eb.dataLength = buflen - 1; while (mssock != ENET_SOCKET_NULL) httpgetrecieve(&eb); return stripheader(buf); } static ENetSocket pongsock = ENET_SOCKET_NULL; static OFString *serverdesc; void serverms(int mode, int numplayers, int minremain, OFString *smapname, int seconds, bool isfull) { checkmasterreply(); updatemasterserver(seconds); // reply all server info requests ENetBuffer buf; ENetAddress addr; unsigned char pong[MAXTRANS], *p; 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; p = &pong[len]; putint(&p, PROTOCOL_VERSION); putint(&p, mode); putint(&p, numplayers); putint(&p, minremain); OFString *mname = [OFString stringWithFormat: @"%@%@", (isfull ? @"[FULL] " : @""), smapname]; sendstring(mname, &p); sendstring(serverdesc, &p); buf.dataLength = p - pong; enet_socket_send(pongsock, &addr, &buf, 1); } } void servermsinit(OFString *master_, OFString *sdesc, bool listen) { const char *master = master_.UTF8String; const char *mid = strstr(master, "/"); if (!mid) mid = master; masterpath = @(mid); masterbase = [OFString stringWithUTF8String: master length: mid - master]; serverdesc = sdesc; if (listen) { pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM); if (pongsock == ENET_SOCKET_NULL) { fatal(@"could not create server info socket\n"); return; } ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; if (enet_socket_bind(pongsock, &address) != 0) { fatal(@"could not create server info socket\n"); return; } } }