sv_init.c (11080B)
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 }