1 // server.cpp: little more than enhanced multicaster
2 // runs dedicated or as client coroutine
3 
4 #include "engine.h"
5 #ifdef WIN32
6 #include <shlobj.h>
7 #endif
8 
9 VAR(version, 1, ENG_VERSION, -1); // for scripts
10 int kidmode = 0;
11 ICOMMAND(getkidmode, "", (void), intret(kidmode));
12 
13 SVARP(consoletimefmt, "%c");
gettime(char * format)14 char *gettime(char *format)
15 {
16 	time_t ltime;
17 	struct tm *t;
18 
19 	ltime = time (NULL);
20 	t = localtime (&ltime);
21 
22 	static string buf;
23 	strftime (buf, sizeof (buf) - 1, format, t);
24 
25 	return buf;
26 }
27 ICOMMAND(gettime, "s", (char *a), result(gettime(a)));
28 
console(int type,const char * s,...)29 void console(int type, const char *s, ...)
30 {
31 	defvformatstring(sf, s, s);
32 	string osf, psf, fmt;
33 	formatstring(fmt)(consoletimefmt);
34 	filtertext(osf, sf);
35 	formatstring(psf)("%s %s", gettime(fmt), osf);
36 	printf("%s\n", osf);
37 	fflush(stdout);
38 #ifndef STANDALONE
39 	conline(type, sf, 0);
40 #endif
41 }
42 
conoutft(int type,const char * s,...)43 void conoutft(int type, const char *s, ...)
44 {
45 	defvformatstring(sf, s, s);
46 	console(type, "%s", sf);
47 #ifdef IRC
48 	string osf;
49 	filtertext(osf, sf);
50 	ircoutf(4, "%s", osf);
51 #endif
52 }
53 
conoutf(const char * s,...)54 void conoutf(const char *s, ...)
55 {
56 	defvformatstring(sf, s, s);
57 	conoutft(0, "%s", sf);
58 }
59 
60 VARP(verbose, 0, 0, 6);
61 
62 #ifdef STANDALONE
localservertoclient(int chan,ENetPacket * packet)63 void localservertoclient(int chan, ENetPacket *packet) {}
fatal(const char * s,...)64 void fatal(const char *s, ...)
65 {
66     void cleanupserver();
67     cleanupserver();
68     defvformatstring(msg,s,s);
69     printf("ERROR: %s\n", msg);
70     exit(EXIT_FAILURE);
71 }
72 int servertype = 3;
73 #else
74 VAR(servertype, 0, 1, 3); // 0: local only, 1: private, 2: public, 3: dedicated
75 #endif
76 VAR(serveruprate, 0, 0, INT_MAX-1);
77 VAR(serverclients, 1, 6, MAXCLIENTS);
78 VAR(serverport, 1, ENG_SERVER_PORT, INT_MAX-1);
79 VAR(serverqueryport, 1, ENG_QUERY_PORT, INT_MAX-1);
80 VAR(servermasterport, 1, ENG_MASTER_PORT, INT_MAX-1);
81 SVAR(servermaster, ENG_MASTER_HOST);
82 SVAR(serverip, "");
83 
84 int curtime = 0, totalmillis = 1, lastmillis = 1, timescale = 100, paused = 0, timeerr = 0;
85 const char *load = NULL;
86 vector<char *> gameargs;
87 
88 // all network traffic is in 32bit ints, which are then compressed using the following simple scheme (assumes that most values are small).
89 
90 template<class T>
putint_(T & p,int n)91 static inline void putint_(T &p, int n)
92 {
93     if(n<128 && n>-127) p.put(n);
94     else if(n<0x8000 && n>=-0x8000) { p.put(0x80); p.put(n); p.put(n>>8); }
95     else { p.put(0x81); p.put(n); p.put(n>>8); p.put(n>>16); p.put(n>>24); }
96 }
putint(ucharbuf & p,int n)97 void putint(ucharbuf &p, int n) { putint_(p, n); }
putint(packetbuf & p,int n)98 void putint(packetbuf &p, int n) { putint_(p, n); }
putint(vector<uchar> & p,int n)99 void putint(vector<uchar> &p, int n) { putint_(p, n); }
100 
getint(ucharbuf & p)101 int getint(ucharbuf &p)
102 {
103     int c = (char)p.get();
104     if(c==-128) { int n = p.get(); n |= char(p.get())<<8; return n; }
105     else if(c==-127) { int n = p.get(); n |= p.get()<<8; n |= p.get()<<16; return n|(p.get()<<24); }
106     else return c;
107 }
108 
109 // much smaller encoding for unsigned integers up to 28 bits, but can handle signed
110 template<class T>
putuint_(T & p,int n)111 static inline void putuint_(T &p, int n)
112 {
113     if(n < 0 || n >= (1<<21))
114     {
115         p.put(0x80 | (n & 0x7F));
116         p.put(0x80 | ((n >> 7) & 0x7F));
117         p.put(0x80 | ((n >> 14) & 0x7F));
118         p.put(n >> 21);
119     }
120     else if(n < (1<<7)) p.put(n);
121     else if(n < (1<<14))
122     {
123         p.put(0x80 | (n & 0x7F));
124         p.put(n >> 7);
125     }
126     else
127     {
128         p.put(0x80 | (n & 0x7F));
129         p.put(0x80 | ((n >> 7) & 0x7F));
130         p.put(n >> 14);
131     }
132 }
putuint(ucharbuf & p,int n)133 void putuint(ucharbuf &p, int n) { putuint_(p, n); }
putuint(packetbuf & p,int n)134 void putuint(packetbuf &p, int n) { putuint_(p, n); }
putuint(vector<uchar> & p,int n)135 void putuint(vector<uchar> &p, int n) { putuint_(p, n); }
136 
getuint(ucharbuf & p)137 int getuint(ucharbuf &p)
138 {
139     int n = p.get();
140     if(n & 0x80)
141     {
142         n += (p.get() << 7) - 0x80;
143         if(n & (1<<14)) n += (p.get() << 14) - (1<<14);
144         if(n & (1<<21)) n += (p.get() << 21) - (1<<21);
145         if(n & (1<<28)) n |= 0xF0000000;
146     }
147     return n;
148 }
149 
150 template<class T>
putfloat_(T & p,float f)151 static inline void putfloat_(T &p, float f)
152 {
153     lilswap(&f, 1);
154     p.put((uchar *)&f, sizeof(float));
155 }
putfloat(ucharbuf & p,float f)156 void putfloat(ucharbuf &p, float f) { putfloat_(p, f); }
putfloat(packetbuf & p,float f)157 void putfloat(packetbuf &p, float f) { putfloat_(p, f); }
putfloat(vector<uchar> & p,float f)158 void putfloat(vector<uchar> &p, float f) { putfloat_(p, f); }
159 
getfloat(ucharbuf & p)160 float getfloat(ucharbuf &p)
161 {
162     float f;
163     p.get((uchar *)&f, sizeof(float));
164     return lilswap(f);
165 }
166 
167 template<class T>
sendstring_(const char * t,T & p)168 static inline void sendstring_(const char *t, T &p)
169 {
170     while(*t) putint(p, *t++);
171     putint(p, 0);
172 }
sendstring(const char * t,ucharbuf & p)173 void sendstring(const char *t, ucharbuf &p) { sendstring_(t, p); }
sendstring(const char * t,packetbuf & p)174 void sendstring(const char *t, packetbuf &p) { sendstring_(t, p); }
sendstring(const char * t,vector<uchar> & p)175 void sendstring(const char *t, vector<uchar> &p) { sendstring_(t, p); }
176 
getstring(char * text,ucharbuf & p,int len)177 void getstring(char *text, ucharbuf &p, int len)
178 {
179 	char *t = text;
180 	do
181 	{
182 		if(t>=&text[len]) { text[len-1] = 0; return; }
183 		if(!p.remaining()) { *t = 0; return; }
184 		*t = getint(p);
185 	}
186 	while(*t++);
187 }
188 
filtertext(char * dst,const char * src,bool whitespace,int len)189 void filtertext(char *dst, const char *src, bool whitespace, int len)
190 {
191 	for(int c = *src; c; c = *++src)
192 	{
193         if(c=='\f')
194         {
195         	c = *++src;
196         	if(c=='z')
197         	{
198         		c = *++src;
199         		if(c) c = *++src;
200         	}
201         	continue;
202         }
203 		if(isspace(c) ? whitespace : isprint(c))
204 		{
205 			*dst++ = c;
206 			if(!--len) break;
207 		}
208 	}
209 	*dst = '\0';
210 }
211 
212 vector<clientdata *> clients;
213 
214 ENetHost *serverhost = NULL;
215 size_t bsend = 0, brec = 0;
216 int laststatus = 0;
217 ENetSocket pongsock = ENET_SOCKET_NULL;
218 
cleanupserver()219 void cleanupserver()
220 {
221 	if(serverhost) enet_host_destroy(serverhost);
222     serverhost = NULL;
223 #ifdef MASTERSERVER
224 	cleanupmaster();
225 #endif
226 #ifdef IRC
227 	irccleanup();
228 #endif
229 }
230 
231 void process(ENetPacket *packet, int sender, int chan);
232 //void disconnect_client(int n, int reason);
233 
getinfo(int i)234 void *getinfo(int i)	{ return !clients.inrange(i) || clients[i]->type==ST_EMPTY ? NULL : clients[i]->info; }
getnumclients()235 int getnumclients()	 { return clients.length(); }
getclientip(int n)236 uint getclientip(int n) { return clients.inrange(n) && clients[n]->type==ST_TCPIP ? clients[n]->peer->address.host : 0; }
237 
sendpacket(int n,int chan,ENetPacket * packet,int exclude)238 void sendpacket(int n, int chan, ENetPacket *packet, int exclude)
239 {
240 	if(n < 0)
241 	{
242 		server::recordpacket(chan, packet->data, packet->dataLength);
243 		loopv(clients) if(i != server::peerowner(exclude) && server::allowbroadcast(i)) sendpacket(i, chan, packet, exclude);
244 		return;
245 	}
246 	switch(clients[n]->type)
247 	{
248 		case ST_REMOTE:
249 		{
250 			int owner = server::peerowner(n);
251 			if(owner >= 0 && clients.inrange(owner) && owner != n && owner != server::peerowner(exclude))
252 			{
253 				//conoutf("redirect %d packet to %d [%d:%d]", n, owner, exclude, server::peerowner(exclude));
254 				sendpacket(owner, chan, packet, exclude);
255 			}
256 			break;
257 		}
258 		case ST_TCPIP:
259 		{
260 			enet_peer_send(clients[n]->peer, chan, packet);
261 			bsend += packet->dataLength;
262 			break;
263 		}
264         case ST_LOCAL:
265         {
266             localservertoclient(chan, packet);
267             break;
268         }
269 		default: break;
270 	}
271 }
272 
sendf(int cn,int chan,const char * format,...)273 void sendf(int cn, int chan, const char *format, ...)
274 {
275 	int exclude = -1;
276 	bool reliable = false;
277 	if(*format=='r') { reliable = true; ++format; }
278 	ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
279 	ucharbuf p(packet->data, packet->dataLength);
280 	va_list args;
281 	va_start(args, format);
282 	while(*format) switch(*format++)
283 	{
284 		case 'x':
285 			exclude = va_arg(args, int);
286 			break;
287 
288 		case 'v':
289 		{
290 			int n = va_arg(args, int);
291 			int *v = va_arg(args, int *);
292 			loopi(n) putint(p, v[i]);
293 			break;
294 		}
295 
296 		case 'i':
297 		{
298 			int n = isdigit(*format) ? *format++-'0' : 1;
299 			loopi(n) putint(p, va_arg(args, int));
300 			break;
301 		}
302 
303         case 'f':
304         {
305             int n = isdigit(*format) ? *format++-'0' : 1;
306             loopi(n) putfloat(p, (float)va_arg(args, double));
307             break;
308         }
309 
310 		case 's': sendstring(va_arg(args, const char *), p); break;
311 
312 		case 'm':
313 		{
314 			int n = va_arg(args, int);
315 			enet_packet_resize(packet, packet->dataLength+n);
316 			p.buf = packet->data;
317 			p.maxlen += n;
318 			p.put(va_arg(args, uchar *), n);
319 			break;
320 		}
321 	}
322 	va_end(args);
323 	enet_packet_resize(packet, p.length());
324 	sendpacket(cn, chan, packet, exclude);
325 	if(packet->referenceCount==0) enet_packet_destroy(packet);
326 }
327 
sendfile(int cn,int chan,stream * file,const char * format,...)328 void sendfile(int cn, int chan, stream *file, const char *format, ...)
329 {
330     if(cn < 0)
331     {
332 #ifdef STANDALONE
333             return;
334 #endif
335     }
336     else if(!clients.inrange(cn)) return;
337 
338     int len = file->size();
339     if(len <= 0) return;
340 
341     bool reliable = false;
342     if(*format=='r') { reliable = true; ++format; }
343     ENetPacket *packet = enet_packet_create(NULL, MAXTRANS+len, ENET_PACKET_FLAG_RELIABLE);
344 
345     ucharbuf p(packet->data, packet->dataLength);
346     va_list args;
347     va_start(args, format);
348     while(*format) switch(*format++)
349     {
350         case 'i':
351         {
352             int n = isdigit(*format) ? *format++-'0' : 1;
353             loopi(n) putint(p, va_arg(args, int));
354             break;
355         }
356         case 's': sendstring(va_arg(args, const char *), p); break;
357         case 'l': putint(p, len); break;
358     }
359     va_end(args);
360     enet_packet_resize(packet, p.length()+len);
361 
362     file->seek(0, SEEK_SET);
363     file->read(&packet->data[p.length()], len);
364     enet_packet_resize(packet, p.length()+len);
365 
366     if(cn >= 0) sendpacket(cn, chan, packet, -1);
367 #ifndef STANDALONE
368     else sendclientpacket(packet, chan);
369 #endif
370 	if(!packet->referenceCount) enet_packet_destroy(packet);
371 }
372 
373 const char *disc_reasons[] = { "normal", "end of packet", "client num", "kicked/banned", "tag type", "ip is banned", "server is in private mode", "server is full", "connection timed out" };
374 
disconnect_client(int n,int reason)375 void disconnect_client(int n, int reason)
376 {
377 	if(clients[n]->type!=ST_TCPIP) return;
378 	enet_peer_disconnect(clients[n]->peer, reason);
379 	server::clientdisconnect(n);
380 	clients[n]->type = ST_EMPTY;
381 	clients[n]->peer->data = NULL;
382 	server::deleteinfo(clients[n]->info);
383 	clients[n]->info = NULL;
384 	defformatstring(s)("client (%s) disconnected because: %s", clients[n]->hostname, disc_reasons[reason]);
385 	conoutf("\fr%s", s);
386 	server::srvmsgf(-1, "%s", s);
387 }
388 
kicknonlocalclients(int reason)389 void kicknonlocalclients(int reason)
390 {
391     loopv(clients) if(clients[i]->type==ST_TCPIP) disconnect_client(i, reason);
392 }
393 
process(ENetPacket * packet,int sender,int chan)394 void process(ENetPacket *packet, int sender, int chan)	// sender may be -1
395 {
396 	packetbuf p(packet);
397 	server::parsepacket(sender, chan, p);
398 	if(p.overread()) { disconnect_client(sender, DISC_EOP); return; }
399 }
400 
localclienttoserver(int chan,ENetPacket * packet)401 void localclienttoserver(int chan, ENetPacket *packet)
402 {
403     clientdata *c = NULL;
404     loopv(clients) if(clients[i]->type==ST_LOCAL) { c = clients[i]; break; }
405     if(c) process(packet, c->num, chan);
406 }
407 
delclient(int n)408 void delclient(int n)
409 {
410 	if(clients.inrange(n))
411 	{
412 		if(clients[n]->type==ST_TCPIP) clients[n]->peer->data = NULL;
413 		clients[n]->type = ST_EMPTY;
414 		server::deleteinfo(clients[n]->info);
415 		clients[n]->info = NULL;
416 	}
417 }
418 
addclient(int type)419 int addclient(int type)
420 {
421 	int n = -1;
422 	loopv(clients) if(clients[i]->type==ST_EMPTY)
423 	{
424 		n = i;
425 		break;
426 	}
427 	if(!clients.inrange(n))
428 	{
429 		clientdata *c = new clientdata;
430 		n = c->num = clients.length();
431 		clients.add(c);
432 	}
433 	clients[n]->info = server::newinfo();
434 	clients[n]->type = type;
435 	return n;
436 }
437 
438 #ifndef STANDALONE
439 VARP(autoconnect, 0, 0, 1);
440 extern bool connectedlocally;
441 extern char *lastaddress;
localconnect(bool force)442 void localconnect(bool force)
443 {
444 	if((!connected() || !connectedlocally) && (force || autoconnect))
445 	{
446 		if(lastaddress) delete[] lastaddress;
447 		lastaddress = NULL;
448 		int cn = addclient(ST_LOCAL);
449 		clientdata &c = *clients[cn];
450 		c.peer = NULL;
451 		copystring(c.hostname, "<local>");
452 		conoutf("\fglocal client %d connected", c.num);
453 		client::gameconnect(false);
454 		server::clientconnect(c.num, 0, true);
455 		connectedlocally = true;
456 	}
457 }
458 
localdisconnect()459 void localdisconnect()
460 {
461     loopv(clients) if(clients[i] && clients[i]->type==ST_LOCAL)
462     {
463         clientdata &c = *clients[i];
464         conoutf("\frlocal client %d disconnected", c.num);
465         server::clientdisconnect(c.num, true);
466         c.type = ST_EMPTY;
467         server::deleteinfo(c.info);
468         c.info = NULL;
469 		connectedlocally = false;
470     }
471 }
472 #endif
473 
474 static ENetAddress pongaddr;
475 
sendqueryreply(ucharbuf & p)476 void sendqueryreply(ucharbuf &p)
477 {
478     ENetBuffer buf;
479     buf.data = p.buf;
480     buf.dataLength = p.length();
481     enet_socket_send(pongsock, &pongaddr, &buf, 1);
482 }
483 
sendpongs()484 void sendpongs()		// reply all server info requests
485 {
486 	ENetBuffer buf;
487 	uchar pong[MAXTRANS];
488 	int len;
489 	enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
490 	buf.data = pong;
491 	while(enet_socket_wait(pongsock, &events, 0) >= 0 && events)
492 	{
493 		buf.dataLength = sizeof(pong);
494         len = enet_socket_receive(pongsock, &pongaddr, &buf, 1);
495 		if(len < 0) return;
496         ucharbuf req(pong, len), p(pong, sizeof(pong));
497         p.len += len;
498         server::queryreply(req, p);
499 	}
500 }
501 
502 #ifdef STANDALONE
resolverwait(const char * name,int port,ENetAddress * address)503 bool resolverwait(const char *name, int port, ENetAddress *address)
504 {
505 	return enet_address_set_host(address, name) >= 0;
506 }
507 
connectwithtimeout(ENetSocket sock,const char * hostname,ENetAddress & remoteaddress)508 int connectwithtimeout(ENetSocket sock, const char *hostname, ENetAddress &remoteaddress)
509 {
510 	int result = enet_socket_connect(sock, &remoteaddress);
511 	if(result<0) enet_socket_destroy(sock);
512 	return result;
513 }
514 #endif
515 
516 #ifndef STANDALONE
mastersend(ENetAddress & remoteaddress,const char * hostname,const char * req,ENetAddress * localaddress=NULL)517 ENetSocket mastersend(ENetAddress &remoteaddress, const char *hostname, const char *req, ENetAddress *localaddress = NULL)
518 {
519 	if(remoteaddress.host==ENET_HOST_ANY)
520 	{
521 		conoutf("\falooking up %s:[%d]...", hostname, remoteaddress.port);
522 		if(!resolverwait(hostname, remoteaddress.port, &remoteaddress)) return ENET_SOCKET_NULL;
523 	}
524     ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM);
525     if(sock!=ENET_SOCKET_NULL && localaddress && enet_socket_bind(sock, localaddress) < 0)
526     {
527         enet_socket_destroy(sock);
528         sock = ENET_SOCKET_NULL;
529     }
530 	if(sock==ENET_SOCKET_NULL || connectwithtimeout(sock, hostname, remoteaddress)<0)
531 	{
532 		conoutf(sock==ENET_SOCKET_NULL ? "could not open socket to %s:[%d]" : "could not connect to %s:[%d]", hostname, remoteaddress.port);
533 		return ENET_SOCKET_NULL;
534 	}
535 	ENetBuffer buf;
536 	defformatstring(mget)("%s\n", req);
537 	buf.data = mget;
538 	buf.dataLength = strlen((char *)buf.data);
539 	conoutf("\fasending request to %s:[%d]", hostname, remoteaddress.port);
540 	enet_socket_send(sock, NULL, &buf, 1);
541 	return sock;
542 }
543 
masterreceive(ENetSocket sock,ENetBuffer & buf,int timeout=0)544 bool masterreceive(ENetSocket sock, ENetBuffer &buf, int timeout = 0)
545 {
546 	if(sock==ENET_SOCKET_NULL) return false;
547 	enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
548 	if(enet_socket_wait(sock, &events, timeout) >= 0 && events)
549 	{
550 		int len = enet_socket_receive(sock, NULL, &buf, 1);
551 		if(len<=0)
552 		{
553 			enet_socket_destroy(sock);
554 			return false;
555 		}
556 		buf.data = ((char *)buf.data)+len;
557 		((char*)buf.data)[0] = 0;
558 		buf.dataLength -= len;
559 	}
560 	return true;
561 }
562 
563 #define RETRIEVELIMIT 20000
retrieveservers(uchar * buf,int buflen)564 uchar *retrieveservers(uchar *buf, int buflen)
565 {
566 	buf[0] = '\0';
567 	ENetAddress address = { ENET_HOST_ANY, servermasterport };
568 	ENetSocket sock = mastersend(address, servermaster, "list");
569 	if(sock==ENET_SOCKET_NULL) return buf;
570 	/* only cache this if connection succeeds */
571 	defformatstring(text)("retrieving servers from %s:[%d]", servermaster, address.port);
572 	progress(0, text);
573 
574 	ENetBuffer eb;
575 	eb.data = buf;
576 	eb.dataLength = buflen-1;
577 
578 	int starttime = SDL_GetTicks(), timeout = 0;
579 	while(masterreceive(sock, eb, 250))
580 	{
581 		timeout = SDL_GetTicks() - starttime;
582         progress(min(float(timeout)/RETRIEVELIMIT, 1.0f), text);
583         if(interceptkey(SDLK_ESCAPE)) timeout = RETRIEVELIMIT + 1;
584 		if(timeout > RETRIEVELIMIT)
585 		{
586 			buf[0] = '\0';
587 			enet_socket_destroy(sock);
588 			return buf;
589 		}
590 	}
591 
592 	return buf;
593 }
594 #endif
595 
serverslice()596 void serverslice()	// main server update, called from main loop in sp, or from below in dedicated server
597 {
598     server::serverupdate();
599 
600 	if(!serverhost) return;
601 
602 	sendpongs();
603 
604 	if(servertype >= 2 && totalmillis-laststatus > 60*1000)	// display bandwidth stats, useful for server ops
605 	{
606 		laststatus = totalmillis;
607 		if(bsend || brec || server::numclients())
608 			conoutf("status: %d clients, %.1f send, %.1f rec (K/sec)", server::numclients(), bsend/60.0f/1024, brec/60.0f/1024);
609 		bsend = brec = 0;
610 	}
611 
612 	ENetEvent event;
613     bool serviced = false;
614     while(!serviced)
615     {
616         if(enet_host_check_events(serverhost, &event) <= 0)
617         {
618             if(enet_host_service(serverhost, &event, 0) <= 0) break;
619             serviced = true;
620         }
621 		switch(event.type)
622 		{
623 			case ENET_EVENT_TYPE_CONNECT:
624 			{
625 				int cn = addclient(ST_TCPIP);
626 				clientdata &c = *clients[cn];
627 				c.peer = event.peer;
628 				c.peer->data = &c;
629 				char hn[1024];
630 				copystring(c.hostname, (enet_address_get_host_ip(&c.peer->address, hn, sizeof(hn))==0) ? hn : "unknown");
631 				conoutf("\fgclient connected (%s)", c.hostname);
632 				int reason = server::clientconnect(c.num, c.peer->address.host);
633 				if(reason) disconnect_client(c.num, reason);
634 				break;
635 			}
636 			case ENET_EVENT_TYPE_RECEIVE:
637 			{
638 				brec += event.packet->dataLength;
639 				clientdata *c = (clientdata *)event.peer->data;
640 				if(c) process(event.packet, c->num, event.channelID);
641 				if(event.packet->referenceCount==0) enet_packet_destroy(event.packet);
642 				break;
643 			}
644 			case ENET_EVENT_TYPE_DISCONNECT:
645 			{
646 				clientdata *c = (clientdata *)event.peer->data;
647 				if(!c) break;
648 				conoutf("\frdisconnected client (%s)", c->hostname);
649 				server::clientdisconnect(c->num);
650 				c->type = ST_EMPTY;
651 				event.peer->data = NULL;
652 				server::deleteinfo(c->info);
653 				c->info = NULL;
654 				break;
655 			}
656 			default:
657 				break;
658 		}
659     }
660 	if(server::sendpackets()) enet_host_flush(serverhost);
661 }
662 
663 #ifndef STANDALONE
664 int clockrealbase = 0, clockvirtbase = 0;
clockreset()665 void clockreset() { clockrealbase = SDL_GetTicks(); clockvirtbase = totalmillis; }
666 VARFP(clockerror, 990000, 1000000, 1010000, clockreset());
667 VARFP(clockfix, 0, 0, 1, clockreset());
668 #endif
669 
updatetimer()670 void updatetimer()
671 {
672 #ifdef STANDALONE
673 	int millis = (int)enet_time_get();
674 #else
675 	int millis = SDL_GetTicks() - clockrealbase;
676 	if(clockfix) millis = int(millis*(double(clockerror)/1000000));
677 	millis += clockvirtbase;
678 	if(millis<totalmillis) millis = totalmillis;
679 	extern void limitfps(int &millis, int curmillis);
680 	limitfps(millis, totalmillis);
681 #endif
682 	int elapsed = millis-totalmillis;
683 	if(paused) curtime = 0;
684 	else if(timescale != 100)
685 	{
686 		int scaledtime = elapsed*timescale + timeerr;
687 		curtime = scaledtime/100;
688 		timeerr = scaledtime%100;
689 		if(curtime>200) curtime = 200;
690 	}
691 	else
692 	{
693 		curtime = elapsed + timeerr;
694 		timeerr = 0;
695 	}
696 	lastmillis += curtime;
697 	totalmillis = millis;
698 }
699 
serverloop()700 void serverloop()
701 {
702 	#ifdef WIN32
703 	SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
704 	#endif
705 	conoutf("\fgdedicated server started, waiting for clients... [Ctrl-C to exit]");
706 	for(;;)
707 	{
708 		int _lastmillis = lastmillis;
709 		lastmillis = totalmillis = (int)enet_time_get();
710 		curtime = lastmillis-_lastmillis;
711 
712 #ifdef MASTERSERVER
713 		checkmaster();
714 #endif
715 		serverslice();
716 #ifdef IRC
717 		ircslice();
718 #endif
719 
720 		if((int)enet_time_get()-lastmillis <= 0)
721 		{
722 			#ifdef WIN32
723 			Sleep(1);
724 			#else
725 			usleep(1000);
726 			#endif
727 		}
728 	}
729 	exit(EXIT_SUCCESS);
730 }
731 
setupserver()732 void setupserver()
733 {
734     server::changemap(load && *load ? load : NULL);
735 
736     if(!servertype) return;
737 
738 #ifdef MASTERSERVER
739     setupmaster();
740 #endif
741 
742 	conoutf("init: server (%s:%d)", *serverip ? serverip : "*", serverport);
743 	ENetAddress address = { ENET_HOST_ANY, serverport };
744 	if(*serverip)
745 	{
746 		if(enet_address_set_host(&address, serverip) < 0) conoutf("\frWARNING: server address not resolved");
747 	}
748 	serverhost = enet_host_create(&address, serverclients + server::reserveclients(), 0, serveruprate);
749 	if(!serverhost)
750 	{
751 		conoutf("\frcould not create server socket");
752 #ifndef STANDALONE
753 		setvar("servertype", 0);
754 #endif
755 		return;
756 	}
757 	loopi(serverclients) serverhost->peers[i].data = NULL;
758 
759 	address.port = serverqueryport;
760     pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
761     if(pongsock != ENET_SOCKET_NULL && enet_socket_bind(pongsock, &address) < 0)
762     {
763         enet_socket_destroy(pongsock);
764         pongsock = ENET_SOCKET_NULL;
765     }
766 	if(pongsock == ENET_SOCKET_NULL)
767 	{
768 		conoutf("\frcould not create server info socket, publicity disabled");
769 #ifndef STANDALONE
770 		setvar("servertype", 1);
771 #endif
772 		}
773 	else
774 	{
775 		enet_socket_set_option(pongsock, ENET_SOCKOPT_NONBLOCK, 1);
776 	}
777 
778 	if(verbose) conoutf("\fggame server started");
779 
780 #ifndef STANDALONE
781 	if(servertype >= 3) serverloop();
782 #endif
783 }
784 
initgame()785 void initgame()
786 {
787 	server::start();
788 #ifndef STANDALONE
789 	game::start();
790 #endif
791 	loopv(gameargs)
792 	{
793 #ifndef STANDALONE
794 		if(game::clientoption(gameargs[i])) continue;
795 #endif
796 		if(server::serveroption(gameargs[i])) continue;
797 		conoutf("\frunknown command-line option: %s", gameargs[i]);
798 	}
799 	execfile("servinit.cfg", false);
800     setupserver();
801 }
802 
803 VAR(hasoctapaks, 1, 0, 0); // mega hack; try to find Cube 2, done after our own data so as to not clobber stuff
804 SVARP(octadir, "");//, if(!hasoctapaks) trytofindocta(false););
805 
serveroption(char * opt)806 bool serveroption(char *opt)
807 {
808 	switch(opt[1])
809 	{
810 		case 'k': kidmode = atoi(opt+2); return true;
811 		case 'h': printf("set home directory: %s\n", &opt[2]); sethomedir(&opt[2]); return true;
812 		case 'o': setsvar("octadir", &opt[2]); return true;
813 		case 'p': printf("add package directory: %s\n", &opt[2]); addpackagedir(&opt[2]); return true;
814 		case 'v': setvar("verbose", atoi(opt+2)); return true;
815 		case 's':
816 		{
817 			switch(opt[2])
818 			{
819 				case 'u': setvar("serveruprate", atoi(opt+3)); return true;
820 				case 'c': setvar("serverclients", atoi(opt+3)); return true;
821 				case 'i': setsvar("serverip", opt+3); return true;
822 				case 'm': setsvar("servermaster", opt+3); return true;
823 				case 'l': load = opt+3; return true;
824 #ifndef STANDALONE
825 				case 's': setvar("servertype", atoi(opt+3)); return true;
826 #endif
827 				case 'p': setvar("serverport", atoi(opt+3)); return true;
828 				case 'q': setvar("serverqueryport", atoi(opt+3)); return true;
829 				case 'a': setvar("servermasterport", atoi(opt+3)); return true;
830 			}
831 		}
832 #ifdef MASTERSERVER
833 		case 'm':
834 		{
835 			switch(opt[2])
836 			{
837 				case 's': setvar("masterserver", atoi(opt+3)); return true;
838 				case 'i': setsvar("masterip", opt+3); return true;
839 				case 'p': setvar("masterport", atoi(opt+3)); return true;
840 				default: return false;
841 			}
842 			return false;
843 		}
844 #endif
845 		default: return false;
846 	}
847 	return false;
848 }
849 
findoctadir(const char * name,bool fallback)850 bool findoctadir(const char *name, bool fallback)
851 {
852 	mkstring(s); copystring(s, name); path(s);
853 	defformatstring(octalogo)("%s/data/default_map_settings.cfg", s);
854 	if(fileexists(findfile(octalogo, "r"), "r"))
855 	{
856 		conoutf("\fgfound octa directory: %s", s);
857 		defformatstring(octadata)("%s/data", s);
858 		defformatstring(octapaks)("%s/packages", s);
859 		addpackagedir(s, PACKAGEDIR_OCTA);
860 		addpackagedir(octadata, PACKAGEDIR_OCTA);
861 		addpackagedir(octapaks, PACKAGEDIR_OCTA);
862 		hasoctapaks = fallback ? 1 : 2;
863 		return true;
864 	}
865 	return false;
866 }
867 
trytofindocta(bool fallback)868 void trytofindocta(bool fallback)
869 {
870 	if(!octadir || !*octadir)
871 	{
872 		const char *dir = getenv("OCTA_DIR");
873 		if(dir && *dir) setsvar("octadir", dir);
874 	}
875 	if((!octadir || !*octadir || !findoctadir(octadir, false)) && fallback)
876 	{ // user hasn't specifically set it, try some common locations alongside our folder
877 #if defined(WIN32)
878 		mkstring(dir);
879 		if(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILESX86, NULL, 0, dir) == S_OK)
880 		{
881 			defformatstring(s)("%s\\Sauerbraten", dir);
882 			if(findoctadir(s, true)) return;
883 		}
884 #elif defined(__APPLE__)
885 		#warning Please put MacOSX specific code here to find the Sauerbraten directory.
886 #endif
887 		const char *tryoctadirs[4] = { // by no means an accurate or definitive list either..
888 			"../Sauerbraten", "../sauerbraten", "../sauer",
889 #if defined(WIN32)
890 			"/Program Files/Sauerbraten"
891 #elif defined(__APPLE__)
892 			"/Volumes/Play/sauerbraten"
893 #else
894 			"/usr/games/sauerbraten"
895 #endif
896 		};
897 		loopi(4) if(findoctadir(tryoctadirs[i], true)) return;
898 	}
899 }
900 
setlocations(bool wanthome)901 void setlocations(bool wanthome)
902 {
903 	addpackagedir("data");
904 	if(wanthome)
905 	{
906 #if defined(WIN32)
907 		#ifndef __GNUC__ // not supported by mingw
908 		OSVERSIONINFO ver;
909 		if(GetVersionEx((OSVERSIONINFO *)&ver))
910 		{
911 			if(ver.dwMajorVersion >= 6)
912 			{
913 				char *dir = NULL;
914 				if(SHGetKnownFolderPath(FOLDERID_SavedGames, 0, NULL, dir) == S_OK)
915 				{
916 					defformatstring(s)("%s\\Blood Frontier", dir);
917 					sethomedir(s);
918 					DELETEP(dir);
919 					return;
920 				}
921 			}
922 			else
923 			{
924 				mkstring(dir);
925 				if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, dir) == S_OK)
926 				{
927 					defformatstring(s)("%s\\My Games\\Blood Frontier", dir);
928 					sethomedir(s);
929 					return;
930 				}
931 			}
932 		}
933 		#else
934 		mkstring(dir);
935 		if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, dir) == S_OK)
936 		{
937 			defformatstring(s)("%s\\My Games\\Blood Frontier", dir);
938 			sethomedir(s);
939 			return;
940 		}
941 		#endif
942 #elif defined(__APPLE__)
943 		#warning Please put MacOSX specific code here to find the personal directory.
944 #else
945 		const char *dir = getenv("HOME");
946 		if(dir && *dir)
947 		{
948 			defformatstring(s)("%s/.bloodfrontier", dir);
949 			sethomedir(s);
950 			return;
951 		}
952 #endif
953 		sethomedir("home");
954 	}
955 }
956 
957 #ifdef STANDALONE
main(int argc,char * argv[])958 int main(int argc, char* argv[])
959 {
960     setlocations(false);
961 	for(int i = 1; i<argc; i++) if(argv[i][0]!='-' || !serveroption(argv[i])) gameargs.add(argv[i]);
962 	if(enet_initialize()<0) fatal("Unable to initialise network module");
963 	atexit(enet_deinitialize);
964 	atexit(cleanupserver);
965 	enet_time_set(0);
966 	initgame();
967 	trytofindocta();
968 	serverloop();
969 	return 0;
970 }
971 #endif
972 
973