Initial sauer
[SauerbratenRemote.git] / src / engine / server.cpp
blob05d73ff30bf28dcecefcc0dbc602bb96fb840a63
1 // server.cpp: little more than enhanced multicaster
2 // runs dedicated or as client coroutine
4 #include "pch.h"
6 #ifdef STANDALONE
7 #include "cube.h"
8 #include "iengine.h"
9 #include "igame.h"
10 void localservertoclient(int chan, uchar *buf, int len) {}
11 void fatal(const char *s, ...)
13 void cleanupserver();
14 cleanupserver();
15 s_sprintfdlv(msg,s,s);
16 printf("servererror: %s\n", msg);
17 exit(EXIT_FAILURE);
19 #else
20 #include "engine.h"
21 #endif
23 igameclient *cl = NULL;
24 igameserver *sv = NULL;
25 iclientcom *cc = NULL;
26 icliententities *et = NULL;
28 hashtable<const char *, igame *> *gamereg = NULL;
30 vector<char *> gameargs;
32 void registergame(const char *name, igame *ig)
34 if(!gamereg) gamereg = new hashtable<const char *, igame *>;
35 (*gamereg)[name] = ig;
38 void initgame(const char *game)
40 igame **ig = gamereg->access(game);
41 if(!ig) fatal("cannot start game module: ", game);
42 sv = (*ig)->newserver();
43 cl = (*ig)->newclient();
44 if(cl)
46 cc = cl->getcom();
47 et = cl->getents();
48 cl->initclient();
50 loopv(gameargs)
52 if(!cl || !cl->clientoption(gameargs[i]))
54 if(!sv->serveroption(gameargs[i]))
55 #ifdef STANDALONE
56 printf("unknown command-line option: %s\n", gameargs[i]);
57 #else
58 conoutf("unknown command-line option: %s", gameargs[i]);
59 #endif
64 // all network traffic is in 32bit ints, which are then compressed using the following simple scheme (assumes that most values are small).
66 void putint(ucharbuf &p, int n)
68 if(n<128 && n>-127) p.put(n);
69 else if(n<0x8000 && n>=-0x8000) { p.put(0x80); p.put(n); p.put(n>>8); }
70 else { p.put(0x81); p.put(n); p.put(n>>8); p.put(n>>16); p.put(n>>24); }
73 int getint(ucharbuf &p)
75 int c = (char)p.get();
76 if(c==-128) { int n = p.get(); n |= char(p.get())<<8; return n; }
77 else if(c==-127) { int n = p.get(); n |= p.get()<<8; n |= p.get()<<16; return n|(p.get()<<24); }
78 else return c;
81 // much smaller encoding for unsigned integers up to 28 bits, but can handle signed
82 void putuint(ucharbuf &p, int n)
84 if(n < 0 || n >= (1<<21))
86 p.put(0x80 | (n & 0x7F));
87 p.put(0x80 | ((n >> 7) & 0x7F));
88 p.put(0x80 | ((n >> 14) & 0x7F));
89 p.put(n >> 21);
91 else if(n < (1<<7)) p.put(n);
92 else if(n < (1<<14))
94 p.put(0x80 | (n & 0x7F));
95 p.put(n >> 7);
97 else
99 p.put(0x80 | (n & 0x7F));
100 p.put(0x80 | ((n >> 7) & 0x7F));
101 p.put(n >> 14);
105 int getuint(ucharbuf &p)
107 int n = p.get();
108 if(n & 0x80)
110 n += (p.get() << 7) - 0x80;
111 if(n & (1<<14)) n += (p.get() << 14) - (1<<14);
112 if(n & (1<<21)) n += (p.get() << 21) - (1<<21);
113 if(n & (1<<28)) n |= 0xF0000000;
115 return n;
118 void sendstring(const char *t, ucharbuf &p)
120 while(*t) putint(p, *t++);
121 putint(p, 0);
124 void getstring(char *text, ucharbuf &p, int len)
126 char *t = text;
129 if(t>=&text[len]) { text[len-1] = 0; return; }
130 if(!p.remaining()) { *t = 0; return; }
131 *t = getint(p);
133 while(*t++);
136 void filtertext(char *dst, const char *src, bool whitespace, int len)
138 for(int c = *src; c; c = *++src)
140 switch(c)
142 case '\f': ++src; continue;
144 if(isspace(c) ? whitespace : isprint(c))
146 *dst++ = c;
147 if(!--len) break;
150 *dst = '\0';
153 enum { ST_EMPTY, ST_LOCAL, ST_TCPIP };
155 struct client // server side version of "dynent" type
157 int type;
158 int num;
159 ENetPeer *peer;
160 string hostname;
161 void *info;
164 vector<client *> clients;
166 ENetHost *serverhost = NULL;
167 size_t bsend = 0, brec = 0;
168 int laststatus = 0;
169 ENetSocket pongsock = ENET_SOCKET_NULL;
171 void cleanupserver()
173 if(serverhost) enet_host_destroy(serverhost);
176 void sendfile(int cn, int chan, FILE *file, const char *format, ...)
178 #ifndef STANDALONE
179 extern ENetHost *clienthost;
180 #endif
181 if(cn < 0)
183 #ifndef STANDALONE
184 if(!clienthost || clienthost->peers[0].state != ENET_PEER_STATE_CONNECTED)
185 #endif
186 return;
188 else if(cn >= clients.length() || clients[cn]->type != ST_TCPIP) return;
190 fseek(file, 0, SEEK_END);
191 int len = ftell(file);
192 bool reliable = false;
193 if(*format=='r') { reliable = true; ++format; }
194 ENetPacket *packet = enet_packet_create(NULL, MAXTRANS+len, ENET_PACKET_FLAG_RELIABLE);
195 rewind(file);
197 ucharbuf p(packet->data, packet->dataLength);
198 va_list args;
199 va_start(args, format);
200 while(*format) switch(*format++)
202 case 'i':
204 int n = isdigit(*format) ? *format++-'0' : 1;
205 loopi(n) putint(p, va_arg(args, int));
206 break;
208 case 's': sendstring(va_arg(args, const char *), p); break;
209 case 'l': putint(p, len); break;
211 va_end(args);
212 enet_packet_resize(packet, p.length()+len);
214 fread(&packet->data[p.length()], 1, len, file);
215 enet_packet_resize(packet, p.length()+len);
217 if(cn >= 0) enet_peer_send(clients[cn]->peer, chan, packet);
218 #ifndef STANDALONE
219 else enet_peer_send(&clienthost->peers[0], chan, packet);
220 #endif
222 if(!packet->referenceCount) enet_packet_destroy(packet);
225 void process(ENetPacket *packet, int sender, int chan);
226 //void disconnect_client(int n, int reason);
228 void *getinfo(int i) { return !clients.inrange(i) || clients[i]->type==ST_EMPTY ? NULL : clients[i]->info; }
229 int getnumclients() { return clients.length(); }
230 uint getclientip(int n) { return clients.inrange(n) && clients[n]->type==ST_TCPIP ? clients[n]->peer->address.host : 0; }
232 void sendpacket(int n, int chan, ENetPacket *packet, int exclude)
234 if(n<0)
236 sv->recordpacket(chan, packet->data, packet->dataLength);
237 loopv(clients) if(i!=exclude) sendpacket(i, chan, packet);
238 return;
240 switch(clients[n]->type)
242 case ST_TCPIP:
244 enet_peer_send(clients[n]->peer, chan, packet);
245 bsend += packet->dataLength;
246 break;
249 case ST_LOCAL:
250 localservertoclient(chan, packet->data, (int)packet->dataLength);
251 break;
255 void sendf(int cn, int chan, const char *format, ...)
257 int exclude = -1;
258 bool reliable = false;
259 if(*format=='r') { reliable = true; ++format; }
260 ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
261 ucharbuf p(packet->data, packet->dataLength);
262 va_list args;
263 va_start(args, format);
264 while(*format) switch(*format++)
266 case 'x':
267 exclude = va_arg(args, int);
268 break;
270 case 'v':
272 int n = va_arg(args, int);
273 int *v = va_arg(args, int *);
274 loopi(n) putint(p, v[i]);
275 break;
278 case 'i':
280 int n = isdigit(*format) ? *format++-'0' : 1;
281 loopi(n) putint(p, va_arg(args, int));
282 break;
284 case 's': sendstring(va_arg(args, const char *), p); break;
285 case 'm':
287 int n = va_arg(args, int);
288 enet_packet_resize(packet, packet->dataLength+n);
289 p.buf = packet->data;
290 p.maxlen += n;
291 p.put(va_arg(args, uchar *), n);
292 break;
295 va_end(args);
296 enet_packet_resize(packet, p.length());
297 sendpacket(cn, chan, packet, exclude);
298 if(packet->referenceCount==0) enet_packet_destroy(packet);
301 const char *disc_reasons[] = { "normal", "end of packet", "client num", "kicked/banned", "tag type", "ip is banned", "server is in private mode", "server FULL (maxclients)" };
303 void disconnect_client(int n, int reason)
305 if(clients[n]->type!=ST_TCPIP) return;
306 s_sprintfd(s)("client (%s) disconnected because: %s\n", clients[n]->hostname, disc_reasons[reason]);
307 puts(s);
308 enet_peer_disconnect(clients[n]->peer, reason);
309 sv->clientdisconnect(n);
310 clients[n]->type = ST_EMPTY;
311 clients[n]->peer->data = NULL;
312 sv->deleteinfo(clients[n]->info);
313 clients[n]->info = NULL;
314 sv->sendservmsg(s);
317 void process(ENetPacket *packet, int sender, int chan) // sender may be -1
319 ucharbuf p(packet->data, (int)packet->dataLength);
320 sv->parsepacket(sender, chan, (packet->flags&ENET_PACKET_FLAG_RELIABLE)!=0, p);
321 if(p.overread()) { disconnect_client(sender, DISC_EOP); return; }
324 void send_welcome(int n)
326 ENetPacket *packet = enet_packet_create (NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
327 ucharbuf p(packet->data, packet->dataLength);
328 int chan = sv->welcomepacket(p, n);
329 enet_packet_resize(packet, p.length());
330 sendpacket(n, chan, packet);
331 if(packet->referenceCount==0) enet_packet_destroy(packet);
334 void localclienttoserver(int chan, ENetPacket *packet)
336 process(packet, 0, chan);
337 if(packet->referenceCount==0) enet_packet_destroy(packet);
340 client &addclient()
342 loopv(clients) if(clients[i]->type==ST_EMPTY)
344 clients[i]->info = sv->newinfo();
345 return *clients[i];
347 client *c = new client;
348 c->num = clients.length();
349 c->info = sv->newinfo();
350 clients.add(c);
351 return *c;
354 int localclients = 0, nonlocalclients = 0;
356 bool hasnonlocalclients() { return nonlocalclients!=0; }
357 bool haslocalclients() { return localclients!=0; }
359 void sendpongs() // reply all server info requests
361 ENetBuffer buf;
362 ENetAddress addr;
363 uchar pong[MAXTRANS];
364 int len;
365 enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
366 buf.data = pong;
367 while(enet_socket_wait(pongsock, &events, 0) >= 0 && events)
369 buf.dataLength = sizeof(pong);
370 len = enet_socket_receive(pongsock, &addr, &buf, 1);
371 if(len < 0) return;
372 ucharbuf p(&pong[len], sizeof(pong)-len);
373 sv->serverinforeply(p);
374 buf.dataLength = len + p.length();
375 enet_socket_send(pongsock, &addr, &buf, 1);
379 #ifdef STANDALONE
380 bool resolverwait(const char *name, ENetAddress *address)
382 return enet_address_set_host(address, name) >= 0;
385 int connectwithtimeout(ENetSocket sock, const char *hostname, ENetAddress &remoteaddress)
387 int result = enet_socket_connect(sock, &remoteaddress);
388 if(result<0) enet_socket_destroy(sock);
389 return result;
391 #endif
393 ENetSocket httpgetsend(ENetAddress &remoteaddress, const char *hostname, const char *req, const char *ref, const char *agent, ENetAddress *localaddress = NULL)
395 if(remoteaddress.host==ENET_HOST_ANY)
397 #ifdef STANDALONE
398 printf("looking up %s...\n", hostname);
399 #endif
400 if(!resolverwait(hostname, &remoteaddress)) return ENET_SOCKET_NULL;
402 ENetSocket sock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, localaddress);
403 if(sock==ENET_SOCKET_NULL || connectwithtimeout(sock, hostname, remoteaddress)<0)
405 #ifdef STANDALONE
406 printf(sock==ENET_SOCKET_NULL ? "could not open socket\n" : "could not connect\n");
407 #endif
408 return ENET_SOCKET_NULL;
410 ENetBuffer buf;
411 s_sprintfd(httpget)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req, hostname, ref, agent);
412 buf.data = httpget;
413 buf.dataLength = strlen((char *)buf.data);
414 #ifdef STANDALONE
415 printf("sending request to %s...\n", hostname);
416 #endif
417 enet_socket_send(sock, NULL, &buf, 1);
418 return sock;
421 bool httpgetreceive(ENetSocket sock, ENetBuffer &buf, int timeout = 0)
423 if(sock==ENET_SOCKET_NULL) return false;
424 enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
425 if(enet_socket_wait(sock, &events, timeout) >= 0 && events)
427 int len = enet_socket_receive(sock, NULL, &buf, 1);
428 if(len<=0)
430 enet_socket_destroy(sock);
431 return false;
433 buf.data = ((char *)buf.data)+len;
434 ((char*)buf.data)[0] = 0;
435 buf.dataLength -= len;
437 return true;
440 uchar *stripheader(uchar *b)
442 char *s = strstr((char *)b, "\n\r\n");
443 if(!s) s = strstr((char *)b, "\n\n");
444 return s ? (uchar *)s : b;
447 ENetSocket mssock = ENET_SOCKET_NULL;
448 ENetAddress msaddress = { ENET_HOST_ANY, ENET_PORT_ANY };
449 ENetAddress masterserver = { ENET_HOST_ANY, 80 };
450 int lastupdatemaster = 0;
451 string masterbase;
452 string masterpath;
453 uchar masterrep[MAXTRANS];
454 ENetBuffer masterb;
456 void updatemasterserver()
458 s_sprintfd(path)("%sregister.do?action=add", masterpath);
459 if(mssock!=ENET_SOCKET_NULL) enet_socket_destroy(mssock);
460 mssock = httpgetsend(masterserver, masterbase, path, sv->servername(), sv->servername(), &msaddress);
461 masterrep[0] = 0;
462 masterb.data = masterrep;
463 masterb.dataLength = MAXTRANS-1;
466 void checkmasterreply()
468 if(mssock!=ENET_SOCKET_NULL && !httpgetreceive(mssock, masterb))
470 mssock = ENET_SOCKET_NULL;
471 printf("masterserver reply: %s\n", stripheader(masterrep));
475 #ifndef STANDALONE
477 #define RETRIEVELIMIT 20000
479 uchar *retrieveservers(uchar *buf, int buflen)
481 buf[0] = '\0';
483 s_sprintfd(path)("%sretrieve.do?item=list", masterpath);
484 ENetAddress address = masterserver;
485 ENetSocket sock = httpgetsend(address, masterbase, path, sv->servername(), sv->servername());
486 if(sock==ENET_SOCKET_NULL) return buf;
487 /* only cache this if connection succeeds */
488 masterserver = address;
490 s_sprintfd(text)("retrieving servers from %s... (esc to abort)", masterbase);
491 show_out_of_renderloop_progress(0, text);
493 ENetBuffer eb;
494 eb.data = buf;
495 eb.dataLength = buflen-1;
497 int starttime = SDL_GetTicks(), timeout = 0;
498 while(httpgetreceive(sock, eb, 250))
500 timeout = SDL_GetTicks() - starttime;
501 show_out_of_renderloop_progress(min(float(timeout)/RETRIEVELIMIT, 1), text);
502 SDL_Event event;
503 while(SDL_PollEvent(&event))
505 if(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) timeout = RETRIEVELIMIT + 1;
507 if(timeout > RETRIEVELIMIT)
509 buf[0] = '\0';
510 enet_socket_destroy(sock);
511 return buf;
515 return stripheader(buf);
517 #endif
519 #define DEFAULTCLIENTS 6
521 int uprate = 0, maxclients = DEFAULTCLIENTS;
522 const char *ip = "", *master = NULL;
523 const char *game = "fps";
525 #ifdef STANDALONE
526 int lastmillis = 0, totalmillis = 0;
527 #endif
529 void serverslice(uint timeout) // main server update, called from main loop in sp, or from below in dedicated server
531 localclients = nonlocalclients = 0;
532 loopv(clients) switch(clients[i]->type)
534 case ST_LOCAL: localclients++; break;
535 case ST_TCPIP: nonlocalclients++; break;
538 if(!serverhost)
540 sv->serverupdate(lastmillis, totalmillis);
541 return;
544 // below is network only
546 lastmillis = totalmillis = (int)enet_time_get();
547 sv->serverupdate(lastmillis, totalmillis);
549 sendpongs();
551 if(*masterpath) checkmasterreply();
553 if(totalmillis-lastupdatemaster>60*60*1000 && *masterpath) // send alive signal to masterserver every hour of uptime
555 updatemasterserver();
556 lastupdatemaster = totalmillis;
559 if(totalmillis-laststatus>60*1000) // display bandwidth stats, useful for server ops
561 laststatus = totalmillis;
562 if(nonlocalclients || bsend || brec) printf("status: %d remote clients, %.1f send, %.1f rec (K/sec)\n", nonlocalclients, bsend/60.0f/1024, brec/60.0f/1024);
563 bsend = brec = 0;
566 ENetEvent event;
567 bool serviced = false;
568 while(!serviced)
570 if(enet_host_check_events(serverhost, &event) <= 0)
572 if(enet_host_service(serverhost, &event, timeout) <= 0) break;
573 serviced = true;
575 switch(event.type)
577 case ENET_EVENT_TYPE_CONNECT:
579 client &c = addclient();
580 c.type = ST_TCPIP;
581 c.peer = event.peer;
582 c.peer->data = &c;
583 char hn[1024];
584 s_strcpy(c.hostname, (enet_address_get_host_ip(&c.peer->address, hn, sizeof(hn))==0) ? hn : "unknown");
585 printf("client connected (%s)\n", c.hostname);
586 int reason = DISC_MAXCLIENTS;
587 if(nonlocalclients<maxclients && !(reason = sv->clientconnect(c.num, c.peer->address.host)))
589 nonlocalclients++;
590 send_welcome(c.num);
592 else disconnect_client(c.num, reason);
593 break;
595 case ENET_EVENT_TYPE_RECEIVE:
597 brec += event.packet->dataLength;
598 client *c = (client *)event.peer->data;
599 if(c) process(event.packet, c->num, event.channelID);
600 if(event.packet->referenceCount==0) enet_packet_destroy(event.packet);
601 break;
603 case ENET_EVENT_TYPE_DISCONNECT:
605 client *c = (client *)event.peer->data;
606 if(!c) break;
607 printf("disconnected client (%s)\n", c->hostname);
608 sv->clientdisconnect(c->num);
609 nonlocalclients--;
610 c->type = ST_EMPTY;
611 event.peer->data = NULL;
612 sv->deleteinfo(c->info);
613 c->info = NULL;
614 break;
616 default:
617 break;
620 if(sv->sendpackets()) enet_host_flush(serverhost);
623 void localdisconnect()
625 loopv(clients) if(clients[i]->type==ST_LOCAL)
627 sv->localdisconnect(i);
628 localclients--;
629 clients[i]->type = ST_EMPTY;
630 sv->deleteinfo(clients[i]->info);
631 clients[i]->info = NULL;
635 void localconnect()
637 client &c = addclient();
638 c.type = ST_LOCAL;
639 s_strcpy(c.hostname, "local");
640 localclients++;
641 sv->localconnect(c.num);
642 send_welcome(c.num);
645 void initserver(bool dedicated)
647 initgame(game);
649 if(!master) master = sv->getdefaultmaster();
650 const char *mid = strstr(master, "/");
651 if(!mid) mid = master;
652 s_strcpy(masterpath, mid);
653 s_strncpy(masterbase, master, mid-master+1);
655 if(dedicated)
657 ENetAddress address = { ENET_HOST_ANY, sv->serverport() };
658 if(*ip)
660 if(enet_address_set_host(&address, ip)<0) printf("WARNING: server ip not resolved");
661 else msaddress.host = address.host;
663 serverhost = enet_host_create(&address, maxclients+1, 0, uprate);
664 if(!serverhost) fatal("could not create server host");
665 loopi(maxclients) serverhost->peers[i].data = NULL;
666 address.port = sv->serverinfoport();
667 pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, &address);
668 if(pongsock == ENET_SOCKET_NULL) fatal("could not create server info socket");
669 else enet_socket_set_option(pongsock, ENET_SOCKOPT_NONBLOCK, 1);
672 sv->serverinit();
674 if(dedicated) // do not return, this becomes main loop
676 #ifdef WIN32
677 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
678 #endif
679 printf("dedicated server started, waiting for clients...\nCtrl-C to exit\n\n");
680 atexit(enet_deinitialize);
681 atexit(cleanupserver);
682 enet_time_set(0);
683 if(*masterpath) updatemasterserver();
684 for(;;) serverslice(5);
688 bool serveroption(char *opt)
690 switch(opt[1])
692 case 'u': uprate = atoi(opt+2); return true;
693 case 'c':
695 int clients = atoi(opt+2);
696 if(clients > 0) maxclients = min(clients, MAXCLIENTS);
697 else maxclients = DEFAULTCLIENTS;
698 return true;
700 case 'i': ip = opt+2; return true;
701 case 'm': master = opt+2; return true;
702 case 'g': game = opt+2; return true;
703 default: return false;
707 #ifdef STANDALONE
708 int main(int argc, char* argv[])
710 for(int i = 1; i<argc; i++) if(argv[i][0]!='-' || !serveroption(argv[i])) gameargs.add(argv[i]);
711 if(enet_initialize()<0) fatal("Unable to initialise network module");
712 initserver(true);
713 return 0;
715 #endif