From 270f020888c5a03b98cc7752957243e6123c1132 Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Thu, 14 Sep 2023 05:38:04 +1000 Subject: [PATCH] Rework handling of stuck entities in engine movetypes 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 --- sv_main.c | 6 ++--- sv_phys.c | 75 +++++++++++++++++++++++++++++++++---------------------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/sv_main.c b/sv_main.c index 5b7c4989..15ac48d6 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", "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)"}; diff --git a/sv_phys.c b/sv_phys.c index 35bc3410..8a4d41df 100644 --- 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); -- 2.39.2