From a7f7a172b5822c21ac9c7c1805d3d24a427761f4 Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Fri, 26 Apr 2024 13:05:42 +1000 Subject: [PATCH] physics: fix and refactor unsticking 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 --- dpdefs/dpextensions.qc | 1 + server.h | 1 + sv_main.c | 6 +-- sv_phys.c | 115 +++++++++++++++-------------------------- 4 files changed, 48 insertions(+), 75 deletions(-) diff --git a/dpdefs/dpextensions.qc b/dpdefs/dpextensions.qc index 3978698c..42c8a2a2 100644 --- a/dpdefs/dpextensions.qc +++ b/dpdefs/dpextensions.qc @@ -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; diff --git a/server.h b/server.h index a3e0436b..20637517 100644 --- 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); diff --git a/sv_main.c b/sv_main.c index f35abf3f..1ba1b34e 100644 --- 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"}; diff --git a/sv_phys.c b/sv_phys.c index 411a7c96..6d776f86 100644 --- 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); -- 2.39.2