]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
Rework handling of stuck entities in engine movetypes
authorbones_was_here <bones_was_here@xonotic.au>
Wed, 13 Sep 2023 19:38:04 +0000 (05:38 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Wed, 13 Sep 2023 19:38:04 +0000 (05:38 +1000)
Avoids most false positives and unnecessary work.
Mentions likely code path and cvar deprecations.

Fixes nades ending up where they shouldn't, which looked like:
Unstuck entity 450 (classname "ogre_grenade") with offset 0.000000 0.000000 28.000000.

Allows certain entities to move while stuck in the world (see comments).
Fixes https://github.com/DarkPlacesEngine/darkplaces/issues/18

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
sv_main.c
sv_phys.c

index 5b7c4989eac9f0d1da1d8d4c239527da188228fb..15ac48d6500dbb997f42f1acaeae21621f0925b6 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -113,7 +113,7 @@ cvar_t sv_gameplayfix_grenadebouncedownslopes = {CF_SERVER, "sv_gameplayfix_gren
 cvar_t sv_gameplayfix_multiplethinksperframe = {CF_SERVER, "sv_gameplayfix_multiplethinksperframe", "1", "allows entities to think more often than the server framerate, primarily useful for very high fire rate weapons"};
 cvar_t sv_gameplayfix_noairborncorpse = {CF_SERVER, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses, items, etc) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
 cvar_t sv_gameplayfix_noairborncorpse_allowsuspendeditems = {CF_SERVER, "sv_gameplayfix_noairborncorpse_allowsuspendeditems", "1", "causes entities sitting ontop of objects that are instantaneously remove to float in midair (special hack to allow a common level design trick for floating items)"};
-cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "0", "attempts to fix physics errors (where an object ended up in solid for some reason)"};
+cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "1", "attempts to fix physics errors where an object ended up in solid for some reason, supersedes sv_gameplayfix_unstickentities"};
 cvar_t sv_gameplayfix_nudgeoutofsolid_separation = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid_separation", "0.03125", "keep objects this distance apart to prevent collision issues on seams"};
 cvar_t sv_gameplayfix_q2airaccelerate = {CF_SERVER, "sv_gameplayfix_q2airaccelerate", "0", "Quake2-style air acceleration"};
 cvar_t sv_gameplayfix_nogravityonground = {CF_SERVER, "sv_gameplayfix_nogravityonground", "0", "turn off gravity when on ground (to get rid of sliding)"};
@@ -126,8 +126,8 @@ cvar_t sv_gameplayfix_swiminbmodels = {CF_SERVER, "sv_gameplayfix_swiminbmodels"
 cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {CF_SERVER, "sv_gameplayfix_upwardvelocityclearsongroundflag", "1", "prevents monsters, items, and most other objects from being stuck to the floor when pushed around by damage, and other situations in mods"};
 cvar_t sv_gameplayfix_downtracesupportsongroundflag = {CF_SERVER, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps), fixes groundentity not being set when walking onto a mover with sv_gameplayfix_nogravityonground"};
 cvar_t sv_gameplayfix_q1bsptracelinereportstexture = {CF_SERVER, "sv_gameplayfix_q1bsptracelinereportstexture", "1", "enables mods to get accurate trace_texture results on q1bsp by using a surface-hitting traceline implementation rather than the standard solidbsp method, q3bsp always reports texture accurately"};
-cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "1", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."};
-cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position"};
+cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "0", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."};
+cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position, superseded by sv_gameplayfix_nudgeoutofsolid"};
 cvar_t sv_gameplayfix_fixedcheckwatertransition = {CF_SERVER, "sv_gameplayfix_fixedcheckwatertransition", "1", "fix two very stupid bugs in SV_CheckWaterTransition when watertype is CONTENTS_EMPTY (the bugs causes waterlevel to be 1 on first frame, -1 on second frame - the fix makes it 0 on both frames)"};
 cvar_t sv_gameplayfix_customstats = {CF_SERVER, "sv_gameplayfix_customstats", "0", "Disable stats higher than 220, for use by certain games such as Xonotic"};
 cvar_t sv_gravity = {CF_SERVER | CF_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
index 35bc3410cc7c7382710358a62afefad8606608a1..8a4d41dfc577a1c596ad08046762258cfeb7555f 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -1194,7 +1194,7 @@ 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)
 {
@@ -1238,7 +1238,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
@@ -1248,7 +1248,7 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
 
                // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
                // abort move if we're stuck in the world (and didn't make it out)
-               if (trace.worldstartsolid && trace.allsolid)
+               if (trace.worldstartsolid && trace.allsolid && trace.startdepth < 0)
                {
                        VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
                        return 3;
@@ -1288,20 +1288,20 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float
                        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;
@@ -1552,7 +1552,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;
@@ -1567,12 +1567,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)
-       {
-               PHYS_NudgeOutOfSolid(prog, ent);
-       }
-
        VectorCopy(PRVM_serveredictvector(ent, origin), start);
        VectorAdd(start, push, end);
 
@@ -1586,9 +1580,37 @@ 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;
+       // abort move if we're stuck in the world (and didn't make it out)
+       if (trace->worldstartsolid && trace->allsolid && trace->startdepth < 0 && checkstuck)
+       {
+               // checking startdepth eliminates many false positives on Q1BSP with mod_q1bsp_polygoncollisions 0
+               // but it's still not guaranteed that we're stuck in a bmodel at this point
+               if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
+               {
+                       switch (PHYS_NudgeOutOfSolid(prog, ent))
+                       {
+                               case 0:
+                                       Con_Printf(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 true; // definitely stuck in a bmodel
+                               case 1:
+                                       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)), PRVM_serveredictvector(ent, origin)[0] - start[0], PRVM_serveredictvector(ent, origin)[1] - start[1], PRVM_serveredictvector(ent, origin)[2] - start[2]);
+                                       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);
+
+                               // definitely not stuck in a bmodel, move may proceed
+                       }
+               }
+               else if (sv_gameplayfix_unstickentities.integer && SV_UnstickEntity(ent))
+               {
+                       // bones_was_here: pretty sure we can deprecate sv_gameplayfix_unstickentities, sv_gameplayfix_nudgeoutofsolid is much nicer
+                       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);
+               }
+               else
+                       return true; // assuming stuck, bones_was_here TODO: always use PHYS_NudgeOutOfSolid (remove sv_gameplayfix_nudgeoutofsolid)?
+       }
 
        VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
 
@@ -1854,7 +1876,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];
@@ -1886,7 +1908,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;
@@ -2371,7 +2393,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;
@@ -2423,7 +2445,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;
@@ -2625,19 +2647,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);