]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
physics: fix and refactor unsticking
authorbones_was_here <bones_was_here@xonotic.au>
Fri, 26 Apr 2024 03:05:42 +0000 (13:05 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Fri, 26 Apr 2024 11:34:12 +0000 (21:34 +1000)
Closes https://github.com/DarkPlacesEngine/darkplaces/issues/165

Bugs came from 270f020888c5a03b98cc7752957243e6123c1132 trying to use
NudgeOutOfSolid on Q1BSP which currently isn't possible, combined with
a map bug on alk_caustic, and unsticking unfortunately being an
essential part of Q1BSP player physics.

Adjusts cvar defaults to match Quake behaviour.
Improves documentation.

Merges SV_UnstickEntity() and SV_CheckStuck().

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
dpdefs/dpextensions.qc
server.h
sv_main.c
sv_phys.c

index 3978698cec36641fb0a302245da3e54cbf7fa60b..42c8a2a2ab787a3c8771a799fa3fde93321dd23a 100644 (file)
@@ -2663,6 +2663,7 @@ float(entity ent) nudgeoutofsolid = #567;
 //description:
 //Attempts to move a stuck entity out of solid brushes, returning 1 if successful, 0 if it remains stuck, -1 if it wasn't stuck.
 //Note: makes only one tracebox call if the entity isn't stuck, so don't call tracebox just to see if you should call nudgeoutofsolid.
+//Currently has no effect on Q1BSP unless mod_q1bsp_polygoncollisions is enabled.
 
 
 float(float dividend, float divisor) mod = #245;
index a3e0436bbbfcba02c6cc4a2f20318c6cd89262e1..20637517eaf873563fbb8d27cd6ce652717e9628 100644 (file)
--- a/server.h
+++ b/server.h
@@ -565,6 +565,7 @@ void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent); //
 
 /*! move an entity that is stuck by small amounts in various directions to try to nudge it back into the collision hull
  * returns true if it found a better place
+ * Replaces SV_TryUnstick() and SV_CheckStuck() which in Quake applied to players only.
  */
 qbool SV_UnstickEntity (prvm_edict_t *ent);
 
index f35abf3fe2aba1764441059aae8d740ceb740436..1ba1b34e3a591fe9f40e631fef57d3be63064429 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", "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 = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "0", "attempts to fix physics errors where an object ended up in solid for some reason, better than sv_gameplayfix_unstick* but currently has no effect on Q1BSP (unless mod_q1bsp_polygoncollisions is enabled)"};
 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", "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_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. Quake did something similar."};
+cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "0", "hack to check if entities are crossing world collision hull and try to move them to the right position. Quake didn't do this so maps shouldn't depend on it."};
 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_gravity = {CF_SERVER | CF_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
 cvar_t sv_init_frame_count = {CF_SERVER, "sv_init_frame_count", "2", "number of frames to run to allow everything to settle before letting clients connect"};
index 411a7c969b748382a723df0a2ef2d28983d17d63..6d776f86f45d4af27d120271495fc405d901bc38 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -1247,7 +1247,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 && trace.startdepth < 0)
+               if (trace.worldstartsolid && trace.allsolid)
                {
                        VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
                        return 3;
@@ -1587,39 +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);
-       // abort move if we're stuck in the world (and didn't make it out)
-       if (trace->worldstartsolid && trace->allsolid && trace->startdepth < 0 && checkstuck)
+       if (trace->allsolid && 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)
+               if (SV_UnstickEntity(ent))
                {
-                       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)?
+               // 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
 
@@ -2034,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;
 
@@ -2054,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;
                }
        }
@@ -2068,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
@@ -2114,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;
        }
 }
 
@@ -2310,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);