home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 113 / EnigmaAmiga113CD.iso / software / sviluppo / quakeworld_src / server / sv_main.c.orig < prev    next >
Text File  |  2000-06-17  |  39KB  |  1,659 lines

  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.  
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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
  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. */
  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. */
  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. */
  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. */
  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.  
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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. */
  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
  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. */
  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. */
  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. */
  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. */
  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.