1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22
23 #include "server.h"
24
25 #if USE_VOIP
26 cvar_t *sv_voip;
27 #endif
28
29 serverStatic_t svs; // persistant server info
30 server_t sv; // local server
31 vm_t *gvm = NULL; // game virtual machine
32
33 cvar_t *sv_fps; // time rate for running non-clients
34 cvar_t *sv_timeout; // seconds without any message
35 cvar_t *sv_zombietime; // seconds to sink messages after disconnect
36 cvar_t *sv_rconPassword; // password for remote server commands
37 cvar_t *sv_privatePassword; // password for the privateClient slots
38 cvar_t *sv_allowDownload;
39 cvar_t *sv_maxclients;
40
41 cvar_t *sv_privateClients; // number of clients reserved for password
42 cvar_t *sv_hostname;
43 cvar_t *sv_master[MAX_MASTER_SERVERS]; // master server ip address
44 cvar_t *sv_reconnectlimit; // minimum seconds between connect messages
45 cvar_t *sv_showloss; // report when usercmds are lost
46 cvar_t *sv_padPackets; // add nop bytes to messages
47 cvar_t *sv_killserver; // menu system can set to 1 to shut server down
48 cvar_t *sv_mapname;
49 cvar_t *sv_mapChecksum;
50 cvar_t *sv_serverid;
51 cvar_t *sv_minRate;
52 cvar_t *sv_maxRate;
53 cvar_t *sv_minPing;
54 cvar_t *sv_maxPing;
55 cvar_t *sv_gametype;
56 cvar_t *sv_pure;
57 cvar_t *sv_floodProtect;
58 cvar_t *sv_lanForceRate; // dedicated 1 (LAN) server forces local client rates to 99999 (bug #491)
59 cvar_t *sv_strictAuth;
60
61 serverBan_t serverBans[SERVER_MAXBANS];
62 int serverBansCount = 0;
63
64 /*
65 =============================================================================
66
67 EVENT MESSAGES
68
69 =============================================================================
70 */
71
72 /*
73 ===============
74 SV_ExpandNewlines
75
76 Converts newlines to "\n" so a line prints nicer
77 ===============
78 */
SV_ExpandNewlines(char * in)79 char *SV_ExpandNewlines( char *in ) {
80 static char string[1024];
81 int l;
82
83 l = 0;
84 while ( *in && l < sizeof(string) - 3 ) {
85 if ( *in == '\n' ) {
86 string[l++] = '\\';
87 string[l++] = 'n';
88 } else {
89 string[l++] = *in;
90 }
91 in++;
92 }
93 string[l] = 0;
94
95 return string;
96 }
97
98 /*
99 ======================
100 SV_ReplacePendingServerCommands
101
102 This is ugly
103 ======================
104 */
SV_ReplacePendingServerCommands(client_t * client,const char * cmd)105 int SV_ReplacePendingServerCommands( client_t *client, const char *cmd ) {
106 int i, index, csnum1, csnum2;
107
108 for ( i = client->reliableSent+1; i <= client->reliableSequence; i++ ) {
109 index = i & ( MAX_RELIABLE_COMMANDS - 1 );
110 //
111 if ( !Q_strncmp(cmd, client->reliableCommands[ index ], strlen("cs")) ) {
112 sscanf(cmd, "cs %i", &csnum1);
113 sscanf(client->reliableCommands[ index ], "cs %i", &csnum2);
114 if ( csnum1 == csnum2 ) {
115 Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) );
116 /*
117 if ( client->netchan.remoteAddress.type != NA_BOT ) {
118 Com_Printf( "WARNING: client %i removed double pending config string %i: %s\n", client-svs.clients, csnum1, cmd );
119 }
120 */
121 return qtrue;
122 }
123 }
124 }
125 return qfalse;
126 }
127
128 /*
129 ======================
130 SV_AddServerCommand
131
132 The given command will be transmitted to the client, and is guaranteed to
133 not have future snapshot_t executed before it is executed
134 ======================
135 */
SV_AddServerCommand(client_t * client,const char * cmd)136 void SV_AddServerCommand( client_t *client, const char *cmd ) {
137 int index, i;
138
139 // this is very ugly but it's also a waste to for instance send multiple config string updates
140 // for the same config string index in one snapshot
141 // if ( SV_ReplacePendingServerCommands( client, cmd ) ) {
142 // return;
143 // }
144
145 // do not send commands until the gamestate has been sent
146 if( client->state < CS_PRIMED )
147 return;
148
149 client->reliableSequence++;
150 // if we would be losing an old command that hasn't been acknowledged,
151 // we must drop the connection
152 // we check == instead of >= so a broadcast print added by SV_DropClient()
153 // doesn't cause a recursive drop client
154 if ( client->reliableSequence - client->reliableAcknowledge == MAX_RELIABLE_COMMANDS + 1 ) {
155 Com_Printf( "===== pending server commands =====\n" );
156 for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) {
157 Com_Printf( "cmd %5d: %s\n", i, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] );
158 }
159 Com_Printf( "cmd %5d: %s\n", i, cmd );
160 SV_DropClient( client, "Server command overflow" );
161 return;
162 }
163 index = client->reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
164 Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) );
165 }
166
167
168 /*
169 =================
170 SV_SendServerCommand
171
172 Sends a reliable command string to be interpreted by
173 the client game module: "cp", "print", "chat", etc
174 A NULL client will broadcast to all clients
175 =================
176 */
SV_SendServerCommand(client_t * cl,const char * fmt,...)177 void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) {
178 va_list argptr;
179 byte message[MAX_MSGLEN];
180 client_t *client;
181 int j;
182
183 va_start (argptr,fmt);
184 Q_vsnprintf ((char *)message, sizeof(message), fmt,argptr);
185 va_end (argptr);
186
187 // Fix to http://aluigi.altervista.org/adv/q3msgboom-adv.txt
188 // The actual cause of the bug is probably further downstream
189 // and should maybe be addressed later, but this certainly
190 // fixes the problem for now
191 if ( strlen ((char *)message) > 1022 ) {
192 return;
193 }
194
195 if ( cl != NULL ) {
196 SV_AddServerCommand( cl, (char *)message );
197 return;
198 }
199
200 // hack to echo broadcast prints to console
201 if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5) ) {
202 Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) );
203 }
204
205 // send the data to all relevent clients
206 for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) {
207 SV_AddServerCommand( client, (char *)message );
208 }
209 }
210
211
212 /*
213 ==============================================================================
214
215 MASTER SERVER FUNCTIONS
216
217 ==============================================================================
218 */
219
220 /*
221 ================
222 SV_MasterHeartbeat
223
224 Send a message to the masters every few minutes to
225 let it know we are alive, and log information.
226 We will also have a heartbeat sent when a server
227 changes from empty to non-empty, and full to non-full,
228 but not on every player enter or exit.
229 ================
230 */
231 #define HEARTBEAT_MSEC 300*1000
232 #define HEARTBEAT_GAME "QuakeArena-1"
SV_MasterHeartbeat(void)233 void SV_MasterHeartbeat( void ) {
234 static netadr_t adr[MAX_MASTER_SERVERS];
235 int i;
236
237 // "dedicated 1" is for lan play, "dedicated 2" is for inet public play
238 if ( !com_dedicated || com_dedicated->integer != 2 ) {
239 return; // only dedicated servers send heartbeats
240 }
241
242 // if not time yet, don't send anything
243 if ( svs.time < svs.nextHeartbeatTime ) {
244 return;
245 }
246 svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC;
247
248
249 // send to group masters
250 for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) {
251 if ( !sv_master[i]->string[0] ) {
252 continue;
253 }
254
255 // see if we haven't already resolved the name
256 // resolving usually causes hitches on win95, so only
257 // do it when needed
258 if ( sv_master[i]->modified ) {
259 sv_master[i]->modified = qfalse;
260
261 Com_Printf( "Resolving %s\n", sv_master[i]->string );
262 if ( !NET_StringToAdr( sv_master[i]->string, &adr[i], NA_UNSPEC ) ) {
263 // if the address failed to resolve, clear it
264 // so we don't take repeated dns hits
265 Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string );
266 Cvar_Set( sv_master[i]->name, "" );
267 sv_master[i]->modified = qfalse;
268 continue;
269 }
270 if ( !strchr( sv_master[i]->string, ':' ) ) {
271 adr[i].port = BigShort( PORT_MASTER );
272 }
273 Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i]));
274 }
275
276
277 Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string );
278 // this command should be changed if the server info / status format
279 // ever incompatably changes
280 NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME );
281 }
282 }
283
284 /*
285 =================
286 SV_MasterShutdown
287
288 Informs all masters that this server is going down
289 =================
290 */
SV_MasterShutdown(void)291 void SV_MasterShutdown( void ) {
292 // send a hearbeat right now
293 svs.nextHeartbeatTime = -9999;
294 SV_MasterHeartbeat();
295
296 // send it again to minimize chance of drops
297 svs.nextHeartbeatTime = -9999;
298 SV_MasterHeartbeat();
299
300 // when the master tries to poll the server, it won't respond, so
301 // it will be removed from the list
302 }
303
304
305 /*
306 ==============================================================================
307
308 CONNECTIONLESS COMMANDS
309
310 ==============================================================================
311 */
312
313 /*
314 ================
315 SVC_Status
316
317 Responds with all the info that qplug or qspy can see about the server
318 and all connected players. Used for getting detailed information after
319 the simple info query.
320 ================
321 */
SVC_Status(netadr_t from)322 void SVC_Status( netadr_t from ) {
323 char player[1024];
324 char status[MAX_MSGLEN];
325 int i;
326 client_t *cl;
327 playerState_t *ps;
328 int statusLength;
329 int playerLength;
330 char infostring[MAX_INFO_STRING];
331
332 // ignore if we are in single player
333 if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) {
334 return;
335 }
336
337 strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) );
338
339 // echo back the parameter to status. so master servers can use it as a challenge
340 // to prevent timed spoofed reply packets that add ghost servers
341 Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
342
343 status[0] = 0;
344 statusLength = 0;
345
346 for (i=0 ; i < sv_maxclients->integer ; i++) {
347 cl = &svs.clients[i];
348 if ( cl->state >= CS_CONNECTED ) {
349 ps = SV_GameClientNum( i );
350 Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n",
351 ps->persistant[PERS_SCORE], cl->ping, cl->name);
352 playerLength = strlen(player);
353 if (statusLength + playerLength >= sizeof(status) ) {
354 break; // can't hold any more
355 }
356 strcpy (status + statusLength, player);
357 statusLength += playerLength;
358 }
359 }
360
361 NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status );
362 }
363
364 /*
365 ================
366 SVC_Info
367
368 Responds with a short info message that should be enough to determine
369 if a user is interested in a server to do a full status
370 ================
371 */
SVC_Info(netadr_t from)372 void SVC_Info( netadr_t from ) {
373 int i, count;
374 char *gamedir;
375 char infostring[MAX_INFO_STRING];
376
377 // ignore if we are in single player
378 if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) {
379 return;
380 }
381
382 /*
383 * Check whether Cmd_Argv(1) has a sane length. This was not done in the original Quake3 version which led
384 * to the Infostring bug discovered by Luigi Auriemma. See http://aluigi.altervista.org/ for the advisory.
385 */
386
387 // A maximum challenge length of 128 should be more than plenty.
388 if(strlen(Cmd_Argv(1)) > 128)
389 return;
390
391 // don't count privateclients
392 count = 0;
393 for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) {
394 if ( svs.clients[i].state >= CS_CONNECTED ) {
395 count++;
396 }
397 }
398
399 infostring[0] = 0;
400
401 // echo back the parameter to status. so servers can use it as a challenge
402 // to prevent timed spoofed reply packets that add ghost servers
403 Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
404
405 Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) );
406 Info_SetValueForKey( infostring, "hostname", sv_hostname->string );
407 Info_SetValueForKey( infostring, "mapname", sv_mapname->string );
408 Info_SetValueForKey( infostring, "clients", va("%i", count) );
409 Info_SetValueForKey( infostring, "sv_maxclients",
410 va("%i", sv_maxclients->integer - sv_privateClients->integer ) );
411 Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) );
412 Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) );
413
414 #if USE_VOIP
415 if (sv_voip->integer) {
416 Info_SetValueForKey( infostring, "voip", va("%i", sv_voip->integer ) );
417 }
418 #endif
419
420 if( sv_minPing->integer ) {
421 Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) );
422 }
423 if( sv_maxPing->integer ) {
424 Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) );
425 }
426 gamedir = Cvar_VariableString( "fs_game" );
427 if( *gamedir ) {
428 Info_SetValueForKey( infostring, "game", gamedir );
429 }
430
431 NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring );
432 }
433
434 /*
435 ================
436 SVC_FlushRedirect
437
438 ================
439 */
SV_FlushRedirect(char * outputbuf)440 void SV_FlushRedirect( char *outputbuf ) {
441 NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf );
442 }
443
444 /*
445 ===============
446 SVC_RemoteCommand
447
448 An rcon packet arrived from the network.
449 Shift down the remaining args
450 Redirect all printfs
451 ===============
452 */
SVC_RemoteCommand(netadr_t from,msg_t * msg)453 void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
454 qboolean valid;
455 unsigned int time;
456 char remaining[1024];
457 // TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc.
458 // (OOB messages are the bottleneck here)
459 #define SV_OUTPUTBUF_LENGTH (1024 - 16)
460 char sv_outputbuf[SV_OUTPUTBUF_LENGTH];
461 static unsigned int lasttime = 0;
462 char *cmd_aux;
463
464 // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534
465 time = Com_Milliseconds();
466 if ( (unsigned)( time - lasttime ) < 500u ) {
467 return;
468 }
469 lasttime = time;
470
471 if ( !strlen( sv_rconPassword->string ) ||
472 strcmp (Cmd_Argv(1), sv_rconPassword->string) ) {
473 valid = qfalse;
474 Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
475 } else {
476 valid = qtrue;
477 Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
478 }
479
480 // start redirecting all print outputs to the packet
481 svs.redirectAddress = from;
482 Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
483
484 if ( !strlen( sv_rconPassword->string ) ) {
485 Com_Printf ("No rconpassword set on the server.\n");
486 } else if ( !valid ) {
487 Com_Printf ("Bad rconpassword.\n");
488 } else {
489 remaining[0] = 0;
490
491 // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
492 // get the command directly, "rcon <pass> <command>" to avoid quoting issues
493 // extract the command by walking
494 // since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing
495 cmd_aux = Cmd_Cmd();
496 cmd_aux+=4;
497 while(cmd_aux[0]==' ')
498 cmd_aux++;
499 while(cmd_aux[0] && cmd_aux[0]!=' ') // password
500 cmd_aux++;
501 while(cmd_aux[0]==' ')
502 cmd_aux++;
503
504 Q_strcat( remaining, sizeof(remaining), cmd_aux);
505
506 Cmd_ExecuteString (remaining);
507
508 }
509
510 Com_EndRedirect ();
511 }
512
513 /*
514 =================
515 SV_ConnectionlessPacket
516
517 A connectionless packet has four leading 0xff
518 characters to distinguish it from a game channel.
519 Clients that are in the game can still send
520 connectionless packets.
521 =================
522 */
SV_ConnectionlessPacket(netadr_t from,msg_t * msg)523 void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
524 char *s;
525 char *c;
526
527 MSG_BeginReadingOOB( msg );
528 MSG_ReadLong( msg ); // skip the -1 marker
529
530 if (!Q_strncmp("connect", (char *) &msg->data[4], 7)) {
531 Huff_Decompress(msg, 12);
532 }
533
534 s = MSG_ReadStringLine( msg );
535 Cmd_TokenizeString( s );
536
537 c = Cmd_Argv(0);
538 Com_DPrintf ("SV packet %s : %s\n", NET_AdrToString(from), c);
539
540 if (!Q_stricmp(c, "getstatus")) {
541 SVC_Status( from );
542 } else if (!Q_stricmp(c, "getinfo")) {
543 SVC_Info( from );
544 } else if (!Q_stricmp(c, "getchallenge")) {
545 SV_GetChallenge( from );
546 } else if (!Q_stricmp(c, "connect")) {
547 SV_DirectConnect( from );
548 #ifndef STANDALONE
549 } else if (!Q_stricmp(c, "ipAuthorize")) {
550 SV_AuthorizeIpPacket( from );
551 #endif
552 } else if (!Q_stricmp(c, "rcon")) {
553 SVC_RemoteCommand( from, msg );
554 } else if (!Q_stricmp(c, "disconnect")) {
555 // if a client starts up a local server, we may see some spurious
556 // server disconnect messages when their new server sees our final
557 // sequenced messages to the old client
558 } else {
559 Com_DPrintf ("bad connectionless packet from %s:\n%s\n"
560 , NET_AdrToString (from), s);
561 }
562 }
563
564 //============================================================================
565
566 /*
567 =================
568 SV_ReadPackets
569 =================
570 */
SV_PacketEvent(netadr_t from,msg_t * msg)571 void SV_PacketEvent( netadr_t from, msg_t *msg ) {
572 int i;
573 client_t *cl;
574 int qport;
575
576 // check for connectionless packet (0xffffffff) first
577 if ( msg->cursize >= 4 && *(int *)msg->data == -1) {
578 SV_ConnectionlessPacket( from, msg );
579 return;
580 }
581
582 // read the qport out of the message so we can fix up
583 // stupid address translating routers
584 MSG_BeginReadingOOB( msg );
585 MSG_ReadLong( msg ); // sequence number
586 qport = MSG_ReadShort( msg ) & 0xffff;
587
588 // find which client the message is from
589 for (i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
590 if (cl->state == CS_FREE) {
591 continue;
592 }
593 if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) {
594 continue;
595 }
596 // it is possible to have multiple clients from a single IP
597 // address, so they are differentiated by the qport variable
598 if (cl->netchan.qport != qport) {
599 continue;
600 }
601
602 // the IP port can't be used to differentiate them, because
603 // some address translating routers periodically change UDP
604 // port assignments
605 if (cl->netchan.remoteAddress.port != from.port) {
606 Com_Printf( "SV_PacketEvent: fixing up a translated port\n" );
607 cl->netchan.remoteAddress.port = from.port;
608 }
609
610 // make sure it is a valid, in sequence packet
611 if (SV_Netchan_Process(cl, msg)) {
612 // zombie clients still need to do the Netchan_Process
613 // to make sure they don't need to retransmit the final
614 // reliable message, but they don't do any other processing
615 if (cl->state != CS_ZOMBIE) {
616 cl->lastPacketTime = svs.time; // don't timeout
617 SV_ExecuteClientMessage( cl, msg );
618 }
619 }
620 return;
621 }
622
623 // if we received a sequenced packet from an address we don't recognize,
624 // send an out of band disconnect packet to it
625 NET_OutOfBandPrint( NS_SERVER, from, "disconnect" );
626 }
627
628
629 /*
630 ===================
631 SV_CalcPings
632
633 Updates the cl->ping variables
634 ===================
635 */
SV_CalcPings(void)636 void SV_CalcPings( void ) {
637 int i, j;
638 client_t *cl;
639 int total, count;
640 int delta;
641 playerState_t *ps;
642
643 for (i=0 ; i < sv_maxclients->integer ; i++) {
644 cl = &svs.clients[i];
645 if ( cl->state != CS_ACTIVE ) {
646 cl->ping = 999;
647 continue;
648 }
649 if ( !cl->gentity ) {
650 cl->ping = 999;
651 continue;
652 }
653 if ( cl->gentity->r.svFlags & SVF_BOT ) {
654 cl->ping = 0;
655 continue;
656 }
657
658 total = 0;
659 count = 0;
660 for ( j = 0 ; j < PACKET_BACKUP ; j++ ) {
661 if ( cl->frames[j].messageAcked <= 0 ) {
662 continue;
663 }
664 delta = cl->frames[j].messageAcked - cl->frames[j].messageSent;
665 count++;
666 total += delta;
667 }
668 if (!count) {
669 cl->ping = 999;
670 } else {
671 cl->ping = total/count;
672 if ( cl->ping > 999 ) {
673 cl->ping = 999;
674 }
675 }
676
677 // let the game dll know about the ping
678 ps = SV_GameClientNum( i );
679 ps->ping = cl->ping;
680 }
681 }
682
683 /*
684 ==================
685 SV_CheckTimeouts
686
687 If a packet has not been received from a client for timeout->integer
688 seconds, drop the conneciton. Server time is used instead of
689 realtime to avoid dropping the local client while debugging.
690
691 When a client is normally dropped, the client_t goes into a zombie state
692 for a few seconds to make sure any final reliable message gets resent
693 if necessary
694 ==================
695 */
SV_CheckTimeouts(void)696 void SV_CheckTimeouts( void ) {
697 int i;
698 client_t *cl;
699 int droppoint;
700 int zombiepoint;
701
702 droppoint = svs.time - 1000 * sv_timeout->integer;
703 zombiepoint = svs.time - 1000 * sv_zombietime->integer;
704
705 for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
706 // message times may be wrong across a changelevel
707 if (cl->lastPacketTime > svs.time) {
708 cl->lastPacketTime = svs.time;
709 }
710
711 if (cl->state == CS_ZOMBIE
712 && cl->lastPacketTime < zombiepoint) {
713 // using the client id cause the cl->name is empty at this point
714 Com_DPrintf( "Going from CS_ZOMBIE to CS_FREE for client %d\n", i );
715 cl->state = CS_FREE; // can now be reused
716 continue;
717 }
718 if ( cl->state >= CS_CONNECTED && cl->lastPacketTime < droppoint) {
719 // wait several frames so a debugger session doesn't
720 // cause a timeout
721 if ( ++cl->timeoutCount > 5 ) {
722 SV_DropClient (cl, "timed out");
723 cl->state = CS_FREE; // don't bother with zombie state
724 }
725 } else {
726 cl->timeoutCount = 0;
727 }
728 }
729 }
730
731
732 /*
733 ==================
734 SV_CheckPaused
735 ==================
736 */
SV_CheckPaused(void)737 qboolean SV_CheckPaused( void ) {
738 int count;
739 client_t *cl;
740 int i;
741
742 if ( !cl_paused->integer ) {
743 return qfalse;
744 }
745
746 // only pause if there is just a single client connected
747 count = 0;
748 for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
749 if ( cl->state >= CS_CONNECTED && cl->netchan.remoteAddress.type != NA_BOT ) {
750 count++;
751 }
752 }
753
754 if ( count > 1 ) {
755 // don't pause
756 if (sv_paused->integer)
757 Cvar_Set("sv_paused", "0");
758 return qfalse;
759 }
760
761 if (!sv_paused->integer)
762 Cvar_Set("sv_paused", "1");
763 return qtrue;
764 }
765
766 /*
767 ==================
768 SV_Frame
769
770 Player movement occurs as a result of packet events, which
771 happen before SV_Frame is called
772 ==================
773 */
SV_Frame(int msec)774 void SV_Frame( int msec ) {
775 int frameMsec;
776 int startTime;
777
778 // the menu kills the server with this cvar
779 if ( sv_killserver->integer ) {
780 SV_Shutdown ("Server was killed");
781 Cvar_Set( "sv_killserver", "0" );
782 return;
783 }
784
785 if (!com_sv_running->integer)
786 {
787 // Running as a server, but no map loaded
788 #ifdef DEDICATED
789 // Block until something interesting happens
790 Sys_Sleep(-1);
791 #endif
792
793 return;
794 }
795
796 // allow pause if only the local client is connected
797 if ( SV_CheckPaused() ) {
798 return;
799 }
800
801 // if it isn't time for the next frame, do nothing
802 if ( sv_fps->integer < 1 ) {
803 Cvar_Set( "sv_fps", "10" );
804 }
805
806 frameMsec = 1000 / sv_fps->integer * com_timescale->value;
807 // don't let it scale below 1ms
808 if(frameMsec < 1)
809 {
810 Cvar_Set("timescale", va("%f", sv_fps->integer / 1000.0f));
811 frameMsec = 1;
812 }
813
814 sv.timeResidual += msec;
815
816 if (!com_dedicated->integer) SV_BotFrame (sv.time + sv.timeResidual);
817
818 if ( com_dedicated->integer && sv.timeResidual < frameMsec ) {
819 // NET_Sleep will give the OS time slices until either get a packet
820 // or time enough for a server frame has gone by
821 NET_Sleep(frameMsec - sv.timeResidual);
822 return;
823 }
824
825 // if time is about to hit the 32nd bit, kick all clients
826 // and clear sv.time, rather
827 // than checking for negative time wraparound everywhere.
828 // 2giga-milliseconds = 23 days, so it won't be too often
829 if ( svs.time > 0x70000000 ) {
830 SV_Shutdown( "Restarting server due to time wrapping" );
831 Cbuf_AddText( va( "map %s\n", Cvar_VariableString( "mapname" ) ) );
832 return;
833 }
834 // this can happen considerably earlier when lots of clients play and the map doesn't change
835 if ( svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities ) {
836 SV_Shutdown( "Restarting server due to numSnapshotEntities wrapping" );
837 Cbuf_AddText( va( "map %s\n", Cvar_VariableString( "mapname" ) ) );
838 return;
839 }
840
841 if( sv.restartTime && sv.time >= sv.restartTime ) {
842 sv.restartTime = 0;
843 Cbuf_AddText( "map_restart 0\n" );
844 return;
845 }
846
847 // update infostrings if anything has been changed
848 if ( cvar_modifiedFlags & CVAR_SERVERINFO ) {
849 SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) );
850 cvar_modifiedFlags &= ~CVAR_SERVERINFO;
851 }
852 if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) {
853 SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) );
854 cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
855 }
856
857 if ( com_speeds->integer ) {
858 startTime = Sys_Milliseconds ();
859 } else {
860 startTime = 0; // quite a compiler warning
861 }
862
863 // update ping based on the all received frames
864 SV_CalcPings();
865
866 if (com_dedicated->integer) SV_BotFrame (sv.time);
867
868 // run the game simulation in chunks
869 while ( sv.timeResidual >= frameMsec ) {
870 sv.timeResidual -= frameMsec;
871 svs.time += frameMsec;
872 sv.time += frameMsec;
873
874 // let everything in the world think and move
875 VM_Call (gvm, GAME_RUN_FRAME, sv.time);
876 }
877
878 if ( com_speeds->integer ) {
879 time_game = Sys_Milliseconds () - startTime;
880 }
881
882 // check timeouts
883 SV_CheckTimeouts();
884
885 // send messages back to the clients
886 SV_SendClientMessages();
887
888 // send a heartbeat to the master if needed
889 SV_MasterHeartbeat();
890 }
891
892 //============================================================================
893
894