1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 //
21 // sv_main.c.
22 // Server main program
23 //
24
25 #include "sv_local.h"
26
27 netAdr_t sv_masterAddresses[MAX_MASTERS]; // address of group servers
28
29 svClient_t *sv_currentClient; // current client
30
31 cVar_t *sv_paused;
32 cVar_t *sv_timedemo;
33
34 cVar_t *sv_enforcetime;
35
36 cVar_t *timeout; // seconds without any message
37 cVar_t *zombietime; // seconds to sink messages after disconnect
38
39 cVar_t *rcon_password; // password for remote server commands
40
41 cVar_t *allow_download;
42 cVar_t *allow_download_players;
43 cVar_t *allow_download_models;
44 cVar_t *allow_download_sounds;
45 cVar_t *allow_download_maps;
46
47 cVar_t *sv_airaccelerate;
48
49 cVar_t *sv_noreload; // don't reload level state when reentering
50
51 cVar_t *maxclients;
52 cVar_t *sv_showclamp;
53
54 cVar_t *hostname;
55 cVar_t *public_server; // should heartbeats be sent
56
57 cVar_t *sv_reconnect_limit; // minimum seconds between connect messages
58
59 struct memPool_s *sv_gameSysPool;
60 struct memPool_s *sv_genericPool;
61
62 /*
63 =====================
64 SV_SetState
65 =====================
66 */
SV_SetState(ssState_t state)67 void SV_SetState (ssState_t state)
68 {
69 Com_SetServerState (state);
70 }
71
72
73 /*
74 =====================
75 SV_DropClient
76
77 Called when the player is totally leaving the server, either willingly
78 or unwillingly. This is NOT called if the entire server is quiting
79 or crashing.
80 =====================
81 */
SV_DropClient(svClient_t * drop)82 void SV_DropClient (svClient_t *drop)
83 {
84 // Add the disconnect
85 MSG_WriteByte (&drop->netChan.message, SVC_DISCONNECT);
86
87 if (drop->state == SVCS_SPAWNED) {
88 // Call the prog function for removing a client
89 // This will remove the body, among other things
90 ge->ClientDisconnect (drop->edict);
91 }
92
93 if (drop->download) {
94 FS_FreeFile (drop->download);
95 drop->download = NULL;
96 }
97
98 drop->state = SVCS_FREE; // become free in a few seconds
99 drop->name[0] = 0;
100 }
101
102
103 /*
104 =================
105 SV_UserinfoChanged
106
107 Pull specific info from a newly changed userinfo string
108 into a more C freindly form.
109 =================
110 */
SV_UserinfoChanged(svClient_t * cl)111 void SV_UserinfoChanged (svClient_t *cl)
112 {
113 char *val;
114 int i;
115
116 // Call prog code to allow overrides
117 ge->ClientUserinfoChanged (cl->edict, cl->userInfo);
118
119 // Name for C code
120 strncpy (cl->name, Info_ValueForKey (cl->userInfo, "name"), sizeof (cl->name)-1);
121 // Mask off high bit
122 for (i=0 ; i<sizeof (cl->name) ; i++)
123 cl->name[i] &= 127;
124
125 // Rate command
126 val = Info_ValueForKey (cl->userInfo, "rate");
127 if (strlen (val)) {
128 i = atoi(val);
129 cl->rate = clamp (i, 100, 15000);
130 }
131 else
132 cl->rate = 5000;
133
134 // MSG command
135 val = Info_ValueForKey (cl->userInfo, "msg");
136 if (strlen (val))
137 cl->messageLevel = atoi (val);
138 }
139
140
141 /*
142 ===================
143 SV_UpdateTitle
144 ===================
145 */
SV_UpdateTitle(void)146 void SV_UpdateTitle (void)
147 {
148 #if 0
149 hostname->modified = qFalse;
150
151 if (Com_ServerState() == SS_GAME) {
152 Sys_SetConsoleTitle (Q_VarArgs ("EGL Server: %s (port %i)", Cvar_GetStringValue ("hostname"), Cvar_GetIntegerValue ("port")));
153 return;
154 }
155
156 Sys_SetConsoleTitle (NULL);
157 #endif
158 }
159
160 /*
161 =============================================================================
162
163 Com_Printf REDIRECTION
164
165 =============================================================================
166 */
167
168 #define SV_OUTPUTBUF_LENGTH (MAX_SV_MSGLEN - 16)
169 enum {
170 RD_NONE,
171 RD_CLIENT,
172 RD_PACKET
173 };
174
175 static char sv_outputBuffer[SV_OUTPUTBUF_LENGTH];
176
177 /*
178 =================
179 SV_FlushRedirect
180 =================
181 */
SV_FlushRedirect(int target,char * outBuffer)182 static void SV_FlushRedirect (int target, char *outBuffer)
183 {
184 switch (target) {
185 case RD_PACKET:
186 Netchan_OutOfBandPrint (NS_SERVER, &sv_netFrom, "print\n%s", outBuffer);
187 break;
188
189 case RD_CLIENT:
190 MSG_WriteByte (&sv_currentClient->netChan.message, SVC_PRINT);
191 MSG_WriteByte (&sv_currentClient->netChan.message, PRINT_HIGH);
192 MSG_WriteString (&sv_currentClient->netChan.message, outBuffer);
193 break;
194 }
195 }
196
197 /*
198 ==============================================================================
199
200 CONNECTIONLESS COMMANDS
201
202 ==============================================================================
203 */
204
205 /*
206 ===============
207 SV_StatusString
208
209 Builds the string that is sent as heartbeats and status replies
210 ===============
211 */
SV_StatusString(void)212 static char *SV_StatusString (void)
213 {
214 char player[1024];
215 static char status[MAX_SV_MSGLEN - 16];
216 int i;
217 svClient_t *cl;
218 int statusLength;
219 int playerLength;
220
221 Q_snprintfz (status, sizeof (status), "%s\n", Cvar_BitInfo (CVAR_SERVERINFO));
222 statusLength = strlen (status);
223
224 for (i=0 ; i<maxclients->intVal ; i++) {
225 if (svs.clients[i].state < SVCS_CONNECTED)
226 continue;
227
228 cl = &svs.clients[i];
229 Q_snprintfz (player, sizeof (player), "%i %i \"%s\"\n",
230 cl->edict->client->playerState.stats[STAT_FRAGS], cl->ping, cl->name);
231
232 playerLength = strlen (player);
233 if (statusLength+playerLength >= sizeof(status))
234 break; // Can't hold any more
235
236 strcpy (status+statusLength, player);
237 statusLength += playerLength;
238 }
239
240 return status;
241 }
242
243
244 /*
245 ================
246 SVC_Status_f
247
248 Responds with all the info that qplug or qspy can see
249 ================
250 */
SVC_Status_f(void)251 static void SVC_Status_f (void)
252 {
253 Netchan_OutOfBandPrint (NS_SERVER, &sv_netFrom, "print\n%s", SV_StatusString());
254 }
255
256
257 /*
258 ================
259 SVC_Ack_f
260 ================
261 */
SVC_Ack_f(void)262 static void SVC_Ack_f (void)
263 {
264 int i;
265
266 // R1: could be used to flood server console - only show acks from masters.
267 for (i=0 ; i<MAX_MASTERS ; i++) {
268 if (!sv_masterAddresses[i].port)
269 continue;
270 if (!NET_CompareBaseAdr (sv_masterAddresses[i], sv_netFrom))
271 continue;
272
273 Com_Printf (0, "Ping acknowledge from %s\n", NET_AdrToString (&sv_netFrom));
274 }
275 }
276
277
278 /*
279 ================
280 SVC_Info_f
281
282 Responds with short info for broadcast scans
283 The second parameter should be the current protocol version number.
284 ================
285 */
SVC_Info_f(void)286 static void SVC_Info_f (void)
287 {
288 char string[64];
289 int i, count;
290 int version;
291
292 if (maxclients->intVal == 1)
293 return; // Ignore in single player
294
295 version = atoi (Cmd_Argv (1));
296 if (version != ORIGINAL_PROTOCOL_VERSION)
297 return;
298
299 count = 0;
300 for (i=0 ; i<maxclients->intVal ; i++) {
301 if (svs.clients[i].state >= SVCS_CONNECTED)
302 count++;
303 }
304
305 Q_snprintfz (string, sizeof (string), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, maxclients->intVal);
306
307 Netchan_OutOfBandPrint (NS_SERVER, &sv_netFrom, "info\n%s", string);
308 }
309
310
311 /*
312 ================
313 SVC_Ping_f
314
315 Just responds with an acknowledgement
316 ================
317 */
SVC_Ping_f(void)318 static void SVC_Ping_f (void)
319 {
320 Netchan_OutOfBandPrint (NS_SERVER, &sv_netFrom, "ack");
321 }
322
323
324 /*
325 =================
326 SVC_GetChallenge_f
327
328 Returns a challenge number that can be used in a subsequent client_connect command.
329 We do this to prevent denial of service attacks that flood the server with invalid connection IPs.
330 With a challenge, they must give a valid IP address.
331 =================
332 */
SVC_GetChallenge_f(void)333 static void SVC_GetChallenge_f (void)
334 {
335 int i;
336 int oldest;
337 uint32 oldestTime;
338
339 oldest = 0;
340 oldestTime = 0xffffffff; // UINT_MAX?
341
342 // See if we already have a challenge for this ip
343 for (i=0 ; i<MAX_CHALLENGES ; i++) {
344 if (NET_CompareBaseAdr (sv_netFrom, svs.challenges[i].adr))
345 break;
346 if (svs.challenges[i].time < oldestTime) {
347 oldestTime = svs.challenges[i].time;
348 oldest = i;
349 }
350 }
351
352 if (i == MAX_CHALLENGES) {
353 // Overwrite the oldest
354 svs.challenges[oldest].challenge = rand() & 0x7fff;
355 svs.challenges[oldest].adr = sv_netFrom;
356 svs.challenges[oldest].time = Sys_UMilliseconds ();
357 i = oldest;
358 }
359
360 // Send it back
361 Netchan_OutOfBandPrint (NS_SERVER, &sv_netFrom, "challenge %i", svs.challenges[i].challenge);
362 }
363
364
365 /*
366 ==================
367 SVC_DirectConnect_f
368
369 A connection request that did not come from the master
370 ==================
371 */
SVC_DirectConnect_f(void)372 static void SVC_DirectConnect_f (void)
373 {
374 char userInfo[MAX_INFO_STRING];
375 netAdr_t adr;
376 int i;
377 svClient_t *cl, *newcl;
378 svClient_t temp;
379 edict_t *ent;
380 int edictNum;
381 int version;
382 int qPort;
383 int challenge;
384
385 adr = sv_netFrom;
386
387 Com_DevPrintf (0, "SVC_DirectConnect_f ()\n");
388
389 // Check protocol version
390 version = atoi (Cmd_Argv (1));
391 if (version != ORIGINAL_PROTOCOL_VERSION) {
392 Netchan_OutOfBandPrint (NS_SERVER, &adr, "print\nServer is not using same protocol (%i != %i).\n",
393 version, ORIGINAL_PROTOCOL_VERSION);
394 Com_DevPrintf (0, " rejected connect from version %i\n", version);
395 return;
396 }
397
398 qPort = atoi (Cmd_Argv (2));
399 challenge = atoi (Cmd_Argv (3));
400 Q_strncpyz (userInfo, Cmd_Argv (4), sizeof (userInfo));
401
402 // Force the IP key/value pair so the game can filter based on ip
403 Info_SetValueForKey (userInfo, "ip", NET_AdrToString (&sv_netFrom));
404
405 // Attractloop servers are ONLY for local clients
406 if (sv.attractLoop) {
407 if (!NET_IsLocalAddress (adr)) {
408 Com_Printf (PRNT_WARNING, "Remote connect in attract loop. Ignored.\n");
409 Netchan_OutOfBandPrint (NS_SERVER, &adr, "print\nConnection refused.\n");
410 return;
411 }
412 }
413
414 // See if the challenge is valid
415 if (!NET_IsLocalAddress (adr)) {
416 for (i=0 ; i<MAX_CHALLENGES ; i++) {
417 if (NET_CompareBaseAdr (sv_netFrom, svs.challenges[i].adr)) {
418 if (challenge == svs.challenges[i].challenge)
419 break; // good
420 Netchan_OutOfBandPrint (NS_SERVER, &adr, "print\nBad challenge.\n");
421 return;
422 }
423 }
424
425 if (i == MAX_CHALLENGES) {
426 Netchan_OutOfBandPrint (NS_SERVER, &adr, "print\nNo challenge for address.\n");
427 return;
428 }
429 }
430
431 newcl = &temp;
432 memset (newcl, 0, sizeof (svClient_t));
433
434 // If there is already a slot for this ip, reuse it
435 for (i=0, cl=svs.clients ; i<maxclients->intVal ; i++, cl++) {
436 if (cl->state == SVCS_FREE)
437 continue;
438
439 if (NET_CompareBaseAdr (adr, cl->netChan.remoteAddress)
440 && (cl->netChan.qPort == qPort || adr.port == cl->netChan.remoteAddress.port)) {
441 if (!NET_IsLocalAddress (adr) && svs.realTime-cl->lastConnect < sv_reconnect_limit->intVal*1000) {
442 Com_DevPrintf (0, "%s:reconnect rejected : too soon\n", NET_AdrToString (&adr));
443 return;
444 }
445
446 Com_Printf (0, "%s:reconnect\n", NET_AdrToString (&adr));
447
448 newcl = cl;
449 goto gotNewClient;
450 }
451 }
452
453 // Find a client slot
454 newcl = NULL;
455 for (i=0, cl=svs.clients ; i<maxclients->intVal ; i++, cl++) {
456 if (cl->state == SVCS_FREE) {
457 newcl = cl;
458 break;
459 }
460 }
461
462 if (!newcl) {
463 Netchan_OutOfBandPrint (NS_SERVER, &adr, "print\nServer is full.\n");
464 Com_DevPrintf (0, "Rejected a connection.\n");
465 return;
466 }
467
468 gotNewClient:
469 /*
470 ** Build a new connection and accept the new client
471 ** This is the only place a svClient_t is ever initialized
472 */
473 *newcl = temp;
474 sv_currentClient = newcl;
475 edictNum = (newcl-svs.clients)+1;
476 ent = EDICT_NUM(edictNum);
477 newcl->edict = ent;
478 newcl->challenge = challenge; // Save challenge for checksumming
479
480 // Get the game a chance to reject this connection or modify the userInfo
481 if (!ge->ClientConnect (ent, userInfo)) {
482 Com_DevPrintf (0, "Game rejected a connection.\n");
483
484 if (*Info_ValueForKey (userInfo, "rejmsg")) {
485 Netchan_OutOfBandPrint (NS_SERVER, &adr, "print\n%s\nConnection refused.\n", Info_ValueForKey (userInfo, "rejmsg"));
486 return;
487 }
488
489 Netchan_OutOfBandPrint (NS_SERVER, &adr, "print\nConnection refused.\n");
490 return;
491 }
492
493 // Parse some info from the info strings
494 Q_strncpyz (newcl->userInfo, userInfo, sizeof (newcl->userInfo));
495 SV_UserinfoChanged (newcl);
496
497 // Send the connect packet to the client
498 Netchan_OutOfBandPrint (NS_SERVER, &adr, "client_connect");
499
500 Netchan_Setup (NS_SERVER, &newcl->netChan, &adr, version, qPort, 0);
501
502 newcl->protocol = version;
503 newcl->state = SVCS_CONNECTED;
504
505 MSG_Init (&newcl->datagram, newcl->datagramBuff, sizeof (newcl->datagramBuff));
506 newcl->datagram.allowOverflow = qTrue;
507
508 newcl->lastMessage = svs.realTime - ((timeout->floatVal - 5) * 1000); // don't timeout
509 newcl->lastConnect = svs.realTime;
510 }
511
512
513 /*
514 ===============
515 SVC_RemoteCommand_f
516
517 A client issued an rcon command.
518 Shift down the remaining args
519 Redirect all printfs
520 ===============
521 */
SVC_RemoteCommand_f(void)522 static void SVC_RemoteCommand_f (void)
523 {
524 char remaining[1024];
525 int i;
526
527 // Check rcon validity
528 if (!strlen (rcon_password->string) || strcmp (Cmd_Argv(1), rcon_password->string)) {
529 Com_Printf (PRNT_ERROR, "Bad rcon from %s:\n%s\n", NET_AdrToString (&sv_netFrom), sv_netMessage.data+4);
530
531 Com_BeginRedirect (RD_PACKET, sv_outputBuffer, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
532 Com_Printf (0, "Bad rcon_password.\n");
533 Com_EndRedirect ();
534
535 return;
536 }
537
538 Com_Printf (0, "Rcon from %s:\n%s\n", NET_AdrToString (&sv_netFrom), sv_netMessage.data+4);
539
540 Com_BeginRedirect (RD_PACKET, sv_outputBuffer, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
541
542 // Echo the command
543 remaining[0] = 0;
544 for (i=2 ; i<Cmd_Argc () ; i++) {
545 Q_strcatz (remaining, Cmd_Argv (i), sizeof (remaining));
546 Q_strcatz (remaining, " ", sizeof (remaining));
547 }
548 Cmd_ExecuteString (remaining);
549
550 Com_EndRedirect ();
551 }
552
553
554 /*
555 =================
556 SV_ConnectionlessPacket
557
558 A connectionless packet has four leading 0xff
559 characters to distinguish it from a game channel.
560 Clients that are in the game can still send
561 connectionless packets.
562 =================
563 */
SV_ConnectionlessPacket(void)564 static void SV_ConnectionlessPacket (void)
565 {
566 char *s;
567 char *c;
568
569 // Should NEVER EVER happen
570 if (sv_netMessage.curSize > 1024) {
571 Com_Printf (PRNT_WARNING, "Illegitimate packet size (%i) received, ignored.\n", sv_netMessage.curSize);
572 return;
573 }
574
575 MSG_BeginReading (&sv_netMessage);
576 assert (MSG_ReadLong (&sv_netMessage) == -1); // skip the -1 marker
577
578 s = MSG_ReadStringLine (&sv_netMessage);
579
580 Cmd_TokenizeString (s, qFalse);
581
582 c = Cmd_Argv (0);
583 Com_DevPrintf (0, "Packet %s : %s\n", NET_AdrToString (&sv_netFrom), c);
584
585 if (!strcmp (c, "ping"))
586 SVC_Ping_f ();
587 else if (!strcmp (c, "ack"))
588 SVC_Ack_f ();
589 else if (!strcmp (c, "status"))
590 SVC_Status_f ();
591 else if (!strcmp (c, "info"))
592 SVC_Info_f ();
593 else if (!strcmp (c, "getchallenge"))
594 SVC_GetChallenge_f ();
595 else if (!strcmp (c, "connect"))
596 SVC_DirectConnect_f ();
597 else if (!strcmp (c, "rcon"))
598 SVC_RemoteCommand_f ();
599 else
600 Com_Printf (PRNT_WARNING, "SV_ConnectionlessPacket: Bad connectionless packet from %s:\n%s\n", NET_AdrToString (&sv_netFrom), s);
601 }
602
603 /*
604 ==============================================================================
605
606 MASTER SERVERS
607
608 ==============================================================================
609 */
610
611 /*
612 ================
613 SV_MasterHeartbeat
614
615 Send a message to the master every few minutes to
616 let it know we are alive, and log information
617 ================
618 */
619 #define HEARTBEAT_SECONDS 300
SV_MasterHeartbeat(void)620 static void SV_MasterHeartbeat (void)
621 {
622 char *string;
623 int i;
624
625 #ifndef DEDICATED_ONLY
626 if (!dedicated->intVal)
627 return; // Only dedicated servers send heartbeats
628 #endif
629
630 if (!public_server->intVal)
631 return; // A private dedicated game
632
633 // Check for time wraparound
634 if (svs.lastHeartBeat > svs.realTime)
635 svs.lastHeartBeat = svs.realTime;
636
637 if (svs.realTime-svs.lastHeartBeat < HEARTBEAT_SECONDS*1000)
638 return; // Not time to send yet
639
640 svs.lastHeartBeat = svs.realTime;
641
642 // Send the same string that we would give for a status OOB command
643 string = SV_StatusString ();
644
645 // Send to group master
646 for (i=0 ; i<MAX_MASTERS ; i++) {
647 if (!sv_masterAddresses[i].port)
648 continue;
649
650 Com_Printf (0, "Sending heartbeat to %s\n", NET_AdrToString (&sv_masterAddresses[i]));
651 Netchan_OutOfBandPrint (NS_SERVER, &sv_masterAddresses[i], "heartbeat\n%s", string);
652 }
653 }
654
655
656 /*
657 =================
658 SV_MasterShutdown
659
660 Informs all masters that this server is going down
661 =================
662 */
SV_MasterShutdown(void)663 static void SV_MasterShutdown (void)
664 {
665 int i;
666
667 #ifndef DEDICATED_ONLY
668 if (!dedicated->intVal)
669 return; // Only dedicated servers send heartbeats
670 #endif
671
672 if (!public_server->intVal)
673 return; // A private dedicated game
674
675 // Send to group master
676 for (i=0 ; i<MAX_MASTERS ; i++) {
677 if (!sv_masterAddresses[i].port)
678 continue;
679
680 if (i > 0)
681 Com_Printf (0, "Sending heartbeat to %s\n", NET_AdrToString (&sv_masterAddresses[i]));
682 Netchan_OutOfBandPrint (NS_SERVER, &sv_masterAddresses[i], "shutdown");
683 }
684 }
685
686 /*
687 ==============================================================================
688
689 SERVER FRAME LOOP
690
691 ==============================================================================
692 */
693
694 /*
695 ===================
696 SV_CalcPings
697
698 Updates the cl->ping variables
699 ===================
700 */
SV_CalcPings(void)701 static void SV_CalcPings (void)
702 {
703 int i, j;
704 svClient_t *cl;
705 int total, count;
706
707 for (i=0 ; i<maxclients->intVal ; i++) {
708 cl = &svs.clients[i];
709 if (!cl->edict || !cl->edict->client || cl->state != SVCS_SPAWNED)
710 continue;
711
712 total = 0;
713 count = 0;
714 for (j=0 ; j<LATENCY_COUNTS ; j++) {
715 if (cl->frameLatency[j] > 0) {
716 count++;
717 total += cl->frameLatency[j];
718 }
719 }
720
721 if (!count)
722 cl->ping = 0;
723 else
724 cl->ping = total / count;
725
726 // Let the game dll know about the ping
727 cl->edict->client->ping = cl->ping;
728 }
729 }
730
731
732 /*
733 ===================
734 SV_GiveMsec
735
736 Every few frames, gives all clients an allotment of milliseconds
737 for their command moves. If they exceed it, assume cheating.
738 ===================
739 */
SV_GiveMsec(void)740 static void SV_GiveMsec (void)
741 {
742 int i;
743 svClient_t *cl;
744
745 if (sv.frameNum & 15)
746 return;
747
748 for (i=0 ; i<maxclients->intVal ; i++) {
749 cl = &svs.clients[i];
750 if (cl->state == SVCS_FREE)
751 continue;
752
753 cl->commandMsec = 1800; // 1600 + some slop
754 }
755 }
756
757
758 /*
759 =================
760 SV_ReadPackets
761 =================
762 */
SV_ReadPackets(void)763 static void SV_ReadPackets (void)
764 {
765 int i;
766 svClient_t *cl;
767 int qPort;
768
769 while (NET_GetPacket (NS_SERVER, &sv_netFrom, &sv_netMessage)) {
770 // Check for connectionless packet (0xffffffff) first
771 if (*(int *)sv_netMessage.data == -1) {
772 SV_ConnectionlessPacket ();
773 continue;
774 }
775
776 /*
777 ** Read the qPort out of the message so we can fix up
778 ** stupid address translating routers
779 */
780 MSG_BeginReading (&sv_netMessage);
781 MSG_ReadLong (&sv_netMessage); // Sequence number
782 MSG_ReadLong (&sv_netMessage); // Sequence number
783 qPort = MSG_ReadShort (&sv_netMessage) & 0xffff;
784
785 // Check for packets from connected clients
786 for (i=0, cl=svs.clients ; i<maxclients->intVal ; i++, cl++) {
787 if (cl->state == SVCS_FREE)
788 continue;
789 if (!NET_CompareBaseAdr (sv_netFrom, cl->netChan.remoteAddress))
790 continue;
791 if (cl->netChan.qPort != qPort)
792 continue;
793 if (cl->netChan.remoteAddress.port != sv_netFrom.port) {
794 Com_Printf (0, "SV_ReadPackets: fixing up a translated port\n");
795 cl->netChan.remoteAddress.port = sv_netFrom.port;
796 }
797
798 if (Netchan_Process (&cl->netChan, &sv_netMessage)) {
799 // This is a valid, sequenced packet, so process it
800 if (cl->state != SVCS_FREE) {
801 cl->lastMessage = svs.realTime; // Don't timeout
802 SV_ExecuteClientMessage (cl);
803 }
804 }
805 break;
806 }
807 }
808 }
809
810
811 /*
812 ==================
813 SV_CheckTimeouts
814
815 If a packet has not been received from a client for timeout->floatVal
816 seconds, drop the conneciton. Server frames are used instead of
817 realtime to avoid dropping the local client while debugging.
818
819 When a client is normally dropped, the svClient_t goes into a zombie state
820 for a few seconds to make sure any final reliable message gets resent
821 if necessary
822 ==================
823 */
SV_CheckTimeouts(void)824 static void SV_CheckTimeouts (void)
825 {
826 int i;
827 svClient_t *cl;
828 int droppoint;
829 int zombiepoint;
830
831 droppoint = svs.realTime - 1000*timeout->floatVal;
832 zombiepoint = svs.realTime - 1000*zombietime->floatVal;
833
834 for (i=0, cl=svs.clients ; i<maxclients->intVal ; i++, cl++) {
835 // Message times may be wrong across a changelevel
836 if (cl->lastMessage > svs.realTime)
837 cl->lastMessage = svs.realTime;
838
839 if (cl->state == SVCS_FREE && cl->lastMessage < zombiepoint) {
840 cl->state = SVCS_FREE; // Can now be reused
841 continue;
842 }
843
844 if (cl->state >= SVCS_CONNECTED && cl->lastMessage < droppoint) {
845 SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name);
846 SV_DropClient (cl);
847 cl->state = SVCS_FREE; // Don't bother with zombie state
848 }
849 }
850 }
851
852
853 /*
854 ================
855 SV_PrepWorldFrame
856
857 This has to be done before the world logic, because
858 player processing happens outside RunWorldFrame
859 ================
860 */
SV_PrepWorldFrame(void)861 static void SV_PrepWorldFrame (void)
862 {
863 edict_t *ent;
864 int i;
865
866 for (i=0 ; i<ge->numEdicts ; i++, ent++) {
867 ent = EDICT_NUM(i);
868 // Events only last for a single message
869 ent->s.event = 0;
870 }
871 }
872
873
874 /*
875 =================
876 SV_RunGameFrame
877 =================
878 */
SV_RunGameFrame(void)879 static void SV_RunGameFrame (void)
880 {
881 /*
882 ** We always need to bump framenum, even if we don't run the world, otherwise the delta
883 ** compression can get confused when a client has the "current" frame
884 */
885 sv.frameNum++;
886 sv.time = sv.frameNum*100;
887
888 // Don't run if paused
889 if (!sv_paused->intVal || maxclients->intVal > 1) {
890 ge->RunFrame ();
891
892 // Never get more than one tic behind
893 if (sv.time < (uint32)svs.realTime) {
894 if (sv_showclamp->intVal)
895 Com_Printf (0, "sv highclamp\n");
896 svs.realTime = sv.time;
897 }
898 }
899 }
900
901
902 /*
903 ==================
904 SV_Frame
905 ==================
906 */
SV_Frame(int msec)907 void SV_Frame (int msec)
908 {
909 // If server is not active, do nothing
910 if (!svs.initialized)
911 return;
912
913 svs.realTime += msec;
914
915 // Keep the random time dependent
916 rand ();
917
918 // Update console title
919 if (hostname->modified)
920 SV_UpdateTitle ();
921
922 // Check timeouts
923 SV_CheckTimeouts ();
924
925 // Get packets from clients
926 SV_ReadPackets ();
927
928 // Move autonomous things around if enough time has passed
929 if (!sv_timedemo->floatVal && (uint32)svs.realTime < sv.time) {
930 // Never let the time get too far off
931 if (sv.time - svs.realTime > 100) {
932 if (sv_showclamp->intVal)
933 Com_Printf (0, "sv lowclamp\n");
934 svs.realTime = sv.time - 100;
935 }
936 NET_Sleep (sv.time - svs.realTime);
937 return;
938 }
939
940 // Update ping based on the last known frame from all clients
941 SV_CalcPings ();
942
943 // Give the clients some timeslices
944 SV_GiveMsec ();
945
946 // Let everything in the world think and move
947 SV_RunGameFrame ();
948
949 // Send messages back to the clients that had packets read this frame
950 SV_SendClientMessages ();
951
952 // Save the entire world state if recording a serverdemo
953 SV_RecordDemoMessage ();
954
955 // Send a heartbeat to the master if needed
956 SV_MasterHeartbeat ();
957
958 // Clear teleport flags, etc for next frame
959 SV_PrepWorldFrame ();
960
961 }
962
963 //============================================================================
964
965 /*
966 ===============
967 SV_ServerInit
968
969 Only called at egl.exe startup, not for each game
970 ===============
971 */
SV_ServerInit(void)972 void SV_ServerInit (void)
973 {
974 sv_gameSysPool = Mem_CreatePool ("Server: Game system");
975 sv_genericPool = Mem_CreatePool ("Server: Generic");
976
977 SV_OperatorCommandInit ();
978
979 Cvar_Register ("skill", "1", 0);
980 Cvar_Register ("deathmatch", "0", CVAR_SERVERINFO|CVAR_LATCH_SERVER);
981 Cvar_Register ("coop", "0", CVAR_SERVERINFO|CVAR_LATCH_SERVER);
982 Cvar_Register ("dmflags", Q_VarArgs ("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO);
983 Cvar_Register ("fraglimit", "0", CVAR_SERVERINFO);
984 Cvar_Register ("timelimit", "0", CVAR_SERVERINFO);
985 Cvar_Register ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH_SERVER);
986 Cvar_Register ("protocol", Q_VarArgs ("%i", ORIGINAL_PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_READONLY);
987 Cvar_Register ("mapname", "", CVAR_SERVERINFO|CVAR_READONLY);
988
989 rcon_password = Cvar_Register ("rcon_password", "", 0);
990
991 maxclients = Cvar_Register ("maxclients", "1", CVAR_SERVERINFO|CVAR_LATCH_SERVER);
992 hostname = Cvar_Register ("hostname", "noname", CVAR_SERVERINFO|CVAR_ARCHIVE);
993 timeout = Cvar_Register ("timeout", "125", 0);
994 zombietime = Cvar_Register ("zombietime", "2", 0);
995
996 sv_showclamp = Cvar_Register ("showclamp", "0", 0);
997 sv_paused = Cvar_Register ("paused", "0", CVAR_CHEAT);
998 sv_timedemo = Cvar_Register ("timedemo", "0", CVAR_CHEAT);
999
1000 sv_enforcetime = Cvar_Register ("sv_enforcetime", "0", 0);
1001 sv_reconnect_limit = Cvar_Register ("sv_reconnect_limit", "3", CVAR_ARCHIVE);
1002 sv_noreload = Cvar_Register ("sv_noreload", "0", 0);
1003 sv_airaccelerate = Cvar_Register ("sv_airaccelerate", "0", CVAR_LATCH_SERVER);
1004
1005 allow_download = Cvar_Register ("allow_download", "1", CVAR_ARCHIVE);
1006 allow_download_players = Cvar_Register ("allow_download_players", "0", CVAR_ARCHIVE);
1007 allow_download_models = Cvar_Register ("allow_download_models", "1", CVAR_ARCHIVE);
1008 allow_download_sounds = Cvar_Register ("allow_download_sounds", "1", CVAR_ARCHIVE);
1009 allow_download_maps = Cvar_Register ("allow_download_maps", "1", CVAR_ARCHIVE);
1010
1011 public_server = Cvar_Register ("public", "0", 0);
1012
1013 MSG_Init (&sv_netMessage, sv_netBuffer, sizeof (sv_netBuffer));
1014 }
1015
1016
1017 /*
1018 ==================
1019 SV_FinalMessage
1020
1021 Used by SV_ServerShutdown to send a final message to all connected clients before
1022 the server goes down. The messages are sent immediately, not just stuck on
1023 the outgoing message list, because the server is going to totally exit after
1024 returning from this function.
1025 ==================
1026 */
SV_FinalMessage(char * message,qBool reconnect)1027 static void SV_FinalMessage (char *message, qBool reconnect)
1028 {
1029 int i;
1030 svClient_t *cl;
1031
1032 MSG_Clear (&sv_netMessage);
1033 MSG_WriteByte (&sv_netMessage, SVC_PRINT);
1034 MSG_WriteByte (&sv_netMessage, PRINT_HIGH);
1035 MSG_WriteString (&sv_netMessage, message);
1036
1037 if (reconnect)
1038 MSG_WriteByte (&sv_netMessage, SVC_RECONNECT);
1039 else
1040 MSG_WriteByte (&sv_netMessage, SVC_DISCONNECT);
1041
1042 // Send it twice
1043 // stagger the packets to crutch operating system limited buffers
1044 for (i=0, cl=svs.clients ; i<maxclients->intVal ; i++, cl++) {
1045 if (cl->state >= SVCS_CONNECTED)
1046 Netchan_Transmit (&cl->netChan, sv_netMessage.curSize, sv_netMessage.data);
1047 }
1048
1049 for (i=0, cl=svs.clients ; i<maxclients->intVal ; i++, cl++) {
1050 if (cl->state >= SVCS_CONNECTED)
1051 Netchan_Transmit (&cl->netChan, sv_netMessage.curSize, sv_netMessage.data);
1052 }
1053 }
1054
1055
1056 /*
1057 ================
1058 SV_ServerShutdown
1059
1060 Called when each game quits, before Sys_Quit or Sys_Error
1061 ================
1062 */
SV_ServerShutdown(char * finalMessage,qBool reconnect,qBool crashing)1063 void SV_ServerShutdown (char *finalMessage, qBool reconnect, qBool crashing)
1064 {
1065 if (svs.clients)
1066 SV_FinalMessage (finalMessage, reconnect);
1067
1068 SV_MasterShutdown ();
1069
1070 if (!crashing) {
1071 SV_GameAPI_Shutdown ();
1072
1073 // Get latched vars
1074 Cvar_GetLatchedVars (CVAR_LATCH_SERVER);
1075 }
1076
1077 // Free current level
1078 if (sv.demoFile) {
1079 FS_CloseFile (sv.demoFile);
1080 sv.demoFile = 0;
1081 }
1082 memset (&sv, 0, sizeof (serverState_t));
1083 Com_SetServerState (SS_DEAD);
1084
1085 // Free server static data
1086 if (svs.clients)
1087 Mem_Free (svs.clients);
1088 if (svs.clientEntities)
1089 Mem_Free (svs.clientEntities);
1090 if (svs.demoFile)
1091 FS_CloseFile (svs.demoFile);
1092 memset (&svs, 0, sizeof (svs));
1093
1094 // If the server is crashing there's no sense in releasing this memory
1095 // (it may even be the cause! *gasp*)
1096 if (!crashing)
1097 CM_UnloadMap ();
1098
1099 MSG_Clear (&sv_netMessage);
1100 }
1101