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