File: server\sv_init.c

    1 /*
    2 Copyright (C) 1997-2001 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 "server.h"
   22 
   23 server_static_t svs;                            // persistant server info
   24 server_t                sv;                                     // local server
   25 
   26 /*
   27 ================
   28 SV_FindIndex
   29 
   30 ================
   31 */
   32 int SV_FindIndex (char *name, int start, int max, qboolean create)
   33 {
   34         int             i;
   35         
   36         if (!name || !name[0])
   37                 return 0;
   38 
   39         for (i=1 ; i<max && sv.configstrings[start+i][0] ; i++)
   40                 if (!strcmp(sv.configstrings[start+i], name))
   41                         return i;
   42 
   43         if (!create)
   44                 return 0;
   45 
   46         if (i == max)
   47                 Com_Error (ERR_DROP, "*Index: overflow");
   48 
   49         strncpy (sv.configstrings[start+i], name, sizeof(sv.configstrings[i]));
   50 
   51         if (sv.state != ss_loading)
   52         {       // send the update to everyone
   53                 SZ_Clear (&sv.multicast);
   54                 MSG_WriteChar (&sv.multicast, svc_configstring);
   55                 MSG_WriteShort (&sv.multicast, start+i);
   56                 MSG_WriteString (&sv.multicast, name);
   57                 SV_Multicast (vec3_origin, MULTICAST_ALL_R);
   58         }
   59 
   60         return i;
   61 }
   62 
   63 
   64 int SV_ModelIndex (char *name)
   65 {
   66         return SV_FindIndex (name, CS_MODELS, MAX_MODELS, true);
   67 }
   68 
   69 int SV_SoundIndex (char *name)
   70 {
   71         return SV_FindIndex (name, CS_SOUNDS, MAX_SOUNDS, true);
   72 }
   73 
   74 int SV_ImageIndex (char *name)
   75 {
   76         return SV_FindIndex (name, CS_IMAGES, MAX_IMAGES, true);
   77 }
   78 
   79 
   80 /*
   81 ================
   82 SV_CreateBaseline
   83 
   84 Entity baselines are used to compress the update messages
   85 to the clients -- only the fields that differ from the
   86 baseline will be transmitted
   87 ================
   88 */
   89 void SV_CreateBaseline (void)
   90 {
   91         edict_t                 *svent;
   92         int                             entnum; 
   93 
   94         for (entnum = 1; entnum < ge->num_edicts ; entnum++)
   95         {
   96                 svent = EDICT_NUM(entnum);
   97                 if (!svent->inuse)
   98                         continue;
   99                 if (!svent->s.modelindex && !svent->s.sound && !svent->s.effects)
  100                         continue;
  101                 svent->s.number = entnum;
  102 
  103                 //
  104                 // take current state as baseline
  105                 //
  106                 VectorCopy (svent->s.origin, svent->s.old_origin);
  107                 sv.baselines[entnum] = svent->s;
  108         }
  109 }
  110 
  111 
  112 /*
  113 =================
  114 SV_CheckForSavegame
  115 =================
  116 */
  117 void SV_CheckForSavegame (void)
  118 {
  119         char            name[MAX_OSPATH];
  120         FILE            *f;
  121         int                     i;
  122 
  123         if (sv_noreload->value)
  124                 return;
  125 
  126         if (Cvar_VariableValue ("deathmatch"))
  127                 return;
  128 
  129         Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name);
  130         f = fopen (name, "rb");
  131         if (!f)
  132                 return;         // no savegame
  133 
  134         fclose (f);
  135 
  136         SV_ClearWorld ();
  137 
  138         // get configstrings and areaportals
  139         SV_ReadLevelFile ();
  140 
  141         if (!sv.loadgame)
  142         {       // coming back to a level after being in a different
  143                 // level, so run it for ten seconds
  144 
  145                 // rlava2 was sending too many lightstyles, and overflowing the
  146                 // reliable data. temporarily changing the server state to loading
  147                 // prevents these from being passed down.
  148                 server_state_t          previousState;          // PGM
  149 
  150                 previousState = sv.state;                               // PGM
  151                 sv.state = ss_loading;                                  // PGM
  152                 for (i=0 ; i<100 ; i++)
  153                         ge->RunFrame ();
  154 
  155                 sv.state = previousState;                               // PGM
  156         }
  157 }
  158 
  159 
  160 /*
  161 ================
  162 SV_SpawnServer
  163 
  164 Change the server to a new map, taking all connected
  165 clients along with it.
  166 
  167 ================
  168 */
  169 void SV_SpawnServer (char *server, char *spawnpoint, server_state_t serverstate, qboolean attractloop, qboolean loadgame)
  170 {
  171         int                     i;
  172         unsigned        checksum;
  173 
  174         if (attractloop)
  175                 Cvar_Set ("paused", "0");
  176 
  177         Com_Printf ("------- Server Initialization -------\n");
  178 
  179         Com_DPrintf ("SpawnServer: %s\n",server);
  180         if (sv.demofile)
  181                 fclose (sv.demofile);
  182 
  183         svs.spawncount++;               // any partially connected client will be
  184                                                         // restarted
  185         sv.state = ss_dead;
  186         Com_SetServerState (sv.state);
  187 
  188         // wipe the entire per-level structure
  189         memset (&sv, 0, sizeof(sv));
  190         svs.realtime = 0;
  191         sv.loadgame = loadgame;
  192         sv.attractloop = attractloop;
  193 
  194         // save name for levels that don't set message
  195         strcpy (sv.configstrings[CS_NAME], server);
  196         if (Cvar_VariableValue ("deathmatch"))
  197         {
  198                 sprintf(sv.configstrings[CS_AIRACCEL], "%g", sv_airaccelerate->value);
  199                 pm_airaccelerate = sv_airaccelerate->value;
  200         }
  201         else
  202         {
  203                 strcpy(sv.configstrings[CS_AIRACCEL], "0");
  204                 pm_airaccelerate = 0;
  205         }
  206 
  207         SZ_Init (&sv.multicast, sv.multicast_buf, sizeof(sv.multicast_buf));
  208 
  209         strcpy (sv.name, server);
  210 
  211         // leave slots at start for clients only
  212         for (i=0 ; i<maxclients->value ; i++)
  213         {
  214                 // needs to reconnect
  215                 if (svs.clients[i].state > cs_connected)
  216                         svs.clients[i].state = cs_connected;
  217                 svs.clients[i].lastframe = -1;
  218         }
  219 
  220         sv.time = 1000;
  221         
  222         strcpy (sv.name, server);
  223         strcpy (sv.configstrings[CS_NAME], server);
  224 
  225         if (serverstate != ss_game)
  226         {
  227                 sv.models[1] = CM_LoadMap ("", false, &checksum);       // no real map
  228         }
  229         else
  230         {
  231                 Com_sprintf (sv.configstrings[CS_MODELS+1],sizeof(sv.configstrings[CS_MODELS+1]),
  232                         "maps/%s.bsp", server);
  233                 sv.models[1] = CM_LoadMap (sv.configstrings[CS_MODELS+1], false, &checksum);
  234         }
  235         Com_sprintf (sv.configstrings[CS_MAPCHECKSUM],sizeof(sv.configstrings[CS_MAPCHECKSUM]),
  236                 "%i", checksum);
  237 
  238         //
  239         // clear physics interaction links
  240         //
  241         SV_ClearWorld ();
  242         
  243         for (i=1 ; i< CM_NumInlineModels() ; i++)
  244         {
  245                 Com_sprintf (sv.configstrings[CS_MODELS+1+i], sizeof(sv.configstrings[CS_MODELS+1+i]),
  246                         "*%i", i);
  247                 sv.models[i+1] = CM_InlineModel (sv.configstrings[CS_MODELS+1+i]);
  248         }
  249 
  250         //
  251         // spawn the rest of the entities on the map
  252         //      
  253 
  254         // precache and static commands can be issued during
  255         // map initialization
  256         sv.state = ss_loading;
  257         Com_SetServerState (sv.state);
  258 
  259         // load and spawn all other entities
  260         ge->SpawnEntities ( sv.name, CM_EntityString(), spawnpoint );
  261 
  262         // run two frames to allow everything to settle
  263         ge->RunFrame ();
  264         ge->RunFrame ();
  265 
  266         // all precaches are complete
  267         sv.state = serverstate;
  268         Com_SetServerState (sv.state);
  269         
  270         // create a baseline for more efficient communications
  271         SV_CreateBaseline ();
  272 
  273         // check for a savegame
  274         SV_CheckForSavegame ();
  275 
  276         // set serverinfo variable
  277         Cvar_FullSet ("mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET);
  278 
  279         Com_Printf ("-------------------------------------\n");
  280 }
  281 
  282 /*
  283 ==============
  284 SV_InitGame
  285 
  286 A brand new game has been started
  287 ==============
  288 */
  289 void SV_InitGame (void)
  290 {
  291         int             i;
  292         edict_t *ent;
  293         char    idmaster[32];
  294 
  295         if (svs.initialized)
  296         {
  297                 // cause any connected clients to reconnect
  298                 SV_Shutdown ("Server restarted\n", true);
  299         }
  300         else
  301         {
  302                 // make sure the client is down
  303                 CL_Drop ();
  304                 SCR_BeginLoadingPlaque ();
  305         }
  306 
  307         // get any latched variable changes (maxclients, etc)
  308         Cvar_GetLatchedVars ();
  309 
  310         svs.initialized = true;
  311 
  312         if (Cvar_VariableValue ("coop") && Cvar_VariableValue ("deathmatch"))
  313         {
  314                 Com_Printf("Deathmatch and Coop both set, disabling Coop\n");
  315                 Cvar_FullSet ("coop", "0",  CVAR_SERVERINFO | CVAR_LATCH);
  316         }
  317 
  318         // dedicated servers are can't be single player and are usually DM
  319         // so unless they explicity set coop, force it to deathmatch
  320         if (dedicated->value)
  321         {
  322                 if (!Cvar_VariableValue ("coop"))
  323                         Cvar_FullSet ("deathmatch", "1",  CVAR_SERVERINFO | CVAR_LATCH);
  324         }
  325 
  326         // init clients
  327         if (Cvar_VariableValue ("deathmatch"))
  328         {
  329                 if (maxclients->value <= 1)
  330                         Cvar_FullSet ("maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH);
  331                 else if (maxclients->value > MAX_CLIENTS)
  332                         Cvar_FullSet ("maxclients", va("%i", MAX_CLIENTS), CVAR_SERVERINFO | CVAR_LATCH);
  333         }
  334         else if (Cvar_VariableValue ("coop"))
  335         {
  336                 if (maxclients->value <= 1 || maxclients->value > 4)
  337                         Cvar_FullSet ("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH);
  338 #ifdef COPYPROTECT
339 if (!sv.attractloop && !dedicated->value) 340 Sys_CopyProtect ();
341 #endif 342 } 343 else // non-deathmatch, non-coop is one player 344 { 345 Cvar_FullSet ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH); 346 #ifdef COPYPROTECT
347 if (!sv.attractloop) 348 Sys_CopyProtect ();
349 #endif 350 } 351 352 svs.spawncount = rand(); 353 svs.clients = Z_Malloc (sizeof(client_t)*maxclients->value); 354 svs.num_client_entities = maxclients->value*UPDATE_BACKUP*64; 355 svs.client_entities = Z_Malloc (sizeof(entity_state_t)*svs.num_client_entities); 356 357 // init network stuff 358 NET_Config ( (maxclients->value > 1) ); 359 360 // heartbeats will always be sent to the id master 361 svs.last_heartbeat = -99999; // send immediately 362 Com_sprintf(idmaster, sizeof(idmaster), "192.246.40.37:%i", PORT_MASTER); 363 NET_StringToAdr (idmaster, &master_adr[0]); 364 365 // init game 366 SV_InitGameProgs (); 367 for (i=0 ; i<maxclients->value ; i++) 368 { 369 ent = EDICT_NUM(i+1); 370 ent->s.number = i+1; 371 svs.clients[i].edict = ent; 372 memset (&svs.clients[i].lastcmd, 0, sizeof(svs.clients[i].lastcmd)); 373 } 374 } 375 376 377 /* 378 ====================== 379 SV_Map 380 381 the full syntax is: 382 383 map [*]<map>$<startspot>+<nextserver> 384 385 command from the console or progs. 386 Map can also be a.cin, .pcx, or .dm2 file 387 Nextserver is used to allow a cinematic to play, then proceed to 388 another level: 389 390 map tram.cin+jail_e3 391 ====================== 392 */ 393 void SV_Map (qboolean attractloop, char *levelstring, qboolean loadgame) 394 { 395 char level[MAX_QPATH]; 396 char *ch; 397 int l; 398 char spawnpoint[MAX_QPATH]; 399 400 sv.loadgame = loadgame; 401 sv.attractloop = attractloop; 402 403 if (sv.state == ss_dead && !sv.loadgame) 404 SV_InitGame (); // the game is just starting 405 406 strcpy (level, levelstring); 407 408 // if there is a + in the map, set nextserver to the remainder 409 ch = strstr(level, "+"); 410 if (ch) 411 { 412 *ch = 0; 413 Cvar_Set ("nextserver", va("gamemap \"%s\"", ch+1)); 414 } 415 else 416 Cvar_Set ("nextserver", ""); 417 418 //ZOID special hack for end game screen in coop mode 419 if (Cvar_VariableValue ("coop") && !Q_stricmp(level, "victory.pcx")) 420 Cvar_Set ("nextserver", "gamemap \"*base1\""); 421 422 // if there is a $, use the remainder as a spawnpoint 423 ch = strstr(level, "$"); 424 if (ch) 425 { 426 *ch = 0; 427 strcpy (spawnpoint, ch+1); 428 } 429 else 430 spawnpoint[0] = 0; 431 432 // skip the end-of-unit flag if necessary 433 if (level[0] == '*') 434 strcpy (level, level+1); 435 436 l = strlen(level); 437 if (l > 4 && !strcmp (level+l-4, ".cin") ) 438 { 439 SCR_BeginLoadingPlaque (); // for local system 440 SV_BroadcastCommand ("changing\n"); 441 SV_SpawnServer (level, spawnpoint, ss_cinematic, attractloop, loadgame); 442 } 443 else if (l > 4 && !strcmp (level+l-4, ".dm2") ) 444 { 445 SCR_BeginLoadingPlaque (); // for local system 446 SV_BroadcastCommand ("changing\n"); 447 SV_SpawnServer (level, spawnpoint, ss_demo, attractloop, loadgame); 448 } 449 else if (l > 4 && !strcmp (level+l-4, ".pcx") ) 450 { 451 SCR_BeginLoadingPlaque (); // for local system 452 SV_BroadcastCommand ("changing\n"); 453 SV_SpawnServer (level, spawnpoint, ss_pic, attractloop, loadgame); 454 } 455 else 456 { 457 SCR_BeginLoadingPlaque (); // for local system 458 SV_BroadcastCommand ("changing\n"); 459 SV_SendClientMessages (); 460 SV_SpawnServer (level, spawnpoint, ss_game, attractloop, loadgame); 461 Cbuf_CopyToDefer (); 462 } 463 464 SV_BroadcastCommand ("reconnect\n"); 465 } 466