Cube  serverms.m at tip

File src/serverms.m from the latest check-in


// 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;
		}
	}
}