]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_phys.c
final speedhack fixes
[xonotic/darkplaces.git] / sv_phys.c
index 476882453082e8757cfc6129167524ffdc4477e5..362689f8795fa954e69421a4e8c524e75bf331e5 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -39,44 +39,10 @@ solid_edge items only clip against bsp models.
 
 */
 
-cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
-cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
-cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
-cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
-cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
-cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
-cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
-cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
-cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
-cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
-cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
-cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
-cvar_t sv_debugmove = {CVAR_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
-
-cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
-cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
-
-// TODO: move this extern to server.h
-extern cvar_t sv_clmovement_waitforinput;
-
 #define        MOVE_EPSILON    0.01
 
 void SV_Physics_Toss (prvm_edict_t *ent);
 
-void SV_Phys_Init (void)
-{
-       Cvar_RegisterVariable(&sv_stepheight);
-       Cvar_RegisterVariable(&sv_jumpstep);
-       Cvar_RegisterVariable(&sv_wallfriction);
-       Cvar_RegisterVariable(&sv_newflymove);
-       Cvar_RegisterVariable(&sv_freezenonclients);
-       Cvar_RegisterVariable(&sv_playerphysicsqc);
-       Cvar_RegisterVariable(&sv_debugmove);
-
-       Cvar_RegisterVariable(&sv_sound_watersplash);
-       Cvar_RegisterVariable(&sv_sound_land);
-}
-
 /*
 ===============================================================================
 
@@ -303,7 +269,7 @@ void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
        prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
 
        // build a list of edicts to touch, because the link loop can be corrupted
-       // by SV_IncreaseEdicts called during touch functions
+       // by IncreaseEdicts called during touch functions
        numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
        if (numtouchedicts > MAX_EDICTS)
        {
@@ -377,7 +343,7 @@ void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
                model = sv.models[modelindex];
                if (model != NULL)
                {
-                       if (!model->TraceBox)
+                       if (!model->TraceBox && developer.integer >= 1)
                                Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
 
                        if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
@@ -459,9 +425,12 @@ SV_TestEntityPosition
 returns true if the entity is in solid currently
 ============
 */
-static int SV_TestEntityPosition (prvm_edict_t *ent)
+static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
 {
-       trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
+       vec3_t org;
+       trace_t trace;
+       VectorAdd(ent->fields.server->origin, offset, org);
+       trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
        if (trace.startsupercontents & SUPERCONTENTS_SOLID)
                return true;
        else
@@ -471,10 +440,11 @@ static int SV_TestEntityPosition (prvm_edict_t *ent)
                        // q1bsp/hlbsp use hulls and if the entity does not exactly match
                        // a hull size it is incorrectly tested, so this code tries to
                        // 'fix' it slightly...
+                       // FIXME: this breaks entities larger than the hull size
                        int i;
                        vec3_t v, m1, m2, s;
-                       VectorAdd(ent->fields.server->origin, ent->fields.server->mins, m1);
-                       VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, m2);
+                       VectorAdd(org, ent->fields.server->mins, m1);
+                       VectorAdd(org, ent->fields.server->maxs, m2);
                        VectorSubtract(m2, m1, s);
 #define EPSILON (1.0f / 32.0f)
                        if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
@@ -489,8 +459,11 @@ static int SV_TestEntityPosition (prvm_edict_t *ent)
                                        return true;
                        }
                }
-               return false;
        }
+       // if the trace found a better position for the entity, move it there
+       if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
+               VectorCopy(trace.endpos, ent->fields.server->origin);
+       return false;
 }
 
 /*
@@ -515,7 +488,7 @@ void SV_CheckAllEnts (void)
                 || check->fields.server->movetype == MOVETYPE_NOCLIP)
                        continue;
 
-               if (SV_TestEntityPosition (check))
+               if (SV_TestEntityPosition (check, vec3_origin))
                        Con_Print("entity in invalid position\n");
        }
 }
@@ -550,6 +523,8 @@ int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
                                PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
                                // New Contents
                                PRVM_G_FLOAT(OFS_PARM1) = nContents;
+                               // Assign Self
+                               prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
                        // Execute VM Function
                        PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
                }
@@ -610,22 +585,27 @@ Returns false if the entity removed itself.
 */
 qboolean SV_RunThink (prvm_edict_t *ent)
 {
-       float thinktime;
-
-       thinktime = ent->fields.server->nextthink;
-       if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
-               return true;
+       int iterations;
 
        // don't let things stay in the past.
        // it is possible to start that way by a trigger with a local time.
-       if (thinktime < sv.time)
-               thinktime = sv.time;
+       if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
+               return true;
 
-       ent->fields.server->nextthink = 0;
-       prog->globals.server->time = thinktime;
-       prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
-       prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
-       PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
+       for (iterations = 0;iterations < 128  && !ent->priv.server->free;iterations++)
+       {
+               prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
+               ent->fields.server->nextthink = 0;
+               prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
+               prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
+               PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
+               // mods often set nextthink to time to cause a think every frame,
+               // we don't want to loop in that case, so exit if the new nextthink is
+               // <= the time the qc was told, also exit if it is past the end of the
+               // frame
+               if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
+                       break;
+       }
        return !ent->priv.server->free;
 }
 
@@ -637,14 +617,17 @@ Two entities have touched, so run their touch functions
 ==================
 */
 extern void VM_SetTraceGlobals(const trace_t *trace);
+extern sizebuf_t vm_tempstringsbuf;
 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
 {
+       int restorevm_tempstringsbuf_cursize;
        int old_self, old_other;
        prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
        prvm_eval_t *val;
 
        old_self = prog->globals.server->self;
        old_other = prog->globals.server->other;
+       restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
 
        VM_SetTraceGlobals(trace);
 
@@ -677,6 +660,7 @@ void SV_Impact (prvm_edict_t *e1, trace_t *trace)
 
        prog->globals.server->self = old_self;
        prog->globals.server->other = old_other;
+       vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
 }
 
 
@@ -1022,6 +1006,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
        model_t *pushermodel;
        trace_t trace;
        matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
+       unsigned short moved_edicts[MAX_EDICTS];
 
        if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
        {
@@ -1141,6 +1126,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
        for (e = 0;e < numcheckentities;e++)
        {
                prvm_edict_t *check = checkentities[e];
+               int checkcontents = SV_GenericHitSuperContentsMask(check);
                if (check->fields.server->movetype == MOVETYPE_NONE
                 || check->fields.server->movetype == MOVETYPE_PUSH
                 || check->fields.server->movetype == MOVETYPE_FOLLOW
@@ -1153,7 +1139,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                // final position, move it
                if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
                {
-                       Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
+                       Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                        if (!trace.startsolid)
                                continue;
                }
@@ -1174,7 +1160,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
 
                VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
                VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
-               sv.moved_edicts[num_moved++] = check;
+               moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
 
                // try moving the contacted entity
                pusher->fields.server->solid = SOLID_NOT;
@@ -1184,13 +1170,14 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                pusher->fields.server->solid = savesolid; // was SOLID_BSP
                //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
 
-               // this check is for items riding platforms that are passing under (or
-               // through) walls intended to knock the items off
-               if (trace.fraction < 1 && check->fields.server->movetype != MOVETYPE_WALK)
+               // this trace.fraction < 1 check causes items to fall off of pushers
+               // if they pass under or through a wall
+               // the groundentity check causes items to fall off of ledges
+               if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
                        check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
 
                // if it is still inside the pusher, block
-               Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
+               Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                if (trace.startsolid)
                {
                        // try moving the contacted entity a tiny bit further to account for precision errors
@@ -1201,7 +1188,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                        VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
                        SV_PushEntity (check, move2, true);
                        pusher->fields.server->solid = savesolid;
-                       Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
+                       Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                        if (trace.startsolid)
                        {
                                // try moving the contacted entity a tiny bit less to account for precision errors
@@ -1211,7 +1198,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                                VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
                                SV_PushEntity (check, move2, true);
                                pusher->fields.server->solid = savesolid;
-                               Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
+                               Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                                if (trace.startsolid)
                                {
                                        // still inside pusher, so it's really blocked
@@ -1235,7 +1222,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                                        // move back any entities we already moved
                                        for (i = 0;i < num_moved;i++)
                                        {
-                                               prvm_edict_t *ed = sv.moved_edicts[i];
+                                               prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
                                                VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
                                                VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
                                                SV_LinkEdict (ed, false);
@@ -1344,20 +1331,17 @@ clipping hull.
 void SV_CheckStuck (prvm_edict_t *ent)
 {
        int i;
-       vec3_t org;
+       vec3_t offset;
 
-       if (!SV_TestEntityPosition(ent))
+       if (!SV_TestEntityPosition(ent, vec3_origin))
        {
                VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
                return;
        }
 
-       VectorCopy (ent->fields.server->origin, org);
-
        for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
        {
-               VectorAdd(org, unstickoffsets + i, ent->fields.server->origin);
-               if (!SV_TestEntityPosition(ent))
+               if (!SV_TestEntityPosition(ent, unstickoffsets + i))
                {
                        Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
                        SV_LinkEdict (ent, true);
@@ -1365,33 +1349,28 @@ void SV_CheckStuck (prvm_edict_t *ent)
                }
        }
 
-       VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
-       if (!SV_TestEntityPosition(ent))
+       VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
+       if (!SV_TestEntityPosition(ent, offset))
        {
                Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
                SV_LinkEdict (ent, true);
                return;
        }
 
-       VectorCopy (org, ent->fields.server->origin);
        Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
 }
 
 static void SV_UnstickEntity (prvm_edict_t *ent)
 {
        int i;
-       vec3_t org;
 
        // if not stuck in a bmodel, just return
-       if (!SV_TestEntityPosition(ent))
+       if (!SV_TestEntityPosition(ent, vec3_origin))
                return;
 
-       VectorCopy (ent->fields.server->origin, org);
-
        for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
        {
-               VectorAdd(org, unstickoffsets + i, ent->fields.server->origin);
-               if (!SV_TestEntityPosition(ent))
+               if (!SV_TestEntityPosition(ent, unstickoffsets + i))
                {
                        Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
                        SV_LinkEdict (ent, true);
@@ -1399,7 +1378,6 @@ static void SV_UnstickEntity (prvm_edict_t *ent)
                }
        }
 
-       VectorCopy (org, ent->fields.server->origin);
        if (developer.integer >= 100)
                Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
 }
@@ -2012,7 +1990,7 @@ static void SV_Physics_Entity (prvm_edict_t *ent)
        case MOVETYPE_FLYMISSILE:
        case MOVETYPE_FLY:
                // regular thinking
-               if (SV_RunThink (ent) && runmove)
+               if (SV_RunThink (ent) && (runmove || !sv_gameplayfix_delayprojectiles.integer))
                        SV_Physics_Toss (ent);
                break;
        default:
@@ -2137,6 +2115,7 @@ void SV_Physics_ClientEntity(prvm_edict_t *ent)
                                SV_AddGravity (ent);
                        SV_CheckStuck (ent);
                        SV_WalkMove (ent);
+                       host_client->cmd.time = max(host_client->cmd.time, sv.time); // ignore client movement data for anything before NOW
                }
                break;
        case MOVETYPE_TOSS: