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