@@ -49,14 +49,17 @@ cvar_t sv_nostep = {"sv_nostep", "0", CVAR_NONE};
4949cvar_t sv_freezenonclients = {"sv_freezenonclients" , "0" , CVAR_NONE };
5050cvar_t sv_gameplayfix_spawnbeforethinks = {"sv_gameplayfix_spawnbeforethinks" , "0" , CVAR_NONE };
5151cvar_t sv_gameplayfix_bouncedownslopes = {"sv_gameplayfix_bouncedownslopes" , "1" , CVAR_NONE }; // fixes grenades making horrible noises on slopes.
52- cvar_t sv_fastpushmove = {"sv_fastpushmove" , "1" , CVAR_ARCHIVE }; // 0=old SV_PushMove processing; 1= faster SV_PushMove, (default)
52+ cvar_t sv_fastpushmove = {"sv_fastpushmove" , "1" , CVAR_NONE }; // 0=old SV_PushMove processing; 1= faster SV_PushMove, (default)
5353cvar_t sv_pushgrid = {"sv_pushgrid" , "1" , CVAR_NONE }; // cull SV_PushMove candidates with a spatial hash, needs sv_fastpushmove
5454cvar_t sv_analyticphysics = {"sv_analyticphysics" , "1" , CVAR_NONE }; // gravity/friction integration matches 72Hz physics at any tick rate
5555
5656qboolean sv_analyticphysics_frame = true; // sv_analyticphysics latched per SV_Physics, QC can flip the cvar mid-tick
5757
5858#define MOVE_EPSILON 0.01
5959
60+ // max depth float rounding can embed an entity into the surface it rests on, anything deeper is a real overlap
61+ #define PUSH_CONTACT_EPSILON (2 * DIST_EPSILON)
62+
6063static void SV_Physics_Toss (edict_t * ent );
6164
6265// For usage by SV_PushMove, allocate at max possible size,
@@ -659,27 +662,32 @@ PUSHMOVE
659662===============================================================================
660663*/
661664
665+ // 0=off; 1=legacy DIST_EPSILON nudge, clients only; 2=legacy nudge, all entities; 3=robust pusher contact (default)
666+ cvar_t sv_gameplayfix_elevators = {"sv_gameplayfix_elevators" , "3" , CVAR_NONE };
667+
668+ static trace_t SV_PushEntityMove (edict_t * ent , vec3_t start , vec3_t end )
669+ {
670+ if (ent -> v .movetype == MOVETYPE_FLYMISSILE )
671+ return SV_Move (start , ent -> v .mins , ent -> v .maxs , end , MOVE_MISSILE , ent );
672+ else if (ent -> v .solid == SOLID_TRIGGER || ent -> v .solid == SOLID_NOT )
673+ // only clip against bmodels
674+ return SV_Move (start , ent -> v .mins , ent -> v .maxs , end , MOVE_NOMONSTERS , ent );
675+ else
676+ return SV_Move (start , ent -> v .mins , ent -> v .maxs , end , MOVE_NORMAL , ent );
677+ }
678+
662679/*
663680============
664- SV_PushEntity
681+ SV_PushEntityTo
665682
666683Does not change the entities velocity at all
667684============
668685*/
669- static trace_t SV_PushEntity (edict_t * ent , vec3_t push )
686+ static trace_t SV_PushEntityTo (edict_t * ent , vec3_t end )
670687{
671688 trace_t trace ;
672- vec3_t end ;
673689
674- VectorAdd (ent -> v .origin , push , end );
675-
676- if (ent -> v .movetype == MOVETYPE_FLYMISSILE )
677- trace = SV_Move (ent -> v .origin , ent -> v .mins , ent -> v .maxs , end , MOVE_MISSILE , ent );
678- else if (ent -> v .solid == SOLID_TRIGGER || ent -> v .solid == SOLID_NOT )
679- // only clip against bmodels
680- trace = SV_Move (ent -> v .origin , ent -> v .mins , ent -> v .maxs , end , MOVE_NOMONSTERS , ent );
681- else
682- trace = SV_Move (ent -> v .origin , ent -> v .mins , ent -> v .maxs , end , MOVE_NORMAL , ent );
690+ trace = SV_PushEntityMove (ent , ent -> v .origin , end );
683691
684692 if (trace .ent )
685693 assert_always (!trace .ent -> free );
@@ -701,7 +709,6 @@ static trace_t SV_PushEntity (edict_t *ent, vec3_t push)
701709SV_PushMove
702710============
703711*/
704- cvar_t sv_gameplayfix_elevators = {"sv_gameplayfix_elevators" , "2" , CVAR_ARCHIVE }; // 0=off; 1=clients only; 2=all entities
705712
706713static void SV_PushMove (edict_t * pusher , float movetime )
707714{
@@ -724,9 +731,27 @@ static void SV_PushMove (edict_t *pusher, float movetime)
724731 return ;
725732 }
726733
734+ const qboolean robust_push = (sv_gameplayfix_elevators .value >= 3.f );
735+ const float newltime = pusher -> v .ltime + movetime ;
736+ vec3_t neworigin ;
737+
727738 for (i = 0 ; i < 3 ; i ++ )
728739 {
729- move [i ] = pusher -> v .velocity [i ] * movetime ;
740+ if (robust_push )
741+ {
742+ // the mover runs at constant velocity until its think at nextthink, so
743+ // evaluate the position on that trajectory in double instead of
744+ // accumulating velocity*movetime rounding every tick: the final partial
745+ // step lands exactly on the destination QC aimed for
746+ const double dest = (double )pusher -> v .origin [i ] + (double )pusher -> v .velocity [i ] * ((double )pusher -> v .nextthink - (double )pusher -> v .ltime );
747+ neworigin [i ] = (float )(dest - (double )pusher -> v .velocity [i ] * ((double )pusher -> v .nextthink - (double )newltime ));
748+ move [i ] = neworigin [i ] - pusher -> v .origin [i ];
749+ }
750+ else
751+ {
752+ move [i ] = pusher -> v .velocity [i ] * movetime ;
753+ neworigin [i ] = pusher -> v .origin [i ] + move [i ];
754+ }
730755 mins [i ] = pusher -> v .absmin [i ] + move [i ];
731756 maxs [i ] = pusher -> v .absmax [i ] + move [i ];
732757 // the grid query must span the whole sweep: riders rest on the pre-move
@@ -739,8 +764,8 @@ static void SV_PushMove (edict_t *pusher, float movetime)
739764
740765 // move the pusher to it's final position
741766
742- VectorAdd ( pusher -> v . origin , move , pusher -> v .origin );
743- pusher -> v .ltime += movetime ;
767+ VectorCopy ( neworigin , pusher -> v .origin );
768+ pusher -> v .ltime = newltime ;
744769 SV_LinkEdict (pusher , false);
745770
746771 // see if any solid entities are inside the final position
@@ -835,9 +860,20 @@ static void SV_PushMove (edict_t *pusher, float movetime)
835860 || solid_backup == SOLID_BBOX // normally boxes
836861 || solid_backup == SOLID_SLIDEBOX ) // normally monsters
837862 {
863+ vec3_t dest ;
864+ if (robust_push )
865+ {
866+ // carry to a target derived from the pusher's new origin so the
867+ // relative offset can't drift from parallel float integration
868+ for (i = 0 ; i < 3 ; i ++ )
869+ dest [i ] = pusher -> v .origin [i ] + (entorig [i ] - pushorig [i ]);
870+ }
871+ else
872+ VectorAdd (entorig , move , dest );
873+
838874 // try moving the contacted entity
839875 pusher -> v .solid = SOLID_NOT ;
840- SV_PushEntity (check , move );
876+ SV_PushEntityTo (check , dest );
841877
842878 // if it is still inside the pusher, block
843879 if (pusher -> v .skin < 0 )
@@ -858,6 +894,31 @@ static void SV_PushMove (edict_t *pusher, float movetime)
858894 if (check -> v .mins [0 ] == check -> v .maxs [0 ])
859895 continue ;
860896
897+ // riders only embed through their ground contact and never deeper than
898+ // PUSH_CONTACT_EPSILON, so a single sweep from above recovers the exact
899+ // contact position. must run before the corpse path so items don't get
900+ // their bbox zeroed over a rounding error; real squeezes still crush.
901+ if (robust_push && riding && block == pusher )
902+ {
903+ vec3_t pushedorg , above ;
904+ trace_t settle ;
905+
906+ VectorCopy (check -> v .origin , pushedorg );
907+ VectorCopy (check -> v .origin , above );
908+ above [2 ] += PUSH_CONTACT_EPSILON ;
909+ settle = SV_PushEntityMove (check , above , pushedorg );
910+ if (!settle .startsolid )
911+ {
912+ VectorCopy (settle .endpos , check -> v .origin );
913+ if (!SV_TestEntityPosition (check ))
914+ {
915+ SV_LinkEdict (check , false);
916+ continue ;
917+ }
918+ VectorCopy (pushedorg , check -> v .origin );
919+ }
920+ }
921+
861922 if (check -> v .solid == SOLID_NOT || check -> v .solid == SOLID_TRIGGER )
862923 { // corpse
863924 check -> v .mins [0 ] = check -> v .mins [1 ] = 0 ;
@@ -866,7 +927,7 @@ static void SV_PushMove (edict_t *pusher, float movetime)
866927 }
867928
868929 // try moving the entity up a bit if it's blocked by the pusher while also standing on it
869- if (riding && block == pusher &&
930+ if (! robust_push && riding && block == pusher &&
870931 (sv_gameplayfix_elevators .value >= 2.f || (sv_gameplayfix_elevators .value && NUM_FOR_EDICT (check ) <= svs .maxclients )))
871932 {
872933 check -> v .origin [2 ] += DIST_EPSILON ;
@@ -1088,7 +1149,7 @@ static int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
10881149{
10891150 int i ;
10901151 vec3_t oldorg ;
1091- vec3_t dir ;
1152+ vec3_t dir , dest ;
10921153 int clip ;
10931154 trace_t steptrace ;
10941155
@@ -1134,7 +1195,8 @@ static int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
11341195 break ;
11351196 }
11361197
1137- SV_PushEntity (ent , dir );
1198+ VectorAdd (ent -> v .origin , dir , dest );
1199+ SV_PushEntityTo (ent , dest );
11381200
11391201 // retry the original move
11401202 ent -> v .velocity [0 ] = oldvel [0 ];
@@ -1207,13 +1269,11 @@ static void SV_WalkMove (edict_t *ent)
12071269 //
12081270 VectorCopy (oldorg , ent -> v .origin ); // back to start pos
12091271
1210- VectorCopy (vec3_origin , upmove );
1211- VectorCopy (vec3_origin , downmove );
1212- upmove [2 ] = STEPSIZE ;
1213- downmove [2 ] = - STEPSIZE + oldvel [2 ] * host_frametime ;
1272+ VectorCopy (ent -> v .origin , upmove );
1273+ upmove [2 ] += STEPSIZE ;
12141274
12151275 // move up
1216- SV_PushEntity (ent , upmove ); // FIXME: don't link?
1276+ SV_PushEntityTo (ent , upmove ); // FIXME: don't link?
12171277
12181278 // move forward
12191279 ent -> v .velocity [0 ] = oldvel [0 ];
@@ -1237,15 +1297,17 @@ static void SV_WalkMove (edict_t *ent)
12371297 SV_WallFriction (ent , & steptrace );
12381298
12391299 // move down
1240- downtrace = SV_PushEntity (ent , downmove ); // FIXME: don't link?
1300+ VectorCopy (ent -> v .origin , downmove );
1301+ downmove [2 ] += - STEPSIZE + oldvel [2 ] * host_frametime ;
1302+ downtrace = SV_PushEntityTo (ent , downmove ); // FIXME: don't link?
12411303
12421304 if (downtrace .plane .normal [2 ] > 0.7 )
12431305 {
12441306 if (ent -> v .solid == SOLID_BSP )
12451307 {
12461308 ent -> v .flags = (int )ent -> v .flags | FL_ONGROUND ;
12471309
1248- // SV_PushEntity () call SV_LinkEdict (true) that could free downtrace.ent
1310+ // SV_PushEntityTo () calls SV_LinkEdict (true) that could free downtrace.ent
12491311 if (downtrace .ent && !downtrace .ent -> free )
12501312 ent -> v .groundentity = EDICT_TO_PROG (downtrace .ent );
12511313 }
@@ -1447,7 +1509,7 @@ Toss, bounce, and fly movement. When onground, do nothing.
14471509static void SV_Physics_Toss (edict_t * ent )
14481510{
14491511 trace_t trace ;
1450- vec3_t move ;
1512+ vec3_t end ;
14511513 float backoff ;
14521514
14531515 // regular thinking
@@ -1468,8 +1530,8 @@ static void SV_Physics_Toss (edict_t *ent)
14681530 VectorMA (ent -> v .angles , host_frametime , ent -> v .avelocity , ent -> v .angles );
14691531
14701532 // move origin
1471- VectorScale (ent -> v .velocity , host_frametime , move );
1472- trace = SV_PushEntity (ent , move );
1533+ VectorMA (ent -> v .origin , host_frametime , ent -> v . velocity , end );
1534+ trace = SV_PushEntityTo (ent , end );
14731535
14741536 if (ent -> free )
14751537 return ;
@@ -1495,7 +1557,7 @@ static void SV_Physics_Toss (edict_t *ent)
14951557 {
14961558 ent -> v .flags = (int )ent -> v .flags | FL_ONGROUND ;
14971559
1498- // SV_PushEntity () call SV_LinkEdict (true) that could free trace.ent
1560+ // SV_PushEntityTo () calls SV_LinkEdict (true) that could free trace.ent
14991561 if (trace .ent && !trace .ent -> free )
15001562 ent -> v .groundentity = EDICT_TO_PROG (trace .ent );
15011563
0 commit comments