From 8d3bd76c3fc8e8fba39f5702c53881faf31edeb6 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Sun, 21 Jun 2026 07:12:09 +0300 Subject: [PATCH 1/2] workflows: ignore coverity download --- .github/workflows/coverity.yml | 45 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index fa46929..d24998b 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -33,32 +33,31 @@ jobs: run: | if [ -z "${{ secrets.COVERITY_SCAN_TOKEN }}" ]; then echo "COVERITY_SCAN_TOKEN is not set. Skipping workflow." + echo "skip=true" >> $GITHUB_ENV else - wget https://scan.coverity.com/download/cxx/linux-ARM64 \ - --post-data "token=${{secrets.COVERITY_SCAN_TOKEN}}&project=${{github.repository}}" \ - -O coverity_tool.tgz -cq - tar -xf coverity_tool.tgz - mv -v cov-analysis-linux-arm64-* cov-analysis-linux-arm64 + if ! wget https://scan.coverity.com/download/cxx/linux-ARM64 \ + --post-data "token=${{secrets.COVERITY_SCAN_TOKEN}}&project=${{github.repository}}" \ + -O coverity_tool.tgz -cq; then + echo "wget failed. Skipping workflow." + echo "skip=true" >> $GITHUB_ENV + else + tar -xf coverity_tool.tgz + mv -v cov-analysis-linux-arm64-* cov-analysis-linux-arm64 + fi fi - name: Build + if: env.skip != 'true' run: | - if [ -z "${{ secrets.COVERITY_SCAN_TOKEN }}" ]; then - echo "COVERITY_SCAN_TOKEN is not set. Skipping workflow." - else - rm -rf cov-int - make clean - cov-analysis-linux-arm64/bin/cov-build --dir cov-int make WITH_SDL3=no -j 4 - fi + rm -rf cov-int + make clean + cov-analysis-linux-arm64/bin/cov-build --dir cov-int make WITH_SDL3=no -j 4 - name: Upload result + if: env.skip != 'true' run: | - if [ -z "${{ secrets.COVERITY_SCAN_TOKEN }}" ]; then - echo "COVERITY_SCAN_TOKEN is not set. Skipping workflow." - else - tar -czf cov-int.tgz cov-int - curl --form token=${{secrets.COVERITY_SCAN_TOKEN}} \ - --form email=${{secrets.COVERITY_SCAN_EMAIL}} \ - --form file=@cov-int.tgz \ - --form version="${{github.sha}}" \ - --form description="${{github.sha}}" \ - https://scan.coverity.com/builds?project=${{github.repository}} - fi + tar -czf cov-int.tgz cov-int + curl --form token=${{secrets.COVERITY_SCAN_TOKEN}} \ + --form email=${{secrets.COVERITY_SCAN_EMAIL}} \ + --form file=@cov-int.tgz \ + --form version="${{github.sha}}" \ + --form description="${{github.sha}}" \ + https://scan.coverity.com/builds?project=${{github.repository}} From b44cde01bf0baf34ba943ece4200ce3c67369593 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Sun, 21 Jun 2026 07:43:56 +0300 Subject: [PATCH 2/2] game: port physics code Based: * https://github.com/yquake2/yquake2/pull/1324 --- src/g_phys.c | 461 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 297 insertions(+), 164 deletions(-) diff --git a/src/g_phys.c b/src/g_phys.c index 384443b..f38b27c 100644 --- a/src/g_phys.c +++ b/src/g_phys.c @@ -26,13 +26,16 @@ #include "header/local.h" +#define STOP_EPSILON 0.1 +#define MAX_CLIP_PLANES 5 + /* - * pushmove objects do not obey gravity, and do not interact with - * each other or trigger fields, but block normal movement and push - * normal objects when they move. + * pushmove objects do not obey gravity, and do not interact + * with each other or trigger fields, but block normal movement + * and push normal objects when they move. * - * onground is set for toss objects when they come to a complete rest. - * It is set for steping or walking objects. + * onground is set for toss objects when they come to a complete + * rest. It is set for steping or walking objects. * * doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH * bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS @@ -44,12 +47,17 @@ * solid_edge items only clip against bsp models. */ -edict_t * +static edict_t * SV_TestEntityPosition(edict_t *ent) { trace_t trace; int mask; + if (!ent) + { + return NULL; + } + if (ent->clipmask) { mask = ent->clipmask; @@ -70,33 +78,35 @@ SV_TestEntityPosition(edict_t *ent) return NULL; } -void +static void SV_CheckVelocity(edict_t *ent) { - int i; + if (!ent) + { + return; + } - /* bound velocity */ - for (i = 0; i < 3; i++) + if (VectorLengthSquared(ent->velocity) > sv_maxvelocity->value * sv_maxvelocity->value) { - if (ent->velocity[i] > sv_maxvelocity->value) - { - ent->velocity[i] = sv_maxvelocity->value; - } - else if (ent->velocity[i] < -sv_maxvelocity->value) - { - ent->velocity[i] = -sv_maxvelocity->value; - } + VectorNormalize(ent->velocity); + VectorScale(ent->velocity, sv_maxvelocity->value, ent->velocity); } } /* - * Runs thinking code for this frame if necessary + * Runs thinking code for + * this frame if necessary */ -qboolean +static qboolean SV_RunThink(edict_t *ent) { float thinktime; + if (!ent) + { + return false; + } + thinktime = ent->nextthink; if (thinktime <= 0) @@ -111,24 +121,28 @@ SV_RunThink(edict_t *ent) ent->nextthink = 0; - if (!ent->think) + if (ent->think) { - gi.error("NULL ent->think"); + ent->think(ent); } - ent->think(ent); - return false; } /* - * Two entities have touched, so run their touch functions + * Two entities have touched, so + * run their touch functions */ -void +static void SV_Impact(edict_t *e1, trace_t *trace) { edict_t *e2; + if (!e1 || !trace) + { + return; + } + e2 = trace->ent; if (e1->touch && (e1->solid != SOLID_NOT)) @@ -147,13 +161,10 @@ SV_Impact(edict_t *e1, trace_t *trace) * returns the blocked flags (1 = floor, * 2 = step / wall) */ -#define STOP_EPSILON 0.1 - -int +static int ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; - float change; int i, blocked; blocked = 0; @@ -172,6 +183,8 @@ ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) for (i = 0; i < 3; i++) { + float change; + change = normal[i] * backoff; out[i] = in[i] - change; @@ -185,20 +198,21 @@ ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) } /* - * The basic solid body movement clip that slides along multiple planes - * Returns the clipflags if the velocity was modified (hit something solid) + * The basic solid body movement clip + * that slides along multiple planes + * Returns the clipflags if the velocity + * was modified (hit something solid) + * * 1 = floor * 2 = wall / step * 4 = dead stop */ -#define MAX_CLIP_PLANES 5 -int +static int SV_FlyMove(edict_t *ent, float time, int mask) { edict_t *hit; int bumpcount, numbumps; vec3_t dir; - float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity, new_velocity; @@ -208,6 +222,11 @@ SV_FlyMove(edict_t *ent, float time, int mask) float time_left; int blocked; + if (!ent) + { + return 0; + } + numbumps = 4; blocked = 0; @@ -287,14 +306,15 @@ SV_FlyMove(edict_t *ent, float time, int mask) VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; - /* modify original_velocity so it parallels all of the clip planes */ + /* modify original_velocity so it + parallels all of the clip planes */ for (i = 0; i < numplanes; i++) { ClipVelocity(original_velocity, planes[i], new_velocity, 1); for (j = 0; j < numplanes; j++) { - if (j != i) + if ((j != i) && !VectorCompare(planes[i], planes[j])) { if (DotProduct(new_velocity, planes[j]) < 0) { @@ -316,6 +336,7 @@ SV_FlyMove(edict_t *ent, float time, int mask) } else { + float d; /* go along the crease */ if (numplanes != 2) @@ -329,8 +350,9 @@ SV_FlyMove(edict_t *ent, float time, int mask) VectorScale(dir, d, ent->velocity); } - /* if original velocity is against the original velocity, - stop dead to avoid tiny occilations in sloping corners */ + /* if original velocity is against the original + velocity, stop dead to avoid tiny occilations + in sloping corners */ if (DotProduct(ent->velocity, primal_velocity) <= 0) { VectorCopy(vec3_origin, ent->velocity); @@ -344,6 +366,11 @@ SV_FlyMove(edict_t *ent, float time, int mask) void SV_AddGravity(edict_t *ent) { + if (!ent) + { + return; + } + ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME; } @@ -357,15 +384,22 @@ SV_AddGravity(edict_t *ent) * This leads to a lot of false block tests in SV_Push * if another bmodel is in the vicinity. */ -void +static void RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs) { vec3_t forward, left, up, f1, l1, u1; vec3_t p[8]; - int i, j, k, j2, k4; + int i, j, k; + + if (!ent) + { + return; + } for (k = 0; k < 2; k++) { + int k4; + k4 = k * 4; if (k) @@ -383,6 +417,8 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs) for (j = 0; j < 2; j++) { + int j2; + j2 = j * 2; if (j) @@ -459,18 +495,14 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs) } } -/* - * =============================================================================== - * - * PUSHMOVE - * - * =============================================================================== - */ +/* ================================================================== */ + +/* PUSHMOVE */ /* * Does not change the entities velocity at all */ -trace_t +static trace_t SV_PushEntity(edict_t *ent, vec3_t push) { trace_t trace; @@ -494,6 +526,17 @@ SV_PushEntity(edict_t *ent, vec3_t push) trace = gi.trace(start, ent->mins, ent->maxs, end, ent, mask); + /* startsolid treats different-content volumes + as continuous, like the bbox of a monster/player + and the floor of an elevator. So do another trace + that only collides with BSP so that we make a best + effort to keep these entities inside non-solid space + */ + if (trace.startsolid && (mask & ~MASK_SOLID)) + { + trace = gi.trace (start, ent->mins, ent->maxs, end, ent, MASK_SOLID); + } + VectorCopy(trace.endpos, ent->s.origin); gi.linkentity(ent); @@ -517,7 +560,8 @@ SV_PushEntity(edict_t *ent, vec3_t push) { SV_Impact(ent, &trace); - /* if the pushed entity went away and the pusher is still there */ + /* if the pushed entity went away + and the pusher is still there */ if (!trace.ent->inuse && ent->inuse) { /* move the pusher back and try again */ @@ -535,6 +579,9 @@ SV_PushEntity(edict_t *ent, vec3_t push) return trace; } +/* + * Pushed API + */ typedef struct { edict_t *ent; @@ -543,22 +590,96 @@ typedef struct float deltayaw; } pushed_t; -pushed_t pushed[MAX_EDICTS], *pushed_p; -edict_t *obstacle; +static pushed_t pushed[MAX_EDICTS], *pushed_p; + +static void +Pushed_Init(void) +{ + pushed_p = pushed; +} + +static qboolean +Pushed_Append(edict_t *e) +{ + if (pushed_p >= ARREND(pushed)) + { + return false; + } + + pushed_p->ent = e; + VectorCopy(e->s.origin, pushed_p->origin); + VectorCopy(e->s.angles, pushed_p->angles); + + if (e->client) + { + pushed_p->deltayaw = e->client->ps.pmove.delta_angles[YAW]; + } + + pushed_p++; + + return true; +} + +static void +Pushed_Pop(void) +{ + if (pushed_p > pushed) + { + pushed_p--; + } +} + +static void +Pushed_Undo(void) +{ + pushed_t *p; + + /* go backwards, so if the same entity was pushed + twice, it goes back to the original position */ + for (p = pushed_p - 1; p >= pushed; p--) + { + VectorCopy(p->origin, p->ent->s.origin); + VectorCopy(p->angles, p->ent->s.angles); + + if (p->ent->client) + { + p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw; + } + + gi.linkentity(p->ent); + } + + pushed_p = pushed; +} + +static void +Pushed_TouchTriggers(void) +{ + pushed_t *p; + + for (p = pushed_p - 1; p >= pushed; p--) + { + G_TouchTriggers(p->ent); + } +} /* * Objects need to be moved back on a failed push, * otherwise riders would continue to slide. */ -qboolean +static edict_t * SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) { - int i, e; - edict_t *check, *block; - pushed_t *p; - vec3_t org, org2, move2, forward, right, up; + int i; + edict_t *check; + vec3_t org, forward, right, up; vec3_t realmins, realmaxs; + if (!pusher) + { + return NULL; + } + /* clamp the move to 1/8 units, so the position will be accurate for client side prediction */ for (i = 0; i < 3; i++) @@ -582,19 +703,11 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) VectorSubtract(vec3_origin, amove, org); AngleVectors(org, forward, right, up); - /* save the pusher's original position */ - pushed_p->ent = pusher; - VectorCopy(pusher->s.origin, pushed_p->origin); - VectorCopy(pusher->s.angles, pushed_p->angles); - - if (pusher->client) + if (!Pushed_Append(pusher)) { - pushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW]; + return NULL; } - pushed_p++; - - /* move the pusher to it's final position */ VectorAdd(pusher->s.origin, move, pusher->s.origin); VectorAdd(pusher->s.angles, amove, pusher->s.angles); gi.linkentity(pusher); @@ -603,10 +716,9 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) rotating brush models. */ RealBoundingBox(pusher, realmins, realmaxs); - /* see if any solid entities are inside the final position */ - check = g_edicts + 1; - - for (e = 1; e < globals.num_edicts; e++, check++) + /* see if any solid entities + are inside the final position */ + for (check = g_edicts + 1; check < &g_edicts[globals.num_edicts]; check++) { if (!check->inuse) { @@ -626,7 +738,8 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) continue; /* not linked in anywhere */ } - /* if the entity is standing on the pusher, it will definitely be moved */ + /* if the entity is standing on the pusher, + it will definitely be moved */ if (check->groundentity != pusher) { /* see if the ent needs to be tested */ @@ -640,7 +753,8 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) continue; } - /* see if the ent's bbox is inside the pusher's final position */ + /* see if the ent's bbox is inside + the pusher's final position */ if (!SV_TestEntityPosition(check)) { continue; @@ -650,11 +764,12 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) if ((pusher->movetype == MOVETYPE_PUSH) || (check->groundentity == pusher)) { - /* move this entity */ - pushed_p->ent = check; - VectorCopy(check->s.origin, pushed_p->origin); - VectorCopy(check->s.angles, pushed_p->angles); - pushed_p++; + vec3_t org2, move2; + + if (!Pushed_Append(check)) + { + continue; + } /* try moving the contacted entity */ VectorAdd(check->s.origin, move, check->s.origin); @@ -678,103 +793,85 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) check->groundentity = NULL; } - block = SV_TestEntityPosition(check); - - if (!block) + if (!SV_TestEntityPosition(check)) { - /* pushed ok */ gi.linkentity(check); continue; } /* if it is ok to leave in the old position, do it - this is only relevent for riding entities, not pushed */ + this is only relevent for riding entities, not + pushed */ VectorSubtract(check->s.origin, move, check->s.origin); - block = SV_TestEntityPosition(check); - if (!block) + if (!SV_TestEntityPosition(check)) { - pushed_p--; + Pushed_Pop(); continue; } } - /* save off the obstacle so we can call the block function */ - obstacle = check; - - /* move back any entities we already moved - go backwards, so if the same entity was pushed/ - twice, it goes back to the original position */ - for (p = pushed_p - 1; p >= pushed; p--) - { - VectorCopy(p->origin, p->ent->s.origin); - VectorCopy(p->angles, p->ent->s.angles); - - if (p->ent->client) - { - p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw; - } - - gi.linkentity(p->ent); - } + Pushed_Undo(); - return false; + return check; } - /* see if anything we moved has touched a trigger */ - for (p = pushed_p - 1; p >= pushed; p--) - { - G_TouchTriggers(p->ent); - } + Pushed_TouchTriggers(); - return true; + return NULL; } /* * Bmodel objects don't interact with each * other, but push all box objects */ -void +static void SV_Physics_Pusher(edict_t *ent) { - vec3_t move, amove; - edict_t *part, *mv; + edict_t *part, *obstacle; - /* if not a team captain, so movement will be handled elsewhere */ + if (!ent) + { + return; + } + + /* if not a team captain, so movement + will be handled elsewhere */ if (ent->flags & FL_TEAMSLAVE) { return; } - /* make sure all team slaves can move before commiting any moves - or calling any think functions if the move is blocked, all - moved objects will be backed out */ - pushed_p = pushed; + Pushed_Init(); + obstacle = NULL; + /* make sure all team slaves can move before commiting + any moves or calling any think functions if the move + is blocked, all moved objects will be backed out */ for (part = ent; part; part = part->teamchain) { - if (part->velocity[0] || part->velocity[1] || part->velocity[2] || - part->avelocity[0] || part->avelocity[1] || part->avelocity[2]) + vec3_t move, amove; + + if (VectorCompare(part->velocity, vec3_origin) && + VectorCompare(part->avelocity, vec3_origin)) { - /* object is moving */ - VectorScale(part->velocity, FRAMETIME, move); - VectorScale(part->avelocity, FRAMETIME, amove); + continue; + } - if (!SV_Push(part, move, amove)) - { - break; /* move was blocked */ - } + VectorScale(part->velocity, FRAMETIME, move); + VectorScale(part->avelocity, FRAMETIME, amove); + + obstacle = SV_Push(part, move, amove); + if (obstacle) + { + break; } } - if (pushed_p > &pushed[MAX_EDICTS-1]) + if (part) /* move failed */ { - gi.error("pushed_p > &pushed[MAX_EDICTS-1], memory corrupted"); - } + edict_t *mv; - if (part) - { - /* the move failed, bump all nextthink times and back out moves */ for (mv = ent; mv; mv = mv->teamchain) { if (mv->nextthink > 0) @@ -783,9 +880,6 @@ SV_Physics_Pusher(edict_t *ent) } } - /* if the pusher has a "blocked" function, call it - otherwise, just stay in place until the obstacle - is gone */ if (part->blocked) { part->blocked(part, obstacle); @@ -793,10 +887,13 @@ SV_Physics_Pusher(edict_t *ent) } else { - /* the move succeeded, so call all think functions */ for (part = ent; part; part = part->teamchain) { - SV_RunThink(part); + /* prevent entities that are on trains that have gone away from thinking! */ + if (part->inuse) + { + SV_RunThink(part); + } } } } @@ -806,20 +903,28 @@ SV_Physics_Pusher(edict_t *ent) /* * Non moving objects can only think */ -void +static void SV_Physics_None(edict_t *ent) { - /* regular thinking */ + if (!ent) + { + return; + } + SV_RunThink(ent); } /* * A moving object that doesn't obey physics */ -void +static void SV_Physics_Noclip(edict_t *ent) { - /* regular thinking */ + if (!ent) + { + return; + } + if (!SV_RunThink(ent)) { return; @@ -831,32 +936,39 @@ SV_Physics_Noclip(edict_t *ent) gi.linkentity(ent); } -/* - * ============================================================================== - * - * TOSS / BOUNCE - * - * ============================================================================== - */ +/* ================================================================== */ + +/* TOSS / BOUNCE */ /* - * Toss, bounce, and fly movement. When onground, do nothing. + * Toss, bounce, and fly movement. + * When onground, do nothing. */ -void +static void SV_Physics_Toss(edict_t *ent) { trace_t trace; vec3_t move; - float backoff; edict_t *slave; qboolean wasinwater; qboolean isinwater; vec3_t old_origin; - /* regular thinking */ + if (!ent) + { + return; + } + SV_RunThink(ent); - /* if not a team captain, so movement will be handled elsewhere */ + /* entities are very often freed during thinking */ + if (!ent->inuse) + { + return; + } + + /* if not a team captain, so movement + will be handled elsewhere */ if (ent->flags & FL_TEAMSLAVE) { return; @@ -907,6 +1019,8 @@ SV_Physics_Toss(edict_t *ent) if (trace.fraction < 1) { + float backoff; + if (ent->movetype == MOVETYPE_BOUNCE) { backoff = 1.5; @@ -984,12 +1098,17 @@ SV_Physics_Toss(edict_t *ent) #define sv_friction 6 #define sv_waterfriction 1 -void +static void SV_AddRotationalFriction(edict_t *ent) { int n; float adjustment; + if (!ent) + { + return; + } + VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); adjustment = FRAMETIME * sv_stopspeed * sv_friction; @@ -1016,16 +1135,19 @@ SV_AddRotationalFriction(edict_t *ent) } } -void +static void SV_Physics_Step(edict_t *ent) { qboolean wasonground; qboolean hitsound = false; - float *vel; float speed, newspeed, control; float friction; - edict_t *groundentity; - int mask; + const edict_t *groundentity; + + if (!ent) + { + return; + } /* airborn monsters should always check for ground */ if (!ent->groundentity) @@ -1109,12 +1231,16 @@ SV_Physics_Step(edict_t *ent) if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0]) { - /* apply friction let dead monsters who + int mask; + + /* apply friction: let dead monsters who aren't completely onground slide */ if ((wasonground) || (ent->flags & (FL_SWIM | FL_FLY))) { if (!((ent->health <= 0.0) && !M_CheckBottom(ent))) { + float *vel; + vel = ent->velocity; speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]); @@ -1164,7 +1290,11 @@ SV_Physics_Step(edict_t *ent) } } - /* regular thinking */ + if (!ent->inuse) /* g_touchtrigger free problem */ + { + return; + } + SV_RunThink(ent); } @@ -1200,7 +1330,10 @@ G_RunEntity(edict_t *ent) SV_Physics_Toss(ent); break; default: - gi.error("SV_Physics: bad movetype %i", (int)ent->movetype); + gi.dprintf("%s:%d has bad movetype: %d\n", + ent->classname, ent->s.number, ent->movetype); + ent->movetype = MOVETYPE_NONE; + SV_Physics_None(ent); + break; } } -