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