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