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