group2 0.1.0
CSE 125 Group 2
Loading...
Searching...
No Matches
systems Namespace Reference

Client-only input sampling system — split into two halves so mouse look can run every iterate() (smooth camera at any FPS) while movement keys run once per physics tick group (server-consistent). More...

Namespaces

namespace  aimassist
namespace  gamepad

Classes

struct  GamepadAimAssistConfig
 Tunable parameters for gamepad aim assist. More...
struct  GamepadAimAssistState
 Per-frame state required to compute the rotational pull as a delta between frames. More...
class  RewindHitboxesGuard
 RAII handle that restores hitbox capsules on scope exit after a rewindHitboxes call swapped them for historical samples. More...

Functions

void runGamepadAimAssist (Registry &registry, SDL_Gamepad *gamepad, const GamepadAimAssistConfig &cfg, GamepadAimAssistState &state, float lookSens, float dt)
 Apply gamepad aim assist (slowdown + movement-tracking pull) to the local player's InputSnapshot.
void runMouseLook (Registry &registry, float mouseSensitivity, bool gravityFlipped=false)
 Sample mouse delta and accumulate into yaw / pitch.
void runMovementKeys (Registry &registry, bool gravityFlipped=false)
 Sample keyboard state into the movement flags.
void runDeadInput (Registry &registry)
 Sample skip-respawn input while the local player is dead.
void runWeaponKeys (Registry &registry)
 Sample keyboard state into the weapon flags.
void runInputSample (Registry &registry, float mouseSensitivity=0.001f)
 Legacy combined sampler — calls both runMouseLook and runMovementKeys.
void runGamepadLook (Registry &registry, SDL_Gamepad *gamepad, float lookSensitivity, float dt, bool gravityFlipped=false)
 Sample the right stick into yaw / pitch.
void runGamepadMovement (Registry &registry, SDL_Gamepad *gamepad, bool gravityFlipped=false)
 Sample gamepad buttons / left stick into the movement flags.
void runGamepadWeapon (Registry &registry, SDL_Gamepad *gamepad)
 Sample gamepad buttons / right trigger into the weapon flags.
void runInputSend (Registry &registry, Client &conn)
 Send the local player's current InputSnapshot over the network.
void runPrediction (Registry &registry, float dt, const physics::WorldGeometry &world)
 Run one tick of client-side prediction for the local player.
void runReconciliation (Registry &registry, const InputRingBuffer &ring, uint32_t ackedTick, uint32_t currentTick, float dt, const physics::WorldGeometry &world)
 Replay stored inputs from ackedTick + 1 through currentTick on the local player, restoring its predicted state after a snapshot apply rewrote it to the server-authoritative value.
static void depenetratePlanes (glm::vec3 &pos, glm::vec3 &vel, const glm::vec3 &halfExtents, std::span< const physics::Plane > planes)
 Push the entity out of any infinite planes it currently overlaps.
static void depenetrateBox (glm::vec3 &pos, glm::vec3 &vel, const glm::vec3 &halfExtents, const physics::WorldAABB &box)
 Push the entity out of a static AABB it currently overlaps.
static void depenetrateBrush (glm::vec3 &pos, glm::vec3 &vel, const glm::vec3 &halfExtents, const physics::WorldBrush &brush)
 Push the entity out of a convex brush it currently overlaps.
static void depenetrateCylinder (glm::vec3 &pos, glm::vec3 &vel, const glm::vec3 &halfExtents, const physics::WorldCylinder &cyl)
 Push the entity out of a vertical cylinder it currently overlaps.
static void depenetrateSphere (glm::vec3 &pos, glm::vec3 &vel, const glm::vec3 &halfExtents, const physics::WorldSphere &sph)
 Push the entity out of a world sphere it currently overlaps.
static void depenetrateTriMesh (glm::vec3 &pos, glm::vec3 &vel, const glm::vec3 &halfExtents, const physics::WorldTriMesh &mesh)
 Push the entity out of a triangle mesh it currently overlaps.
static void depenetrate (glm::vec3 &pos, glm::vec3 &vel, const glm::vec3 &halfExtents, const physics::WorldGeometry &world)
 Run all depenetration passes (planes, boxes, brushes, cylinders, spheres).
static bool tryStepUp (glm::vec3 &pos, glm::vec3 &vel, const glm::vec3 &halfExtents, float remainingTime, const physics::WorldGeometry &world)
 Attempt to step over a low obstacle when a wall is hit.
static void snapToGround (glm::vec3 &pos, glm::vec3 &vel, const glm::vec3 &halfExtents, const physics::WorldGeometry &world)
 Keep the entity glued to descending slopes and step-downs.
void runCollision (Registry &registry, float dt, const physics::WorldGeometry &world)
 Run one tick of swept-AABB collision for all physics entities.
void queueExplosion (Registry &registry, glm::vec3 position, float radius, float maxDamage, entt::entity owner)
 Create an explosion entity at the given position.
void runExplosion (Registry &registry, std::vector< NetParticleEvent > &outParticles, std::vector< NetKillEvent > &killEvents)
 Process all pending explosions: apply radial damage and emit particle events.
void updateHitboxes (Registry &registry, const HitboxRig &hitboxRig, float rigScale, float rigMeshMinY)
 Update world-space hitbox capsules for all entities that have JointMatrices.
RewindHitboxesGuard rewindHitboxes (Registry &registry, entt::entity shooter, const glm::vec3 *rayOrigin=nullptr, const glm::vec3 *rayDirection=nullptr, float rayMaxDistance=0.0f)
 Rewind every other player's hitbox capsules to where they were on shooter's screen at fire time.
bool handleWinCondition (Registry &registry, int killsToWin)
 Checks if any player has met win condition and updates their PlayerMatchStats accordingly.
void resetStats (Registry &registry)
 Resets all players' match scores to initial values.
float currentWishSpeed (const PlayerVisState &vis)
 Determine the current ground wish speed based on movement mode and stance.
void runMovement (Registry &registry, float dt, const physics::WorldGeometry &world)
 Apply one tick of player movement physics to all eligible entities.
void applyHeal (float amount, Health &playerHealth)
glm::vec3 chooseRespawnPoint (Registry &registry)
 Choose a respawn point with cooldown-aware selection.
void handleRespawn (entt::entity &player, Registry &registry)
 Reset a dead player to a fresh spawn state.
void handleDeath (entt::entity &player, Health &playerHealth, entt::entity &killer, Registry &registry, std::vector< NetKillEvent > &killEvents, BodyRegion hitRegion)
 Transition a player to the dead state if health has reached zero.
void applyDamage (float damage, entt::entity player, entt::entity &killer, Registry &registry, std::vector< NetKillEvent > &killEvents, BodyRegion hitRegion=BodyRegion::UpperTorso)
 Apply damage to a player, splitting across armor then health.
void handleHealing (Health &playerHealth, float dt)
 Tick passive health regeneration after the heal cooldown expires.
void runPlayerStatus (Registry &registry, float dt)
 Run one tick of player status: respawn timers and passive healing.
void runSpawnPointCooldowns (Registry &registry, float dt)
 Tick spawn point cooldowns.
void update (Registry &registry, float dt)
 Placeholder system — replace with real logic.
bool overlapsAABB (glm::vec3 aPos, glm::vec3 aHalf, glm::vec3 bPos, glm::vec3 bHalf)
 Test whether two axis-aligned bounding boxes overlap.
glm::vec3 viewForward (float yaw, float pitch)
 Compute the player's 3D view direction from yaw and pitch angles.
void checkForPlayers (Registry &registry, Position spawnerPos, CollisionShape spawnerShape, WeaponSpawner &spawner)
 Check if any player overlaps the spawner and transfer the weapon on pickup.
void runWeaponSpawners (Registry &registry, float dt)
 Tick weapon spawners: check player overlap for pickup, manage cooldowns.
GunInstancegetEquippedGun (WeaponState &weapon)
 Return the GunInstance for the currently selected weapon slot.
void handleSwitch (const InputSnapshot &input, WeaponState &weapon)
 Apply weapon slot switch from player input.
void handleCooldown (WeaponState &weapon, float dt)
 Tick fire cooldowns for all weapon slots.
void handleReload (GunInstance &gun)
 Reload the gun's magazine from reserve ammo.
bool handleAmmo (GunInstance &gun)
 Consume one round from the magazine; auto-reload if empty.
glm::vec3 muzzleOrigin (glm::vec3 eye, glm::vec3 direction, bool gravityFlipped=false)
 Offset the muzzle origin from the eye position for tracer visuals.
void logShot (Registry &registry, entt::entity shooter, std::uint32_t shotInputTick, const glm::vec3 &origin, const glm::vec3 &direction, const physics::HitboxHit &hit)
void captureShotDebug (Registry &registry, entt::entity shooter, std::uint32_t shotInputTick, const glm::vec3 &origin, const glm::vec3 &direction, float range, const physics::HitboxHit &hit, std::vector< net::shotdebug::ShotDebugCapture > *out)
void handleFire (Registry &registry, entt::entity shooter, const InputSnapshot &input, const Position &pos, const CollisionShape &shape, WeaponState &weapon, bool gravityFlipped, float dt, std::vector< NetParticleEvent > &outParticles, std::vector< NetKillEvent > &killEvents, std::vector< net::shotdebug::ShotDebugCapture > *outShotDebug)
 Process fire input: hitscan raycasts, beam weapons, charge shots, and projectiles.
void runWeapon (Registry &registry, float dt, std::vector< NetParticleEvent > &outParticles, std::vector< NetKillEvent > &killEvents, std::vector< net::shotdebug::ShotDebugCapture > *outShotDebug=nullptr)
 Run one tick of weapon logic for all armed entities.
void pushHitboxHistory (Registry &registry, uint32_t serverTick)
 Capture this tick's hitbox capsules into each entity's HitboxHistory ring.
Event runInputReceive (const void *data)
 Deserialise a raw InputSnapshot packet into a gameplay Event.

Variables

bool prevKillSelfKey = false
 Tracks previous-frame key state for edge detection.
bool prevFlipGravityKey = false
 Tracks previous-frame G key state for gravity flip edge detection.
static constexpr float k_pushback = 0.03125f
static constexpr float k_groundProbeDistance = physics::k_stepHeight
constexpr float k_spawnPointCooldown = 5.0f
 Cooldown duration set on a spawn point after a player spawns there.
const float armorMax = 100.0f
 Maximum armor value.
const float healthMax = 100.0f
 Maximum health value.
const float healCooldown = 5.0f
 Seconds after last damage before passive healing starts.
const float healingRate = 20.0f
 Passive healing amount per second.
constexpr float weaponCooldownTime = 10.0f

Detailed Description

Client-only input sampling system — split into two halves so mouse look can run every iterate() (smooth camera at any FPS) while movement keys run once per physics tick group (server-consistent).

Input receive system functions.

Weapon state update system.

Weapon spawner update system.

ECS systems namespace.

Player status update system.

Shared movement system — compiled identically on client and server.

Shared collision system — compiled identically on client and server.

Client-only system that serialises and sends input to the server.

Any divergence between client and server builds is a bug (breaks prediction).

Any divergence between client and server builds is a bug (breaks prediction).

Implements the full Titanfall-inspired movement state machine: OnFoot → Sliding → WallRunning → Climbing → LedgeGrabbing with sprint, double jump, coyote time, jump lurch, air strafing, and speed cap.

Note
Position integration is NOT done here — CollisionSystem owns that via swept AABB.

Each free function (or callable) represents one system. A system receives the shared Registry and any per-frame state it needs, then queries and mutates components.

Concrete system implementations live in individual .hpp/.cpp pairs under this directory.

Function Documentation

◆ applyDamage()

void systems::applyDamage ( float damage,
entt::entity player,
entt::entity & killer,
Registry & registry,
std::vector< NetKillEvent > & killEvents,
BodyRegion hitRegion = BodyRegion::UpperTorso )

Apply damage to a player, splitting across armor then health.

Resets the heal cooldown timer. If health reaches zero, triggers death handling (respawn timer, kill event, stats update).

Parameters
damageDamage amount being applied.
playerPlayer entity who took damage.
killerEntity who dealt the final blow.
registryThe ECS registry.
killEventsAccumulates kill events for network broadcast.
hitRegionBody region that was hit (for kill feed / headshot tracking).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ applyHeal()

void systems::applyHeal ( float amount,
Health & playerHealth )

Apply a healing amount, filling health first then armor.

Apply a healing amount, filling health first then armor.

Parameters
amountHealing amount being applied.
playerHealthEntity's health component (modified in place).
amountHealing amount being applied.
playerHealthEntity's health component (modified in place).
Here is the caller graph for this function:

◆ captureShotDebug()

void systems::captureShotDebug ( Registry & registry,
entt::entity shooter,
std::uint32_t shotInputTick,
const glm::vec3 & origin,
const glm::vec3 & direction,
float range,
const physics::HitboxHit & hit,
std::vector< net::shotdebug::ShotDebugCapture > * out )
inline
Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkForPlayers()

void systems::checkForPlayers ( Registry & registry,
Position spawnerPos,
CollisionShape spawnerShape,
WeaponSpawner & spawner )
inline

Check if any player overlaps the spawner and transfer the weapon on pickup.

Parameters
registryThe ECS registry.
spawnerPosPosition of the spawner entity.
spawnerShapeCollision shape of the spawner.
spawnerSpawner component (weapon type, availability, cooldown).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ chooseRespawnPoint()

glm::vec3 systems::chooseRespawnPoint ( Registry & registry)
inline

Choose a respawn point with cooldown-aware selection.

Prefers available (cooldown = 0) spawn points, picking randomly among them. If all spawn points are on cooldown, picks the one with the lowest remaining cooldown. Sets a 3-second cooldown on the chosen point.

Here is the caller graph for this function:

◆ currentWishSpeed()

float systems::currentWishSpeed ( const PlayerVisState & vis)

Determine the current ground wish speed based on movement mode and stance.

Returns the speed the player is accelerating toward on the ground this tick: tms::k_crouchSpeed / k_sprintSpeed / k_walkSpeed, or 0 during a slide. For air movement use physics::k_airMaxSpeed directly.

Parameters
visReplicated player vis state (for moveMode + crouching + sprinting).
Returns
Target ground wish speed (u/s).
Here is the caller graph for this function:

◆ depenetrate()

void systems::depenetrate ( glm::vec3 & pos,
glm::vec3 & vel,
const glm::vec3 & halfExtents,
const physics::WorldGeometry & world )
static

Run all depenetration passes (planes, boxes, brushes, cylinders, spheres).

Parameters
posEntity position (modified in place).
velEntity velocity (modified in place).
halfExtentsAABB half-extents of the entity.
worldWorld collision geometry.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ depenetrateBox()

void systems::depenetrateBox ( glm::vec3 & pos,
glm::vec3 & vel,
const glm::vec3 & halfExtents,
const physics::WorldAABB & box )
static

Push the entity out of a static AABB it currently overlaps.

Parameters
posEntity position (modified in place).
velEntity velocity (modified in place).
halfExtentsAABB half-extents of the entity.
boxStatic axis-aligned bounding box to test against.
Here is the caller graph for this function:

◆ depenetrateBrush()

void systems::depenetrateBrush ( glm::vec3 & pos,
glm::vec3 & vel,
const glm::vec3 & halfExtents,
const physics::WorldBrush & brush )
static

Push the entity out of a convex brush it currently overlaps.

Only fires if the entity is inside ALL planes simultaneously.

Parameters
posEntity position (modified in place).
velEntity velocity (modified in place).
halfExtentsAABB half-extents of the entity.
brushConvex brush to test against.
Here is the caller graph for this function:

◆ depenetrateCylinder()

void systems::depenetrateCylinder ( glm::vec3 & pos,
glm::vec3 & vel,
const glm::vec3 & halfExtents,
const physics::WorldCylinder & cyl )
static

Push the entity out of a vertical cylinder it currently overlaps.

Here is the caller graph for this function:

◆ depenetratePlanes()

void systems::depenetratePlanes ( glm::vec3 & pos,
glm::vec3 & vel,
const glm::vec3 & halfExtents,
std::span< const physics::Plane > planes )
static

Push the entity out of any infinite planes it currently overlaps.

Parameters
posEntity position (modified in place).
velEntity velocity (modified in place).
halfExtentsAABB half-extents of the entity.
planesInfinite planes to test against.
Here is the caller graph for this function:

◆ depenetrateSphere()

void systems::depenetrateSphere ( glm::vec3 & pos,
glm::vec3 & vel,
const glm::vec3 & halfExtents,
const physics::WorldSphere & sph )
static

Push the entity out of a world sphere it currently overlaps.

Here is the caller graph for this function:

◆ depenetrateTriMesh()

void systems::depenetrateTriMesh ( glm::vec3 & pos,
glm::vec3 & vel,
const glm::vec3 & halfExtents,
const physics::WorldTriMesh & mesh )
static

Push the entity out of a triangle mesh it currently overlaps.

Delegates to physics::depenetrateAABBvsTriMesh, which uses per-triangle SAT MTV — accurate enough that curved surfaces (cylinders, spheres) feel curved instead of cubical. Trade-off: at sharp triangle edges where adjacent normals fight, per-triangle pushes can briefly disagree. In practice the pushback bias keeps the entity off the surface and the swept collision (which still does precise per-triangle hits) is the primary path for normal motion; this depenetration is only a safety net.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getEquippedGun()

GunInstance & systems::getEquippedGun ( WeaponState & weapon)
inline

Return the GunInstance for the currently selected weapon slot.

Parameters
weaponWeapon state to query.
Returns
Reference to the equipped gun.
Here is the caller graph for this function:

◆ handleAmmo()

bool systems::handleAmmo ( GunInstance & gun)
inline

Consume one round from the magazine; auto-reload if empty.

Parameters
gunGun instance to consume ammo from (modified in place).
Returns
True if a round was consumed, false if the gun is empty.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleCooldown()

void systems::handleCooldown ( WeaponState & weapon,
float dt )
inline

Tick fire cooldowns for all weapon slots.

Parameters
weaponWeapon state (modified in place).
dtFixed physics delta time in seconds.
Here is the caller graph for this function:

◆ handleDeath()

void systems::handleDeath ( entt::entity & player,
Health & playerHealth,
entt::entity & killer,
Registry & registry,
std::vector< NetKillEvent > & killEvents,
BodyRegion hitRegion )
inline

Transition a player to the dead state if health has reached zero.

Hides the player, removes hitboxes, starts a 5-second respawn timer, updates death/kill stats, and emits a NetKillEvent for the kill feed.

Parameters
playerEntity that died.
playerHealthHealth component (already at or below zero).
killerEntity that dealt the killing blow.
registryThe ECS registry.
killEventsAccumulates kill events for network broadcast.
hitRegionBody region of the killing blow.
Here is the caller graph for this function:

◆ handleFire()

void systems::handleFire ( Registry & registry,
entt::entity shooter,
const InputSnapshot & input,
const Position & pos,
const CollisionShape & shape,
WeaponState & weapon,
bool gravityFlipped,
float dt,
std::vector< NetParticleEvent > & outParticles,
std::vector< NetKillEvent > & killEvents,
std::vector< net::shotdebug::ShotDebugCapture > * outShotDebug )
inline

Process fire input: hitscan raycasts, beam weapons, charge shots, and projectiles.

Handles three weapon archetypes:

  • Beam — continuous DPS drain while held, capsule raycast each tick.
  • Charge — accumulates chargeTime while held, fires on release.
  • Discrete — standard per-click hitscan or projectile spawn.

Emits NetParticleEvent entries for tracer/impact effects and applies damage through applyDamage() which may trigger kill events.

Parameters
registryThe ECS registry.
shooterEntity that is firing.
inputCurrent input snapshot.
posShooter position.
shapeShooter collision shape (for eye height).
weaponShooter weapon state (modified in place).
gravityFlippedTrue when the shooter's gravity is inverted.
dtFixed physics delta time in seconds.
outParticlesAccumulates particle events for network broadcast.
killEventsAccumulates kill events for network broadcast.
outShotDebugPR-20: optional server-side debug capture sink. When non-null, each hitscan-fire path pushes one ShotDebugCapture row while RewindHitboxesGuard is still active, so the captured targets[*]. capsules reflect the historical sample the server actually raycast against.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleHealing()

void systems::handleHealing ( Health & playerHealth,
float dt )
inline

Tick passive health regeneration after the heal cooldown expires.

Parameters
playerHealthHealth component (modified in place).
dtFixed physics delta time in seconds.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleReload()

void systems::handleReload ( GunInstance & gun)
inline

Reload the gun's magazine from reserve ammo.

Parameters
gunGun instance to reload (modified in place).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleRespawn()

void systems::handleRespawn ( entt::entity & player,
Registry & registry )
inline

Reset a dead player to a fresh spawn state.

Clears the respawn timer and death info, restores visibility, resets position/velocity/health/weapons to defaults, and places the player at the spawn point.

Parameters
playerEntity to respawn (modified in place).
registryThe ECS registry.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleSwitch()

void systems::handleSwitch ( const InputSnapshot & input,
WeaponState & weapon )

Apply weapon slot switch from player input.

Parameters
inputCurrent input snapshot.
weaponWeapon state (modified in place).
Here is the caller graph for this function:

◆ handleWinCondition()

bool systems::handleWinCondition ( Registry & registry,
int killsToWin )

Checks if any player has met win condition and updates their PlayerMatchStats accordingly.

Here is the caller graph for this function:

◆ logShot()

void systems::logShot ( Registry & registry,
entt::entity shooter,
std::uint32_t shotInputTick,
const glm::vec3 & origin,
const glm::vec3 & direction,
const physics::HitboxHit & hit )
inline
Here is the call graph for this function:
Here is the caller graph for this function:

◆ muzzleOrigin()

glm::vec3 systems::muzzleOrigin ( glm::vec3 eye,
glm::vec3 direction,
bool gravityFlipped = false )
inline

Offset the muzzle origin from the eye position for tracer visuals.

Shifts the origin right and down from eye, slightly forward, so tracers don't originate from the center of the screen.

Parameters
eyeEye position (camera origin).
directionNormalized view direction.
gravityFlippedWhen true, negate the horizontal offset so the tracer originates from the viewmodel side. The viewmodel stays on screen-right, but the 180° camera roll reverses world-left/right — so world-right (the normal offset) appears on the wrong side of the screen.
Returns
Offset muzzle position in world space.
Here is the caller graph for this function:

◆ overlapsAABB()

bool systems::overlapsAABB ( glm::vec3 aPos,
glm::vec3 aHalf,
glm::vec3 bPos,
glm::vec3 bHalf )
inline

Test whether two axis-aligned bounding boxes overlap.

Parameters
aPosCenter of box A.
aHalfHalf-extents of box A.
bPosCenter of box B.
bHalfHalf-extents of box B.
Returns
True if the boxes overlap on all three axes.
Here is the caller graph for this function:

◆ pushHitboxHistory()

void systems::pushHitboxHistory ( Registry & registry,
uint32_t serverTick )

Capture this tick's hitbox capsules into each entity's HitboxHistory ring.

For every entity that has a HitboxInstance, copies its current capsules vector into the next slot of the ring and records the owning server tick. Entities without a HitboxInstance (e.g. dead players whose capsules were dropped by updateHitboxes) are skipped — their existing samples stay in the ring, so a target that died ~200 ms ago can still be hit by a delayed shot once rewind lands.

Parameters
registryThe ECS registry.
serverTickCurrent server tick (monotonic, 128 Hz). Stored alongside the capsules so the rewind guard can pick the closest sample to the attacker's tick.
Here is the caller graph for this function:

◆ queueExplosion()

void systems::queueExplosion ( Registry & registry,
glm::vec3 position,
float radius,
float maxDamage,
entt::entity owner )

Create an explosion entity at the given position.

The explosion is processed on the next call to runExplosion().

Parameters
registryThe ECS registry.
positionWorld-space center of the explosion.
radiusBlast radius (damage falls off linearly to zero at edge).
maxDamageMaximum damage at the epicenter.
ownerEntity that caused the explosion (for self-damage / kill credit).
Here is the caller graph for this function:

◆ resetStats()

void systems::resetStats ( Registry & registry)

Resets all players' match scores to initial values.

Here is the caller graph for this function:

◆ rewindHitboxes()

RewindHitboxesGuard systems::rewindHitboxes ( Registry & registry,
entt::entity shooter,
const glm::vec3 * rayOrigin = nullptr,
const glm::vec3 * rayDirection = nullptr,
float rayMaxDistance = 0.0f )
inline

Rewind every other player's hitbox capsules to where they were on shooter's screen at fire time.

Reads LagCompTarget from shooter. If absent or zero, returns a no-op guard immediately (the common case for client-side and for sub-tick-RTT shooters). Otherwise walks every entity with both HitboxInstance and HitboxHistory, finds the most recent history sample whose tick is ≤ targetServerTick, swaps the live capsules for the historical ones, and stashes the originals in the returned guard for restore-on-destruction.

PR-5 (server-perf): two overloads.

  • The unfiltered form (kept for compatibility) rewinds every player. O(N) per shot. Pre-PR-5 measurements at 200 bots during fire bursts: this dominated the weapon scope's 6.29 ms p99 — 25 shots × 200 candidate rewinds × ~1–5 µs each ≈ 5–25 ms / sec.
  • The ray-filtered form rewindHitboxes(registry, shooter, origin, direction, maxDistance) adds a broad-phase ray-vs- AABB test BEFORE rewinding each candidate. Players whose bounding box doesn't intersect the shot ray are skipped — no ring scan, no capsule swap. The AABB test costs ~10 ns and prunes >95 % of candidates for typical shot geometry.

shooter itself is not rewound — resolveHitscanHitbox already excludes the shooter from the player-hitbox raycast.

Parameters
registryThe server ECS registry.
shooterEntity firing the hitscan. Read for LagCompTarget.
Returns
Guard whose destructor restores the original capsules.
Here is the caller graph for this function:

◆ runCollision()

void systems::runCollision ( Registry & registry,
float dt,
const physics::WorldGeometry & world )

Run one tick of swept-AABB collision for all physics entities.

For every entity with [Position, Velocity, CollisionShape, PlayerVisState]:

  1. Clears the grounded flag.
  2. Sweeps the AABB from current position toward pos + vel * dt.
  3. On hit: moves to the contact point, clips velocity, repeats up to 4 times (Quake-style bumping handles corners and multi-surface contacts).
  4. Sets grounded = true if a floor surface (normal.y > 0.7) is hit.
Note
Position integration lives here, not in MovementSystem.
Parameters
registryThe ECS registry.
dtFixed physics delta time in seconds.
worldWorld collision geometry (planes, boxes, brushes) for this tick.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runDeadInput()

void systems::runDeadInput ( Registry & registry)
inline

Sample skip-respawn input while the local player is dead.

Runs independently of Controllable — dead players can press Space to skip the remaining respawn timer and respawn immediately.

Parameters
registryThe ECS registry.
Here is the caller graph for this function:

◆ runExplosion()

void systems::runExplosion ( Registry & registry,
std::vector< NetParticleEvent > & outParticles,
std::vector< NetKillEvent > & killEvents )

Process all pending explosions: apply radial damage and emit particle events.

For each Explosion component, damages every player within the blast radius (linear falloff), emits a ParticleEffectType::Explosion event, and destroys the explosion entity.

Parameters
registryThe ECS registry.
outParticlesAccumulates particle events for network broadcast.
killEventsAccumulates kill events for network broadcast.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runGamepadAimAssist()

void systems::runGamepadAimAssist ( Registry & registry,
SDL_Gamepad * gamepad,
const GamepadAimAssistConfig & cfg,
GamepadAimAssistState & state,
float lookSens,
float dt )
inline

Apply gamepad aim assist (slowdown + movement-tracking pull) to the local player's InputSnapshot.

Must run AFTER runGamepadLook so we can refund part of the player input it just integrated.

Parameters
registryECS registry.
gamepadOpen gamepad handle (nullptr → no-op).
cfgTuning parameters.
statePersistent per-frame state (anchor + previous-frame snapshot).
lookSensSame value passed to runGamepadLook (rad/s @ full deflection).
dtFrame delta time (seconds).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runGamepadLook()

void systems::runGamepadLook ( Registry & registry,
SDL_Gamepad * gamepad,
float lookSensitivity,
float dt,
bool gravityFlipped = false )
inline

Sample the right stick into yaw / pitch.

Must be called every iterate() call — same cadence as runMouseLook — so camera rotation is smooth at any frame rate. Unlike mouse delta (which is integrated over the time between calls), the stick gives an instantaneous angular velocity, so we multiply by frame delta time.

Adds to existing yaw/pitch (so simultaneous mouse + stick compose).

Parameters
registryThe ECS registry.
gamepadOpen gamepad, or nullptr to no-op.
lookSensitivityRadians per second at full stick deflection.
dtFrame delta time in seconds.
gravityFlippedWhen true, both axes are inverted for 180° camera roll.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runGamepadMovement()

void systems::runGamepadMovement ( Registry & registry,
SDL_Gamepad * gamepad,
bool gravityFlipped = false )
inline

Sample gamepad buttons / left stick into the movement flags.

ORs with whatever the keyboard sampler set so kbm + pad coexist. Should run on the same cadence as runMovementKeys (once per physics tick group when synced, otherwise every iterate).

Mapping: Left stick → forward / back / strafe A (south) → jump B (east) → crouch L3 (LS click) → sprint LT → grapple (analog, threshold @ 0.5)

Parameters
registryThe ECS registry.
gamepadOpen gamepad, or nullptr to no-op.
gravityFlippedWhen true, left/right stick are swapped to match the 180° camera roll.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runGamepadWeapon()

void systems::runGamepadWeapon ( Registry & registry,
SDL_Gamepad * gamepad )
inline

Sample gamepad buttons / right trigger into the weapon flags.

ORs with whatever the keyboard sampler set so kbm + pad coexist.

Mapping: RT → shoot (analog, threshold @ 0.5) X (west) → reload Y (north) → pickup LB → switchToPrimary D-pad up → switchToPrimary (alt) RB → switchToSecondary D-pad down → switchToSecondary (alt)

Parameters
registryThe ECS registry.
gamepadOpen gamepad, or nullptr to no-op.
Here is the caller graph for this function:

◆ runInputReceive()

Event systems::runInputReceive ( const void * data)
inline

Deserialise a raw InputSnapshot packet into a gameplay Event.

Parameters
dataPointer to the raw InputSnapshot bytes.
Returns
An Event populated with the decoded movement and action intents.

◆ runInputSample()

void systems::runInputSample ( Registry & registry,
float mouseSensitivity = 0.001f )
inline

Legacy combined sampler — calls both runMouseLook and runMovementKeys.

Parameters
registryThe ECS registry.
mouseSensitivityRadians per pixel (default 0.001).
Here is the call graph for this function:

◆ runInputSend()

void systems::runInputSend ( Registry & registry,
Client & conn )
inline

Send the local player's current InputSnapshot over the network.

Parameters
registryThe ECS registry.
connNetwork connection to the server.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runMouseLook()

void systems::runMouseLook ( Registry & registry,
float mouseSensitivity,
bool gravityFlipped = false )
inline

Sample mouse delta and accumulate into yaw / pitch.

Must be called every iterate() call regardless of whether a physics tick fires — this keeps camera rotation smooth at the render frame rate. SDL_GetRelativeMouseState returns the accumulated delta since the previous call, so the total rotation over any time window is identical regardless of call frequency.

Parameters
registryThe ECS registry.
mouseSensitivityRadians per pixel of mouse movement.
gravityFlippedWhen true, both mouse axes are inverted so controls feel natural while the camera is rolled 180° for upside-down gravity.
Here is the caller graph for this function:

◆ runMovement()

void systems::runMovement ( Registry & registry,
float dt,
const physics::WorldGeometry & world )

Apply one tick of player movement physics to all eligible entities.

Parameters
registryThe ECS registry.
dtFixed physics delta time in seconds.
worldWorld collision geometry (needed for wall/climb/ledge detection).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runMovementKeys()

void systems::runMovementKeys ( Registry & registry,
bool gravityFlipped = false )
inline

Sample keyboard state into the movement flags.

Should be called once per physics tick group when input is synced with physics (the default) so movement calculations match the server. Can also be called every iterate() when the sync toggle is off.

Parameters
registryThe ECS registry.
gravityFlippedWhen true, A/D are swapped so strafing feels correct while the camera is rolled 180°.
Here is the caller graph for this function:

◆ runPlayerStatus()

void systems::runPlayerStatus ( Registry & registry,
float dt )

Run one tick of player status: respawn timers and passive healing.

Parameters
registryThe ECS registry.
dtFixed physics delta time in seconds.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runPrediction()

void systems::runPrediction ( Registry & registry,
float dt,
const physics::WorldGeometry & world )
inline

Run one tick of client-side prediction for the local player.

Just calls the shared runMovement and runCollision against the local registry. The View filter (PlayerSimState) ensures only the local player is processed on the client. Server's runMovement still processes every player in the same single call.

Parameters
registryThe client ECS registry.
dtFixed physics delta (must match server's dt for prediction parity — typically 1/128 s).
worldActive world geometry. Must match the server's world exactly or prediction diverges.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runReconciliation()

void systems::runReconciliation ( Registry & registry,
const InputRingBuffer & ring,
uint32_t ackedTick,
uint32_t currentTick,
float dt,
const physics::WorldGeometry & world )
inline

Replay stored inputs from ackedTick + 1 through currentTick on the local player, restoring its predicted state after a snapshot apply rewrote it to the server-authoritative value.

Parameters
registryThe client ECS registry. Local player's Position etc. should be the server's just-applied value.
ringInput history ring; entries from earlier than ackedTick may have been overwritten and are not used.
ackedTickThe client tick the server has acknowledged processing through. The local player's current Position represents state as-of this tick.
currentTickThe client's current clientPredictTick. Replay runs runMovement+runCollision once per tick from ackedTick + 1 through currentTick (inclusive).
dtPhysics delta (match server: 1/128 s).
worldWorld geometry (must match server).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runSpawnPointCooldowns()

void systems::runSpawnPointCooldowns ( Registry & registry,
float dt )

Tick spawn point cooldowns.

Each spawn point's cooldown decrements by dt and becomes available again when it reaches zero.

Parameters
registryThe ECS registry.
dtFixed physics delta time in seconds.
Here is the caller graph for this function:

◆ runWeapon()

void systems::runWeapon ( Registry & registry,
float dt,
std::vector< NetParticleEvent > & outParticles,
std::vector< NetKillEvent > & killEvents,
std::vector< net::shotdebug::ShotDebugCapture > * outShotDebug = nullptr )

Run one tick of weapon logic for all armed entities.

For each entity with [InputSnapshot, Position, CollisionShape, WeaponState]: handles weapon switching, cooldown ticking, fire/beam/charge processing, hitscan raycasting, projectile spawning, damage application, and ammo refill.

Parameters
registryThe ECS registry.
dtFixed physics delta time in seconds.
outParticlesAccumulates particle events for network broadcast.
killEventsAccumulates kill events for network broadcast.
outShotDebugPR-20 (server-side only): if non-null, every hitscan shot pushes a ShotDebugCapture row while the rewind guard is still active so the capsule data reflects the historical sample the server actually raycast against. ServerGame serialises and sends each entry to the shooter via Server::enqueueTo. Client TUs leave this null — clients run WeaponSystem for prediction only and don't generate debug reports.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ runWeaponKeys()

void systems::runWeaponKeys ( Registry & registry)
inline

Sample keyboard state into the weapon flags.

Should be called once per physics tick group when input is synced with physics (the default) so movement calculations match the server. Can also be called every iterate() when the sync toggle is off.

Parameters
registryThe ECS registry.
Here is the caller graph for this function:

◆ runWeaponSpawners()

void systems::runWeaponSpawners ( Registry & registry,
float dt )

Tick weapon spawners: check player overlap for pickup, manage cooldowns.

Parameters
registryThe ECS registry.
dtFixed physics delta time in seconds.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ snapToGround()

void systems::snapToGround ( glm::vec3 & pos,
glm::vec3 & vel,
const glm::vec3 & halfExtents,
const physics::WorldGeometry & world )
static

Keep the entity glued to descending slopes and step-downs.

Parameters
posEntity position (modified in place).
velEntity velocity (modified in place).
halfExtentsAABB half-extents of the entity.
worldWorld collision geometry.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ tryStepUp()

bool systems::tryStepUp ( glm::vec3 & pos,
glm::vec3 & vel,
const glm::vec3 & halfExtents,
float remainingTime,
const physics::WorldGeometry & world )
static

Attempt to step over a low obstacle when a wall is hit.

Parameters
posEntity position (modified in place on success).
velEntity velocity (modified in place on success).
halfExtentsAABB half-extents of the entity.
remainingTimeTime remaining in the current bump iteration.
worldWorld collision geometry.
Returns
True if the step succeeded and position/velocity were updated.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ update()

void systems::update ( Registry & registry,
float dt )
inline

Placeholder system — replace with real logic.

Parameters
registryThe ECS registry.
dtFixed physics delta time in seconds.

◆ updateHitboxes()

void systems::updateHitboxes ( Registry & registry,
const HitboxRig & hitboxRig,
float rigScale,
float rigMeshMinY )

Update world-space hitbox capsules for all entities that have JointMatrices.

For each entity with [Position, JointMatrices] this builds the world transform (entity position + yaw rotation + rig scale + vertical offset) and transforms every HitboxDef capsule into a WorldCapsule stored in HitboxInstance.

Parameters
registryThe ECS registry.
hitboxRigShared hitbox definitions (bone-to-capsule mapping).
rigScaleScale factor applied to the rig (model space -> game units).
rigMeshMinYMinimum Y vertex of the bind-pose mesh (model space).
Here is the call graph for this function:
Here is the caller graph for this function:

◆ viewForward()

glm::vec3 systems::viewForward ( float yaw,
float pitch )
inline

Compute the player's 3D view direction from yaw and pitch angles.

Parameters
yawHorizontal angle (radians).
pitchVertical angle (radians, positive = down).
Returns
Normalized forward direction vector.
Here is the caller graph for this function:

Variable Documentation

◆ armorMax

const float systems::armorMax = 100.0f

Maximum armor value.

◆ healCooldown

const float systems::healCooldown = 5.0f

Seconds after last damage before passive healing starts.

◆ healingRate

const float systems::healingRate = 20.0f

Passive healing amount per second.

◆ healthMax

const float systems::healthMax = 100.0f

Maximum health value.

◆ k_groundProbeDistance

float systems::k_groundProbeDistance = physics::k_stepHeight
staticconstexpr

◆ k_pushback

float systems::k_pushback = 0.03125f
staticconstexpr

◆ k_spawnPointCooldown

float systems::k_spawnPointCooldown = 5.0f
inlineconstexpr

Cooldown duration set on a spawn point after a player spawns there.

◆ prevFlipGravityKey

bool systems::prevFlipGravityKey = false
inline

Tracks previous-frame G key state for gravity flip edge detection.

◆ prevKillSelfKey

bool systems::prevKillSelfKey = false
inline

Tracks previous-frame key state for edge detection.

◆ weaponCooldownTime

float systems::weaponCooldownTime = 10.0f
constexpr