1 /*
2 Copyright (C) 1996-1997 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 "qwsvdef.h"
22
23 quakeparms_t host_parms;
24
25 qboolean host_initialized; // true if into command execution (compatability)
26
27 double host_frametime;
28 double realtime; // without any filtering or bounding
29
30 int host_hunklevel;
31
32 netadr_t master_adr[MAX_MASTERS]; // address of group servers
33
34 client_t *host_client; // current client
35
36 cvar_t sv_mintic = {"sv_mintic","0.03"}; // bound the size of the
37 cvar_t sv_maxtic = {"sv_maxtic","0.1"}; // physics time tic
38
39 cvar_t developer = {"developer","0"}; // show extra messages
40
41 cvar_t timeout = {"timeout","65"}; // seconds without any message
42 cvar_t zombietime = {"zombietime", "2"}; // seconds to sink messages
43 // after disconnect
44
45 cvar_t rcon_password = {"rcon_password", ""}; // password for remote server commands
46 cvar_t password = {"password", ""}; // password for entering the game
47 cvar_t spectator_password = {"spectator_password", ""}; // password for entering as a sepctator
48
49 cvar_t allow_download = {"allow_download", "1"};
50 cvar_t allow_download_skins = {"allow_download_skins", "1"};
51 cvar_t allow_download_models = {"allow_download_models", "1"};
52 cvar_t allow_download_sounds = {"allow_download_sounds", "1"};
53 cvar_t allow_download_maps = {"allow_download_maps", "1"};
54
55 cvar_t sv_highchars = {"sv_highchars", "1"};
56
57 cvar_t sv_phs = {"sv_phs", "1"};
58
59 cvar_t pausable = {"pausable", "1"};
60
61
62 //
63 // game rules mirrored in svs.info
64 //
65 cvar_t fraglimit = {"fraglimit","0",false,true};
66 cvar_t timelimit = {"timelimit","0",false,true};
67 cvar_t teamplay = {"teamplay","0",false,true};
68 cvar_t samelevel = {"samelevel","0", false, true};
69 cvar_t maxclients = {"maxclients","8", false, true};
70 cvar_t maxspectators = {"maxspectators","8", false, true};
71 cvar_t deathmatch = {"deathmatch","1", false, true}; // 0, 1, or 2
72 cvar_t spawn = {"spawn","0", false, true};
73 cvar_t watervis = {"watervis", "0", false, true};
74
75 cvar_t hostname = {"hostname","unnamed", false, true};
76
77 FILE *sv_logfile;
78 FILE *sv_fraglogfile;
79
80 void SV_AcceptClient (netadr_t adr, int userid, char *userinfo);
81 void Master_Shutdown (void);
82
83 //============================================================================
84
ServerPaused(void)85 qboolean ServerPaused(void)
86 {
87 return sv.paused;
88 }
89
90 /*
91 ================
92 SV_Shutdown
93
94 Quake calls this before calling Sys_Quit or Sys_Error
95 ================
96 */
SV_Shutdown(void)97 void SV_Shutdown (void)
98 {
99 Master_Shutdown ();
100 if (sv_logfile)
101 {
102 fclose (sv_logfile);
103 sv_logfile = NULL;
104 }
105 if (sv_fraglogfile)
106 {
107 fclose (sv_fraglogfile);
108 sv_logfile = NULL;
109 }
110 NET_Shutdown ();
111 }
112
113 /*
114 ================
115 SV_Error
116
117 Sends a datagram to all the clients informing them of the server crash,
118 then exits
119 ================
120 */
SV_Error(char * error,...)121 void SV_Error (char *error, ...)
122 {
123 va_list argptr;
124 static char string[1024];
125 static qboolean inerror = false;
126
127 if (inerror)
128 Sys_Error ("SV_Error: recursively entered (%s)", string);
129
130 inerror = true;
131
132 va_start (argptr,error);
133 vsprintf (string,error,argptr);
134 va_end (argptr);
135
136 Con_Printf ("SV_Error: %s\n",string);
137
138 SV_FinalMessage (va("server crashed: %s\n", string));
139
140 SV_Shutdown ();
141
142 Sys_Error ("SV_Error: %s\n",string);
143 }
144
145 /*
146 ==================
147 SV_FinalMessage
148
149 Used by SV_Error and SV_Quit_f to send a final message to all connected
150 clients before the server goes down. The messages are sent immediately,
151 not just stuck on the outgoing message list, because the server is going
152 to totally exit after returning from this function.
153 ==================
154 */
SV_FinalMessage(char * message)155 void SV_FinalMessage (char *message)
156 {
157 int i;
158 client_t *cl;
159
160 SZ_Clear (&net_message);
161 MSG_WriteByte (&net_message, svc_print);
162 MSG_WriteByte (&net_message, PRINT_HIGH);
163 MSG_WriteString (&net_message, message);
164 MSG_WriteByte (&net_message, svc_disconnect);
165
166 for (i=0, cl = svs.clients ; i<MAX_CLIENTS ; i++, cl++)
167 if (cl->state >= cs_spawned)
168 Netchan_Transmit (&cl->netchan, net_message.cursize
169 , net_message.data);
170 }
171
172
173
174 /*
175 =====================
176 SV_DropClient
177
178 Called when the player is totally leaving the server, either willingly
179 or unwillingly. This is NOT called if the entire server is quiting
180 or crashing.
181 =====================
182 */
SV_DropClient(client_t * drop)183 void SV_DropClient (client_t *drop)
184 {
185 // add the disconnect
186 MSG_WriteByte (&drop->netchan.message, svc_disconnect);
187
188 if (drop->state == cs_spawned)
189 if (!drop->spectator)
190 {
191 // call the prog function for removing a client
192 // this will set the body to a dead frame, among other things
193 pr_global_struct->self = EDICT_TO_PROG(drop->edict);
194 PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
195 }
196 else if (SpectatorDisconnect)
197 {
198 // call the prog function for removing a client
199 // this will set the body to a dead frame, among other things
200 pr_global_struct->self = EDICT_TO_PROG(drop->edict);
201 PR_ExecuteProgram (SpectatorDisconnect);
202 }
203
204 if (drop->spectator)
205 Con_Printf ("Spectator %s removed\n",drop->name);
206 else
207 Con_Printf ("Client %s removed\n",drop->name);
208
209 if (drop->download)
210 {
211 fclose (drop->download);
212 drop->download = NULL;
213 }
214 if (drop->upload)
215 {
216 fclose (drop->upload);
217 drop->upload = NULL;
218 }
219 *drop->uploadfn = 0;
220
221 drop->state = cs_zombie; // become free in a few seconds
222 drop->connection_started = realtime; // for zombie timeout
223
224 drop->old_frags = 0;
225 drop->edict->v.frags = 0;
226 drop->name[0] = 0;
227 memset (drop->userinfo, 0, sizeof(drop->userinfo));
228
229 // send notification to all remaining clients
230 SV_FullClientUpdate (drop, &sv.reliable_datagram);
231 }
232
233
234 //====================================================================
235
236 /*
237 ===================
238 SV_CalcPing
239
240 ===================
241 */
SV_CalcPing(client_t * cl)242 int SV_CalcPing (client_t *cl)
243 {
244 float ping;
245 int i;
246 int count;
247 register client_frame_t *frame;
248
249 ping = 0;
250 count = 0;
251 for (frame = cl->frames, i=0 ; i<UPDATE_BACKUP ; i++, frame++)
252 {
253 if (frame->ping_time > 0)
254 {
255 ping += frame->ping_time;
256 count++;
257 }
258 }
259 if (!count)
260 return 9999;
261 ping /= count;
262
263 return ping*1000;
264 }
265
266 /*
267 ===================
268 SV_FullClientUpdate
269
270 Writes all update values to a sizebuf
271 ===================
272 */
SV_FullClientUpdate(client_t * client,sizebuf_t * buf)273 void SV_FullClientUpdate (client_t *client, sizebuf_t *buf)
274 {
275 int i;
276 char info[MAX_INFO_STRING];
277
278 i = client - svs.clients;
279
280 //Sys_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i);
281
282 MSG_WriteByte (buf, svc_updatefrags);
283 MSG_WriteByte (buf, i);
284 MSG_WriteShort (buf, client->old_frags);
285
286 MSG_WriteByte (buf, svc_updateping);
287 MSG_WriteByte (buf, i);
288 MSG_WriteShort (buf, SV_CalcPing (client));
289
290 MSG_WriteByte (buf, svc_updatepl);
291 MSG_WriteByte (buf, i);
292 MSG_WriteByte (buf, client->lossage);
293
294 MSG_WriteByte (buf, svc_updateentertime);
295 MSG_WriteByte (buf, i);
296 MSG_WriteFloat (buf, realtime - client->connection_started);
297
298 strcpy (info, client->userinfo);
299 Info_RemovePrefixedKeys (info, '_'); // server passwords, etc
300
301 MSG_WriteByte (buf, svc_updateuserinfo);
302 MSG_WriteByte (buf, i);
303 MSG_WriteLong (buf, client->userid);
304 MSG_WriteString (buf, info);
305 }
306
307 /*
308 ===================
309 SV_FullClientUpdateToClient
310
311 Writes all update values to a client's reliable stream
312 ===================
313 */
SV_FullClientUpdateToClient(client_t * client,client_t * cl)314 void SV_FullClientUpdateToClient (client_t *client, client_t *cl)
315 {
316 ClientReliableCheckBlock(cl, 24 + strlen(client->userinfo));
317 if (cl->num_backbuf) {
318 SV_FullClientUpdate (client, &cl->backbuf);
319 ClientReliable_FinishWrite(cl);
320 } else
321 SV_FullClientUpdate (client, &cl->netchan.message);
322 }
323
324
325 /*
326 ==============================================================================
327
328 CONNECTIONLESS COMMANDS
329
330 ==============================================================================
331 */
332
333 /*
334 ================
335 SVC_Status
336
337 Responds with all the info that qplug or qspy can see
338 This message can be up to around 5k with worst case string lengths.
339 ================
340 */
SVC_Status(void)341 void SVC_Status (void)
342 {
343 int i;
344 client_t *cl;
345 int ping;
346 int top, bottom;
347
348 Cmd_TokenizeString ("status");
349 SV_BeginRedirect (RD_PACKET);
350 Con_Printf ("%s\n", svs.info);
351 for (i=0 ; i<MAX_CLIENTS ; i++)
352 {
353 cl = &svs.clients[i];
354 if ((cl->state == cs_connected || cl->state == cs_spawned ) && !cl->spectator)
355 {
356 top = atoi(Info_ValueForKey (cl->userinfo, "topcolor"));
357 bottom = atoi(Info_ValueForKey (cl->userinfo, "bottomcolor"));
358 top = (top < 0) ? 0 : ((top > 13) ? 13 : top);
359 bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom);
360 ping = SV_CalcPing (cl);
361 Con_Printf ("%i %i %i %i \"%s\" \"%s\" %i %i\n", cl->userid,
362 cl->old_frags, (int)(realtime - cl->connection_started)/60,
363 ping, cl->name, Info_ValueForKey (cl->userinfo, "skin"), top, bottom);
364 }
365 }
366 SV_EndRedirect ();
367 }
368
369 /*
370 ===================
371 SV_CheckLog
372
373 ===================
374 */
375 #define LOG_HIGHWATER 4096
376 #define LOG_FLUSH 10*60
SV_CheckLog(void)377 void SV_CheckLog (void)
378 {
379 sizebuf_t *sz;
380
381 sz = &svs.log[svs.logsequence&1];
382
383 // bump sequence if allmost full, or ten minutes have passed and
384 // there is something still sitting there
385 if (sz->cursize > LOG_HIGHWATER
386 || (realtime - svs.logtime > LOG_FLUSH && sz->cursize) )
387 {
388 // swap buffers and bump sequence
389 svs.logtime = realtime;
390 svs.logsequence++;
391 sz = &svs.log[svs.logsequence&1];
392 sz->cursize = 0;
393 Con_Printf ("beginning fraglog sequence %i\n", svs.logsequence);
394 }
395
396 }
397
398 /*
399 ================
400 SVC_Log
401
402 Responds with all the logged frags for ranking programs.
403 If a sequence number is passed as a parameter and it is
404 the same as the current sequence, an A2A_NACK will be returned
405 instead of the data.
406 ================
407 */
SVC_Log(void)408 void SVC_Log (void)
409 {
410 int seq;
411 char data[MAX_DATAGRAM+64];
412
413 if (Cmd_Argc() == 2)
414 seq = atoi(Cmd_Argv(1));
415 else
416 seq = -1;
417
418 if (seq == svs.logsequence-1 || !sv_fraglogfile)
419 { // they allready have this data, or we aren't logging frags
420 data[0] = A2A_NACK;
421 NET_SendPacket (1, data, net_from);
422 return;
423 }
424
425 Con_DPrintf ("sending log %i to %s\n", svs.logsequence-1, NET_AdrToString(net_from));
426
427 sprintf (data, "stdlog %i\n", svs.logsequence-1);
428 strcat (data, (char *)svs.log_buf[((svs.logsequence-1)&1)]);
429
430 NET_SendPacket (strlen(data)+1, data, net_from);
431 }
432
433 /*
434 ================
435 SVC_Ping
436
437 Just responds with an acknowledgement
438 ================
439 */
SVC_Ping(void)440 void SVC_Ping (void)
441 {
442 char data;
443
444 data = A2A_ACK;
445
446 NET_SendPacket (1, &data, net_from);
447 }
448
449 /*
450 =================
451 SVC_GetChallenge
452
453 Returns a challenge number that can be used
454 in a subsequent client_connect command.
455 We do this to prevent denial of service attacks that
456 flood the server with invalid connection IPs. With a
457 challenge, they must give a valid IP address.
458 =================
459 */
SVC_GetChallenge(void)460 void SVC_GetChallenge (void)
461 {
462 int i;
463 int oldest;
464 int oldestTime;
465
466 oldest = 0;
467 oldestTime = 0x7fffffff;
468
469 // see if we already have a challenge for this ip
470 for (i = 0 ; i < MAX_CHALLENGES ; i++)
471 {
472 if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
473 break;
474 if (svs.challenges[i].time < oldestTime)
475 {
476 oldestTime = svs.challenges[i].time;
477 oldest = i;
478 }
479 }
480
481 if (i == MAX_CHALLENGES)
482 {
483 // overwrite the oldest
484 svs.challenges[oldest].challenge = (rand() << 16) ^ rand();
485 svs.challenges[oldest].adr = net_from;
486 svs.challenges[oldest].time = realtime;
487 i = oldest;
488 }
489
490 // send it back
491 Netchan_OutOfBandPrint (net_from, "%c%i", S2C_CHALLENGE,
492 svs.challenges[i].challenge);
493 }
494
495 /*
496 ==================
497 SVC_DirectConnect
498
499 A connection request that did not come from the master
500 ==================
501 */
SVC_DirectConnect(void)502 void SVC_DirectConnect (void)
503 {
504 char userinfo[1024];
505 static int userid;
506 netadr_t adr;
507 int i;
508 client_t *cl, *newcl;
509 client_t temp;
510 edict_t *ent;
511 int edictnum;
512 char *s;
513 int clients, spectators;
514 qboolean spectator;
515 int qport;
516 int version;
517 int challenge;
518
519 version = atoi(Cmd_Argv(1));
520 if (version != PROTOCOL_VERSION)
521 {
522 Netchan_OutOfBandPrint (net_from, "%c\nServer is version %4.2f.\n", A2C_PRINT, VERSION);
523 Con_Printf ("* rejected connect from version %i\n", version);
524 return;
525 }
526
527 qport = atoi(Cmd_Argv(2));
528
529 challenge = atoi(Cmd_Argv(3));
530
531 // note an extra byte is needed to replace spectator key
532 strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-2);
533 userinfo[sizeof(userinfo) - 2] = 0;
534
535 // see if the challenge is valid
536 for (i=0 ; i<MAX_CHALLENGES ; i++)
537 {
538 if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
539 {
540 if (challenge == svs.challenges[i].challenge)
541 break; // good
542 Netchan_OutOfBandPrint (net_from, "%c\nBad challenge.\n", A2C_PRINT);
543 return;
544 }
545 }
546 if (i == MAX_CHALLENGES)
547 {
548 Netchan_OutOfBandPrint (net_from, "%c\nNo challenge for address.\n", A2C_PRINT);
549 return;
550 }
551
552 // check for password or spectator_password
553 s = Info_ValueForKey (userinfo, "spectator");
554 if (s[0] && strcmp(s, "0"))
555 {
556 if (spectator_password.string[0] &&
557 stricmp(spectator_password.string, "none") &&
558 strcmp(spectator_password.string, s) )
559 { // failed
560 Con_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from));
561 Netchan_OutOfBandPrint (net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT);
562 return;
563 }
564 Info_RemoveKey (userinfo, "spectator"); // remove passwd
565 Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING);
566 spectator = true;
567 }
568 else
569 {
570 s = Info_ValueForKey (userinfo, "password");
571 if (password.string[0] &&
572 stricmp(password.string, "none") &&
573 strcmp(password.string, s) )
574 {
575 Con_Printf ("%s:password failed\n", NET_AdrToString (net_from));
576 Netchan_OutOfBandPrint (net_from, "%c\nserver requires a password\n\n", A2C_PRINT);
577 return;
578 }
579 spectator = false;
580 Info_RemoveKey (userinfo, "password"); // remove passwd
581 }
582
583 adr = net_from;
584 userid++; // so every client gets a unique id
585
586 newcl = &temp;
587 memset (newcl, 0, sizeof(client_t));
588
589 newcl->userid = userid;
590
591 // works properly
592 if (!sv_highchars.value) {
593 byte *p, *q;
594
595 for (p = (byte *)newcl->userinfo, q = (byte *)userinfo;
596 *q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++)
597 if (*q > 31 && *q <= 127)
598 *p++ = *q;
599 } else
600 strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1);
601
602 // if there is allready a slot for this ip, drop it
603 for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
604 {
605 if (cl->state == cs_free)
606 continue;
607 if (NET_CompareBaseAdr (adr, cl->netchan.remote_address)
608 && ( cl->netchan.qport == qport
609 || adr.port == cl->netchan.remote_address.port ))
610 {
611 if (cl->state == cs_connected) {
612 Con_Printf("%s:dup connect\n", NET_AdrToString (adr));
613 userid--;
614 return;
615 }
616
617 Con_Printf ("%s:reconnect\n", NET_AdrToString (adr));
618 SV_DropClient (cl);
619 break;
620 }
621 }
622
623 // count up the clients and spectators
624 clients = 0;
625 spectators = 0;
626 for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
627 {
628 if (cl->state == cs_free)
629 continue;
630 if (cl->spectator)
631 spectators++;
632 else
633 clients++;
634 }
635
636 // if at server limits, refuse connection
637 if ( maxclients.value > MAX_CLIENTS )
638 Cvar_SetValue ("maxclients", MAX_CLIENTS);
639 if (maxspectators.value > MAX_CLIENTS)
640 Cvar_SetValue ("maxspectators", MAX_CLIENTS);
641 if (maxspectators.value + maxclients.value > MAX_CLIENTS)
642 Cvar_SetValue ("maxspectators", MAX_CLIENTS - maxspectators.value + maxclients.value);
643 if ( (spectator && spectators >= (int)maxspectators.value)
644 || (!spectator && clients >= (int)maxclients.value) )
645 {
646 Con_Printf ("%s:full connect\n", NET_AdrToString (adr));
647 Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT);
648 return;
649 }
650
651 // find a client slot
652 newcl = NULL;
653 for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
654 {
655 if (cl->state == cs_free)
656 {
657 newcl = cl;
658 break;
659 }
660 }
661 if (!newcl)
662 {
663 Con_Printf ("WARNING: miscounted available clients\n");
664 return;
665 }
666
667
668 // build a new connection
669 // accept the new client
670 // this is the only place a client_t is ever initialized
671 *newcl = temp;
672
673 Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION );
674
675 edictnum = (newcl-svs.clients)+1;
676
677 Netchan_Setup (&newcl->netchan , adr, qport);
678
679 newcl->state = cs_connected;
680
681 newcl->datagram.allowoverflow = true;
682 newcl->datagram.data = newcl->datagram_buf;
683 newcl->datagram.maxsize = sizeof(newcl->datagram_buf);
684
685 // spectator mode can ONLY be set at join time
686 newcl->spectator = spectator;
687
688 ent = EDICT_NUM(edictnum);
689 newcl->edict = ent;
690
691 // parse some info from the info strings
692 SV_ExtractFromUserinfo (newcl);
693
694 // JACK: Init the floodprot stuff.
695 for (i=0; i<10; i++)
696 newcl->whensaid[i] = 0.0;
697 newcl->whensaidhead = 0;
698 newcl->lockedtill = 0;
699
700 // call the progs to get default spawn parms for the new client
701 PR_ExecuteProgram (pr_global_struct->SetNewParms);
702 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
703 newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i];
704
705 if (newcl->spectator)
706 Con_Printf ("Spectator %s connected\n", newcl->name);
707 else
708 Con_DPrintf ("Client %s connected\n", newcl->name);
709 newcl->sendinfo = true;
710 }
711
Rcon_Validate(void)712 int Rcon_Validate (void)
713 {
714 if (!strlen (rcon_password.string))
715 return 0;
716
717 if (strcmp (Cmd_Argv(1), rcon_password.string) )
718 return 0;
719
720 return 1;
721 }
722
723 /*
724 ===============
725 SVC_RemoteCommand
726
727 A client issued an rcon command.
728 Shift down the remaining args
729 Redirect all printfs
730 ===============
731 */
SVC_RemoteCommand(void)732 void SVC_RemoteCommand (void)
733 {
734 int i;
735 char remaining[1024];
736
737
738 if (!Rcon_Validate ()) {
739 Con_Printf ("Bad rcon from %s:\n%s\n"
740 , NET_AdrToString (net_from), net_message.data+4);
741
742 SV_BeginRedirect (RD_PACKET);
743
744 Con_Printf ("Bad rcon_password.\n");
745
746 } else {
747
748 Con_Printf ("Rcon from %s:\n%s\n"
749 , NET_AdrToString (net_from), net_message.data+4);
750
751 SV_BeginRedirect (RD_PACKET);
752
753 remaining[0] = 0;
754
755 for (i=2 ; i<Cmd_Argc() ; i++)
756 {
757 strcat (remaining, Cmd_Argv(i) );
758 strcat (remaining, " ");
759 }
760
761 Cmd_ExecuteString (remaining);
762
763 }
764
765 SV_EndRedirect ();
766 }
767
768
769 /*
770 =================
771 SV_ConnectionlessPacket
772
773 A connectionless packet has four leading 0xff
774 characters to distinguish it from a game channel.
775 Clients that are in the game can still send
776 connectionless packets.
777 =================
778 */
SV_ConnectionlessPacket(void)779 void SV_ConnectionlessPacket (void)
780 {
781 char *s;
782 char *c;
783
784 MSG_BeginReading ();
785 MSG_ReadLong (); // skip the -1 marker
786
787 s = MSG_ReadStringLine ();
788
789 Cmd_TokenizeString (s);
790
791 c = Cmd_Argv(0);
792
793 if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) )
794 {
795 SVC_Ping ();
796 return;
797 }
798 if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n') )
799 {
800 Con_Printf ("A2A_ACK from %s\n", NET_AdrToString (net_from));
801 return;
802 }
803 else if (!strcmp(c,"status"))
804 {
805 SVC_Status ();
806 return;
807 }
808 else if (!strcmp(c,"log"))
809 {
810 SVC_Log ();
811 return;
812 }
813 else if (!strcmp(c,"connect"))
814 {
815 SVC_DirectConnect ();
816 return;
817 }
818 else if (!strcmp(c,"getchallenge"))
819 {
820 SVC_GetChallenge ();
821 return;
822 }
823 else if (!strcmp(c, "rcon"))
824 SVC_RemoteCommand ();
825 else
826 Con_Printf ("bad connectionless packet from %s:\n%s\n"
827 , NET_AdrToString (net_from), s);
828 }
829
830 /*
831 ==============================================================================
832
833 PACKET FILTERING
834
835
836 You can add or remove addresses from the filter list with:
837
838 addip <ip>
839 removeip <ip>
840
841 The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
842
843 Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host.
844
845 listip
846 Prints the current list of filters.
847
848 writeip
849 Dumps "addip <ip>" commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion.
850
851 filterban <0 or 1>
852
853 If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting.
854
855 If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network.
856
857
858 ==============================================================================
859 */
860
861
862 typedef struct
863 {
864 unsigned mask;
865 unsigned compare;
866 } ipfilter_t;
867
868 #define MAX_IPFILTERS 1024
869
870 ipfilter_t ipfilters[MAX_IPFILTERS];
871 int numipfilters;
872
873 cvar_t filterban = {"filterban", "1"};
874
875 /*
876 =================
877 StringToFilter
878 =================
879 */
StringToFilter(char * s,ipfilter_t * f)880 qboolean StringToFilter (char *s, ipfilter_t *f)
881 {
882 char num[128];
883 int i, j;
884 byte b[4];
885 byte m[4];
886
887 for (i=0 ; i<4 ; i++)
888 {
889 b[i] = 0;
890 m[i] = 0;
891 }
892
893 for (i=0 ; i<4 ; i++)
894 {
895 if (*s < '0' || *s > '9')
896 {
897 Con_Printf ("Bad filter address: %s\n", s);
898 return false;
899 }
900
901 j = 0;
902 while (*s >= '0' && *s <= '9')
903 {
904 num[j++] = *s++;
905 }
906 num[j] = 0;
907 b[i] = atoi(num);
908 if (b[i] != 0)
909 m[i] = 255;
910
911 if (!*s)
912 break;
913 s++;
914 }
915
916 f->mask = *(unsigned *)m;
917 f->compare = *(unsigned *)b;
918
919 return true;
920 }
921
922 /*
923 =================
924 SV_AddIP_f
925 =================
926 */
SV_AddIP_f(void)927 void SV_AddIP_f (void)
928 {
929 int i;
930
931 for (i=0 ; i<numipfilters ; i++)
932 if (ipfilters[i].compare == 0xffffffff)
933 break; // free spot
934 if (i == numipfilters)
935 {
936 if (numipfilters == MAX_IPFILTERS)
937 {
938 Con_Printf ("IP filter list is full\n");
939 return;
940 }
941 numipfilters++;
942 }
943
944 if (!StringToFilter (Cmd_Argv(1), &ipfilters[i]))
945 ipfilters[i].compare = 0xffffffff;
946 }
947
948 /*
949 =================
950 SV_RemoveIP_f
951 =================
952 */
SV_RemoveIP_f(void)953 void SV_RemoveIP_f (void)
954 {
955 ipfilter_t f;
956 int i, j;
957
958 if (!StringToFilter (Cmd_Argv(1), &f))
959 return;
960 for (i=0 ; i<numipfilters ; i++)
961 if (ipfilters[i].mask == f.mask
962 && ipfilters[i].compare == f.compare)
963 {
964 for (j=i+1 ; j<numipfilters ; j++)
965 ipfilters[j-1] = ipfilters[j];
966 numipfilters--;
967 Con_Printf ("Removed.\n");
968 return;
969 }
970 Con_Printf ("Didn't find %s.\n", Cmd_Argv(1));
971 }
972
973 /*
974 =================
975 SV_ListIP_f
976 =================
977 */
SV_ListIP_f(void)978 void SV_ListIP_f (void)
979 {
980 int i;
981 byte b[4];
982
983 Con_Printf ("Filter list:\n");
984 for (i=0 ; i<numipfilters ; i++)
985 {
986 *(unsigned *)b = ipfilters[i].compare;
987 Con_Printf ("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
988 }
989 }
990
991 /*
992 =================
993 SV_WriteIP_f
994 =================
995 */
SV_WriteIP_f(void)996 void SV_WriteIP_f (void)
997 {
998 FILE *f;
999 char name[MAX_OSPATH];
1000 byte b[4];
1001 int i;
1002
1003 sprintf (name, "%s/listip.cfg", com_gamedir);
1004
1005 Con_Printf ("Writing %s.\n", name);
1006
1007 f = fopen (name, "wb");
1008 if (!f)
1009 {
1010 Con_Printf ("Couldn't open %s\n", name);
1011 return;
1012 }
1013
1014 for (i=0 ; i<numipfilters ; i++)
1015 {
1016 *(unsigned *)b = ipfilters[i].compare;
1017 fprintf (f, "addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
1018 }
1019
1020 fclose (f);
1021 }
1022
1023 /*
1024 =================
1025 SV_SendBan
1026 =================
1027 */
SV_SendBan(void)1028 void SV_SendBan (void)
1029 {
1030 char data[128];
1031
1032 data[0] = data[1] = data[2] = data[3] = 0xff;
1033 data[4] = A2C_PRINT;
1034 data[5] = 0;
1035 strcat (data, "\nbanned.\n");
1036
1037 NET_SendPacket (strlen(data), data, net_from);
1038 }
1039
1040 /*
1041 =================
1042 SV_FilterPacket
1043 =================
1044 */
SV_FilterPacket(void)1045 qboolean SV_FilterPacket (void)
1046 {
1047 int i;
1048 unsigned in;
1049
1050 in = *(unsigned *)net_from.ip;
1051
1052 for (i=0 ; i<numipfilters ; i++)
1053 if ( (in & ipfilters[i].mask) == ipfilters[i].compare)
1054 return filterban.value;
1055
1056 return !filterban.value;
1057 }
1058
1059 //============================================================================
1060
1061 /*
1062 =================
1063 SV_ReadPackets
1064 =================
1065 */
SV_ReadPackets(void)1066 void SV_ReadPackets (void)
1067 {
1068 int i;
1069 client_t *cl;
1070 qboolean good;
1071 int qport;
1072
1073 good = false;
1074 while (NET_GetPacket ())
1075 {
1076 if (SV_FilterPacket ())
1077 {
1078 SV_SendBan (); // tell them we aren't listening...
1079 continue;
1080 }
1081
1082 // check for connectionless packet (0xffffffff) first
1083 if (*(int *)net_message.data == -1)
1084 {
1085 SV_ConnectionlessPacket ();
1086 continue;
1087 }
1088
1089 // read the qport out of the message so we can fix up
1090 // stupid address translating routers
1091 MSG_BeginReading ();
1092 MSG_ReadLong (); // sequence number
1093 MSG_ReadLong (); // sequence number
1094 qport = MSG_ReadShort () & 0xffff;
1095
1096 // check for packets from connected clients
1097 for (i=0, cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
1098 {
1099 if (cl->state == cs_free)
1100 continue;
1101 if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address))
1102 continue;
1103 if (cl->netchan.qport != qport)
1104 continue;
1105 if (cl->netchan.remote_address.port != net_from.port)
1106 {
1107 Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n");
1108 cl->netchan.remote_address.port = net_from.port;
1109 }
1110 if (Netchan_Process(&cl->netchan))
1111 { // this is a valid, sequenced packet, so process it
1112 svs.stats.packets++;
1113 good = true;
1114 cl->send_message = true; // reply at end of frame
1115 if (cl->state != cs_zombie)
1116 SV_ExecuteClientMessage (cl);
1117 }
1118 break;
1119 }
1120
1121 if (i != MAX_CLIENTS)
1122 continue;
1123
1124 // packet is not from a known client
1125 // Con_Printf ("%s:sequenced packet without connection\n"
1126 // ,NET_AdrToString(net_from));
1127 }
1128 }
1129
1130 /*
1131 ==================
1132 SV_CheckTimeouts
1133
1134 If a packet has not been received from a client in timeout.value
1135 seconds, drop the conneciton.
1136
1137 When a client is normally dropped, the client_t goes into a zombie state
1138 for a few seconds to make sure any final reliable message gets resent
1139 if necessary
1140 ==================
1141 */
SV_CheckTimeouts(void)1142 void SV_CheckTimeouts (void)
1143 {
1144 int i;
1145 client_t *cl;
1146 float droptime;
1147 int nclients;
1148
1149 droptime = realtime - timeout.value;
1150 nclients = 0;
1151
1152 for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
1153 {
1154 if (cl->state == cs_connected || cl->state == cs_spawned) {
1155 if (!cl->spectator)
1156 nclients++;
1157 if (cl->netchan.last_received < droptime) {
1158 SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name);
1159 SV_DropClient (cl);
1160 cl->state = cs_free; // don't bother with zombie state
1161 }
1162 }
1163 if (cl->state == cs_zombie &&
1164 realtime - cl->connection_started > zombietime.value)
1165 {
1166 cl->state = cs_free; // can now be reused
1167 }
1168 }
1169 if (sv.paused && !nclients) {
1170 // nobody left, unpause the server
1171 SV_TogglePause("Pause released since no players are left.\n");
1172 }
1173 }
1174
1175 /*
1176 ===================
1177 SV_GetConsoleCommands
1178
1179 Add them exactly as if they had been typed at the console
1180 ===================
1181 */
SV_GetConsoleCommands(void)1182 void SV_GetConsoleCommands (void)
1183 {
1184 char *cmd;
1185
1186 while (1)
1187 {
1188 cmd = Sys_ConsoleInput ();
1189 if (!cmd)
1190 break;
1191 Cbuf_AddText (cmd);
1192 }
1193 }
1194
1195 /*
1196 ===================
1197 SV_CheckVars
1198
1199 ===================
1200 */
SV_CheckVars(void)1201 void SV_CheckVars (void)
1202 {
1203 static char *pw, *spw;
1204 int v;
1205
1206 if (password.string == pw && spectator_password.string == spw)
1207 return;
1208 pw = password.string;
1209 spw = spectator_password.string;
1210
1211 v = 0;
1212 if (pw && pw[0] && strcmp(pw, "none"))
1213 v |= 1;
1214 if (spw && spw[0] && strcmp(spw, "none"))
1215 v |= 2;
1216
1217 Con_Printf ("Updated needpass.\n");
1218 if (!v)
1219 Info_SetValueForKey (svs.info, "needpass", "", MAX_SERVERINFO_STRING);
1220 else
1221 Info_SetValueForKey (svs.info, "needpass", va("%i",v), MAX_SERVERINFO_STRING);
1222 }
1223
1224 /*
1225 ==================
1226 SV_Frame
1227
1228 ==================
1229 */
SV_Frame(float time)1230 void SV_Frame (float time)
1231 {
1232 static double start, end;
1233
1234 start = Sys_DoubleTime ();
1235 svs.stats.idle += start - end;
1236
1237 // keep the random time dependent
1238 rand ();
1239
1240 // decide the simulation time
1241 if (!sv.paused) {
1242 realtime += time;
1243 sv.time += time;
1244 }
1245
1246 // check timeouts
1247 SV_CheckTimeouts ();
1248
1249 // toggle the log buffer if full
1250 SV_CheckLog ();
1251
1252 // move autonomous things around if enough time has passed
1253 if (!sv.paused)
1254 SV_Physics ();
1255
1256 // get packets
1257 SV_ReadPackets ();
1258
1259 // check for commands typed to the host
1260 SV_GetConsoleCommands ();
1261
1262 // process console commands
1263 Cbuf_Execute ();
1264
1265 SV_CheckVars ();
1266
1267 // send messages back to the clients that had packets read this frame
1268 SV_SendClientMessages ();
1269
1270 // send a heartbeat to the master if needed
1271 Master_Heartbeat ();
1272
1273 // collect timing statistics
1274 end = Sys_DoubleTime ();
1275 svs.stats.active += end-start;
1276 if (++svs.stats.count == STATFRAMES)
1277 {
1278 svs.stats.latched_active = svs.stats.active;
1279 svs.stats.latched_idle = svs.stats.idle;
1280 svs.stats.latched_packets = svs.stats.packets;
1281 svs.stats.active = 0;
1282 svs.stats.idle = 0;
1283 svs.stats.packets = 0;
1284 svs.stats.count = 0;
1285 }
1286 }
1287
1288 /*
1289 ===============
1290 SV_InitLocal
1291 ===============
1292 */
SV_InitLocal(void)1293 void SV_InitLocal (void)
1294 {
1295 int i;
1296 extern cvar_t sv_maxvelocity;
1297 extern cvar_t sv_gravity;
1298 extern cvar_t sv_aim;
1299 extern cvar_t sv_stopspeed;
1300 extern cvar_t sv_spectatormaxspeed;
1301 extern cvar_t sv_accelerate;
1302 extern cvar_t sv_airaccelerate;
1303 extern cvar_t sv_wateraccelerate;
1304 extern cvar_t sv_friction;
1305 extern cvar_t sv_waterfriction;
1306
1307 SV_InitOperatorCommands ();
1308 SV_UserInit ();
1309
1310 Cvar_RegisterVariable (&rcon_password);
1311 Cvar_RegisterVariable (&password);
1312 Cvar_RegisterVariable (&spectator_password);
1313
1314 Cvar_RegisterVariable (&sv_mintic);
1315 Cvar_RegisterVariable (&sv_maxtic);
1316
1317 Cvar_RegisterVariable (&fraglimit);
1318 Cvar_RegisterVariable (&timelimit);
1319 Cvar_RegisterVariable (&teamplay);
1320 Cvar_RegisterVariable (&samelevel);
1321 Cvar_RegisterVariable (&maxclients);
1322 Cvar_RegisterVariable (&maxspectators);
1323 Cvar_RegisterVariable (&hostname);
1324 Cvar_RegisterVariable (&deathmatch);
1325 Cvar_RegisterVariable (&spawn);
1326 Cvar_RegisterVariable (&watervis);
1327
1328 Cvar_RegisterVariable (&developer);
1329
1330 Cvar_RegisterVariable (&timeout);
1331 Cvar_RegisterVariable (&zombietime);
1332
1333 Cvar_RegisterVariable (&sv_maxvelocity);
1334 Cvar_RegisterVariable (&sv_gravity);
1335 Cvar_RegisterVariable (&sv_stopspeed);
1336 Cvar_RegisterVariable (&sv_maxspeed);
1337 Cvar_RegisterVariable (&sv_spectatormaxspeed);
1338 Cvar_RegisterVariable (&sv_accelerate);
1339 Cvar_RegisterVariable (&sv_airaccelerate);
1340 Cvar_RegisterVariable (&sv_wateraccelerate);
1341 Cvar_RegisterVariable (&sv_friction);
1342 Cvar_RegisterVariable (&sv_waterfriction);
1343
1344 Cvar_RegisterVariable (&sv_aim);
1345
1346 Cvar_RegisterVariable (&filterban);
1347
1348 Cvar_RegisterVariable (&allow_download);
1349 Cvar_RegisterVariable (&allow_download_skins);
1350 Cvar_RegisterVariable (&allow_download_models);
1351 Cvar_RegisterVariable (&allow_download_sounds);
1352 Cvar_RegisterVariable (&allow_download_maps);
1353
1354 Cvar_RegisterVariable (&sv_highchars);
1355
1356 Cvar_RegisterVariable (&sv_phs);
1357
1358 Cvar_RegisterVariable (&pausable);
1359
1360 Cmd_AddCommand ("addip", SV_AddIP_f);
1361 Cmd_AddCommand ("removeip", SV_RemoveIP_f);
1362 Cmd_AddCommand ("listip", SV_ListIP_f);
1363 Cmd_AddCommand ("writeip", SV_WriteIP_f);
1364
1365 for (i=0 ; i<MAX_MODELS ; i++)
1366 sprintf (localmodels[i], "*%i", i);
1367
1368 Info_SetValueForStarKey (svs.info, "*version", va("%4.2f", VERSION), MAX_SERVERINFO_STRING);
1369
1370 // init fraglog stuff
1371 svs.logsequence = 1;
1372 svs.logtime = realtime;
1373 svs.log[0].data = svs.log_buf[0];
1374 svs.log[0].maxsize = sizeof(svs.log_buf[0]);
1375 svs.log[0].cursize = 0;
1376 svs.log[0].allowoverflow = true;
1377 svs.log[1].data = svs.log_buf[1];
1378 svs.log[1].maxsize = sizeof(svs.log_buf[1]);
1379 svs.log[1].cursize = 0;
1380 svs.log[1].allowoverflow = true;
1381 }
1382
1383
1384 //============================================================================
1385
1386 /*
1387 ================
1388 Master_Heartbeat
1389
1390 Send a message to the master every few minutes to
1391 let it know we are alive, and log information
1392 ================
1393 */
1394 #define HEARTBEAT_SECONDS 300
Master_Heartbeat(void)1395 void Master_Heartbeat (void)
1396 {
1397 char string[2048];
1398 int active;
1399 int i;
1400
1401 if (realtime - svs.last_heartbeat < HEARTBEAT_SECONDS)
1402 return; // not time to send yet
1403
1404 svs.last_heartbeat = realtime;
1405
1406 //
1407 // count active users
1408 //
1409 active = 0;
1410 for (i=0 ; i<MAX_CLIENTS ; i++)
1411 if (svs.clients[i].state == cs_connected ||
1412 svs.clients[i].state == cs_spawned )
1413 active++;
1414
1415 svs.heartbeat_sequence++;
1416 sprintf (string, "%c\n%i\n%i\n", S2M_HEARTBEAT,
1417 svs.heartbeat_sequence, active);
1418
1419
1420 // send to group master
1421 for (i=0 ; i<MAX_MASTERS ; i++)
1422 if (master_adr[i].port)
1423 {
1424 Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
1425 NET_SendPacket (strlen(string), string, master_adr[i]);
1426 }
1427 }
1428
1429 /*
1430 =================
1431 Master_Shutdown
1432
1433 Informs all masters that this server is going down
1434 =================
1435 */
Master_Shutdown(void)1436 void Master_Shutdown (void)
1437 {
1438 char string[2048];
1439 int i;
1440
1441 sprintf (string, "%c\n", S2M_SHUTDOWN);
1442
1443 // send to group master
1444 for (i=0 ; i<MAX_MASTERS ; i++)
1445 if (master_adr[i].port)
1446 {
1447 Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
1448 NET_SendPacket (strlen(string), string, master_adr[i]);
1449 }
1450 }
1451
1452 /*
1453 =================
1454 SV_ExtractFromUserinfo
1455
1456 Pull specific info from a newly changed userinfo string
1457 into a more C freindly form.
1458 =================
1459 */
SV_ExtractFromUserinfo(client_t * cl)1460 void SV_ExtractFromUserinfo (client_t *cl)
1461 {
1462 char *val, *p, *q;
1463 int i;
1464 client_t *client;
1465 int dupc = 1;
1466 char newname[80];
1467
1468
1469 // name for C code
1470 val = Info_ValueForKey (cl->userinfo, "name");
1471
1472 // trim user name
1473 strncpy(newname, val, sizeof(newname) - 1);
1474 newname[sizeof(newname) - 1] = 0;
1475
1476 for (p = newname; (*p == ' ' || *p == '\r' || *p == '\n') && *p; p++)
1477 ;
1478
1479 if (p != newname && !*p) {
1480 //white space only
1481 strcpy(newname, "unnamed");
1482 p = newname;
1483 }
1484
1485 if (p != newname && *p) {
1486 for (q = newname; *p; *q++ = *p++)
1487 ;
1488 *q = 0;
1489 }
1490 for (p = newname + strlen(newname) - 1; p != newname && (*p == ' ' || *p == '\r' || *p == '\n') ; p--)
1491 ;
1492 p[1] = 0;
1493
1494 if (strcmp(val, newname)) {
1495 Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING);
1496 val = Info_ValueForKey (cl->userinfo, "name");
1497 }
1498
1499 if (!val[0] || !stricmp(val, "console")) {
1500 Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING);
1501 val = Info_ValueForKey (cl->userinfo, "name");
1502 }
1503
1504 // check to see if another user by the same name exists
1505 while (1) {
1506 for (i=0, client = svs.clients ; i<MAX_CLIENTS ; i++, client++) {
1507 if (client->state != cs_spawned || client == cl)
1508 continue;
1509 if (!stricmp(client->name, val))
1510 break;
1511 }
1512 if (i != MAX_CLIENTS) { // dup name
1513 if (strlen(val) > sizeof(cl->name) - 1)
1514 val[sizeof(cl->name) - 4] = 0;
1515 p = val;
1516
1517 if (val[0] == '(')
1518 if (val[2] == ')')
1519 p = val + 3;
1520 else if (val[3] == ')')
1521 p = val + 4;
1522
1523 sprintf(newname, "(%d)%-.40s", dupc++, p);
1524 Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING);
1525 val = Info_ValueForKey (cl->userinfo, "name");
1526 } else
1527 break;
1528 }
1529
1530 if (strncmp(val, cl->name, strlen(cl->name))) {
1531 if (!sv.paused) {
1532 if (!cl->lastnametime || realtime - cl->lastnametime > 5) {
1533 cl->lastnamecount = 0;
1534 cl->lastnametime = realtime;
1535 } else if (cl->lastnamecount++ > 4) {
1536 SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for name spam\n", cl->name);
1537 SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game for name spamming\n");
1538 SV_DropClient (cl);
1539 return;
1540 }
1541 }
1542
1543 if (cl->state >= cs_spawned && !cl->spectator)
1544 SV_BroadcastPrintf (PRINT_HIGH, "%s changed name to %s\n", cl->name, val);
1545 }
1546
1547
1548 strncpy (cl->name, val, sizeof(cl->name)-1);
1549
1550 // rate command
1551 val = Info_ValueForKey (cl->userinfo, "rate");
1552 if (strlen(val))
1553 {
1554 i = atoi(val);
1555 if (i < 500)
1556 i = 500;
1557 if (i > 10000)
1558 i = 10000;
1559 cl->netchan.rate = 1.0/i;
1560 }
1561
1562 // msg command
1563 val = Info_ValueForKey (cl->userinfo, "msg");
1564 if (strlen(val))
1565 {
1566 cl->messagelevel = atoi(val);
1567 }
1568
1569 }
1570
1571
1572 //============================================================================
1573
1574 /*
1575 ====================
1576 SV_InitNet
1577 ====================
1578 */
SV_InitNet(void)1579 void SV_InitNet (void)
1580 {
1581 int port;
1582 int p;
1583
1584 port = PORT_SERVER;
1585 p = COM_CheckParm ("-port");
1586 if (p && p < com_argc)
1587 {
1588 port = atoi(com_argv[p+1]);
1589 Con_Printf ("Port: %i\n", port);
1590 }
1591 NET_Init (port);
1592
1593 Netchan_Init ();
1594
1595 // heartbeats will allways be sent to the id master
1596 svs.last_heartbeat = -99999; // send immediately
1597 // NET_StringToAdr ("192.246.40.70:27000", &idmaster_adr);
1598 }
1599
1600
1601 /*
1602 ====================
1603 SV_Init
1604 ====================
1605 */
SV_Init(quakeparms_t * parms)1606 void SV_Init (quakeparms_t *parms)
1607 {
1608 COM_InitArgv (parms->argc, parms->argv);
1609 COM_AddParm ("-game");
1610 COM_AddParm ("qw");
1611
1612 if (COM_CheckParm ("-minmemory"))
1613 parms->memsize = MINIMUM_MEMORY;
1614
1615 host_parms = *parms;
1616
1617 if (parms->memsize < MINIMUM_MEMORY)
1618 SV_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000);
1619
1620 Memory_Init (parms->membase, parms->memsize);
1621 Cbuf_Init ();
1622 Cmd_Init ();
1623
1624 COM_Init ();
1625
1626 PR_Init ();
1627 Mod_Init ();
1628
1629 SV_InitNet ();
1630
1631 SV_InitLocal ();
1632 Sys_Init ();
1633 Pmove_Init ();
1634
1635 Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
1636 host_hunklevel = Hunk_LowMark ();
1637
1638 Cbuf_InsertText ("exec server.cfg\n");
1639
1640 host_initialized = true;
1641
1642 Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
1643 Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0));
1644
1645 Con_Printf ("\nServer Version %4.2f (Build %04d)\n\n", VERSION, build_number());
1646
1647 Con_Printf ("======== QuakeWorld Initialized ========\n");
1648
1649 // process command line arguments
1650 Cmd_StuffCmds_f ();
1651 Cbuf_Execute ();
1652
1653 // if a map wasn't specified on the command line, spawn start.map
1654 if (sv.state == ss_dead)
1655 Cmd_ExecuteString ("map start");
1656 if (sv.state == ss_dead)
1657 SV_Error ("Couldn't spawn a server");
1658 }
1659