/* Copyright (C) 1996-1997 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 "qwsvdef.h" #ifndef CLIENTONLY extern int total_loading_size, current_loading_size, loading_stage; char *T_GetString(int num); #define Q2EDICT_NUM(i) (q2edict_t*)((char *)ge->edicts+(i)*ge->edict_size) server_static_t svs; // persistant server info server_t sv; // local server char localmodels[MAX_MODELS][5]; // inline model names for precache char localinfo[MAX_LOCALINFO_STRING+1]; // local game info extern cvar_t skill, sv_loadentfiles; extern cvar_t sv_cheats; extern cvar_t sv_bigcoords; extern cvar_t sv_gamespeed; extern cvar_t sv_csqcdebug; extern qboolean sv_allow_cheats; /* ================ SV_ModelIndex ================ */ int SV_ModelIndex (char *name) { int i; if (!name || !name[0]) return 0; for (i=1 ; ifree) continue; // create baselines for all player slots, // and any other edict that has a visible model if (entnum > MAX_CLIENTS && !svent->v->modelindex) continue; // // create entity baseline // VectorCopy (svent->v->origin, svent->baseline.origin); VectorCopy (svent->v->angles, svent->baseline.angles); svent->baseline.frame = svent->v->frame; svent->baseline.skinnum = svent->v->skin; if (entnum > 0 && entnum <= MAX_CLIENTS) { svent->baseline.colormap = entnum; svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl")&255; } else { svent->baseline.colormap = 0; svent->baseline.modelindex = SV_ModelIndex(PR_GetString(svent->v->model))&255; } #ifdef PEXT_SCALE svent->baseline.scale = 1; #endif #ifdef PEXT_TRANS svent->baseline.trans = 1; #endif // // flush the signon message out to a seperate buffer if // nearly full // SV_FlushSignon (); // // add to the message // MSG_WriteByte (&sv.signon,svc_spawnbaseline); MSG_WriteShort (&sv.signon,entnum); MSG_WriteByte (&sv.signon, svent->baseline.modelindex); MSG_WriteByte (&sv.signon, svent->baseline.frame); MSG_WriteByte (&sv.signon, svent->baseline.colormap); MSG_WriteByte (&sv.signon, svent->baseline.skinnum); for (i=0 ; i<3 ; i++) { MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); } } } */ void SV_EdictToEntState (int num, edict_t *ent, entity_state_t *state) { int i; state->number = num; state->flags = 0; VectorCopy (ent->v->origin, state->origin); VectorCopy (ent->v->angles, state->angles); state->modelindex = ent->v->modelindex; state->frame = ent->v->frame; state->colormap = ent->v->colormap; state->skinnum = ent->v->skin; state->effects = ent->v->effects; state->hexen2flags = ent->v->drawflags; state->abslight = (int)(ent->v->abslight*255) & 255; state->tagentity = ent->v->tag_entity; state->tagindex = ent->v->tag_index; state->light[0] = ent->v->color[0]*255; state->light[1] = ent->v->color[1]*255; state->light[2] = ent->v->color[2]*255; state->light[3] = ent->v->light_lev; state->lightstyle = ent->v->style; state->lightpflags = ent->v->pflags; /* if ((int)ent->v->flags & FL_CLASS_DEPENDENT && client->playerclass) //hexen2 wierdness. { char modname[MAX_QPATH]; Q_strncpyz(modname, sv.strings.model_precache[state->modelindex], sizeof(modname)); if (strlen(modname)>5) { modname[strlen(modname)-5] = client->playerclass+'0'; state->modelindex = SV_ModelIndex(modname); } }*/ if (/*progstype == PROG_H2 &&*/ ent->v->solid == SOLID_BSP) state->angles[0]*=-1; if (state->effects & EF_FULLBRIGHT) { state->hexen2flags |= MLS_FULLBRIGHT; } if (!ent->v->alpha) state->trans = 255; else state->trans = ent->v->alpha*255; if (!ent->v->colormod[0] && !ent->v->colormod[1] && !ent->v->colormod[2]) { state->colormod[0] = (256)/8; state->colormod[1] = (256)/8; state->colormod[2] = (256)/8; } else { i = ent->v->colormod[0]*(256/8); state->colormod[0] = bound(0, i, 255); i = ent->v->colormod[1]*(256/8); state->colormod[1] = bound(0, i, 255); i = ent->v->colormod[2]*(256/8); state->colormod[2] = bound(0, i, 255); } state->glowsize = ent->v->glow_size*0.25; state->glowcolour = ent->v->glow_color; #define RENDER_GLOWTRAIL 2 if (ent->v->glow_trail) state->dpflags |= RENDER_GLOWTRAIL; if (!ent->v->scale) state->scale = 1*16; else state->scale = ent->v->scale*16; state->fatness = ent->v->fatness*2; } void SVNQ_CreateBaseline (void) { edict_t *svent; int entnum; int playermodel = SV_SafeModelIndex("progs/player.mdl"); for (entnum = 0; entnum < sv.num_edicts ; entnum++) { svent = EDICT_NUM(svprogfuncs, entnum); memset(&svent->baseline, 0, sizeof(entity_state_t)); svent->baseline.number = entnum; #ifdef PEXT_SCALE svent->baseline.scale = 1*16; #endif #ifdef PEXT_TRANS svent->baseline.trans = 255; #endif if (svent->isfree) continue; // create baselines for all player slots, // and any other edict that has a visible model if (entnum > sv.allocated_client_slots && !svent->v->modelindex) continue; // // create entity baseline // SV_EdictToEntState(entnum, svent, &svent->baseline); if (entnum > 0 && entnum <= sv.allocated_client_slots) { if (entnum > 0 && entnum <= 16) svent->baseline.colormap = entnum; else svent->baseline.colormap = 0; //this would crash NQ. svent->baseline.modelindex = playermodel; } svent->baseline.modelindex&=255; } } /* ================ SV_SaveSpawnparms Grabs the current state of the progs serverinfo flags and each client for saving across the transition to another level ================ */ void SV_SaveSpawnparms (qboolean dontsave) { int i, j; if (!sv.state) return; // no progs loaded yet if (!svprogfuncs) return; // serverflags is the only game related thing maintained svs.serverflags = pr_global_struct->serverflags; for (i=0, host_client = svs.clients ; istate != cs_spawned) continue; // needs to reconnect host_client->state = cs_connected; if (dontsave) //level restart requires that stats can be reset continue; // call the progs to get default spawn parms for the new client if (PR_FindGlobal(svprogfuncs, "ClientReEnter", 0)) {//oooh, evil. char buffer[65536]; int bufsize = sizeof(buffer); char *buf; for (j=0 ; jspawn_parms[j] = 0; buf = svprogfuncs->saveent(svprogfuncs, buffer, &bufsize, host_client->edict); if (host_client->spawninfo) Z_Free(host_client->spawninfo); host_client->spawninfo = Z_Malloc(bufsize+1); memcpy(host_client->spawninfo, buf, bufsize); host_client->spawninfotime = sv.time; } else if (pr_nqglobal_struct->SetChangeParms) { pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, host_client->edict); PR_ExecuteProgram (svprogfuncs, pr_global_struct->SetChangeParms); for (j=0 ; jspawn_parms[j] = *spawnparamglobals[j]; else host_client->spawn_parms[j] = 0; } } #ifdef SVRANKING if (host_client->rankid) { rankstats_t rs; if (Rank_GetPlayerStats(host_client->rankid, &rs)) { rs.timeonserver += realtime - host_client->stats_started; host_client->stats_started = realtime; rs.kills += host_client->kills; rs.deaths += host_client->deaths; host_client->kills=0; host_client->deaths=0; for (j=0 ; jrankid, &rs); } } #endif } } /* ================ SV_CalcPHS Expands the PVS and calculates the PHS (Potentially Hearable Set) ================ */ void SV_CalcPHS (void) { int rowbytes, rowwords; int i, j, k, l, index, num; int bitbyte; unsigned *dest, *src; qbyte *scan; int count, vcount; if (sv.worldmodel->fromgame == fg_quake2 || sv.worldmodel->fromgame == fg_quake3) { //PHS calcs are pointless with Q2 bsps return; } if (developer.value) Con_TPrintf (STL_BUILDINGPHS); num = sv.worldmodel->numleafs; rowwords = (num+31)>>5; rowbytes = rowwords*4; sv.pvs = Hunk_AllocName (rowbytes*num, "phs vis"); scan = sv.pvs; vcount = 0; for (i=0 ; ifuncs.LeafPVS(sv.worldmodel, i, NULL), rowbytes); if (i == 0) continue; for (j=0 ; j>3] & (1<<(j&7)) ) { vcount++; } } } sv.phs = Hunk_AllocName (rowbytes*num, "phs hear"); count = 0; scan = sv.pvs; dest = (unsigned *)sv.phs; for (i=0 ; i= num) continue; src = (unsigned *)sv.pvs + index*rowwords; for (l=0 ; l>3] & (1<<(j&7)) ) count++; } if (num) if (developer.value) Con_TPrintf (STL_PHSINFO, vcount/num, count/num, num); } unsigned SV_CheckModel(char *mdl) { qbyte stackbuf[1024]; // avoid dirtying the cache heap qbyte *buf; unsigned short crc; // int len; buf = (qbyte *)COM_LoadStackFile (mdl, stackbuf, sizeof(stackbuf)); if (!buf) return 0; crc = QCRC_Block(buf, com_filesize); // for (len = com_filesize; len; len--, buf++) // CRC_ProcessByte(&crc, *buf); return crc; } void SV_UnspawnServer (void) //terminate the running server. { int i; if (sv.state) { Con_TPrintf(STL_SERVERUNSPAWNED); SV_FinalMessage("Server unspawned\n"); for (i = 0; i < sv.allocated_client_slots; i++) { if (svs.clients[i].state) SV_DropClient(svs.clients+i); } #ifdef Q2SERVER SVQ2_ShutdownGameProgs(); #endif sv.worldmodel = NULL; sv.state = ss_dead; *sv.name = '\0'; } for (i = 0; i < MAX_CLIENTS; i++) { svs.clients[i].state = 0; *svs.clients[i].namebuf = '\0'; svs.clients[i].name = NULL; } NET_CloseServer (); } /* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. This is only called from the SV_Map_f() function. ================ */ extern cvar_t progs; void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean usecinematic) { func_t f; char *file; gametype_e newgametype; edict_t *ent; #ifdef Q2SERVER q2edict_t *q2ent; #endif int i, j; int spawnflagmask; #ifndef SERVERONLY if (!isDedicated && (!qrenderer || qrenderer == -1)) { R_RestartRenderer_f(); } #endif Con_DPrintf ("SpawnServer: %s\n",server); svs.spawncount++; // any partially connected client will be // restarted #ifndef SERVERONLY total_loading_size = 100; current_loading_size = 0; loading_stage = 1; SCR_BeginLoadingPlaque(); #endif NET_InitServer(); sv.state = ss_dead; if (sv.gamedirchanged) { sv.gamedirchanged = false; #ifndef SERVERONLY Wads_Flush(); //server code is responsable for flushing old state #endif #ifdef SVRANKING Rank_Flush(); #endif for (i = 0; i < MAX_CLIENTS; i++) { if (svs.clients[i].state && ISQWCLIENT(&svs.clients[i])) ReloadRanking(&svs.clients[i], svs.clients[i].name); if (svs.clients[i].spawninfo) //don't remember this stuff. Z_Free(svs.clients[i].spawninfo); svs.clients[i].spawninfo = NULL; } T_FreeStrings(); } for (i = 0; i < MAX_CLIENTS; i++) { svs.clients[i].nextservertimeupdate = 0; if (!svs.clients[i].state) //bots with the net_preparse module. svs.clients[i].userinfo[0] = '\0'; //clear the userinfo to clear the name if (svs.clients[i].netchan.remote_address.type == NA_LOOPBACK) { //forget this client's message buffers, so that any shared client/server network state persists (eg: float coords) svs.clients[i].num_backbuf = 0; svs.clients[i].datagram.cursize = 0; } svs.clients[i].csqcactive = false; } if (sv_bigcoords.value) { sizeofcoord = 4; sizeofangle = 2; } else { sizeofcoord = 2; sizeofangle = 1; } VoteFlushAll(); #ifndef SERVERONLY D_FlushCaches(); cl.worldmodel = NULL; r_worldentity.model = NULL; if (0) cls.state = ca_connected; #endif #ifdef Q3SERVER if (svs.gametype == GT_QUAKE3) SVQ3_ShutdownGame(); //botlib kinda mandates this. :( #endif Mod_ClearAll (); Hunk_FreeToLowMark (host_hunklevel); if (svs.gametype == GT_PROGS) { for (i = 0; i < MAX_LIGHTSTYLES; i++) { if (sv.strings.lightstyles[i]) Z_Free(sv.strings.lightstyles[i]); sv.strings.lightstyles[i] = NULL; sv.strings.lightstylecolours[i] = 7; } } // wipe the entire per-level structure memset (&sv, 0, sizeof(sv)); sv.datagram.maxsize = sizeof(sv.datagram_buf); sv.datagram.data = sv.datagram_buf; sv.datagram.allowoverflow = true; sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); sv.reliable_datagram.data = sv.reliable_datagram_buf; sv.multicast.maxsize = sizeof(sv.multicast_buf); sv.multicast.data = sv.multicast_buf; #ifdef NQPROT sv.nqdatagram.maxsize = sizeof(sv.nqdatagram_buf); sv.nqdatagram.data = sv.nqdatagram_buf; sv.nqdatagram.allowoverflow = true; sv.nqreliable_datagram.maxsize = sizeof(sv.nqreliable_datagram_buf); sv.nqreliable_datagram.data = sv.nqreliable_datagram_buf; sv.nqmulticast.maxsize = sizeof(sv.nqmulticast_buf); sv.nqmulticast.data = sv.nqmulticast_buf; #endif sv.q2datagram.maxsize = sizeof(sv.q2datagram_buf); sv.q2datagram.data = sv.q2datagram_buf; sv.q2datagram.allowoverflow = true; sv.q2reliable_datagram.maxsize = sizeof(sv.q2reliable_datagram_buf); sv.q2reliable_datagram.data = sv.q2reliable_datagram_buf; sv.q2multicast.maxsize = sizeof(sv.q2multicast_buf); sv.q2multicast.data = sv.q2multicast_buf; sv.master.maxsize = sizeof(sv.master_buf); sv.master.data = sv.master_buf; sv.signon.maxsize = sizeof(sv.signon_buffers[0]); sv.signon.data = sv.signon_buffers[0]; sv.num_signon_buffers = 1; sv.numextrastatics = 0; strcpy (sv.name, server); #ifndef SERVERONLY current_loading_size+=10; SCR_BeginLoadingPlaque(); #endif Cvar_ApplyLatches(CVAR_LATCH); //work out the gamespeed sv.gamespeed = sv_gamespeed.value; Info_SetValueForStarKey(svs.info, "*gamespeed", va("%i", (int)(sv.gamespeed*100)), MAX_SERVERINFO_STRING); sv.gamespeed = atof(Info_ValueForKey(svs.info, "*gamespeed"))/100; if (sv.gamespeed < 0.1 || sv.gamespeed == 1) { sv.gamespeed = 1; Info_SetValueForStarKey(svs.info, "*gamespeed", "", MAX_SERVERINFO_STRING); } //reset the server time. sv.time = 0.1; //some progs don't like time starting at 0. //cos of spawn funcs like self.nextthink = time... sv.starttime = Sys_DoubleTime(); COM_FlushTempoaryPacks(); //This fixes a bug where the server advertises cheats, the internal client connects, and doesn't think cheats are allowed. //this applies to a few other things too, but cheats is the only special one (because of the *) if (sv_cheats.value) { sv_allow_cheats = true; Info_SetValueForStarKey(svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING); } else { sv_allow_cheats = false; Info_SetValueForStarKey(svs.info, "*cheats", "", MAX_SERVERINFO_STRING); } #ifndef SERVERONLY Q_strncpyz(cl.serverinfo, svs.info, sizeof(cl.serverinfo)); CL_CheckServerInfo(); #endif if (usecinematic) { strcpy (sv.name, server); strcpy (sv.modelname, ""); } else { strcpy (sv.name, server); sprintf (sv.modelname,"maps/%s.bsp", server); } sv.state = ss_loading; sv.worldmodel = Mod_ForName (sv.modelname, true); if (sv.worldmodel->needload) Sys_Error("%s is missing\n", sv.modelname); if (sv.worldmodel->type != mod_brush && sv.worldmodel->type != mod_heightmap) Sys_Error("%s is not a bsp model\n", sv.modelname); sv.state = ss_dead; #ifndef SERVERONLY current_loading_size+=10; SCR_BeginLoadingPlaque(); #endif SV_CalcPHS (); #ifndef SERVERONLY current_loading_size+=10; SCR_BeginLoadingPlaque(); #endif if (sv.worldmodel->fromgame == fg_doom) Info_SetValueForStarKey(svs.info, "*bspversion", "1", MAX_SERVERINFO_STRING); else if (sv.worldmodel->fromgame == fg_halflife) Info_SetValueForStarKey(svs.info, "*bspversion", "30", MAX_SERVERINFO_STRING); else if (sv.worldmodel->fromgame == fg_quake2) Info_SetValueForStarKey(svs.info, "*bspversion", "38", MAX_SERVERINFO_STRING); else if (sv.worldmodel->fromgame == fg_quake3) Info_SetValueForStarKey(svs.info, "*bspversion", "46", MAX_SERVERINFO_STRING); else Info_SetValueForStarKey(svs.info, "*bspversion", "", MAX_SERVERINFO_STRING); if (startspot) Info_SetValueForStarKey(svs.info, "*startspot", startspot, MAX_SERVERINFO_STRING); else Info_SetValueForStarKey(svs.info, "*startspot", "", MAX_SERVERINFO_STRING); // // clear physics interaction links // SV_ClearWorld (); //do we allow csprogs? #ifdef PEXT_CSQC file = COM_LoadTempFile("csprogs.dat"); if (file) { char text[64]; sprintf(text, "0x%x", Com_BlockChecksum(file, com_filesize)); Info_SetValueForStarKey(svs.info, "*csprogs", text, MAX_SERVERINFO_STRING); } else Info_SetValueForStarKey(svs.info, "*csprogs", "", MAX_SERVERINFO_STRING); sv.csqcdebug = sv_csqcdebug.value; if (sv.csqcdebug) Info_SetValueForStarKey(svs.info, "*csqcdebug", "1", MAX_SERVERINFO_STRING); else Info_RemoveKey(svs.info, "*csqcdebug"); #endif if (svprogfuncs) //we don't want the q1 stuff anymore. { CloseProgs(svprogfuncs); svprogfuncs = NULL; } sv.state = ss_loading; newgametype = svs.gametype; #ifdef Q3SERVER if (SVQ3_InitGame()) newgametype = GT_QUAKE3; else #endif #ifdef Q2SERVER if ((sv.worldmodel->fromgame == fg_quake2 || sv.worldmodel->fromgame == fg_quake3) && !*progs.string && SVQ2_InitGameProgs()) //these are the rules for running a q2 server newgametype = GT_QUAKE2; //we loaded the dll else #endif { newgametype = GT_PROGS; //let's just hope this loads. Q_InitProgs(); } // if ((sv.worldmodel->fromgame == fg_quake2 || sv.worldmodel->fromgame == fg_quake3) && !*progs.string && SVQ2_InitGameProgs()) //full q2 dll decision in one if statement if (newgametype != svs.gametype) { for (i=0 ; inumsubmodels ; i++) { sv.strings.model_precache[1+i] = PR_AddString(svprogfuncs, localmodels[i], 0); sv.models[i+1] = Mod_ForName (localmodels[i], false); } //check player/eyes models for hacks sv.model_player_checksum = SV_CheckModel("progs/player.mdl"); sv.eyes_player_checksum = SV_CheckModel("progs/eyes.mdl"); } #ifdef Q2SERVER else if (svs.gametype == GT_QUAKE2) { extern int map_checksum; extern cvar_t sv_airaccelerate; memset(sv.strings.configstring, 0, sizeof(sv.strings.configstring)); if (deathmatch.value) sprintf(sv.strings.configstring[Q2CS_AIRACCEL], "%g", sv_airaccelerate.value); else strcpy(sv.strings.configstring[Q2CS_AIRACCEL], "0"); // init map checksum config string but only for Q2/Q3 maps if (sv.worldmodel->fromgame == fg_quake2 || sv.worldmodel->fromgame == fg_quake3) sprintf(sv.strings.configstring[Q2CS_MAPCHECKSUM], "%i", map_checksum); else strcpy(sv.strings.configstring[Q2CS_MAPCHECKSUM], "0"); strcpy(sv.strings.configstring[Q2CS_MODELS+1], sv.modelname); for (i=1; inumsubmodels; i++) { strcpy(sv.strings.configstring[Q2CS_MODELS+1+i], localmodels[i]); sv.models[i+1] = Mod_ForName (localmodels[i], false); } } #endif #ifndef SERVERONLY current_loading_size+=10; SCR_BeginLoadingPlaque(); #endif for (i=0 ; iisfree = false; // leave slots at start for clients only // sv.num_edicts = MAX_CLIENTS+1; for (i=0 ; iisfree = false; //ZOID - make sure we update frags right svs.clients[i].old_frags = 0; if (!svs.clients[i].state && svs.clients[i].name[0]) //this is a bot. svs.clients[i].name[0] = '\0'; //make it go away svs.clients[i].name = PR_AddString(svprogfuncs, svs.clients[i].namebuf, sizeof(svs.clients[i].namebuf)); svs.clients[i].team = PR_AddString(svprogfuncs, svs.clients[i].teambuf, sizeof(svs.clients[i].teambuf)); #ifdef PEXT_CSQC memset(svs.clients[i].csqcentsequence, 0, sizeof(svs.clients[i].csqcentsequence)); memset(svs.clients[i].csqcentversions, 0, sizeof(svs.clients[i].csqcentversions)); #endif } sv.allocated_client_slots = i; #ifdef PEXT_CSQC for (i=0 ; is.number = i+1; svs.clients[i].q2edict = q2ent; } sv.allocated_client_slots = i; break; #endif #ifdef Q3SERVER case GT_QUAKE3: sv.allocated_client_slots = 32; break; #endif } for (i=0 ; iisfree = false; ent->v->model = PR_NewString(svprogfuncs, sv.worldmodel->name, 0); ent->v->modelindex = 1; // world model ent->v->solid = SOLID_BSP; ent->v->movetype = MOVETYPE_PUSH; ent->v->dimension_see = 255; ent->v->dimension_seen = 255; ent->v->dimension_ghost = 0; ent->v->dimension_solid = 255; ent->v->dimension_hit = 255; if (progstype == PROG_QW && pr_imitatemvdsv.value>0) { ent->v->targetname = PR_NewString(svprogfuncs, "mvdsv", 0); ent->v->netname = PR_NewString(svprogfuncs, va("%s %f %s, build %d\nBuild date: " __DATE__ ", " __TIME__ "", DISTRIBUTIONLONG, 2.4, PLATFORM, build_number()), 0); ent->v->impulse = 0;//QWE_VERNUM; ent->v->items = 103; } pr_global_struct->mapname = PR_NewString(svprogfuncs, sv.name, 0); // serverflags are for cross level information (sigils) pr_global_struct->serverflags = svs.serverflags; pr_global_struct->time = 0.1; //HACK!!!! A few QuakeC mods expect time to be non-zero in spawn funcs - like prydon gate... if (progstype == PROG_H2) { cvar_t *cv; if (coop.value) { eval = PR_FindGlobal(svprogfuncs, "coop", 0); if (eval) eval->_float = coop.value; } else { eval = PR_FindGlobal(svprogfuncs, "deathmatch", 0); if (eval) eval->_float = deathmatch.value; } cv = Cvar_Get("randomclass", "0", CVAR_LATCH, "Hexen2"); eval = PR_FindGlobal(svprogfuncs, "randomclass", 0); if (eval && cv) eval->_float = cv->value; cv = Cvar_Get("cl_playerclass", "1", CVAR_USERINFO|CVAR_ARCHIVE, "Hexen2"); eval = PR_FindGlobal(svprogfuncs, "cl_playerclass", 0); if (eval && cv) eval->_float = cv->value; } else { if (pr_nqglobal_struct->coop && coop.value) pr_global_struct->coop = coop.value; else if (pr_nqglobal_struct->deathmatch) pr_global_struct->deathmatch = deathmatch.value; } if (progstype == PROG_QW) // run the frame start qc function to let progs check cvars SV_ProgStartFrame (); //prydon gate seems to fail because of this allowance for (i = 0; i < svs.numprogs; i++) //do this AFTER precaches have been played with... { f = PR_FindFunction (svprogfuncs, "initents", svs.progsnum[i]); if (f) { PR_ExecuteProgram(svprogfuncs, f); } } } // load and spawn all other entities if (progstype == PROG_H2) { extern cvar_t coop; spawnflagmask = 0; if (deathmatch.value) spawnflagmask |= SPAWNFLAG_NOT_H2DEATHMATCH; else if (coop.value) spawnflagmask |= SPAWNFLAG_NOT_H2COOP; else spawnflagmask |= SPAWNFLAG_NOT_H2SINGLE; if (skill.value < 0.5) spawnflagmask |= SPAWNFLAG_NOT_H2EASY; else if (skill.value > 1.5) spawnflagmask |= SPAWNFLAG_NOT_H2HARD; else spawnflagmask |= SPAWNFLAG_NOT_H2MEDIUM; //don't filter based on player class. we're lame and don't have any real concept of player classes. } else if (!deathmatch.value) //decide if we are to inhibit single player game ents instead { spawnflagmask = SPAWNFLAG_NOT_DEATHMATCH; if (skill.value < 0.5) spawnflagmask = SPAWNFLAG_NOT_EASY; else if (skill.value > 1.5) spawnflagmask = SPAWNFLAG_NOT_HARD; else spawnflagmask = SPAWNFLAG_NOT_MEDIUM; } else spawnflagmask = SPAWNFLAG_NOT_DEATHMATCH; //do this and get the precaches/start up the game if (sv_loadentfiles.value) file = COM_LoadMallocFile(va("maps/%s.ent", server)); else file = NULL; if (file) { char crc[12]; sprintf(crc, "%i", QCRC_Block(file, com_filesize)); Info_SetValueForStarKey(svs.info, "*entfile", crc, MAX_SERVERINFO_STRING); switch(svs.gametype) { case GT_PROGS: pr_edict_size = PR_LoadEnts(svprogfuncs, file, spawnflagmask); break; #ifdef Q2SERVER case GT_QUAKE2: ge->SpawnEntities(sv.name, file, startspot?startspot:""); break; #endif } BZ_Free(file); } else { Info_SetValueForStarKey(svs.info, "*entfile", "", MAX_SERVERINFO_STRING); switch(svs.gametype) { case GT_PROGS: pr_edict_size = PR_LoadEnts(svprogfuncs, sv.worldmodel->entities, spawnflagmask); break; #ifdef Q2SERVER case GT_QUAKE2: ge->SpawnEntities(sv.name, sv.worldmodel->entities, startspot?startspot:""); break; #endif } } #ifndef SERVERONLY current_loading_size+=10; SCR_BeginLoadingPlaque(); #endif Q_strncpyz(sv.mapname, sv.name, sizeof(sv.mapname)); if (svprogfuncs) { eval_t *val; ent = EDICT_NUM(svprogfuncs, 0); ent->v->angles[0] = ent->v->angles[1] = ent->v->angles[2] = 0; val = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, "message", NULL); if (val) { if (progstype == PROG_H2) snprintf(sv.mapname, sizeof(sv.mapname), "%s", T_GetString(val->_float-1)); else snprintf(sv.mapname, sizeof(sv.mapname), "%s", PR_GetString(svprogfuncs, val->string)); } if (Cvar_Get("sv_readonlyworld", "1", 0, "DP compatability")->value) ent->readonly = true; //lock it down! // look up some model indexes for specialized message compression SV_FindModelNumbers (); } #ifndef SERVERONLY current_loading_size+=10; SCR_BeginLoadingPlaque(); #endif // run two frames to allow everything to settle realtime += 0.1; SV_Physics (); #ifndef SERVERONLY current_loading_size+=10; SCR_BeginLoadingPlaque(); #endif realtime += 0.1; sv.time += 0.1; sv.starttime -= 0.1; SV_Physics (); #ifndef SERVERONLY current_loading_size+=10; SCR_BeginLoadingPlaque(); #endif // save movement vars SV_SetMoveVars(); // create a baseline for more efficient communications // SV_CreateBaseline (); if (svprogfuncs) SVNQ_CreateBaseline(); sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; // all spawning is completed, any further precache statements // or prog writes to the signon message are errors if (usecinematic) sv.state = ss_cinematic; else sv.state = ss_active; SV_GibFilterInit(); SV_FilterImpulseInit(); Info_SetValueForKey (svs.info, "map", sv.name, MAX_SERVERINFO_STRING); Con_TPrintf (STL_SERVERSPAWNED); //misc filenotfounds can be misleading. if (!startspot) { SV_FlushLevelCache(); //to make sure it's caught for (i=0 ; istring = PR_NewString(svprogfuncs, startspot, 0); } if (Cmd_AliasExist("f_svnewmap", RESTRICT_LOCAL)) Cbuf_AddText("f_svnewmap\n", RESTRICT_LOCAL); #ifndef SERVERONLY current_loading_size+=10; SCR_BeginLoadingPlaque(); #endif if (svs.gametype == GT_PROGS) { for (i = 0; i < sv.allocated_client_slots; i++) { host_client = &svs.clients[i]; if (host_client->state == cs_connected && host_client->protocol == SCP_BAD) { sv_player = host_client->edict; SV_ExtractFromUserinfo(host_client); // copy spawn parms out of the client_t for (j=0 ; j< NUM_SPAWN_PARMS ; j++) { if (spawnparamglobals[j]) *spawnparamglobals[j] = host_client->spawn_parms[j]; } SV_SetUpClientEdict(host_client, sv_player); sv_player->v->clientcolors = atoi(Info_ValueForKey(host_client->userinfo, "topcolor"))*16 + atoi(Info_ValueForKey(host_client->userinfo, "bottomcolor")); // call the spawn function pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect); // actually spawn the player pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player); PR_ExecuteProgram (svprogfuncs, pr_global_struct->PutClientInServer); // send notification to all clients host_client->sendinfo = true; host_client->state = cs_spawned; SV_UpdateToReliableMessages(); //so that we don't flood too much with 31 bots and one player. } } } } #endif