ref: 468277a38c028f5f00c89379c1cc705d127e9e84
dir: /server/sv_init.c/
/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <u.h> #include <libc.h> #include <stdio.h> #include "../dat.h" #include "../fns.h" server_static_t svs; // persistant server info server_t sv; // local server /* ================ SV_FindIndex ================ */ int SV_FindIndex (char *name, int start, int max, qboolean create) { int i; if (!name || !name[0]) return 0; for (i=1 ; i<max && sv.configstrings[start+i][0] ; i++) if (!strcmp(sv.configstrings[start+i], name)) return i; if (!create) return 0; if (i == max) Com_Error (ERR_DROP, "*Index: overflow"); strncpy (sv.configstrings[start+i], name, sizeof(sv.configstrings[i])); if (sv.state != ss_loading) { // send the update to everyone SZ_Clear (&sv.multicast); MSG_WriteChar (&sv.multicast, svc_configstring); MSG_WriteShort (&sv.multicast, start+i); MSG_WriteString (&sv.multicast, name); SV_Multicast (vec3_origin, MULTICAST_ALL_R); } return i; } int SV_ModelIndex (char *name) { return SV_FindIndex (name, CS_MODELS, MAX_MODELS, true); } int SV_SoundIndex (char *name) { return SV_FindIndex (name, CS_SOUNDS, MAX_SOUNDS, true); } int SV_ImageIndex (char *name) { return SV_FindIndex (name, CS_IMAGES, MAX_IMAGES, true); } /* ================ SV_CreateBaseline Entity baselines are used to compress the update messages to the clients -- only the fields that differ from the baseline will be transmitted ================ */ void SV_CreateBaseline (void) { edict_t *svent; int entnum; for (entnum = 1; entnum < ge->num_edicts ; entnum++) { svent = EDICT_NUM(entnum); if (!svent->inuse) continue; if (!svent->s.modelindex && !svent->s.sound && !svent->s.effects) continue; svent->s.number = entnum; // // take current state as baseline // VectorCopy (svent->s.origin, svent->s.old_origin); sv.baselines[entnum] = svent->s; } } /* ================= SV_CheckForSavegame ================= */ void SV_CheckForSavegame (void) { char name[MAX_OSPATH]; FILE *f; int i; if (sv_noreload->value) return; if (Cvar_VariableValue ("deathmatch")) return; Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name); f = fopen (name, "rb"); if (!f) return; // no savegame fclose (f); SV_ClearWorld (); // get configstrings and areaportals SV_ReadLevelFile (); if (!sv.loadgame) { // coming back to a level after being in a different // level, so run it for ten seconds // rlava2 was sending too many lightstyles, and overflowing the // reliable data. temporarily changing the server state to loading // prevents these from being passed down. server_state_t previousState; // PGM previousState = sv.state; // PGM sv.state = ss_loading; // PGM for (i=0 ; i<100 ; i++) ge->RunFrame (); sv.state = previousState; // PGM } } /* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. ================ */ void SV_SpawnServer (char *server, char *spawnpoint, server_state_t serverstate, qboolean attractloop, qboolean loadgame) { int i; unsigned checksum; if (attractloop) Cvar_Set ("paused", "0"); Com_Printf ("------- Server Initialization -------\n"); Com_DPrintf ("SpawnServer: %s\n",server); if (sv.demofile) fclose (sv.demofile); svs.spawncount++; // any partially connected client will be // restarted sv.state = ss_dead; Com_SetServerState (sv.state); // wipe the entire per-level structure memset (&sv, 0, sizeof(sv)); svs.realtime = 0; sv.loadgame = loadgame; sv.attractloop = attractloop; // save name for levels that don't set message strcpy (sv.configstrings[CS_NAME], server); if (Cvar_VariableValue ("deathmatch")) { sprintf(sv.configstrings[CS_AIRACCEL], "%g", sv_airaccelerate->value); pm_airaccelerate = sv_airaccelerate->value; } else { strcpy(sv.configstrings[CS_AIRACCEL], "0"); pm_airaccelerate = 0; } SZ_Init (&sv.multicast, sv.multicast_buf, sizeof(sv.multicast_buf)); strcpy (sv.name, server); // leave slots at start for clients only for (i=0 ; i<maxclients->value ; i++) { // needs to reconnect if (svs.clients[i].state > cs_connected) svs.clients[i].state = cs_connected; svs.clients[i].lastframe = -1; } sv.time = 1000; strcpy (sv.name, server); strcpy (sv.configstrings[CS_NAME], server); if (serverstate != ss_game) { sv.models[1] = CM_LoadMap ("", false, &checksum); // no real map } else { Com_sprintf (sv.configstrings[CS_MODELS+1],sizeof(sv.configstrings[CS_MODELS+1]), "maps/%s.bsp", server); sv.models[1] = CM_LoadMap (sv.configstrings[CS_MODELS+1], false, &checksum); } Com_sprintf (sv.configstrings[CS_MAPCHECKSUM],sizeof(sv.configstrings[CS_MAPCHECKSUM]), "%i", checksum); // // clear physics interaction links // SV_ClearWorld (); for (i=1 ; i< CM_NumInlineModels() ; i++) { Com_sprintf (sv.configstrings[CS_MODELS+1+i], sizeof(sv.configstrings[CS_MODELS+1+i]), "*%i", i); sv.models[i+1] = CM_InlineModel (sv.configstrings[CS_MODELS+1+i]); } // // spawn the rest of the entities on the map // // precache and static commands can be issued during // map initialization sv.state = ss_loading; Com_SetServerState (sv.state); // load and spawn all other entities ge->SpawnEntities ( sv.name, CM_EntityString(), spawnpoint ); // run two frames to allow everything to settle ge->RunFrame (); ge->RunFrame (); // all precaches are complete sv.state = serverstate; Com_SetServerState (sv.state); // create a baseline for more efficient communications SV_CreateBaseline (); // check for a savegame SV_CheckForSavegame (); // set serverinfo variable Cvar_FullSet ("mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET); Com_Printf ("-------------------------------------\n"); } /* ============== SV_InitGame A brand new game has been started ============== */ void SV_InitGame (void) { int i; edict_t *ent; char idmaster[32]; if (svs.initialized) { // cause any connected clients to reconnect SV_Shutdown ("Server restarted\n", true); } else { // make sure the client is down CL_Drop (); SCR_BeginLoadingPlaque (); } // get any latched variable changes (maxclients, etc) Cvar_GetLatchedVars (); svs.initialized = true; if (Cvar_VariableValue ("coop") && Cvar_VariableValue ("deathmatch")) { Com_Printf("Deathmatch and Coop both set, disabling Coop\n"); Cvar_FullSet ("coop", "0", CVAR_SERVERINFO | CVAR_LATCH); } // dedicated servers are can't be single player and are usually DM // so unless they explicity set coop, force it to deathmatch if (dedicated->value) { if (!Cvar_VariableValue ("coop")) Cvar_FullSet ("deathmatch", "1", CVAR_SERVERINFO | CVAR_LATCH); } // init clients if (Cvar_VariableValue ("deathmatch")) { if (maxclients->value <= 1) Cvar_FullSet ("maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH); else if (maxclients->value > MAX_CLIENTS) Cvar_FullSet ("maxclients", va("%i", MAX_CLIENTS), CVAR_SERVERINFO | CVAR_LATCH); } else if (Cvar_VariableValue ("coop")) { if (maxclients->value <= 1 || maxclients->value > 4) Cvar_FullSet ("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH); #ifdef COPYPROTECT if (!sv.attractloop && !dedicated->value) Sys_CopyProtect (); #endif } else // non-deathmatch, non-coop is one player { Cvar_FullSet ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH); #ifdef COPYPROTECT if (!sv.attractloop) Sys_CopyProtect (); #endif } svs.spawncount = rand(); svs.clients = Z_Malloc (sizeof(client_t)*maxclients->value); svs.num_client_entities = maxclients->value*UPDATE_BACKUP*64; svs.client_entities = Z_Malloc (sizeof(entity_state_t)*svs.num_client_entities); // init network stuff NET_Config ( (maxclients->value > 1) ); // heartbeats will always be sent to the id master svs.last_heartbeat = -99999; // send immediately Com_sprintf(idmaster, sizeof(idmaster), "192.246.40.37:%i", PORT_MASTER); NET_StringToAdr (idmaster, &master_adr[0]); // init game SV_InitGameProgs (); for (i=0 ; i<maxclients->value ; i++) { ent = EDICT_NUM(i+1); ent->s.number = i+1; svs.clients[i].edict = ent; memset (&svs.clients[i].lastcmd, 0, sizeof(svs.clients[i].lastcmd)); } } /* ====================== SV_Map the full syntax is: map [*]<map>$<startspot>+<nextserver> command from the console or progs. Map can also be a.cin, .pcx, or .dm2 file Nextserver is used to allow a cinematic to play, then proceed to another level: map tram.cin+jail_e3 ====================== */ void SV_Map (qboolean attractloop, char *levelstring, qboolean loadgame) { char level[MAX_QPATH]; char *ch; int l; char spawnpoint[MAX_QPATH]; sv.loadgame = loadgame; sv.attractloop = attractloop; if (sv.state == ss_dead && !sv.loadgame) SV_InitGame (); // the game is just starting strcpy (level, levelstring); // if there is a + in the map, set nextserver to the remainder ch = strstr(level, "+"); if (ch) { *ch = 0; Cvar_Set ("nextserver", va("gamemap \"%s\"", ch+1)); } else Cvar_Set ("nextserver", ""); //ZOID special hack for end game screen in coop mode if (Cvar_VariableValue ("coop") && !Q_strcasecmp(level, "victory.pcx")) Cvar_Set ("nextserver", "gamemap \"*base1\""); // if there is a $, use the remainder as a spawnpoint ch = strstr(level, "$"); if (ch) { *ch = 0; strcpy (spawnpoint, ch+1); } else spawnpoint[0] = 0; // skip the end-of-unit flag if necessary if (level[0] == '*') strcpy (level, level+1); l = strlen(level); if (l > 4 && !strcmp (level+l-4, ".cin") ) { SCR_BeginLoadingPlaque (); // for local system SV_BroadcastCommand ("changing\n"); SV_SpawnServer (level, spawnpoint, ss_cinematic, attractloop, loadgame); } else if (l > 4 && !strcmp (level+l-4, ".dm2") ) { SCR_BeginLoadingPlaque (); // for local system SV_BroadcastCommand ("changing\n"); SV_SpawnServer (level, spawnpoint, ss_demo, attractloop, loadgame); } else if (l > 4 && !strcmp (level+l-4, ".pcx") ) { SCR_BeginLoadingPlaque (); // for local system SV_BroadcastCommand ("changing\n"); SV_SpawnServer (level, spawnpoint, ss_pic, attractloop, loadgame); } else { SCR_BeginLoadingPlaque (); // for local system SV_BroadcastCommand ("changing\n"); SV_SendClientMessages (); SV_SpawnServer (level, spawnpoint, ss_game, attractloop, loadgame); Cbuf_CopyToDefer (); } SV_BroadcastCommand ("reconnect\n"); }