1 // server.cpp: little more than enhanced multicaster
2 // runs dedicated or as client coroutine
10 void localservertoclient(int chan
, uchar
*buf
, int len
) {}
11 void fatal(const char *s
, ...)
15 s_sprintfdlv(msg
,s
,s
);
16 printf("servererror: %s\n", msg
);
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();
52 if(!cl
|| !cl
->clientoption(gameargs
[i
]))
54 if(!sv
->serveroption(gameargs
[i
]))
56 printf("unknown command-line option: %s\n", gameargs
[i
]);
58 conoutf("unknown command-line option: %s", gameargs
[i
]);
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); }
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));
91 else if(n
< (1<<7)) p
.put(n
);
94 p
.put(0x80 | (n
& 0x7F));
99 p
.put(0x80 | (n
& 0x7F));
100 p
.put(0x80 | ((n
>> 7) & 0x7F));
105 int getuint(ucharbuf
&p
)
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;
118 void sendstring(const char *t
, ucharbuf
&p
)
120 while(*t
) putint(p
, *t
++);
124 void getstring(char *text
, ucharbuf
&p
, int len
)
129 if(t
>=&text
[len
]) { text
[len
-1] = 0; return; }
130 if(!p
.remaining()) { *t
= 0; return; }
136 void filtertext(char *dst
, const char *src
, bool whitespace
, int len
)
138 for(int c
= *src
; c
; c
= *++src
)
142 case '\f': ++src
; continue;
144 if(isspace(c
) ? whitespace
: isprint(c
))
153 enum { ST_EMPTY
, ST_LOCAL
, ST_TCPIP
};
155 struct client
// server side version of "dynent" type
164 vector
<client
*> clients
;
166 ENetHost
*serverhost
= NULL
;
167 size_t bsend
= 0, brec
= 0;
169 ENetSocket pongsock
= ENET_SOCKET_NULL
;
173 if(serverhost
) enet_host_destroy(serverhost
);
176 void sendfile(int cn
, int chan
, FILE *file
, const char *format
, ...)
179 extern ENetHost
*clienthost
;
184 if(!clienthost
|| clienthost
->peers
[0].state
!= ENET_PEER_STATE_CONNECTED
)
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
);
197 ucharbuf
p(packet
->data
, packet
->dataLength
);
199 va_start(args
, format
);
200 while(*format
) switch(*format
++)
204 int n
= isdigit(*format
) ? *format
++-'0' : 1;
205 loopi(n
) putint(p
, va_arg(args
, int));
208 case 's': sendstring(va_arg(args
, const char *), p
); break;
209 case 'l': putint(p
, len
); break;
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
);
219 else enet_peer_send(&clienthost
->peers
[0], chan
, packet
);
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
)
236 sv
->recordpacket(chan
, packet
->data
, packet
->dataLength
);
237 loopv(clients
) if(i
!=exclude
) sendpacket(i
, chan
, packet
);
240 switch(clients
[n
]->type
)
244 enet_peer_send(clients
[n
]->peer
, chan
, packet
);
245 bsend
+= packet
->dataLength
;
250 localservertoclient(chan
, packet
->data
, (int)packet
->dataLength
);
255 void sendf(int cn
, int chan
, const char *format
, ...)
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
);
263 va_start(args
, format
);
264 while(*format
) switch(*format
++)
267 exclude
= va_arg(args
, int);
272 int n
= va_arg(args
, int);
273 int *v
= va_arg(args
, int *);
274 loopi(n
) putint(p
, v
[i
]);
280 int n
= isdigit(*format
) ? *format
++-'0' : 1;
281 loopi(n
) putint(p
, va_arg(args
, int));
284 case 's': sendstring(va_arg(args
, const char *), p
); break;
287 int n
= va_arg(args
, int);
288 enet_packet_resize(packet
, packet
->dataLength
+n
);
289 p
.buf
= packet
->data
;
291 p
.put(va_arg(args
, uchar
*), n
);
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
]);
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
;
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
);
342 loopv(clients
) if(clients
[i
]->type
==ST_EMPTY
)
344 clients
[i
]->info
= sv
->newinfo();
347 client
*c
= new client
;
348 c
->num
= clients
.length();
349 c
->info
= sv
->newinfo();
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
363 uchar pong
[MAXTRANS
];
365 enet_uint32 events
= ENET_SOCKET_WAIT_RECEIVE
;
367 while(enet_socket_wait(pongsock
, &events
, 0) >= 0 && events
)
369 buf
.dataLength
= sizeof(pong
);
370 len
= enet_socket_receive(pongsock
, &addr
, &buf
, 1);
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);
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
);
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
)
398 printf("looking up %s...\n", hostname
);
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)
406 printf(sock
==ENET_SOCKET_NULL
? "could not open socket\n" : "could not connect\n");
408 return ENET_SOCKET_NULL
;
411 s_sprintfd(httpget
)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req
, hostname
, ref
, agent
);
413 buf
.dataLength
= strlen((char *)buf
.data
);
415 printf("sending request to %s...\n", hostname
);
417 enet_socket_send(sock
, NULL
, &buf
, 1);
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);
430 enet_socket_destroy(sock
);
433 buf
.data
= ((char *)buf
.data
)+len
;
434 ((char*)buf
.data
)[0] = 0;
435 buf
.dataLength
-= len
;
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;
453 uchar masterrep
[MAXTRANS
];
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
);
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
));
477 #define RETRIEVELIMIT 20000
479 uchar
*retrieveservers(uchar
*buf
, int buflen
)
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
);
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
);
503 while(SDL_PollEvent(&event
))
505 if(event
.type
== SDL_KEYDOWN
&& event
.key
.keysym
.sym
== SDLK_ESCAPE
) timeout
= RETRIEVELIMIT
+ 1;
507 if(timeout
> RETRIEVELIMIT
)
510 enet_socket_destroy(sock
);
515 return stripheader(buf
);
519 #define DEFAULTCLIENTS 6
521 int uprate
= 0, maxclients
= DEFAULTCLIENTS
;
522 const char *ip
= "", *master
= NULL
;
523 const char *game
= "fps";
526 int lastmillis
= 0, totalmillis
= 0;
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;
540 sv
->serverupdate(lastmillis
, totalmillis
);
544 // below is network only
546 lastmillis
= totalmillis
= (int)enet_time_get();
547 sv
->serverupdate(lastmillis
, totalmillis
);
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);
567 bool serviced
= false;
570 if(enet_host_check_events(serverhost
, &event
) <= 0)
572 if(enet_host_service(serverhost
, &event
, timeout
) <= 0) break;
577 case ENET_EVENT_TYPE_CONNECT
:
579 client
&c
= addclient();
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
)))
592 else disconnect_client(c
.num
, reason
);
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
);
603 case ENET_EVENT_TYPE_DISCONNECT
:
605 client
*c
= (client
*)event
.peer
->data
;
607 printf("disconnected client (%s)\n", c
->hostname
);
608 sv
->clientdisconnect(c
->num
);
611 event
.peer
->data
= NULL
;
612 sv
->deleteinfo(c
->info
);
620 if(sv
->sendpackets()) enet_host_flush(serverhost
);
623 void localdisconnect()
625 loopv(clients
) if(clients
[i
]->type
==ST_LOCAL
)
627 sv
->localdisconnect(i
);
629 clients
[i
]->type
= ST_EMPTY
;
630 sv
->deleteinfo(clients
[i
]->info
);
631 clients
[i
]->info
= NULL
;
637 client
&c
= addclient();
639 s_strcpy(c
.hostname
, "local");
641 sv
->localconnect(c
.num
);
645 void initserver(bool dedicated
)
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);
657 ENetAddress address
= { ENET_HOST_ANY
, sv
->serverport() };
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);
674 if(dedicated
) // do not return, this becomes main loop
677 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS
);
679 printf("dedicated server started, waiting for clients...\nCtrl-C to exit\n\n");
680 atexit(enet_deinitialize
);
681 atexit(cleanupserver
);
683 if(*masterpath
) updatemasterserver();
684 for(;;) serverslice(5);
688 bool serveroption(char *opt
)
692 case 'u': uprate
= atoi(opt
+2); return true;
695 int clients
= atoi(opt
+2);
696 if(clients
> 0) maxclients
= min(clients
, MAXCLIENTS
);
697 else maxclients
= DEFAULTCLIENTS
;
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;
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");