/* ================ SV_Physics_Client Player character actions ================ */ void SV_Physics_Client (edict_t *ent, int num) { client_t *cl; vec3_t v; qboolean was_angle_set; cl = &svs.clients[num-1]; if (!cl->active) return; // unconnected slot // call standard client pre-think PR_GLOBAL(time) = sv.time; PR_GLOBAL(self) = EDICT_TO_PROG(ent); PR_ExecuteProgram (PR_GLOBAL(PlayerPreThink)); // for cutscene hack (see below) if (isDedicated || (num != 1)) was_angle_set = false; // do it on local client only else was_angle_set = (ent->v.fixangle != 0); // do a move SV_CheckVelocity (ent); // decide which move function to call switch ((int)ent->v.movetype) { case MOVETYPE_NONE: if (!SV_RunThink(ent)) return; break; case MOVETYPE_WALK: if (!SV_RunThink(ent)) return; if (!SV_CheckWater(ent) && !((int)ent->v.flags & FL_WATERJUMP)) SV_AddGravity (ent); SV_CheckStuck (ent); SV_WalkMove (ent); break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_Physics_Toss (ent); break; case MOVETYPE_FLY: #ifdef HEXEN2_SUPPORT case MOVETYPE_SWIM: #endif if (!SV_RunThink(ent)) return; SV_FlyMove (ent, host_frametime, NULL); break; case MOVETYPE_NOCLIP: if (!SV_RunThink(ent)) return; VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); break; default: Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype); } // JDH: hack for cutscenes made by Darin McNeil's Cutscene Construction Kit: // (note that the extra precision is noticeable only if the viewangles // are sent from server to client as 2-byte values; hence the addition // of the new svc_setpreciseangle message code) if (was_angle_set && (ent->v.view_ofs[2] == 0) && host_cutscenehack.value && !strcmp (pr_strings + ent->v.classname, "camera")) { // - when camera changes back to player, classname remains "camera" for // 1 frame, but movedir is no longer valid. So as an additional check, // I verify that view_ofs[2] is still 0 // - early version(s?) of Cutscene Construction Kit don't move the camera, // so movedir is not used. I determine the version by checking *when* // the viewangle is set: early version does it in the .think function; // later ones in PlayerPreThink. was_angle_set will be true only if // it was changed in PlayerPreThink //if (!sv_oldprotocol.value) { v[0] = ent->v.movedir[0] - ent->v.origin[0]; v[1] = ent->v.movedir[1] - ent->v.origin[1]; v[2] = ent->v.origin[2] - ent->v.movedir[2]; //vectoangles (v, ent->v.angles); vectoangles (v, cl->cutscene_viewangles); } if (!cl->in_cutscene) { int i; edict_t *ed; // by this time, the player's viewangles have already been changed. // But the dummy entity spawned in place of the player has the values for (i = 1 ; i < sv.num_edicts ; i++) { // get the current server version ed = EDICT_NUM(i); if (ed->free) continue; if (!strcmp(pr_strings + ed->v.classname, "dummy")) { VectorCopy (ed->v.angles, cl->prev_viewangles); break; } } cl->in_cutscene = true; } //sv.found_cutscene = true; } else { if (cl->in_cutscene) { // I'm not sure why, but last viewangle while in_cutscene isn't final angle ent->v.fixangle = 1; VectorCopy (cl->prev_viewangles, ent->v.angles); cl->in_cutscene = false; } } SV_LinkEdict (ent, true); PR_GLOBAL(time) = sv.time; PR_GLOBAL(self) = EDICT_TO_PROG(ent); // JDH: another hack, this time for progs that lack CycleWeaponReverse if ((ent->v.impulse == 12.0) && ((sv_imp12hack.value >= 2) || (sv_imp12hack.value && !pr_handles_imp12)) && !ent->v.deadflag && (ent->v.view_ofs[0] || ent->v.view_ofs[1] || ent->v.view_ofs[2])) { eval_t *val = GETEDICTFIELD(ent, eval_attack_finished); if (val && (sv.time >= val->_float)) { SV_CycleWeaponReverse (ent); } } // call standard player post-think PR_ExecuteProgram (PR_GLOBAL(PlayerPostThink)); }
void ED_PrintNum (int ent) { ED_Print (EDICT_NUM(ent)); }
/* ================ ED_LoadFromFile The entities are directly placed in the array, rather than allocated with ED_Alloc, because otherwise an error loading the map would have entity number references out of order. Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. Used for both fresh maps and savegame loads. A fresh map would also need to call ED_CallSpawnFunctions () to let the objects initialize themselves. ================ */ void ED_LoadFromFile (char *data) { edict_t *ent; int inhibit; dfunction_t *func; ent = NULL; inhibit = 0; pr_global_struct->time = sv.time; // parse ents while (1) { // parse the opening brace data = COM_Parse (data); if (!data) break; if (com_token[0] != '{') Host_Error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = EDICT_NUM(0); else ent = ED_Alloc (); data = ED_ParseEdict (data, ent); // remove things from different skill levels or deathmatch if (deathmatch.value) { if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) { ED_Free (ent); inhibit++; continue; } } else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY)) || (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) ) { ED_Free (ent); inhibit++; continue; } // // immediately call spawn function // if (!ent->v.classname) { Com_Printf ("No classname for:\n"); ED_Print (ent); ED_Free (ent); continue; } // look for the spawn function func = ED_FindFunction ( PR_GetString(ent->v.classname) ); if (!func) { Com_Printf ("No spawn function for:\n"); ED_Print (ent); ED_Free (ent); continue; } pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (func - pr_functions); SV_FlushSignon(); } Com_DPrintf ("%i entities inhibited\n", inhibit); }
/* ============== SV_InitGame A brand new game has been started ============== */ void SV_InitGame( void ) { edict_t *ent; int i; if( svs.initialized ) { // cause any connected clients to reconnect Q_strncpy( host.finalmsg, "Server restarted", MAX_STRING ); SV_Shutdown( true ); } else { // init game after host error if( !svgame.hInstance ) { if( !SV_LoadProgs( GI->game_dll )) { MsgDev( D_ERROR, "SV_InitGame: can't initialize %s\n", GI->game_dll ); return; // can't load } MsgDev( D_INFO, "Server loaded\n" ); } // make sure the client is down CL_Drop(); } // now apply latched commands Cmd_ExecuteString( "latch\n", src_command ); if( Cvar_VariableValue( "coop" ) && Cvar_VariableValue ( "deathmatch" ) && Cvar_VariableValue( "teamplay" )) { MsgDev( D_WARN, "Deathmatch, Teamplay and Coop set, defaulting to Deathmatch\n"); Cvar_FullSet( "coop", "0", CVAR_LATCH ); Cvar_FullSet( "teamplay", "0", 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( host.type == HOST_DEDICATED ) { if( !Cvar_VariableValue( "coop" ) && !Cvar_VariableValue( "teamplay" )) Cvar_FullSet( "deathmatch", "1", CVAR_LATCH ); } // init clients if( Cvar_VariableValue( "deathmatch" ) || Cvar_VariableValue( "teamplay" )) { if( sv_maxclients->integer <= 1 ) Cvar_FullSet( "maxplayers", "8", CVAR_LATCH ); else if( sv_maxclients->integer > MAX_CLIENTS ) Cvar_FullSet( "maxplayers", "32", CVAR_LATCH ); } else if( Cvar_VariableValue( "coop" )) { if( sv_maxclients->integer <= 1 || sv_maxclients->integer > 4 ) Cvar_FullSet( "maxplayers", "4", CVAR_LATCH ); } else { // non-deathmatch, non-coop is one player Cvar_FullSet( "maxplayers", "1", CVAR_LATCH ); } svgame.globals->maxClients = sv_maxclients->integer; SV_UPDATE_BACKUP = ( svgame.globals->maxClients == 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP; svs.clients = Z_Malloc( sizeof( sv_client_t ) * sv_maxclients->integer ); svs.num_client_entities = sv_maxclients->integer * SV_UPDATE_BACKUP * 64; svs.packet_entities = Z_Malloc( sizeof( entity_state_t ) * svs.num_client_entities ); svs.baselines = Z_Malloc( sizeof( entity_state_t ) * GI->max_edicts ); // client frames will be allocated in SV_DirectConnect // init network stuff NET_Config(( sv_maxclients->integer > 1 )); // copy gamemode into svgame.globals svgame.globals->deathmatch = Cvar_VariableInteger( "deathmatch" ); svgame.globals->teamplay = Cvar_VariableInteger( "teamplay" ); svgame.globals->coop = Cvar_VariableInteger( "coop" ); // heartbeats will always be sent to the id master svs.last_heartbeat = MAX_HEARTBEAT; // send immediately // set client fields on player ents for( i = 0; i < svgame.globals->maxClients; i++ ) { // setup all the clients ent = EDICT_NUM( i + 1 ); SV_InitEdict( ent ); svs.clients[i].edict = ent; } // get actual movevars SV_UpdateMovevars( true ); svgame.numEntities = svgame.globals->maxClients + 1; // clients + world svs.initialized = true; }
/* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void SpawnEntities (const char *mapname, const char *entities_, const char *spawnpoint) { char *entities = strdup(entities_); edict_t *ent; int inhibit; const char *com_token; int i; float skill_level; skill_level = floor (skill->value); if (skill_level < 0) skill_level = 0; if (skill_level > 3) skill_level = 3; if (skill->value != skill_level) gi.cvar_forceset("skill", va("%f", skill_level)); SaveClientData (); gi.FreeTags (TAG_LEVEL); memset (&level, 0, sizeof(level)); memset (g_edicts, 0, game.maxentities * sizeof (g_edicts[0])); strncpy (level.mapname, mapname, sizeof(level.mapname)-1); strncpy (game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)-1); // set client fields on player ents for (i=0 ; i<game.maxclients ; i++) g_edicts[i+1].client = game.clients + i; ent = NULL; inhibit = 0; bool isCommandLevel = (Q_stricmp(level.mapname, "command") != 0); // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = g_edicts; else ent = G_Spawn (); entities = ED_ParseEdict (entities, ent); // yet another map hack if (isCommandLevel && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if (deathmatch->value) { if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { G_FreeEdict (ent); inhibit++; continue; } } else { if ( /* ((coop->value) && (ent->spawnflags & SPAWNFLAG_NOT_COOP)) || */ ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict (ent); inhibit++; continue; } } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_COOP|SPAWNFLAG_NOT_DEATHMATCH); } ED_CallSpawn (ent); } gi.dprintf ("%i entities inhibited\n", inhibit); #if 0 i = 1; ent = EDICT_NUM(i); while (i < globals.num_edicts) { if (ent->inuse != 0 || ent->inuse != 1) Com_DPrintf("Invalid entity %d\n", i); i++, ent++; } #endif G_FindTeams (); PlayerTrail_Init (); free(entities); }
static void SV_FinishPMove( playermove_t *pmove, sv_client_t *cl ) { edict_t *clent = cl->edict; clent->v.teleport_time = pmove->waterjumptime; VectorCopy( pmove->origin, clent->v.origin ); VectorCopy( pmove->view_ofs, clent->v.view_ofs ); VectorCopy( pmove->velocity, clent->v.velocity ); VectorCopy( pmove->basevelocity, clent->v.basevelocity ); VectorCopy( pmove->punchangle, clent->v.punchangle ); VectorCopy( pmove->movedir, clent->v.movedir ); clent->v.flTimeStepSound = pmove->flTimeStepSound; clent->v.flFallVelocity = pmove->flFallVelocity; clent->v.oldbuttons = pmove->oldbuttons; clent->v.waterlevel = pmove->waterlevel; clent->v.watertype = pmove->watertype; clent->v.maxspeed = pmove->clientmaxspeed; clent->v.flDuckTime = pmove->flDuckTime; clent->v.flSwimTime = pmove->flSwimTime; clent->v.iStepLeft = pmove->iStepLeft; clent->v.movetype = pmove->movetype; clent->v.friction = pmove->friction; clent->v.deadflag = pmove->deadflag; clent->v.effects = pmove->effects; clent->v.bInDuck = pmove->bInDuck; clent->v.flags = pmove->flags; // copy back user variables clent->v.iuser1 = pmove->iuser1; clent->v.iuser2 = pmove->iuser2; clent->v.iuser3 = pmove->iuser3; clent->v.iuser4 = pmove->iuser4; clent->v.fuser1 = pmove->fuser1; clent->v.fuser2 = pmove->fuser2; clent->v.fuser3 = pmove->fuser3; clent->v.fuser4 = pmove->fuser4; VectorCopy( pmove->vuser1, clent->v.vuser1 ); VectorCopy( pmove->vuser2, clent->v.vuser2 ); VectorCopy( pmove->vuser3, clent->v.vuser3 ); VectorCopy( pmove->vuser4, clent->v.vuser4 ); if( svgame.pmove->onground == -1 ) { clent->v.flags &= ~FL_ONGROUND; } else if( pmove->onground >= 0 && pmove->onground < pmove->numphysent ) { clent->v.flags |= FL_ONGROUND; clent->v.groundentity = EDICT_NUM( svgame.pmove->physents[svgame.pmove->onground].info ); } // angles // show 1/3 the pitch angle and all the roll angle if( !clent->v.fixangle ) { VectorCopy( pmove->angles, clent->v.v_angle ); clent->v.angles[PITCH] = -( clent->v.v_angle[PITCH] / 3.0f ); clent->v.angles[ROLL] = clent->v.v_angle[ROLL]; clent->v.angles[YAW] = clent->v.v_angle[YAW]; } SV_SetMinMaxSize( clent, pmove->player_mins[pmove->usehull], pmove->player_maxs[pmove->usehull] ); // all next calls ignore footstep sounds pmove->runfuncs = false; }
/* ============ SV_PushMove ============ */ static edict_t *SV_PushMove( edict_t *pusher, float movetime ) { int i, e, block; int num_moved, oldsolid; vec3_t mins, maxs, lmove; sv_pushed_t *p, *pushed_p; edict_t *check; if( sv.state == ss_loading || VectorIsNull( pusher->v.velocity )) { pusher->v.ltime += movetime; return NULL; } for( i = 0; i < 3; i++ ) { lmove[i] = pusher->v.velocity[i] * movetime; mins[i] = pusher->v.absmin[i] + lmove[i]; maxs[i] = pusher->v.absmax[i] + lmove[i]; } pushed_p = svgame.pushed; // save the pusher's original position pushed_p->ent = pusher; VectorCopy( pusher->v.origin, pushed_p->origin ); VectorCopy( pusher->v.angles, pushed_p->angles ); pushed_p++; // move the pusher to it's final position SV_LinearMove( pusher, movetime, pusher->v.friction ); SV_LinkEdict( pusher, false ); pusher->v.ltime += movetime; oldsolid = pusher->v.solid; // non-solid pushers can't push anything if( pusher->v.solid == SOLID_NOT ) return NULL; // see if any solid entities are inside the final position num_moved = 0; for( e = 1; e < svgame.numEntities; e++ ) { check = EDICT_NUM( e ); if( !SV_IsValidEdict( check )) continue; // filter movetypes to collide with if( !SV_CanPushed( check )) continue; pusher->v.solid = SOLID_NOT; block = SV_TestEntityPosition( check, pusher ); pusher->v.solid = oldsolid; if( block ) continue; // if the entity is standing on the pusher, it will definately be moved if( !(( check->v.flags & FL_ONGROUND ) && check->v.groundentity == pusher )) { if( check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] || check->v.absmin[2] >= maxs[2] || check->v.absmax[0] <= mins[0] || check->v.absmax[1] <= mins[1] || check->v.absmax[2] <= mins[2] ) continue; // see if the ent's bbox is inside the pusher's final position if( !SV_TestEntityPosition( check, NULL )) continue; } // remove the onground flag for non-players if( check->v.movetype != MOVETYPE_WALK ) check->v.flags &= ~FL_ONGROUND; // save original position of contacted entity pushed_p->ent = check; VectorCopy( check->v.origin, pushed_p->origin ); VectorCopy( check->v.angles, pushed_p->angles ); pushed_p++; // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity( check, lmove, vec3_origin, &block ); pusher->v.solid = oldsolid; // if it is still inside the pusher, block if( SV_TestEntityPosition( check, NULL ) && block ) { if( !SV_CanBlock( check )) continue; pusher->v.ltime -= movetime; // move back any entities we already moved // go backwards, so if the same entity was pushed // twice, it goes back to the original position for( p = pushed_p - 1; p >= svgame.pushed; p-- ) { VectorCopy( p->origin, p->ent->v.origin ); VectorCopy( p->angles, p->ent->v.angles ); SV_LinkEdict( p->ent, (p->ent == check) ? true : false ); } return check; } } return NULL; }
int LoadGamestate(char *level, char *startspot) { char name[MAX_OSPATH]; FILE *f; char mapname[MAX_QPATH]; float time, sk; char str[32768], *start; int i, r; edict_t *ent; int entnum; int version; // float spawn_parms[NUM_SPAWN_PARMS]; sprintf (name, "%s/%s.gip", com_gamedir, level); Con_Printf ("Loading game from %s...\n", name); f = fopen (name, "r"); if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return -1; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return -1; } fscanf (f, "%s\n", str); // for (i=0 ; i<NUM_SPAWN_PARMS ; i++) // fscanf (f, "%f\n", &spawn_parms[i]); fscanf (f, "%f\n", &sk); Cvar_SetValue ("skill", sk); fscanf (f, "%s\n",mapname); fscanf (f, "%f\n",&time); SV_SpawnServer (mapname, startspot); if (!sv.active) { Con_Printf ("Couldn't load map\n"); return -1; } // load the light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1); strcpy (sv.lightstyles[i], str); } // load the edicts out of the savegame file while (!feof(f)) { fscanf (f, "%i\n",&entnum); for (i=0 ; i<sizeof(str)-1 ; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == sizeof(str)-1) Sys_Error ("Loadgame buffer overflow"); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Sys_Error ("First token isn't a brace"); // parse an edict ent = EDICT_NUM(entnum); memset (&ent->v, 0, progs->entityfields * 4); ent->free = false; ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } // sv.num_edicts = entnum; sv.time = time; fclose (f); // for (i=0 ; i<NUM_SPAWN_PARMS ; i++) // svs.clients->spawn_parms[i] = spawn_parms[i]; return 0; }
void SV_LoadGame_f (void) { extern cvar_t sv_progtype; char name[MAX_OSPATH], mapname[MAX_QPATH], str[32 * 1024], *start; FILE *f; float time, tfloat, spawn_parms[NUM_SPAWN_PARMS]; edict_t *ent; int entnum, version, r; unsigned int i; if (Cmd_Argc() != 2) { Con_Printf ("Usage: %s <savename> : load a game\n", Cmd_Argv(0)); return; } snprintf (name, sizeof (name), "%s/save/%s", fs_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); Con_Printf ("Loading game from %s...\n", name); if (!(f = fopen (name, "rb"))) { Con_Printf ("ERROR: couldn't open.\n"); return; } if (fscanf (f, "%i\n", &version) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } if (version != SAVEGAME_VERSION) { fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } if (fscanf (f, "%s\n", str) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } for (i = 0; i < NUM_SPAWN_PARMS; i++) { if (fscanf (f, "%f\n", &spawn_parms[i]) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } } // this silliness is so we can load 1.06 save files, which have float skill values if (fscanf (f, "%f\n", &tfloat) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } current_skill = (int)(tfloat + 0.1); Cvar_Set (&skill, va("%i", current_skill)); Cvar_SetValue (&deathmatch, 0); Cvar_SetValue (&coop, 0); Cvar_SetValue (&teamplay, 0); Cvar_SetValue (&maxclients, 1); Cvar_Set (&sv_progsname, "spprogs"); // force progsname #ifdef USE_PR2 Cvar_Set (&sv_progtype, "0"); // force .dat #endif if (fscanf (f, "%s\n", mapname) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } if (fscanf (f, "%f\n", &time) != 1) { fclose (f); Con_Printf ("Error reading savegame data\n"); return; } #ifndef SERVERONLY Host_EndGame(); CL_BeginLocalConnection (); #endif SV_SpawnServer (mapname, false); if (sv.state != ss_active) { Con_Printf ("Couldn't load map\n"); fclose (f); return; } // load the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { if (fscanf (f, "%s\n", str) != 1) { Con_Printf("Couldn't read lightstyles\n"); fclose (f); return; } sv.lightstyles[i] = (char *) Hunk_Alloc (strlen(str) + 1); strlcpy (sv.lightstyles[i], str, strlen(str) + 1); } // pause until all clients connect if (!(sv.paused & 1)) SV_TogglePause (NULL, 1); sv.loadgame = true; // load the edicts out of the savegame file entnum = -1; // -1 is the globals while (!feof(f)) { for (i = 0; i < sizeof(str) - 1; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == sizeof(str)-1) Host_Error ("Loadgame buffer overflow"); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Host_Error ("First token isn't a brace"); if (entnum == -1) { // parse the global vars ED_ParseGlobals (start); } else { // parse an edict ent = EDICT_NUM(entnum); ED_ClearEdict (ent); // FIXME: we also clear world edict here, is it OK? ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->e->free) SV_LinkEdict (ent, false); } entnum++; } sv.num_edicts = entnum; sv.time = time; fclose (f); for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; }
void Host_Savegame_f (void) { char name[256]; FILE *f; int i; char comment[SAVEGAME_COMMENT_LENGTH+1]; if (cmd_source != src_command) return; if (!sv.active) { Con_Printf ("Not playing a local game.\n"); return; } if (cl.intermission) { Con_Printf ("Can't save in intermission.\n"); return; } if (svs.maxclients != 1) { Con_Printf ("Can't save multiplayer games.\n"); return; } if (Cmd_Argc() != 2) { Con_Printf ("save <savename> : save a game\n"); return; } if (strstr(Cmd_Argv(1), "..")) { Con_Printf ("Relative pathnames are not allowed.\n"); return; } for (i=0 ; i<svs.maxclients ; i++) { if (svs.clients[i].active && (svs.clients[i].edict->u.v.health <= 0) ) { Con_Printf ("Can't savegame with a dead player\n"); return; } } sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); Con_Printf ("Saving game to %s...\n", name); f = fopen (name, "w"); if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return; } fprintf (f, "%i\n", SAVEGAME_VERSION); Host_SavegameComment (comment); fprintf (f, "%s\n", comment); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) fprintf (f, "%f\n", svs.clients->spawn_parms[i]); fprintf (f, "%d\n", current_skill); fprintf (f, "%s\n", sv.name); fprintf (f, "%f\n",sv.time); // write the light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { if (sv.lightstyles[i]) fprintf (f, "%s\n", sv.lightstyles[i]); else fprintf (f,"m\n"); } ED_WriteGlobals (f); for (i=0 ; i<sv.num_edicts ; i++) { ED_Write (f, EDICT_NUM(i)); fflush (f); } fclose (f); Con_Printf ("done.\n"); }
void Host_Loadgame_f (void) { char name[MAX_OSPATH]; FILE *f; char mapname[MAX_QPATH]; float time, tfloat; char str[32768], *start; int i, r; edict_t *ent; int entnum; int version; float spawn_parms[NUM_SPAWN_PARMS]; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("load <savename> : load a game\n"); return; } cls.demonum = -1; // stop demo loop in case this fails sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Con_Printf ("Loading game from %s...\n", name); f = fopen (name, "r"); if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } fscanf (f, "%s\n", str); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) fscanf (f, "%f\n", &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values fscanf (f, "%f\n", &tfloat); current_skill = (int)(tfloat + 0.1); Cvar_SetValue ("skill", (float)current_skill); #ifdef QUAKE2 Cvar_SetValue ("deathmatch", 0); Cvar_SetValue ("coop", 0); Cvar_SetValue ("teamplay", 0); #endif fscanf (f, "%s\n",mapname); fscanf (f, "%f\n",&time); CL_Disconnect_f (); #ifdef QUAKE2 SV_SpawnServer (mapname, NULL); #else SV_SpawnServer (mapname); #endif if (!sv.active) { Con_Printf ("Couldn't load map\n"); return; } sv.paused = true; // pause until all clients connect sv.loadgame = true; // load the light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = (char*) Hunk_Alloc (strlen(str)+1); strcpy (sv.lightstyles[i], str); } // load the edicts out of the savegame file entnum = -1; // -1 is the globals while (!feof(f)) { for (i=0 ; i< (int) (sizeof(str)-1) ; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == sizeof(str)-1) Sys_Error ("Loadgame buffer overflow"); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Sys_Error ("First token isn't a brace"); if (entnum == -1) { // parse the global vars ED_ParseGlobals (start); } else { // parse an edict ent = EDICT_NUM(entnum); memset (&ent->u.v, 0, progs->entityfields * 4); ent->free = false; ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } entnum++; } sv.num_edicts = entnum; sv.time = time; fclose (f); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; if (cls.state != ca_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } }
static void Host_Spawn_f (void) { int i; client_t *client; edict_t *ent; float *sendangles; if (cmd_source == src_command) { Sys_Printf ("spawn is not valid from the console\n"); return; } if (host_client->spawned) { Sys_Printf ("Spawn not valid -- already spawned\n"); return; } // run the entrance script if (sv.loadgame) { // loaded games are fully inited already // if this is the last client to be connected, unpause sv.paused = false; } else { // set up the edict ent = host_client->edict; memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); SVfloat (ent, colormap) = NUM_FOR_EDICT (&sv_pr_state, ent); SVfloat (ent, team) = (host_client->colors & 15) + 1; SVstring (ent, netname) = PR_SetString (&sv_pr_state, host_client->name); // copy spawn parms out of the client_t for (i = 0; i < NUM_SPAWN_PARMS; i++) sv_globals.parms[i] = host_client->spawn_parms[i]; // call the spawn function *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player); PR_ExecuteProgram (&sv_pr_state, sv_funcs.ClientConnect); if ((Sys_DoubleTime () - host_client->netconnection->connecttime) <= sv.time) Sys_Printf ("%s entered the game\n", host_client->name); PR_ExecuteProgram (&sv_pr_state, sv_funcs.PutClientInServer); } // send all current names, colors, and frag counts SZ_Clear (&host_client->message); // send time of update MSG_WriteByte (&host_client->message, svc_time); MSG_WriteFloat (&host_client->message, sv.time); for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { MSG_WriteByte (&host_client->message, svc_updatename); MSG_WriteByte (&host_client->message, i); MSG_WriteString (&host_client->message, client->name); MSG_WriteByte (&host_client->message, svc_updatefrags); MSG_WriteByte (&host_client->message, i); MSG_WriteShort (&host_client->message, client->old_frags); MSG_WriteByte (&host_client->message, svc_updatecolors); MSG_WriteByte (&host_client->message, i); MSG_WriteByte (&host_client->message, client->colors); } // send all current light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { MSG_WriteByte (&host_client->message, svc_lightstyle); MSG_WriteByte (&host_client->message, (char) i); MSG_WriteString (&host_client->message, sv.lightstyles[i]); } // send some stats MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS); MSG_WriteLong (&host_client->message, *sv_globals.total_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS); MSG_WriteLong (&host_client->message, *sv_globals.total_monsters); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_SECRETS); MSG_WriteLong (&host_client->message, *sv_globals.found_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_MONSTERS); MSG_WriteLong (&host_client->message, *sv_globals.killed_monsters); // send a fixangle // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = EDICT_NUM (&sv_pr_state, 1 + (host_client - svs.clients)); MSG_WriteByte (&host_client->message, svc_setangle); sendangles = sv.loadgame ? SVvector (ent, v_angle): SVvector (ent, angles); MSG_WriteAngle (&host_client->message, sendangles[0]); MSG_WriteAngle (&host_client->message, sendangles[1]); MSG_WriteAngle (&host_client->message, 0); SV_WriteClientdataToMessage (sv_player, &host_client->message); MSG_WriteByte (&host_client->message, svc_signonnum); MSG_WriteByte (&host_client->message, 3); host_client->sendsignon = true; }
static void Host_Loadgame_f (void) { dstring_t *name = 0; QFile *f; char *mapname = 0; script_t *script = 0; plitem_t *game = 0; plitem_t *list; plitem_t *item; char *script_data = 0; int i; int entnum; int count; int version; float spawn_parms[NUM_SPAWN_PARMS]; if (cmd_source != src_command) goto end; if (Cmd_Argc () != 2) { Sys_Printf ("load <savename> : load a game\n"); goto end; } cls.demonum = -1; // stop demo loop in case this fails name = dstring_newstr (); dsprintf (name, "%s/%s", qfs_gamedir->dir.def, Cmd_Argv (1)); QFS_DefaultExtension (name, ".sav"); cl.loading = true; CL_UpdateScreen (cl.time); Sys_Printf ("Loading game from %s...\n", name->str); f = QFS_Open (name->str, "rz"); if (!f) { Sys_Printf ("ERROR: couldn't open.\n"); goto end; } script_data = malloc (Qfilesize (f) + 1); i = Qread (f, script_data, Qfilesize (f)); script_data[i] = 0; Qclose (f); script = Script_New (); script->single = ""; // disable {}()': lexing Script_Start (script, name->str, script_data); Script_GetToken (script, 1); if (strequal (script->token->str, PACKAGE_NAME)) { if (!Script_TokenAvailable (script, 1)) { Sys_Printf ("Unexpected EOF reading %s\n", name->str); goto end; } game = PL_GetPropertyList (script->p); } else { sscanf (script->token->str, "%i", &version); if (version != SAVEGAME_VERSION) { Sys_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); goto end; } game = convert_to_game_dict (script); } item = PL_ObjectForKey (game, "spawn_parms"); for (i = 0; i < NUM_SPAWN_PARMS; i++) { if (i >= PL_A_NumObjects (item)) break; spawn_parms[i] = atof (PL_String (PL_ObjectAtIndex (item, i))); } current_skill = atoi (PL_String (PL_ObjectForKey (game, "current_skill"))); Cvar_SetValue (skill, current_skill); mapname = strdup (PL_String (PL_ObjectForKey (game, "name"))); CL_Disconnect_f (); SV_SpawnServer (mapname); if (!sv.active) { Sys_Printf ("Couldn't load map %s\n", mapname); goto end; } sv.paused = true; // pause until all clients connect sv.loadgame = true; list = PL_ObjectForKey (game, "lightstyles"); for (i = 0; i < MAX_LIGHTSTYLES; i++) { const char *style; char *str; if (i >= PL_A_NumObjects (list)) break; item = PL_ObjectAtIndex (list, i); style = PL_String (item); sv.lightstyles[i] = str = Hunk_Alloc (strlen (style) + 1); strcpy (str, style); } ED_InitGlobals (&sv_pr_state, PL_ObjectForKey (game, "globals")); list = PL_ObjectForKey (game, "entities"); entnum = 0; count = PL_A_NumObjects (list); if (count > sv.max_edicts) Host_Error ("too many entities in saved game. adjust max_edicts\n"); for (entnum = 0; entnum < count; entnum++) { plitem_t *entity = PL_ObjectAtIndex (list, entnum); edict_t *ent = EDICT_NUM (&sv_pr_state, entnum); memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); ent->free = false; ED_InitEntity (&sv_pr_state, entity, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } sv.num_edicts = entnum; sv.time = atof (PL_String (PL_ObjectForKey (game, "time"))); for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; if (cls.state != ca_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } end: if (game) PL_Free (game); if (mapname) free (mapname); if (script) Script_Delete (script); if (script_data) free (script_data); if (name) dstring_delete (name); }
/* * sv_clientconnect_done * callback for clientconnect POST request */ static void sv_mm_clientconnect_done( stat_query_t *query, qboolean success, void *customp ) { stat_query_section_t *root, *ratings_section; int session_id, isession_id; client_t *cl; edict_t *ent; /* * ch : JSON API * { * id: [int], // 0 on error, > 0 on logged-in user, < 0 for "anonymous" user * login: [string], // login-name for user on success * ratings: [ * { gametype: [string], rating: [float]: deviation: [float] } * .. * ] * } */ /* * since we are now generating the local session in SV_MM_ClientConnect, we should * check if session_id < 0 just in case so that we wont try to regenerate local * session or do anything stupid like that * (currently we dont even tell MM about these so ignore) */ session_id = (int)customp; isession_id = 0; cl = SV_MM_ClientForSession( session_id ); if( cl == NULL ) { // or figure out the validation anyway from the received session-id? Com_Printf("SV_MM_ClientConnect: Couldnt find client with session-id %d\n", session_id ); return; } if( !success ) Com_Printf( "SV_MM_ClientConnect: Error\n" ); else { root = sq_api->GetRoot( query ); if( query == NULL ) { Com_Printf("SV_MM_ParseResponse: Failed to parse data\n"); } else { isession_id = (int)sq_api->GetNumber( root, "id" ); if( isession_id == 0 ) { Com_Printf( "SV_MM_ClientConnect: Client not logged in\n"); } else if( isession_id != session_id ) { Com_Printf( "SV_MM_ClientConnect: Session-id doesnt match %d -> %d\n", isession_id, session_id ); isession_id = 0; } else { const char *login = sq_api->GetString( root, "login" ); ratings_section = sq_api->GetSection( root, "ratings" ); Q_strncpyz( cl->mm_login, login, sizeof( cl->mm_login ) ); if( !Info_SetValueForKey( cl->userinfo, "cl_mm_login", login ) ) { Com_Printf( "Failed to set infokey cl_mm_login for player %s\n", login ); } if( ge != NULL && ratings_section != NULL ) { int idx = 0; stat_query_section_t *element = sq_api->GetArraySection( ratings_section, idx++ ); ent = EDICT_NUM( (cl - svs.clients) + 1 ); while( element != NULL ) { ge->AddRating( ent, sq_api->GetString( element, "gametype" ), sq_api->GetNumber( element, "rating" ), sq_api->GetNumber( element, "deviation" ) ); element = sq_api->GetArraySection( ratings_section, idx++ ); } } SV_UserinfoChanged( cl ); } } } // unable to validate client, either kick him out or force local session if( isession_id == 0 ) { if( sv_mm_loginonly->integer ) { SV_DropClient( cl, DROP_TYPE_GENERAL, "Error: This server requires login. Create account at http://www.warsow.net/" ); return; } // TODO: check that session_id >= 0 isession_id = SV_MM_GenerateLocalSession(); Com_Printf("SV_MM_ClientConnect: Forcing local_session %d on client %s\n", isession_id, cl->name ); cl->mm_session = isession_id; // TODO: reflect this to the userinfo Info_SetValueForKey( cl->userinfo, "cl_mm_session", va("%d", isession_id ) ); // We should also notify MM about the new local session id? // Or another option would be that MM doesnt track local sessions at all, // it just emits the results straight away. // resend scc query // cl->socket->address } Com_Printf("SV_MM_ClientConnect: %s with session id %d\n", cl->name, cl->mm_session ); }
/* =========== SV_RunCmd =========== */ void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed ) { usercmd_t lastcmd; edict_t *clent, *touch; double frametime; int i, oldmsec; pmtrace_t *pmtrace; trace_t trace; vec3_t oldvel; clent = cl->edict; if( cl->next_movetime > host.realtime ) { cl->last_movetime += ( ucmd->msec * 0.001f ); return; } cl->next_movetime = 0.0; // chop up very long commands if( ucmd->msec > 50 ) { lastcmd = *ucmd; oldmsec = ucmd->msec; lastcmd.msec = oldmsec / 2; SV_RunCmd( cl, &lastcmd, random_seed ); lastcmd.msec = oldmsec / 2 + (oldmsec & 1); // give them back thier msec. lastcmd.impulse = 0; SV_RunCmd( cl, &lastcmd, random_seed ); return; } if( !cl->fakeclient ) { SV_SetupMoveInterpolant( cl ); } lastcmd = *ucmd; svgame.dllFuncs.pfnCmdStart( cl->edict, ucmd, random_seed ); frametime = ucmd->msec * 0.001; cl->timebase += frametime; cl->last_movetime += frametime; PM_CheckMovingGround( clent, frametime ); VectorCopy( clent->v.v_angle, svgame.pmove->oldangles ); // save oldangles if( !clent->v.fixangle ) VectorCopy( ucmd->viewangles, clent->v.v_angle ); VectorClear( clent->v.clbasevelocity ); // copy player buttons clent->v.button = ucmd->buttons; if( ucmd->impulse ) clent->v.impulse = ucmd->impulse; if( ucmd->impulse == 204 ) { // force client.dll update SV_RefreshUserinfo(); } svgame.globals->time = cl->timebase; svgame.dllFuncs.pfnPlayerPreThink( clent ); SV_PlayerRunThink( clent, frametime, cl->timebase ); // If conveyor, or think, set basevelocity, then send to client asap too. if( !VectorIsNull( clent->v.basevelocity )) VectorCopy( clent->v.basevelocity, clent->v.clbasevelocity ); // setup playermove state SV_SetupPMove( svgame.pmove, cl, ucmd, cl->physinfo ); // motor! svgame.dllFuncs.pfnPM_Move( svgame.pmove, true ); // copy results back to client SV_FinishPMove( svgame.pmove, cl ); // link into place and touch triggers SV_LinkEdict( clent, true ); VectorCopy( clent->v.velocity, oldvel ); // save velocity // touch other objects for( i = 0; i < svgame.pmove->numtouch; i++ ) { // never touch the objects when "playersonly" is active if( i == MAX_PHYSENTS || ( sv.hostflags & SVF_PLAYERSONLY )) break; pmtrace = &svgame.pmove->touchindex[i]; touch = EDICT_NUM( svgame.pmove->physents[pmtrace->ent].info ); if( touch == clent ) continue; VectorCopy( pmtrace->deltavelocity, clent->v.velocity ); SV_ConvertPMTrace( &trace, pmtrace, touch ); SV_Impact( touch, clent, &trace ); } // restore velocity VectorCopy( oldvel, clent->v.velocity ); svgame.pmove->numtouch = 0; svgame.globals->time = cl->timebase; svgame.globals->frametime = frametime; // run post-think svgame.dllFuncs.pfnPlayerPostThink( clent ); svgame.dllFuncs.pfnCmdEnd( clent ); if( !cl->fakeclient ) { SV_RestoreMoveInterpolant( cl ); } }
/* ================ 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. ================ */ void SV_SpawnServer (char *mapname, qbool devmap, char* entityfile) { extern func_t ED_FindFunctionOffset (char *name); edict_t *ent; int i; extern cvar_t sv_loadentfiles, sv_loadentfiles_dir; char *entitystring; char oldmap[MAP_NAME_LEN]; extern qbool sv_allow_cheats; extern cvar_t sv_cheats, sv_paused, sv_bigcoords; #ifndef SERVERONLY extern void CL_ClearState (void); #endif // store old map name snprintf (oldmap, MAP_NAME_LEN, "%s", sv.mapname); Con_DPrintf ("SpawnServer: %s\n",mapname); #ifndef SERVERONLY // As client+server we do it here. // As serveronly we do it in NET_Init(). NET_InitServer(); #endif SV_SaveSpawnparms (); SV_LoadAccounts(); #ifdef USE_PR2 // remove bot clients for (i = 0; i < MAX_CLIENTS; i++) { if( sv_vm && svs.clients[i].isBot ) { svs.clients[i].old_frags = 0; svs.clients[i].edict->v.frags = 0.0; svs.clients[i].name[0] = 0; svs.clients[i].state = cs_free; Info_RemoveAll(&svs.clients[i]._userinfo_ctx_); Info_RemoveAll(&svs.clients[i]._userinfoshort_ctx_); SV_FullClientUpdate(&svs.clients[i], &sv.reliable_datagram); svs.clients[i].isBot = 0; } } #endif // Shutdown game. PR_GameShutDown(); PR_UnLoadProgs(); svs.spawncount++; // any partially connected client will be restarted #ifndef SERVERONLY com_serveractive = false; #endif sv.state = ss_dead; sv.paused = false; Cvar_SetROM(&sv_paused, "0"); Host_ClearMemory(); #ifdef FTE_PEXT_FLOATCOORDS if (sv_bigcoords.value) { msg_coordsize = 4; msg_anglesize = 2; } else { msg_coordsize = 2; msg_anglesize = 1; } #endif if ((int)coop.value) Cvar_Set (&deathmatch, "0"); current_skill = (int) (skill.value + 0.5); if (current_skill < 0) current_skill = 0; Cvar_Set (&skill, va("%d", current_skill)); if (current_skill > 3) current_skill = 3; if ((sv_cheats.value || devmap) && !sv_allow_cheats) { sv_allow_cheats = true; Info_SetValueForStarKey (svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING); } else if ((!sv_cheats.value && !devmap) && sv_allow_cheats) { sv_allow_cheats = false; Info_SetValueForStarKey (svs.info, "*cheats", "", MAX_SERVERINFO_STRING); } // wipe the entire per-level structure // NOTE: this also set sv.mvdrecording to false, so calling SV_MVD_Record() at end of function 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; sv.signon.maxsize = sizeof(sv.signon_buffers[0]); sv.signon.data = sv.signon_buffers[0]; sv.num_signon_buffers = 1; sv.time = 1.0; // load progs to get entity field count // which determines how big each edict is // and allocate edicts PR_LoadProgs (); #ifdef WITH_NQPROGS PR_InitPatchTables(); #endif PR_InitProg(); for (i = 0; i < MAX_EDICTS; i++) { ent = EDICT_NUM(i); ent->e = &sv.sv_edicts[i]; // assigning ->e field in each edict_t ent->e->entnum = i; ent->e->area.ed = ent; // yeah, pretty funny, but this help to find which edict_t own this area (link_t) } fofs_items2 = ED_FindFieldOffset ("items2"); // ZQ_ITEMS2 extension fofs_maxspeed = ED_FindFieldOffset ("maxspeed"); fofs_gravity = ED_FindFieldOffset ("gravity"); fofs_movement = ED_FindFieldOffset ("movement"); fofs_vw_index = ED_FindFieldOffset ("vw_index"); fofs_hideentity = ED_FindFieldOffset ("hideentity"); fofs_trackent = ED_FindFieldOffset ("trackent"); // find optional QC-exported functions. // we have it here, so we set it to NULL in case of PR2 progs. mod_SpectatorConnect = ED_FindFunctionOffset ("SpectatorConnect"); mod_SpectatorThink = ED_FindFunctionOffset ("SpectatorThink"); mod_SpectatorDisconnect = ED_FindFunctionOffset ("SpectatorDisconnect"); mod_ChatMessage = ED_FindFunctionOffset ("ChatMessage"); mod_UserInfo_Changed = ED_FindFunctionOffset ("UserInfo_Changed"); mod_ConsoleCmd = ED_FindFunctionOffset ("ConsoleCmd"); mod_UserCmd = ED_FindFunctionOffset ("UserCmd"); mod_localinfoChanged = ED_FindFunctionOffset ("localinfoChanged"); GE_ClientCommand = ED_FindFunctionOffset ("GE_ClientCommand"); GE_PausedTic = ED_FindFunctionOffset ("GE_PausedTic"); GE_ShouldPause = ED_FindFunctionOffset ("GE_ShouldPause"); // leave slots at start for clients only sv.num_edicts = MAX_CLIENTS+1; for (i=0 ; i<MAX_CLIENTS ; i++) { ent = EDICT_NUM(i+1); // restore client name. ent->v.netname = PR_SetString(svs.clients[i].name); // reserve edict. svs.clients[i].edict = ent; //ZOID - make sure we update frags right svs.clients[i].old_frags = 0; } // fill sv.mapname and sv.modelname with new map name strlcpy (sv.mapname, mapname, sizeof(sv.mapname)); snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", sv.mapname); #ifndef SERVERONLY // set cvar Cvar_ForceSet (&host_mapname, mapname); #endif if (!(sv.worldmodel = CM_LoadMap (sv.modelname, false, &sv.map_checksum, &sv.map_checksum2))) // true if bad map { Con_Printf ("Cant load map %s, falling back to %s\n", mapname, oldmap); // fill mapname, sv.mapname and sv.modelname with old map name strlcpy (sv.mapname, oldmap, sizeof(sv.mapname)); snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", sv.mapname); mapname = oldmap; // and re-load old map sv.worldmodel = CM_LoadMap (sv.modelname, false, &sv.map_checksum, &sv.map_checksum2); // this should never happen if (!sv.worldmodel) SV_Error ("CM_LoadMap: bad map"); } sv.map_checksum2 = Com_TranslateMapChecksum (sv.mapname, sv.map_checksum2); SV_ClearWorld (); // clear physics interaction links #ifdef USE_PR2 if ( sv_vm ) { sv.sound_precache[0] = ""; sv.model_precache[0] = ""; } else #endif { sv.sound_precache[0] = pr_strings; sv.model_precache[0] = pr_strings; } sv.model_precache[1] = sv.modelname; sv.models[1] = sv.worldmodel; for (i=1 ; i< CM_NumInlineModels() ; i++) { sv.model_precache[1+i] = localmodels[i]; sv.models[i+1] = CM_InlineModel (localmodels[i]); } //check player/eyes models for hacks sv.model_player_checksum = SV_CheckModel("progs/player.mdl"); sv.eyes_player_checksum = SV_CheckModel("progs/eyes.mdl"); // // spawn the rest of the entities on the map // // precache and static commands can be issued during // map initialization sv.state = ss_loading; #ifndef SERVERONLY com_serveractive = true; #endif ent = EDICT_NUM(0); ent->e->free = false; ent->v.model = PR_SetString(sv.modelname); ent->v.modelindex = 1; // world model ent->v.solid = SOLID_BSP; ent->v.movetype = MOVETYPE_PUSH; // information about the server ent->v.netname = PR_SetString(VersionStringFull()); ent->v.targetname = PR_SetString(SERVER_NAME); ent->v.impulse = VERSION_NUM; ent->v.items = pr_numbuiltins - 1; PR_GLOBAL(mapname) = PR_SetString(sv.mapname); // serverflags are for cross level information (sigils) PR_GLOBAL(serverflags) = svs.serverflags; if (pr_nqprogs) { pr_globals[35] = deathmatch.value; pr_globals[36] = coop.value; pr_globals[37] = teamplay.value; NQP_Reset (); } if (pr_nqprogs) { // register the cvars that NetQuake provides for mod use const char **var, *nqcvars[] = {"gamecfg", "scratch1", "scratch2", "scratch3", "scratch4", "saved1", "saved2", "saved3", "saved4", "savedgamecfg", "temp1", NULL }; for (var = nqcvars; *var; var++) Cvar_Create((char *)/*stupid const warning*/ *var, "0", 0); } // run the frame start qc function to let progs check cvars if (!pr_nqprogs) SV_ProgStartFrame (); // ********* External Entity support (.ent file(s) in gamedir/maps) pinched from ZQuake ********* // load and spawn all other entities entitystring = NULL; if ((int)sv_loadentfiles.value) { char ent_path[1024] = {0}; if (!entityfile || !entityfile[0]) entityfile = sv.mapname; // first try maps/sv_loadentfiles_dir/ if (sv_loadentfiles_dir.string[0]) { snprintf(ent_path, sizeof(ent_path), "maps/%s/%s.ent", sv_loadentfiles_dir.string, entityfile); entitystring = (char *) FS_LoadHunkFile(ent_path, NULL); } // try maps/ if not loaded yet. if (!entitystring) { snprintf(ent_path, sizeof(ent_path), "maps/%s.ent", entityfile); entitystring = (char *) FS_LoadHunkFile(ent_path, NULL); } if (entitystring) { Con_DPrintf ("Using entfile %s\n", ent_path); } } if (!entitystring) { entitystring = CM_EntityString(); } PR_LoadEnts(entitystring); // ********* End of External Entity support code ********* // look up some model indexes for specialized message compression SV_FindModelNumbers (); // all spawning is completed, any further precache statements // or prog writes to the signon message are errors sv.state = ss_active; // run two frames to allow everything to settle SV_Physics (); sv.time += 0.1; SV_Physics (); sv.time += 0.1; sv.old_time = sv.time; // save movement vars SV_SetMoveVars(); // create a baseline for more efficient communications SV_CreateBaseline (); sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; Info_SetValueForKey (svs.info, "map", sv.mapname, MAX_SERVERINFO_STRING); // calltimeofday. { extern void PF_calltimeofday (void); pr_global_struct->time = sv.time; pr_global_struct->self = 0; PF_calltimeofday(); } Con_DPrintf ("Server spawned.\n"); // we change map - clear whole demo struct and sent initial state to all dest if any (for QTV only I thought) SV_MVD_Record(NULL, true); #ifndef SERVERONLY CL_ClearState (); #endif }
/* ==================== SV_AddLinksToPmove collect solid entities ==================== */ void SV_AddLinksToPmove( areanode_t *node, const vec3_t pmove_mins, const vec3_t pmove_maxs ) { link_t *l, *next; edict_t *check, *pl; vec3_t mins, maxs; physent_t *pe; pl = EDICT_NUM( svgame.pmove->player_index + 1 ); //ASSERT( SV_IsValidEdict( pl )); if( !SV_IsValidEdict( pl ) ) { MsgDev( D_ERROR, "SV_AddLinksToPmove: you have broken clients!\n"); return; } // touch linked edicts for( l = node->solid_edicts.next; l != &node->solid_edicts; l = next ) { next = l->next; check = EDICT_FROM_AREA( l ); if( check->v.groupinfo != 0 ) { if(( !svs.groupop && (check->v.groupinfo & pl->v.groupinfo ) == 0) || ( svs.groupop == 1 && ( check->v.groupinfo & pl->v.groupinfo ) != 0 )) continue; } if( ( ( check->v.owner > 0) && check->v.owner == pl ) || check->v.solid == SOLID_TRIGGER ) continue; // player or player's own missile if( svgame.pmove->numvisent < MAX_PHYSENTS ) { pe = &svgame.pmove->visents[svgame.pmove->numvisent]; if( SV_CopyEdictToPhysEnt( pe, check )) svgame.pmove->numvisent++; } if( check->v.solid == SOLID_NOT && ( check->v.skin == CONTENTS_NONE || check->v.modelindex == 0 )) continue; // ignore monsterclip brushes if(( check->v.flags & FL_MONSTERCLIP ) && check->v.solid == SOLID_BSP ) continue; if( check == pl ) continue; // himself if( !sv_corpse_solid->value ) { if((( check->v.flags & FL_CLIENT ) && check->v.health <= 0 ) || check->v.deadflag == DEAD_DEAD ) continue; // dead body } if( VectorIsNull( check->v.size )) continue; VectorCopy( check->v.absmin, mins ); VectorCopy( check->v.absmax, maxs ); if( check->v.flags & FL_CLIENT ) { // trying to get interpolated values if( svs.currentPlayer ) SV_GetTrueMinMax( svs.currentPlayer, ( NUM_FOR_EDICT( check ) - 1), mins, maxs ); } if( !BoundsIntersect( pmove_mins, pmove_maxs, mins, maxs )) continue; if( svgame.pmove->numphysent < MAX_PHYSENTS ) { pe = &svgame.pmove->physents[svgame.pmove->numphysent]; if( SV_CopyEdictToPhysEnt( pe, check )) svgame.pmove->numphysent++; } } // recurse down both sides if( node->axis == -1 ) return; if( pmove_maxs[node->axis] > node->dist ) SV_AddLinksToPmove( node->children[0], pmove_mins, pmove_maxs ); if( pmove_mins[node->axis] < node->dist ) SV_AddLinksToPmove( node->children[1], pmove_mins, pmove_maxs ); }
/* =========== SV_RunCmd =========== */ static void SV_RunCmd (usercmd_t *ucmd) { edict_t *ent; int i, n; int oldmsec; cmd = *ucmd; // chop up very long commands if (cmd.msec > 50) { oldmsec = ucmd->msec; cmd.msec = oldmsec/2; SV_RunCmd (&cmd); cmd.msec = oldmsec/2; cmd.impulse = 0; SV_RunCmd (&cmd); return; } if (!sv_player->v.fixangle) VectorCopy (ucmd->angles, sv_player->v.v_angle); sv_player->v.button0 = ucmd->buttons & 1; sv_player->v.button2 = (ucmd->buttons & 2)>>1; if (ucmd->buttons & 4 || sv_player->v.playerclass == CLASS_DWARF) // crouched? sv_player->v.flags2 = ((int)sv_player->v.flags2) | FL2_CROUCHED; else sv_player->v.flags2 = ((int)sv_player->v.flags2) & (~FL2_CROUCHED); if (ucmd->impulse) sv_player->v.impulse = ucmd->impulse; // // angles // show 1/3 the pitch angle and all the roll angle if (sv_player->v.health > 0) { if (!sv_player->v.fixangle) { sv_player->v.angles[PITCH] = -sv_player->v.v_angle[PITCH]/3; sv_player->v.angles[YAW] = sv_player->v.v_angle[YAW]; } sv_player->v.angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; } host_frametime = ucmd->msec * 0.001; if (host_frametime > HX_FRAME_TIME) host_frametime = HX_FRAME_TIME; if (!host_client->spectator) { pr_global_struct->frametime = host_frametime; pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->PlayerPreThink); SV_RunThink (sv_player); } for (i = 0; i < 3; i++) pmove.origin[i] = sv_player->v.origin[i] + (sv_player->v.mins[i] - player_mins[i]); VectorCopy (sv_player->v.velocity, pmove.velocity); VectorCopy (sv_player->v.v_angle, pmove.angles); pmove.spectator = host_client->spectator; // pmove.waterjumptime = sv_player->v.teleport_time; pmove.numphysent = 1; pmove.physents[0].model = sv.worldmodel; pmove.cmd = *ucmd; pmove.dead = sv_player->v.health <= 0; pmove.oldbuttons = host_client->oldbuttons; pmove.hasted = sv_player->v.hasted; pmove.movetype = sv_player->v.movetype; pmove.crouched = (sv_player->v.hull == HULL_CROUCH); pmove.teleport_time = realtime + (sv_player->v.teleport_time - sv.time); // movevars.entgravity = host_client->entgravity; movevars.entgravity = sv_player->v.gravity; movevars.maxspeed = host_client->maxspeed; for (i = 0; i < 3; i++) { pmove_mins[i] = pmove.origin[i] - 256; pmove_maxs[i] = pmove.origin[i] + 256; } #if 1 AddLinksToPmove ( sv_areanodes ); #else AddAllEntsToPmove (); #endif #if 0 { int before, after; before = PM_TestPlayerPosition (pmove.origin); PlayerMove (); after = PM_TestPlayerPosition (pmove.origin); if (sv_player->v.health > 0 && before && !after ) Con_Printf ("player %s got stuck in playermove!!!!\n", host_client->name); } #else PlayerMove (); #endif host_client->oldbuttons = pmove.oldbuttons; // sv_player->v.teleport_time = pmove.waterjumptime; sv_player->v.waterlevel = waterlevel; sv_player->v.watertype = watertype; if (onground != -1) { sv_player->v.flags = (int)sv_player->v.flags | FL_ONGROUND; sv_player->v.groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[onground].info)); } else sv_player->v.flags = (int)sv_player->v.flags & ~FL_ONGROUND; for (i = 0; i < 3; i++) sv_player->v.origin[i] = pmove.origin[i] - (sv_player->v.mins[i] - player_mins[i]); #if 0 // truncate velocity the same way the net protocol will for (i = 0; i < 3; i++) sv_player->v.velocity[i] = (int)pmove.velocity[i]; #else VectorCopy (pmove.velocity, sv_player->v.velocity); #endif VectorCopy (pmove.angles, sv_player->v.v_angle); if (!host_client->spectator) { // link into place and touch triggers SV_LinkEdict (sv_player, true); // touch other objects for (i = 0; i < pmove.numtouch; i++) { n = pmove.physents[pmove.touchindex[i]].info; ent = EDICT_NUM(n); // Why not just do an SV_Impact here? // SV_Impact(sv_player,ent); if (sv_player->v.touch) { pr_global_struct->self = EDICT_TO_PROG(sv_player); pr_global_struct->other = EDICT_TO_PROG(ent); PR_ExecuteProgram (sv_player->v.touch); } if (!ent->v.touch || (playertouch[n/8]&(1<<(n%8)))) continue; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (ent->v.touch); playertouch[n/8] |= 1 << (n%8); } } }
/* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void SpawnEntities (char *mapname, char *entities, char *spawnpoint) { edict_t *ent; int inhibit; char *com_token; int i; float skill_level; extern int max_modelindex; extern int max_soundindex; extern int lastgibframe; if (developer->value) gi.dprintf("====== SpawnEntities ========\n"); skill_level = floor (skill->value); if (skill_level < 0) skill_level = 0; if (skill_level > 3) skill_level = 3; if (skill->value != skill_level) gi.cvar_forceset("skill", va("%f", skill_level)); SaveClientData (); gi.FreeTags (TAG_LEVEL); memset (&level, 0, sizeof(level)); memset (g_edicts, 0, game.maxentities * sizeof (g_edicts[0])); // Lazarus: these are used to track model and sound indices // in g_main.c: max_modelindex = 0; max_soundindex = 0; // Lazarus: last frame a gib was spawned in lastgibframe = 0; strncpy (level.mapname, mapname, sizeof(level.mapname)-1); strncpy (game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)-1); // set client fields on player ents for (i=0 ; i<game.maxclients ; i++) g_edicts[i+1].client = game.clients + i; ent = NULL; inhibit = 0; // Knightamre- load the entity alias script file LoadAliasData(); //gi.dprintf ("Size of alias data: %i\n", alias_data_size); // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = g_edicts; else ent = G_Spawn (); entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if (deathmatch->value) { if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { G_FreeEdict (ent); inhibit++; continue; } } else if (coop->value) // Knightmare added- not in coop flag support { if ( ent->spawnflags & SPAWNFLAG_NOT_COOP ) { G_FreeEdict (ent); inhibit++; continue; } // NOT_EASY && NOT_MEDIUM && NOT_HARD && NOT_DM == COOP_ONLY if ( (ent->spawnflags & SPAWNFLAG_NOT_EASY) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM) && (ent->spawnflags & SPAWNFLAG_NOT_HARD) && (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH) ) goto removeflags; if( ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || ((skill->value >= 2) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict (ent); inhibit++; continue; } } else // single player { if( ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || ((skill->value >= 2) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict (ent); inhibit++; continue; } } removeflags: // Knightmare- remove no coop flag ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_DEATHMATCH|SPAWNFLAG_NOT_COOP); } ED_CallSpawn (ent); ent->s.renderfx |= RF_IR_VISIBLE; // ir goggles flag } // Knightmare- unload the alias script file if (alias_data) { // If no alias file was loaded, don't bother #ifdef KMQUAKE2_ENGINE_MOD // use new engine function instead gi.FreeFile(alias_data); #else // if (alias_from_pak) gi.TagFree(alias_data); // else // free(&alias_data); #endif } // Knightmare- unload the replacement entity data /* if (newents) // If no alias file was loaded, don't bother #ifdef KMQUAKE2_ENGINE_MOD // use new engine function instead gi.FreeFile(newents); #else free(&newents); #endif*/ gi.dprintf ("%i entities inhibited\n", inhibit); #ifdef DEBUG i = 1; ent = EDICT_NUM(i); while (i < globals.num_edicts) { if (ent->inuse != 0 || ent->inuse != 1) Com_DPrintf("Invalid entity %d\n", i); i++, ent++; } #endif G_FindTeams (); // DWH G_FindCraneParts (); // Get origin offsets (mainly for brush models w/o origin brushes) for (i=1, ent=g_edicts+i ; i < globals.num_edicts ; i++,ent++) { VectorAdd(ent->absmin,ent->absmax,ent->origin_offset); VectorScale(ent->origin_offset,0.5,ent->origin_offset); VectorSubtract(ent->origin_offset,ent->s.origin,ent->origin_offset); } // end DWH PlayerTrail_Init (); //ZOID CTFSpawn(); // Knightmare added if (deathmatch->value && !ctf->value) CTFSetupTechSpawn (); //ZOID if (!deathmatch->value) SetupHintPaths(); for (i=1, ent=g_edicts+i; i < globals.num_edicts; i++, ent++) { if (!ent->movewith) continue; if (ent->movewith_ent) continue; ent->movewith_ent = G_Find(NULL, FOFS(targetname), ent->movewith); // Make sure that we can really "movewith" this guy. This check // allows us to have movewith parent with same targetname as // other entities while (ent->movewith_ent && (Q_stricmp(ent->movewith_ent->classname,"func_train") && Q_stricmp(ent->movewith_ent->classname,"model_train") && Q_stricmp(ent->movewith_ent->classname,"func_door") && Q_stricmp(ent->movewith_ent->classname,"func_vehicle") && Q_stricmp(ent->movewith_ent->classname,"func_tracktrain") )) ent->movewith_ent = G_Find (ent->movewith_ent, FOFS(targetname), ent->movewith); if (ent->movewith_ent) movewith_init (ent->movewith_ent); } /* for(i=1, ent=g_edicts+i; i < globals.num_edicts; i++, ent++) { gi.dprintf("%s:%s - movewith=%s, movewith_ent=%s:%s, movewith_next=%s:%s\n====================\n", ent->classname, (ent->targetname ? ent->targetname : "noname"), (ent->movewith ? ent->movewith : "N/A"), (ent->movewith_ent ? ent->movewith_ent->classname : "N/A"), (ent->movewith_ent ? (ent->movewith_ent->targetname ? ent->movewith_ent->targetname : "noname") : "N/A"), (ent->movewith_next ? ent->movewith_next->classname : "N/A"), (ent->movewith_next ? (ent->movewith_next->targetname ? ent->movewith_next->targetname : "noname") : "N/A")); } */ if (game.transition_ents) LoadTransitionEnts (); actor_files (); }
/* ================== SV_Begin_f ================== */ static void SV_Begin_f (void) { int i; host_client->state = cs_spawned; // handle the case of a level changing while a client was connecting if ( atoi(Cmd_Argv(1)) != svs.spawncount ) { Con_Printf ("%s from different level\n", __thisfunc__); SV_New_f (); return; } if (host_client->spectator) { SV_SpawnSpectator (); if (SpectatorConnect) { // copy spawn parms out of the client_t for (i = 0; i < NUM_SPAWN_PARMS; i++) (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; // call the spawn function pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (SpectatorConnect); } } else { // copy spawn parms out of the client_t for (i = 0; i < NUM_SPAWN_PARMS; i++) (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; host_client->send_all_v = true; // call the spawn function pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->ClientConnect); // actually spawn the player pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->PutClientInServer); } // clear the net statistics, because connecting gives a bogus picture host_client->netchan.frame_latency = 0; host_client->netchan.frame_rate = 0; host_client->netchan.drop_count = 0; host_client->netchan.good_count = 0; #if 0 // // send a fixangle over the reliable channel to make sure it gets there // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = EDICT_NUM( 1 + (host_client - svs.clients) ); MSG_WriteByte (&host_client->netchan.message, svc_setangle); for (i = 0; i < 2; i++) MSG_WriteAngle (&host_client->netchan.message, ent->v.angles[i] ); MSG_WriteAngle (&host_client->netchan.message, 0 ); #endif }
/* ============ SV_PushRotate ============ */ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) { int i, e, block, oldsolid; matrix4x4 start_l, end_l; vec3_t lmove, amove; sv_pushed_t *p, *pushed_p; vec3_t org, org2, temp; edict_t *check; if( sv.state == ss_loading || VectorIsNull( pusher->v.avelocity )) { pusher->v.ltime += movetime; return NULL; } for( i = 0; i < 3; i++ ) amove[i] = pusher->v.avelocity[i] * movetime; // create pusher initial position Matrix4x4_CreateFromEntity( start_l, pusher->v.angles, pusher->v.origin, 1.0f ); pushed_p = svgame.pushed; // save the pusher's original position pushed_p->ent = pusher; VectorCopy( pusher->v.origin, pushed_p->origin ); VectorCopy( pusher->v.angles, pushed_p->angles ); pushed_p++; // move the pusher to it's final position SV_AngularMove( pusher, movetime, pusher->v.friction ); SV_LinkEdict( pusher, false ); pusher->v.ltime += movetime; oldsolid = pusher->v.solid; // non-solid pushers can't push anything if( pusher->v.solid == SOLID_NOT ) return NULL; // create pusher final position Matrix4x4_CreateFromEntity( end_l, pusher->v.angles, pusher->v.origin, 1.0f ); // see if any solid entities are inside the final position for( e = 1; e < svgame.numEntities; e++ ) { check = EDICT_NUM( e ); if( !SV_IsValidEdict( check )) continue; // filter movetypes to collide with if( !SV_CanPushed( check )) continue; pusher->v.solid = SOLID_NOT; block = SV_TestEntityPosition( check, pusher ); pusher->v.solid = oldsolid; if( block ) continue; // if the entity is standing on the pusher, it will definately be moved if( !(( check->v.flags & FL_ONGROUND ) && check->v.groundentity == pusher )) { if( check->v.absmin[0] >= pusher->v.absmax[0] || check->v.absmin[1] >= pusher->v.absmax[1] || check->v.absmin[2] >= pusher->v.absmax[2] || check->v.absmax[0] <= pusher->v.absmin[0] || check->v.absmax[1] <= pusher->v.absmin[1] || check->v.absmax[2] <= pusher->v.absmin[2] ) continue; // see if the ent's bbox is inside the pusher's final position if( !SV_TestEntityPosition( check, NULL )) continue; } // save original position of contacted entity pushed_p->ent = check; VectorCopy( check->v.origin, pushed_p->origin ); VectorCopy( check->v.angles, pushed_p->angles ); pushed_p->fixangle = check->v.fixangle; pushed_p++; // calculate destination position if( check->v.movetype == MOVETYPE_PUSHSTEP ) VectorAverage( check->v.absmin, check->v.absmax, org ); else VectorCopy( check->v.origin, org ); Matrix4x4_VectorITransform( start_l, org, temp ); Matrix4x4_VectorTransform( end_l, temp, org2 ); VectorSubtract( org2, org, lmove ); // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity( check, lmove, amove, &block ); pusher->v.solid = oldsolid; // if it is still inside the pusher, block if( SV_TestEntityPosition( check, NULL ) && block ) { if( !SV_CanBlock( check )) continue; pusher->v.ltime -= movetime; // move back any entities we already moved // go backwards, so if the same entity was pushed // twice, it goes back to the original position for( p = pushed_p - 1; p >= svgame.pushed; p-- ) { VectorCopy( p->origin, p->ent->v.origin ); VectorCopy( p->angles, p->ent->v.angles ); SV_LinkEdict( p->ent, (p->ent == check) ? true : false ); p->ent->v.fixangle = p->fixangle; } return check; } } return NULL; }
/* ============== 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)); } }
void ParseEntityString (qboolean respawn) { edict_t *ent; int inhibit; const char *com_token; const char *entities; int i; entities = level.entity_string; ent = NULL; inhibit = 0; i = 0; //mark all the spawned_entities as inuse so G_Spawn doesn't try to pick them during //trigger spawning etc for (i = 0; i < num_spawned_entities; i++) spawned_entities[i]->inuse = true; i = 0; // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (respawn) { ent = spawned_entities[i]; if (ent != world) { //double check we aren't overwriting stuff that we shouldn't be if (ent->enttype == ENT_DOOR_TRIGGER || ent->enttype == ENT_PLAT_TRIGGER) TDM_Error ("ParseEntityString: Trying to reuse an already spawned ent!"); G_FreeEdict (ent); G_InitEdict (ent); } i++; } else { if (!ent) ent = g_edicts; else ent = G_Spawn (); } entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { G_FreeEdict (ent); inhibit++; //have to keep num_spawned in sync for respawn if (!respawn) spawned_entities[num_spawned_entities++] = ent; continue; } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_COOP|SPAWNFLAG_NOT_DEATHMATCH); } else if (respawn) continue; ED_CallSpawn (ent); if (!respawn) { if (num_spawned_entities == MAX_EDICTS * 4) TDM_Error ("Exceeded MAX_EDICTS * 4 entities during entity string parse"); spawned_entities[num_spawned_entities++] = ent; } else { //an elevator or something respawned on top of a player? don't do it for match start, since //players will be respawning anyway. if (tdm_match_status < MM_PLAYING && (ent->solid == SOLID_BBOX || ent->solid == SOLID_BSP)) { edict_t *touch[MAX_EDICTS]; int count; int j; //r1: fix 00000196, can't killbox during map spawn due to risk of killing half-spawned //map entities. do a pseudo-killbox that only whacks players instead. count = gi.BoxEdicts (ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_SOLID); for (j = 0; j < count; j++) { if (touch[j] == ent) continue; //no point killing anything that won't clip (eg corpses) if (touch[j]->solid == SOLID_NOT || touch[j]->enttype == ENT_BODYQUE) continue; if (!touch[j]->client) continue; if (touch[j]->inuse) T_Damage (touch[j], ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); } } } } //gi.dprintf ("%i entities inhibited\n", inhibit); #ifdef DEBUG i = 1; ent = EDICT_NUM(i); while (i < globals.num_edicts) { if (ent->inuse != 0 || ent->inuse != 1) Com_DPrintf("Invalid entity %d\n", i); i++, ent++; } #endif G_FindTeams (); }
/* * SV_InitGame * A brand new game has been started */ void SV_InitGame( void ) { int i; edict_t *ent; netadr_t address, ipv6_address; bool socket_opened = false; // make sure the client is down CL_Disconnect( NULL ); SCR_BeginLoadingPlaque(); if( svs.initialized ) { // cause any connected clients to reconnect SV_ShutdownGame( "Server restarted", true ); // SV_ShutdownGame will also call Cvar_GetLatchedVars } else { // get any latched variable changes (sv_maxclients, etc) Cvar_GetLatchedVars( CVAR_LATCH ); } svs.initialized = true; if( sv_skilllevel->integer > 2 ) Cvar_ForceSet( "sv_skilllevel", "2" ); if( sv_skilllevel->integer < 0 ) Cvar_ForceSet( "sv_skilllevel", "0" ); // init clients if( sv_maxclients->integer < 1 ) Cvar_FullSet( "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH, true ); else if( sv_maxclients->integer > MAX_CLIENTS ) Cvar_FullSet( "sv_maxclients", va( "%i", MAX_CLIENTS ), CVAR_SERVERINFO | CVAR_LATCH, true ); svs.spawncount = rand(); svs.clients = Mem_Alloc( sv_mempool, sizeof( client_t )*sv_maxclients->integer ); svs.client_entities.num_entities = sv_maxclients->integer * UPDATE_BACKUP * MAX_SNAP_ENTITIES; svs.client_entities.entities = Mem_Alloc( sv_mempool, sizeof( entity_state_t ) * svs.client_entities.num_entities ); // init network stuff address.type = NA_NOTRANSMIT; ipv6_address.type = NA_NOTRANSMIT; if( !dedicated->integer ) { NET_InitAddress( &address, NA_LOOPBACK ); if( !NET_OpenSocket( &svs.socket_loopback, SOCKET_LOOPBACK, &address, true ) ) Com_Error( ERR_FATAL, "Couldn't open loopback socket: %s\n", NET_ErrorString() ); } if( dedicated->integer || sv_maxclients->integer > 1 ) { // IPv4 NET_StringToAddress( sv_ip->string, &address ); NET_SetAddressPort( &address, sv_port->integer ); if( !NET_OpenSocket( &svs.socket_udp, SOCKET_UDP, &address, true ) ) Com_Printf( "Error: Couldn't open UDP socket: %s\n", NET_ErrorString() ); else socket_opened = true; // IPv6 NET_StringToAddress( sv_ip6->string, &ipv6_address ); if( ipv6_address.type == NA_IP6 ) { NET_SetAddressPort( &ipv6_address, sv_port6->integer ); if( !NET_OpenSocket( &svs.socket_udp6, SOCKET_UDP, &ipv6_address, true ) ) Com_Printf( "Error: Couldn't open UDP6 socket: %s\n", NET_ErrorString() ); else socket_opened = true; } else Com_Printf( "Error: invalid IPv6 address: %s\n", sv_ip6->string ); } #ifdef TCP_ALLOW_CONNECT if( sv_tcp->integer && ( dedicated->integer || sv_maxclients->integer > 1 ) ) { bool err = true; if( !NET_OpenSocket( &svs.socket_tcp, SOCKET_TCP, &address, true ) ) { Com_Printf( "Error: Couldn't open TCP socket: %s\n", NET_ErrorString() ); } else { NET_SetSocketNoDelay( &svs.socket_tcp, 1 ); if( !NET_Listen( &svs.socket_tcp ) ) { Com_Printf( "Error: Couldn't listen to TCP socket: %s\n", NET_ErrorString() ); NET_CloseSocket( &svs.socket_tcp ); } else { err = false; socket_opened = true; } } if( ipv6_address.type == NA_IP6 ) { if( !NET_OpenSocket( &svs.socket_tcp6, SOCKET_TCP, &ipv6_address, true ) ) { Com_Printf( "Error: Couldn't open TCP6 socket: %s\n", NET_ErrorString() ); } else { NET_SetSocketNoDelay( &svs.socket_tcp6, 1 ); if( !NET_Listen( &svs.socket_tcp6 ) ) { Com_Printf( "Error: Couldn't listen to TCP6 socket: %s\n", NET_ErrorString() ); NET_CloseSocket( &svs.socket_tcp6 ); } else { err = false; socket_opened = true; } } } if( err ) { Cvar_ForceSet( "sv_tcp", "0" ); } } #endif if( dedicated->integer && !socket_opened ) Com_Error( ERR_FATAL, "Couldn't open any socket\n" ); // init mm // SV_MM_Init(); // init game SV_InitGameProgs(); for( i = 0; i < sv_maxclients->integer; i++ ) { ent = EDICT_NUM( i+1 ); ent->s.number = i+1; svs.clients[i].edict = ent; } // load the map assert( !svs.cms ); svs.cms = CM_New( NULL ); CM_AddReference( svs.cms ); // keep CPU awake assert( !svs.wakelock ); svs.wakelock = Sys_AcquireWakeLock(); }
/* ============= ED_ParseEval Can parse either fields or globals returns false if error ============= */ static qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s) { int i; char string[128]; ddef_t *def; char *v, *w; void *d; dfunction_t *func; d = (void *)((int *)base + key->ofs); switch (key->type & ~DEF_SAVEGLOBAL) { case ev_string: *(string_t *)d = ED_NewString(s); break; case ev_float: *(float *)d = atof (s); break; case ev_vector: strcpy (string, s); v = string; w = string; for (i = 0; i < 3; i++) { while (*v && *v != ' ') v++; *v = 0; ((float *)d)[i] = atof (w); w = v = v+1; } break; case ev_entity: *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); break; case ev_field: def = ED_FindField (s); if (!def) { //johnfitz -- HACK -- suppress error becuase fog/sky fields might not be mentioned in defs.qc if (strncmp(s, "sky", 3) && strcmp(s, "fog")) Con_DPrintf ("Can't find field %s\n", s); return false; } *(int *)d = G_INT(def->ofs); break; case ev_function: func = ED_FindFunction (s); if (!func) { Con_Printf ("Can't find function %s\n", s); return false; } *(func_t *)d = func - pr_functions; break; default: break; } return true; }
void SV_SaveGame_f (void) { char fname[MAX_OSPATH], comment[SAVEGAME_COMMENT_LENGTH+1]; FILE *f; int i; if (Cmd_Argc() != 2) { Com_Printf ("Usage: %s <savefname> : save a game\n", Cmd_Argv(0)); return; } else if (strstr(Cmd_Argv(1), "..")) { Com_Printf ("Relative pathfnames are not allowed.\n"); return; } else if (sv.state != ss_active) { Com_Printf ("Not playing a local game.\n"); return; } else if (cl.intermission) { Com_Printf ("Can't save in intermission.\n"); return; } else if (deathmatch.value != 0 || coop.value != 0 || maxclients.value != 1) { Com_Printf ("Can't save multiplayer games.\n"); return; } for (i = 1; i < MAX_CLIENTS; i++) { if (svs.clients[i].state == cs_spawned) { Com_Printf ("Can't save multiplayer games.\n"); return; } } if (svs.clients[0].state != cs_spawned) { Com_Printf ("Can't save, client #0 not spawned.\n"); return; } else if (svs.clients[0].edict->v.health <= 0) { Com_Printf ("Can't save game with a dead player\n"); // in fact, we can, but does it make sense? return; } snprintf (fname, sizeof(fname), "%s/save/%s", com_gamedir, Cmd_Argv(1)); COM_DefaultExtension (fname, ".sav"); Com_Printf ("Saving game to %s...\n", fname); if (!(f = fopen (fname, "w"))) { FS_CreatePath (fname); if (!(f = fopen (fname, "w"))) { Com_Printf ("ERROR: couldn't open.\n"); return; } } fprintf (f, "%i\n", SAVEGAME_VERSION); SV_SavegameComment (comment); fprintf (f, "%s\n", comment); for (i = 0 ; i < NUM_SPAWN_PARMS; i++) fprintf (f, "%f\n", svs.clients->spawn_parms[i]); fprintf (f, "%d\n", current_skill); fprintf (f, "%s\n", sv.mapname); fprintf (f, "%f\n", sv.time); // write the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { if (sv.lightstyles[i]) fprintf (f, "%s\n", sv.lightstyles[i]); else fprintf (f,"m\n"); } ED_WriteGlobals (f); for (i = 0; i < sv.num_edicts; i++) { ED_Write (f, EDICT_NUM(i)); fflush (f); } fclose (f); Com_Printf ("done.\n"); }
/* ============= ED_ParseEval Can parse either fields or globals returns false if error ============= */ qbool ED_ParseEpair (void *base, ddef_t *key, char *s) { int i; char string[128]; ddef_t *def; char *v, *w; void *d; dfunction_t *func; d = (void *)((int *)base + key->ofs); switch (key->type & ~DEF_SAVEGLOBAL) { case ev_string: *(string_t *)d = PR_SetString(ED_NewString (s)); break; case ev_float: *(float *)d = atof (s); break; case ev_vector: strlcpy (string, s, sizeof(string)); v = string; w = string; for (i=0 ; i<3 ; i++) { while (*v && *v != ' ') v++; *v = 0; ((float *)d)[i] = atof (w); w = v = v+1; } break; case ev_entity: *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); break; case ev_field: def = ED_FindField (s); if (!def) { Com_Printf ("Can't find field %s\n", s); return false; } *(int *)d = G_INT(def->ofs); break; case ev_function: func = ED_FindFunction (s); if (!func) { Com_Printf ("Can't find function %s\n", s); return false; } *(func_t *)d = func - pr_functions; break; default: break; } return true; }
/* ================== SVC_DirectConnect A connection request that did not come from the master ================== */ void SVC_DirectConnect (void) { char userinfo[MAX_INFO_STRING]; netadr_t adr; int i; client_t *cl, *newcl; client_t temp; edict_t *ent; int edictnum; int version; int qport; int challenge; adr = net_from; Com_DPrintf ("SVC_DirectConnect ()\n"); version = atoi(Cmd_Argv(1)); if (version != PROTOCOL_VERSION) { Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION); Com_DPrintf (" rejected connect from version %i\n", version); return; } qport = atoi(Cmd_Argv(2)); challenge = atoi(Cmd_Argv(3)); strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1); userinfo[sizeof(userinfo) - 1] = 0; // force the IP key/value pair so the game can filter based on ip Info_SetValueForKey (userinfo, "ip", NET_AdrToString(net_from)); // attractloop servers are ONLY for local clients if (sv.attractloop) { if (!NET_IsLocalAddress (adr)) { Com_Printf ("Remote connect in attract loop. Ignored.\n"); Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n"); return; } } // see if the challenge is valid if (!NET_IsLocalAddress (adr)) { for (i=0 ; i<MAX_CHALLENGES ; i++) { if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) { if (challenge == svs.challenges[i].challenge) break; // good Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nBad challenge.\n"); return; } } if (i == MAX_CHALLENGES) { Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nNo challenge for address.\n"); return; } } newcl = &temp; memset (newcl, 0, sizeof(client_t)); // if there is already a slot for this ip, reuse it for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++) { if (cl->state == cs_free) continue; if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) && ( cl->netchan.qport == qport || adr.port == cl->netchan.remote_address.port ) ) { if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value * 1000)) { Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (adr)); return; } Com_Printf ("%s:reconnect\n", NET_AdrToString (adr)); newcl = cl; goto gotnewcl; } } // find a client slot newcl = NULL; for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++) { if (cl->state == cs_free) { newcl = cl; break; } } if (!newcl) { Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is full.\n"); Com_DPrintf ("Rejected a connection.\n"); return; } gotnewcl: // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = temp; sv_client = newcl; edictnum = (newcl-svs.clients)+1; ent = EDICT_NUM(edictnum); newcl->edict = ent; newcl->challenge = challenge; // save challenge for checksumming // get the game a chance to reject this connection or modify the userinfo if (!(ge->ClientConnect (ent, userinfo))) { if (*Info_ValueForKey (userinfo, "rejmsg")) Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n", Info_ValueForKey (userinfo, "rejmsg")); else Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" ); Com_DPrintf ("Game rejected a connection.\n"); return; } // parse some info from the info strings strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1); SV_UserinfoChanged (newcl); // send the connect packet to the client Netchan_OutOfBandPrint (NS_SERVER, adr, "client_connect"); Netchan_Setup (NS_SERVER, &newcl->netchan , adr, qport); newcl->state = cs_connected; SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf) ); newcl->datagram.allowoverflow = true; newcl->lastmessage = svs.realtime; // don't timeout newcl->lastconnect = svs.realtime; }
void SV_LoadGame_f (void) { char name[MAX_OSPATH], mapname[MAX_QPATH], str[32 * 1024], *start; FILE *f; float time, tfloat, spawn_parms[NUM_SPAWN_PARMS]; edict_t *ent; int entnum, version, i, r; if (Cmd_Argc() != 2) { Com_Printf ("Usage: %s <savename> : load a game\n", Cmd_Argv(0)); return; } Q_snprintfz(name, sizeof(name), "%s/save/%s", com_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); Com_Printf ("Loading game from %s...\n", name); if (!(f = fopen (name, "r"))) { Com_Printf ("ERROR: couldn't open.\n"); return; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Com_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } fscanf (f, "%s\n", str); for (i = 0; i < NUM_SPAWN_PARMS; i++) fscanf (f, "%f\n", &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values fscanf (f, "%f\n", &tfloat); current_skill = (int)(tfloat + 0.1); Cvar_Set (&skill, va("%i", current_skill)); Cvar_SetValue (&deathmatch, 0); Cvar_SetValue (&coop, 0); Cvar_SetValue (&teamplay, 0); Cvar_SetValue (&maxclients, 1); fscanf (f, "%s\n", mapname); fscanf (f, "%f\n", &time); Host_EndGame(); CL_BeginLocalConnection (); SV_SpawnServer (mapname, false); if (sv.state != ss_active) { Com_Printf ("Couldn't load map\n"); return; } sv.paused = true; // pause until all clients connect sv.loadgame = true; Sys_Error("The local server is currently broken. Sorry."); // load the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { fscanf (f, "%s\n", str); #if 0 /* You need to fix that call */ sv.lightstyles[i] = Junk_Alloc(strlen(str) + 1); #endif strcpy (sv.lightstyles[i], str); } // load the edicts out of the savegame file entnum = -1; // -1 is the globals while (!feof(f)) { for (i = 0; i < sizeof(str) - 1; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == sizeof(str)-1) Host_Error ("Loadgame buffer overflow"); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Host_Error ("First token isn't a brace"); if (entnum == -1) { // parse the global vars ED_ParseGlobals (start); } else { // parse an edict ent = EDICT_NUM(entnum); memset (&ent->v, 0, progs->entityfields * 4); ent->free = false; ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } entnum++; } sv.num_edicts = entnum; sv.time = time; fclose (f); for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients[0]->spawn_parms[i] = spawn_parms[i]; }
void SV_Begin_f (void) { unsigned pmodel = 0, emodel = 0; int i; if (host_client->state == cs_spawned) return; // don't begin again host_client->state = cs_spawned; // handle the case of a level changing while a client was connecting if ( atoi(Cmd_Argv(1)) != svs.spawncount ) { Con_Printf ("SV_Begin_f from different level\n"); SV_New_f (); return; } if (host_client->spectator) { SV_SpawnSpectator (); if (SpectatorConnect) { // copy spawn parms out of the client_t for (i=0 ; i< NUM_SPAWN_PARMS ; i++) (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; // call the spawn function pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (SpectatorConnect); } } else { // copy spawn parms out of the client_t for (i=0 ; i< NUM_SPAWN_PARMS ; i++) (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; // call the spawn function pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->ClientConnect); // actually spawn the player pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->PutClientInServer); } // clear the net statistics, because connecting gives a bogus picture host_client->netchan.frame_latency = 0; host_client->netchan.frame_rate = 0; host_client->netchan.drop_count = 0; host_client->netchan.good_count = 0; //check he's not cheating pmodel = atoi(Info_ValueForKey (host_client->userinfo, "pmodel")); emodel = atoi(Info_ValueForKey (host_client->userinfo, "emodel")); if (pmodel != sv.model_player_checksum || emodel != sv.eyes_player_checksum) SV_BroadcastPrintf (PRINT_HIGH, "%s WARNING: non standard player/eyes model detected\n", host_client->name); // if we are paused, tell the client if (sv.paused) { ClientReliableWrite_Begin (host_client, svc_setpause, 2); ClientReliableWrite_Byte (host_client, sv.paused); SV_ClientPrintf(host_client, PRINT_HIGH, "Server is paused.\n"); } #if 0 // // send a fixangle over the reliable channel to make sure it gets there // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = EDICT_NUM( 1 + (host_client - svs.clients) ); MSG_WriteByte (&host_client->netchan.message, svc_setangle); for (i=0 ; i < 2 ; i++) MSG_WriteAngle (&host_client->netchan.message, ent->v.angles[i] ); MSG_WriteAngle (&host_client->netchan.message, 0 ); #endif }