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 (<ime);
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