]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_phys.c
physics: fix and refactor unsticking
[xonotic/darkplaces.git] / sv_phys.c
index b26ebe225e90f6020e8411a7d6491565605a320b..6d776f86f45d4af27d120271495fc405d901bc38 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -409,7 +409,7 @@ SV_Move
 ==================
 */
 #if COLLISIONPARANOID >= 1
-trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
+static trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
 #else
 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
 #endif
@@ -588,17 +588,17 @@ finished:
 }
 
 #if COLLISIONPARANOID >= 1
-trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
+trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
 {
        prvm_prog_t *prog = SVVM_prog;
        int endstuck;
        trace_t trace;
        vec3_t temp;
-       trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
+       trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
        if (passedict)
        {
                VectorCopy(trace.endpos, temp);
-               endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask).startsolid;
+               endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend).startsolid;
 #if COLLISIONPARANOID < 3
                if (trace.startsolid || endstuck)
 #endif
@@ -875,35 +875,36 @@ void SV_LinkEdict (prvm_edict_t *ent)
                VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
        }
 
-//
-// to make items easier to pick up and allow them to be grabbed off
-// of shelves, the abs sizes are expanded
-//
-       if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
+       if (sv_legacy_bbox_expand.integer)
        {
-               mins[0] -= 15;
-               mins[1] -= 15;
-               mins[2] -= 1;
-               maxs[0] += 15;
-               maxs[1] += 15;
-               maxs[2] += 1;
-       }
-       else
-       {
-               // because movement is clipped an epsilon away from an actual edge,
-               // we must fully check even when bounding boxes don't quite touch
-               mins[0] -= 1;
-               mins[1] -= 1;
-               mins[2] -= 1;
-               maxs[0] += 1;
-               maxs[1] += 1;
-               maxs[2] += 1;
+               if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
+               {
+                       // to make items easier to pick up and allow them to be grabbed off
+                       // of shelves, the abs sizes are expanded
+                       mins[0] -= 15;
+                       mins[1] -= 15;
+                       mins[2] -= 1;
+                       maxs[0] += 15;
+                       maxs[1] += 15;
+                       maxs[2] += 1;
+               }
+               else
+               {
+                       // because movement is clipped an epsilon away from an actual edge,
+                       // we must fully check even when bounding boxes don't quite touch
+                       mins[0] -= 1;
+                       mins[1] -= 1;
+                       mins[2] -= 1;
+                       maxs[0] += 1;
+                       maxs[1] += 1;
+                       maxs[2] += 1;
+               }
        }
 
        VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
        VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
 
-       World_LinkEdict(&sv.world, ent, mins, maxs);
+       World_LinkEdict(&sv.world, ent, mins, maxs, sv_areagrid_link_SOLID_NOT.integer);
 }
 
 /*
@@ -1040,12 +1041,12 @@ void SV_CheckVelocity (prvm_edict_t *ent)
 //
        for (i=0 ; i<3 ; i++)
        {
-               if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
+               if (isnan(PRVM_serveredictvector(ent, velocity)[i]))
                {
                        Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
                        PRVM_serveredictvector(ent, velocity)[i] = 0;
                }
-               if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
+               if (isnan(PRVM_serveredictvector(ent, origin)[i]))
                {
                        Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
                        PRVM_serveredictvector(ent, origin)[i] = 0;
@@ -1193,13 +1194,12 @@ If stepnormal is not NULL, the plane normal of any vertical wall hit will be sto
 ============
 */
 static float SV_Gravity (prvm_edict_t *ent);
-static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink);
+static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink, qbool checkstuck);
 #define MAX_CLIP_PLANES 5
 static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
 {
        prvm_prog_t *prog = SVVM_prog;
-       int blocked, bumpcount;
-       int i, j, numplanes;
+       unsigned int i, j, numplanes, blocked, bumpcount;
        float d, time_left, gravity;
        vec3_t dir, push, planes[MAX_CLIP_PLANES];
        prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
@@ -1237,7 +1237,7 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
                        break;
 
                VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
-               if(!SV_PushEntity(&trace, ent, push, false))
+               if(!SV_PushEntity(&trace, ent, push, false, true))
                {
                        // we got teleported by a touch function
                        // let's abort the move
@@ -1255,6 +1255,9 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
 
                if (trace.fraction == 1)
                        break;
+
+               time_left *= 1 - trace.fraction;
+
                if (trace.plane.normal[2])
                {
                        if (trace.plane.normal[2] > 0.7)
@@ -1282,28 +1285,30 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
                        trace_t steptrace3;
                        //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
                        VectorSet(steppush, 0, 0, stepheight);
+                       VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
                        VectorCopy(PRVM_serveredictvector(ent, origin), org);
-                       if(!SV_PushEntity(&steptrace, ent, steppush, false))
+                       if(!SV_PushEntity(&steptrace, ent, steppush, false, true))
                        {
                                blocked |= 8;
                                break;
                        }
                        //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
-                       if(!SV_PushEntity(&steptrace2, ent, push, false))
+                       if(!SV_PushEntity(&steptrace2, ent, push, false, true))
                        {
                                blocked |= 8;
                                break;
                        }
                        //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
                        VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
-                       if(!SV_PushEntity(&steptrace3, ent, steppush, false))
+                       if(!SV_PushEntity(&steptrace3, ent, steppush, false, true))
                        {
                                blocked |= 8;
                                break;
                        }
                        //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
                        // accept the new position if it made some progress...
-                       if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
+                       // previously this checked if absolute distance >= 0.03125 which made stepping up unreliable
+                       if (PRVM_serveredictvector(ent, origin)[0] - org[0] || PRVM_serveredictvector(ent, origin)[1] - org[1])
                        {
                                //Con_Printf("accepted (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
                                trace = steptrace2;
@@ -1326,6 +1331,14 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
                        if (stepnormal)
                                VectorCopy(trace.plane.normal, stepnormal);
                }
+
+               // Unlike some other movetypes Quake's SV_FlyMove calls SV_Impact only after setting ONGROUND which id1 fiends rely on.
+               // If we stepped up (sv_gameplayfix_stepmultipletimes) this will impact the steptrace2 plane instead of the original.
+               if (PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace.ent)
+                       SV_Impact(ent, &trace);
+               if (ent->free)
+                       return blocked; // removed by the impact function
+
                if (trace.fraction >= 0.001)
                {
                        // actually covered some distance
@@ -1333,8 +1346,6 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
                        numplanes = 0;
                }
 
-               time_left *= 1 - trace.fraction;
-
                // clipped to another plane
                if (numplanes >= MAX_CLIP_PLANES)
                {
@@ -1539,46 +1550,6 @@ static qbool SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot
        return true;
 }
 
-qbool SV_NudgeOutOfSolid(prvm_edict_t *ent)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int bump, pass;
-       trace_t stucktrace;
-       vec3_t stuckorigin;
-       vec3_t stuckmins, stuckmaxs;
-       vec_t nudge;
-       vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
-       if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
-               separation = 0.0f; // when using hulls, it can not be enlarged
-       VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
-       VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
-       stuckmins[0] -= separation;
-       stuckmins[1] -= separation;
-       stuckmins[2] -= separation;
-       stuckmaxs[0] += separation;
-       stuckmaxs[1] += separation;
-       stuckmaxs[2] += separation;
-       // first pass we try to get it out of brush entities
-       // second pass we try to get it out of world only (can't win them all)
-       for (pass = 0;pass < 2;pass++)
-       {
-               VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
-               for (bump = 0;bump < 10;bump++)
-               {
-                       stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
-                       if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
-                       {
-                               // found a good location, use it
-                               VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
-                               return true;
-                       }
-                       nudge = -stucktrace.startdepth;
-                       VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
-               }
-       }
-       return false;
-}
-
 /*
 ============
 SV_PushEntity
@@ -1588,7 +1559,7 @@ The trace struct is filled with the trace that has been done.
 Returns true if the push did not result in the entity being teleported by QC code.
 ============
 */
-static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink)
+static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink, qbool checkstuck)
 {
        prvm_prog_t *prog = SVVM_prog;
        int solid;
@@ -1603,12 +1574,6 @@ static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboo
        VectorCopy(PRVM_serveredictvector(ent, mins), mins);
        VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
 
-       // move start position out of solids
-       if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
-       {
-               SV_NudgeOutOfSolid(ent);
-       }
-
        VectorCopy(PRVM_serveredictvector(ent, origin), start);
        VectorAdd(start, push, end);
 
@@ -1622,11 +1587,21 @@ static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboo
                type = MOVE_NORMAL;
 
        *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
-       // fail the move if stuck in world
-       if (trace->worldstartsolid)
-               return true;
+       if (trace->allsolid && checkstuck)
+       {
+               if (SV_UnstickEntity(ent))
+               {
+                       VectorCopy(PRVM_serveredictvector(ent, origin), start);
+                       VectorAdd(start, push, end);
+                       *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
+               }
+               // abort move if we're stuck in the world (and didn't make it out)
+               else if (trace->worldstartsolid)
+                       return true;
+       }
 
        VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
+       VectorCopy(trace->endpos, PRVM_serveredictvector(ent, oldorigin)); // for SV_UnstickEntity()
 
        ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
 
@@ -1641,10 +1616,12 @@ static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboo
 #endif
 
        if (dolink)
+       {
                SV_LinkEdict_TouchAreaGrid(ent);
 
-       if((PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace->ent && (!((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || PRVM_serveredictedict(ent, groundentity) != PRVM_EDICT_TO_PROG(trace->ent))))
-               SV_Impact (ent, trace);
+               if((PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace->ent && (!((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || PRVM_serveredictedict(ent, groundentity) != PRVM_EDICT_TO_PROG(trace->ent))))
+                       SV_Impact (ent, trace);
+       }
 
        if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
        {
@@ -1890,7 +1867,7 @@ static void SV_PushMove (prvm_edict_t *pusher, float movetime)
 
                // try moving the contacted entity
                PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
-               if(!SV_PushEntity (&trace, check, move, true))
+               if(!SV_PushEntity(&trace, check, move, true, true))
                {
                        // entity "check" got teleported
                        PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
@@ -1922,7 +1899,7 @@ static void SV_PushMove (prvm_edict_t *pusher, float movetime)
                        {
                                // hack to invoke all necessary movement triggers
                                VectorClear(move2);
-                               if(!SV_PushEntity(&trace2, check, move2, true))
+                               if(!SV_PushEntity(&trace2, check, move2, true, true))
                                {
                                        // entity "check" got teleported
                                        continue;
@@ -2039,9 +2016,10 @@ static float unstickoffsets[] =
 
 typedef enum unstickresult_e
 {
+       // matching the DP_QC_NUDGEOUTOFSOLID return values
        UNSTICK_STUCK = 0,
-       UNSTICK_GOOD = 1,
-       UNSTICK_UNSTUCK = 2
+       UNSTICK_GOOD = -1, ///< didn't need to be unstuck
+       UNSTICK_UNSTUCK = 1
 }
 unstickresult_t;
 
@@ -2059,8 +2037,6 @@ static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t o
                if (!SV_TestEntityPosition(ent, unstickoffsets + i))
                {
                        VectorCopy(unstickoffsets + i, offset);
-                       SV_LinkEdict(ent);
-                       //SV_LinkEdict_TouchAreaGrid(ent);
                        return UNSTICK_UNSTUCK;
                }
        }
@@ -2073,44 +2049,15 @@ static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t o
                VectorClear(offset);
                offset[2] = -i;
                if (!SV_TestEntityPosition(ent, offset))
-               {
-                       SV_LinkEdict(ent);
-                       //SV_LinkEdict_TouchAreaGrid(ent);
                        return UNSTICK_UNSTUCK;
-               }
                offset[2] = i;
                if (!SV_TestEntityPosition(ent, offset))
-               {
-                       SV_LinkEdict(ent);
-                       //SV_LinkEdict_TouchAreaGrid(ent);
                        return UNSTICK_UNSTUCK;
-               }
        }
 
        return UNSTICK_STUCK;
 }
 
-qbool SV_UnstickEntity (prvm_edict_t *ent)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       vec3_t offset;
-       switch(SV_UnstickEntityReturnOffset(ent, offset))
-       {
-               case UNSTICK_GOOD:
-                       return true;
-               case UNSTICK_UNSTUCK:
-                       Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
-                       return true;
-               case UNSTICK_STUCK:
-                       if (developer_extra.integer)
-                               Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
-                       return false;
-               default:
-                       Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
-                       return false;
-       }
-}
-
 /*
 =============
 SV_CheckStuck
@@ -2119,32 +2066,54 @@ This is a big hack to try and fix the rare case of getting stuck in the world
 clipping hull.
 =============
 */
-static void SV_CheckStuck (prvm_edict_t *ent)
+qbool SV_UnstickEntity (prvm_edict_t *ent)
 {
        prvm_prog_t *prog = SVVM_prog;
        vec3_t offset;
 
+       if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0
+       && sv.worldmodel->TraceBox == Mod_CollisionBIH_TraceBox) // Mod_Q1BSP_TraceBox doesn't support startdepth
+       {
+               VectorCopy(PRVM_serveredictvector(ent, origin), offset);
+               switch (PHYS_NudgeOutOfSolid(prog, ent))
+               {
+                       case UNSTICK_GOOD:
+                               return true;
+                       case UNSTICK_UNSTUCK:
+                               VectorSubtract(PRVM_serveredictvector(ent, origin), offset, offset);
+                               Con_DPrintf("NudgeOutOfSolid fixed stuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
+                               return true;
+                       case UNSTICK_STUCK:
+                               Con_DPrintf(CON_WARN "NudgeOutOfSolid couldn't fix stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
+                               return false;
+                       default:
+                               Con_Printf("NudgeOutOfSolid returned a value outside its enum.\n");
+                               return false;
+               }
+       }
+
+       if (!(PRVM_NUM_FOR_EDICT(ent) <= svs.maxclients ? sv_gameplayfix_unstickplayers : sv_gameplayfix_unstickentities).integer)
+               return false;
+
        switch(SV_UnstickEntityReturnOffset(ent, offset))
        {
                case UNSTICK_GOOD:
-                       VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
-                       break;
+                       return true;
                case UNSTICK_UNSTUCK:
-                       Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
-                       break;
+                       Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
+                       return true;
                case UNSTICK_STUCK:
                        VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, 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(prog, PRVM_serveredictstring(ent, classname)));
-                               SV_LinkEdict(ent);
-                               //SV_LinkEdict_TouchAreaGrid(ent);
+                               Con_DPrintf("Unstuck entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
+                               return true;
                        }
-                       else
-                               Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
-                       break;
+                       Con_DPrintf(CON_WARN "Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
+                       return false;
                default:
                        Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
+                       return false;
        }
 }
 
@@ -2315,9 +2284,6 @@ static void SV_WalkMove (prvm_edict_t *ent)
        if (sv.frametime <= 0)
                return;
 
-       if (sv_gameplayfix_unstickplayers.integer)
-               SV_CheckStuck (ent);
-
        applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
 
        SV_CheckVelocity(ent);
@@ -2407,7 +2373,7 @@ static void SV_WalkMove (prvm_edict_t *ent)
                // move up
                VectorClear (upmove);
                upmove[2] = sv_stepheight.value;
-               if(!SV_PushEntity(&trace, ent, upmove, true))
+               if(!SV_PushEntity(&trace, ent, upmove, true, true))
                {
                        // we got teleported when upstepping... must abort the move
                        return;
@@ -2459,7 +2425,7 @@ static void SV_WalkMove (prvm_edict_t *ent)
        // move down
        VectorClear (downmove);
        downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
-       if(!SV_PushEntity (&downtrace, ent, downmove, true))
+       if(!SV_PushEntity(&downtrace, ent, downmove, true, true))
        {
                // we got teleported when downstepping... must abort the move
                return;
@@ -2661,19 +2627,12 @@ void SV_Physics_Toss (prvm_edict_t *ent)
        {
        // move origin
                VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
-               if(!SV_PushEntity(&trace, ent, move, true))
+               // The buzzsaw traps in r2m6 and r2m7 use MOVETYPE_FLY and rely on moving while stuck in the world.
+               // Quake movetypes checked allsolid only in SV_FlyMove().
+               if(!SV_PushEntity(&trace, ent, move, true, PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY))
                        return; // teleported
                if (ent->free)
                        return;
-               if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
-               {
-                       // try to unstick the entity
-                       SV_UnstickEntity(ent);
-                       if(!SV_PushEntity(&trace, ent, move, true))
-                               return; // teleported
-                       if (ent->free)
-                               return;
-               }
                if (trace.fraction == 1)
                        break;
                movetime *= 1 - min(1, trace.fraction);
@@ -2961,6 +2920,7 @@ static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
        }
 }
 
+// asynchronous path
 void SV_Physics_ClientMove(void)
 {
        prvm_prog_t *prog = SVVM_prog;