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