void PlayerMoveSingle(pmove_t pmove) { pm = pmove; // clear all pmove local vars pml = new pml_t(); pml.forward = Vector3.Zero; pml.up = Vector3.Zero; pml.right = Vector3.Zero; pm.upmove = pm.cmd.upmove; pm.rightmove = pm.cmd.rightmove; pm.forwardmove = pm.cmd.forwardmove; // determine the time pml.msec = pm.cmd.serverTime - pm.ps.commandTime; if (pml.msec < 1) pml.msec = 1; else if (pml.msec > 200) pml.msec = 200; pm.ps.commandTime = pm.cmd.serverTime; // save old org in case we get stuck pml.previous_origin = pm.ps.origin; // save old velocity for crashlanding pml.previous_velocity = pm.ps.velocity; pml.frametime = pml.msec * 0.001f; UpdateViewAngles(ref pm.ps, pm.cmd); // Convert view angles to vectors AngleVectors(pm.ps.viewangles, ref pml.forward, ref pml.right, ref pml.up); // Adjust speeds etc. CheckParameters(); // Assume we don't touch anything pm.numtouch = 0; DropTimers(); pm.mins = Common.playerMins; pm.maxs = Common.playerMaxs; // UnStuck // Now that we are "unstuck", see where we are ( waterlevel and type, pmove->onground ). CategorizePosition(); // If we are not on ground, store off how fast we are moving down if (!pml.groundPlane) pml.fallVelocity = -pm.ps.velocity[2]; Duck(); if (LadderMove()) { } else if(pm.ps.pm_type == PMType.LADDER) { // clear ladder stuff unless player is noclipping or being lifted by barnacle. // it will be set immediately again next frame if necessary pm.ps.pm_type = PMType.NORMAL; } // if (pml.groundPlane && (pm.cmd.buttons & (int)Input.ButtonDef.USE) > 0 && !speedCropped) { float frac = 1f / 3.0f; pm.rightmove *= frac; pm.upmove *= frac; pm.forwardmove *= frac; speedCropped = true; } switch (pm.ps.pm_type) { case PMType.SPECTATOR: TryPlayerMove(); DropTimers(); break; case PMType.LADDER: FullLadderMove(false); break; case PMType.NORMAL: FullWalkMove(false); break; default: Common.Instance.WriteLine("Unknown movetype: " + Enum.GetName(typeof(PMType), pm.ps.pm_type)); break; } }
void SpectatorThink(gentity_t ent, Input.UserCommand ucmd) { gclient_t client = ent.client; if (client.sess.spectatorState != spectatorState_t.SPECTATOR_FOLLOW) { client.ps.pm_type = Common.PMType.SPECTATOR; client.ps.speed = sv_speed.Integer; // faster than normal // set up for pmove pmove_t pm = new pmove_t(); pm.Trace = new TraceDelegate(Server.Instance.SV_Trace); pm.ps = client.ps; pm.cmd = ucmd; pm.tracemask = (int)(brushflags.CONTENTS_SOLID | brushflags.CONTENTS_MOVEABLE | brushflags.CONTENTS_SLIME | brushflags.CONTENTS_OPAQUE); pm.pmove_fixed = CVars.Instance.VariableIntegerValue("pmove_fixed"); pm.pmove_msec = CVars.Instance.VariableIntegerValue("pmove_msec"); //pm.Trace += new TraceDelegate(Server.Instance.Trace); //pm.PointContents += new TraceContentsDelegate(Server.Instance.PointContents); pm.mins = Common.playerMins; pm.maxs = Common.playerMaxs; // perform a pmove Common.Instance.Pmove(pm); //if(!pm.ps.velocity.Equals(Vector3.Zero)) //Common.Instance.WriteLine("vel: {0}", pm.ps.velocity); // save results of pmove ent.s.origin = client.ps.origin; Server.Instance.UnlinkEntity(GEntityToSharedEntity(ent)); } client.oldbuttons = client.buttons; client.buttons = ucmd.buttons; }
//float pm_duckScale = 0.25f; //int c_pmove = 0; /* ================ Pmove Can be called by either the server or the client ================ */ public void Pmove(pmove_t pm) { //this.pm = pm; speedCropped = false; int finalTime = pm.cmd.serverTime; if (finalTime < pm.ps.commandTime) return; // should not happen if (finalTime > pm.ps.commandTime + 1000) pm.ps.commandTime = finalTime - 1000; pm.ps.speed = 320; pm.ps.gravity = CVars.Instance.VariableIntegerValue("sv_gravity"); pm.ps.pmove_framecount = (pm.ps.pmove_framecount + 1) & ((1 << 6) - 1); // chop the move up if it is too long, to prevent framerate // dependent behavior while (pm.ps.commandTime != finalTime) { int msec = finalTime - pm.ps.commandTime; if (pm.pmove_fixed > 0) { if (msec > pm.pmove_msec) msec = pm.pmove_msec; } else { if (msec > 50) msec = 50; } pm.cmd.serverTime = pm.ps.commandTime + msec; pm.maxSpeed = pm_maxspeed; PlayerMoveSingle(pm); //PmoveSingle(pm); //if ((pm.ps.pm_flags & PMFlags.JUMP_HELD) == PMFlags.JUMP_HELD) // pm.cmd.upmove = 20; } //pm.ps.OldButtons = pm.cmd.buttons; }
/* ================== ClientThink A new command has arrived from the client This will be called once for each client frame, which will usually be a couple times for each server frame on fast clients. ================== */ public void Client_Think(gentity_t ent) { gclient_t client = ent.client; // don't think if the client is not yet connected (and thus not yet spawned in) if (client.pers.connected != clientConnected_t.CON_CONNECTED) return; Input.UserCommand ucmd = ent.client.pers.cmd; // sanity check the command time to prevent speedup cheating if(ucmd.serverTime > level.time + 200) ucmd.serverTime = (int)level.time + 200; if (ucmd.serverTime < level.time - 1000) ucmd.serverTime = (int)level.time - 1000; // mark the time we got info, so we can display the //ent.client.pers.cmd = Server.Instance.GetUsercmd(ent.s.clientNum); // phone jack if they don't get any for a while ent.client.lastCmdTime = (int)level.time; int msec = ucmd.serverTime - ent.client.ps.commandTime; // following others may result in bad times, but we still want // to check for follow toggles if (msec < 1 && ent.client.sess.spectatorState != spectatorState_t.SPECTATOR_FOLLOW) return; if (msec > 200) msec = 200; CVar pmove_msec = CVars.Instance.FindVar("pmove_msec"); if (pmove_msec.Integer < 8) CVars.Instance.Set("pmove_msec", "8"); else if (pmove_msec.Integer > 33) CVars.Instance.Set("pmove_msec", "33"); if (CVars.Instance.FindVar("pmove_fixed").Bool || client.pers.pmoveFixed) { ucmd.serverTime = ((ucmd.serverTime + pmove_msec.Integer - 1) / pmove_msec.Integer) * pmove_msec.Integer; } // // check for exiting intermission // if (level.intermissiontime > 0) { //ClientIntermissionThink(client); return; } // spectators don't do much if (client.sess.sessionTeam == team_t.TEAM_SPECTATOR) { if (client.sess.spectatorState == spectatorState_t.SPECTATOR_SCOREBOARD) return; //client.ps.speed = sv_speed.Integer; SpectatorThink(ent, ucmd); return; } // check for inactivity timer, but never drop the local client of a non-dedicated server //if (!ClientInactivityTimer(ent.client)) // return; if (client.noclip) client.ps.pm_type = Common.PMType.NOCLIP; else if (client.ps.stats[0] <= 0) client.ps.pm_type = Common.PMType.DEAD; else client.ps.pm_type = Common.PMType.NORMAL; // Gravity & speed client.ps.gravity = sv_gravity.Integer; client.ps.speed = sv_speed.Integer; // Set up for pmove int oldEventSequence = client.ps.eventSequence; pmove_t pm = new pmove_t(); //#define MASK_ALL (-1) //#define MASK_SOLID (1) //#define MASK_PLAYERSOLID (1|0x10000|0x2000000) //#define MASK_DEADSOLID (1|0x10000) //#define MASK_WATER (32) //#define MASK_OPAQUE (1) //#define MASK_SHOT (1|0x2000000|0x4000000) pm.Trace = new TraceDelegate(Server.Instance.SV_Trace); pm.ps = client.ps; pm.cmd = ucmd; if (pm.ps.pm_type == Common.PMType.DEAD) pm.tracemask = (int)brushflags.MASK_PLAYERSOLID & ~(int)brushflags.CONTENTS_MONSTER; else pm.tracemask = (int)brushflags.MASK_PLAYERSOLID; pm.pmove_fixed = ((CVars.Instance.FindVar("pmove_fixed").Bool) | client.pers.pmoveFixed)?1:0; pm.pmove_msec = pmove_msec.Integer; client.oldOrigin = client.ps.origin; //pm.mins = Common.playerMins; //pm.maxs = Common.playerMaxs; Common.Instance.Pmove(pm); //client.ps.pm_type = Common.PMType.SPECTATOR; //client.ps.speed = 400; // faster than normal // save results of pmove if (ent.client.ps.eventSequence != oldEventSequence) ent.eventTime = (int)level.time; if (g_smoothClients.Integer == 1) { } else { CGame.PlayerStateToEntityState(ent.client.ps, ent.s, true); } SendPredictableEvents(ent.client.ps); ent.r.currentOrigin = ent.s.pos.trBase; ent.r.mins = pm.mins; ent.r.maxs = pm.maxs; // execute client events //ClientEvents(ent, oldEventSequence); // link entity now, after any personal teleporters have been used Server.Instance.LinkEntity(GEntityToSharedEntity(ent)); if (!ent.client.noclip) { //TouchTriggers(ent); } // NOTE: now copy the exact origin over otherwise clients can be snapped into solid ent.r.currentOrigin = ent.client.ps.origin; // touch other objects //ClientImpacts(ent, pm); // save results of triggers and client events if (ent.client.ps.eventSequence != oldEventSequence) { ent.eventTime = (int)level.time; } // swap and latch button actions client.oldbuttons = client.buttons; client.buttons = ucmd.buttons; client.latched_buttons |= client.buttons & ~client.oldbuttons; // check for respawning if (client.ps.stats[0] <= 0) { // wait for the attack button to be pressed if (level.time > client.respawnTime) { // forcerespawn is to prevent users from waiting out powerups if (g_forcerespawn.Integer > 0 && level.time - client.respawnTime > g_forcerespawn.Integer * 1000) { respawn(ent); return; } // pressing attack or use is the normal respawn method if ((ucmd.buttons & ((int)Input.ButtonDef.ATTACK | (int)Input.ButtonDef.USE)) > 0) respawn(ent); } return; } ClientTimerActions(ent, msec); }