group2 0.1.0
CSE 125 Group 2
Loading...
Searching...
No Matches
LagCompensation.hpp
Go to the documentation of this file.
1
36
37#pragma once
38
47
48#include <cstdint>
49#include <glm/common.hpp> // glm::abs
50#include <glm/vec3.hpp>
51#include <utility>
52#include <vector>
53
54namespace systems
55{
56
67{
68public:
70
71 ~RewindHitboxesGuard() noexcept { restore(); }
72
75
77 : registry_(other.registry_), saved_(std::move(other.saved_))
78 {
79 other.registry_ = nullptr;
80 }
81
83 {
84 if (this != &other) {
85 restore();
86 registry_ = other.registry_;
87 saved_ = std::move(other.saved_);
88 other.registry_ = nullptr;
89 }
90 return *this;
91 }
92
93private:
97 void restore() noexcept
98 {
99 if (registry_ == nullptr)
100 return;
101 for (auto& [entity, capsules] : saved_) {
102 if (!registry_->valid(entity))
103 continue;
104 if (auto* inst = registry_->try_get<HitboxInstance>(entity))
105 inst->capsules = std::move(capsules);
106 }
107 saved_.clear();
108 registry_ = nullptr;
109 }
110
111 Registry* registry_ = nullptr;
112 std::vector<std::pair<entt::entity, std::vector<WorldCapsule>>> saved_;
113
115 rewindHitboxes(Registry&, entt::entity, const glm::vec3*, const glm::vec3*, float, float);
116};
117
149 entt::entity shooter,
150 const glm::vec3* rayOrigin = nullptr,
151 const glm::vec3* rayDirection = nullptr,
152 float rayMaxDistance = 0.0f,
153 float bulletRadius = 0.0f)
154{
156
157 const auto* target = registry.try_get<LagCompTarget>(shooter);
158 if (target == nullptr || target->targetServerTick == 0)
159 return guard; // no-op: no rewind requested for this shooter
160
161 const uint32_t targetTick = target->targetServerTick;
162 const bool haveFilter = rayOrigin != nullptr && rayDirection != nullptr && rayMaxDistance > 0.0f;
163
164 // PR-24 (broad-phase fix): how far back in time we're rewinding,
165 // in seconds. The pre-PR-24 broad-phase filter tested ray vs the
166 // entity's LIVE AABB (live position ± live halfExtents). At
167 // sprint speed (~700 u/s) and 100 ms RTT the entity has moved
168 // ~70 u between the rewind tick and now — well outside the 32 u
169 // X/Z width of the default player AABB. Result: fast-moving
170 // targets whose live position was OFF the ray but whose
171 // historical position was ON the ray got broad-phase REJECTED.
172 // Their capsules stayed LIVE (no rewind), so the raycast tested
173 // against the unrewound capsules and reported a miss; the user
174 // saw the shot-debug visualizer's red capsules at the LIVE
175 // position, identical to "no rollback at all". Original PR-5
176 // assumed 72 u half-extent — that's our Y, but X/Z is only 16 u.
177 //
178 // Fix: dilate the broad-phase AABB by `|velocity| × lagWindow` on
179 // each axis to cover the full range of positions the entity could
180 // have occupied during the rewind window. Cheap (one mul + one
181 // add) and rarely false-rejects even at sprint+200ms.
182 const float lagWindowSec = static_cast<float>(target->lagTicks) / 128.0f;
183
184 auto view = registry.view<HitboxInstance, HitboxHistory>();
185 view.each([&](entt::entity entity, HitboxInstance& inst, const HitboxHistory& hist) {
186 if (entity == shooter)
187 return;
188
189 // PR-5/PR-24: ray-AABB broad-phase pre-filter, dilated by
190 // possible motion over the rewind window so fast-moving
191 // entities aren't false-rejected (see comment above).
192 if (haveFilter) {
193 const auto* pos = registry.try_get<Position>(entity);
194 const auto* shape = registry.try_get<CollisionShape>(entity);
195 if (pos != nullptr && shape != nullptr) {
196 glm::vec3 expand = shape->halfExtents;
197 if (const auto* vel = registry.try_get<Velocity>(entity)) {
198 expand += glm::abs(vel->value) * lagWindowSec;
199 }
200 // Match the inflated narrow-phase hit shape ("cylinder hitreg"):
201 // dilate the broad-phase AABB by the bullet radius so a thick
202 // shot that grazes this player isn't pruned before rewind.
203 expand += glm::vec3{bulletRadius};
204 const physics::WorldAABB bounds{
205 .min = pos->value - expand,
206 .max = pos->value + expand,
207 };
208 float aabbDist = rayMaxDistance;
209 glm::vec3 aabbNormal{0.0f};
210 if (!physics::raycastAABB(*rayOrigin, *rayDirection, bounds, rayMaxDistance, aabbDist, aabbNormal)) {
211 return; // skip rewind for this player — ray doesn't reach their motion-extruded AABB
212 }
213 }
214 }
215
216 // Linear scan over the ring (≤ 32 slots). Find the sample
217 // with the latest tick that's still ≤ targetTick. Empty slots
218 // (tick == 0) and slots newer than targetTick are skipped.
219 const HitboxHistorySample* best = nullptr;
220 for (std::size_t i = 0; i < hist.count; ++i) {
221 const auto& sample = hist.ring[i];
222 if (sample.tick == 0)
223 continue;
224 if (sample.tick > targetTick)
225 continue;
226 if (best == nullptr || sample.tick > best->tick)
227 best = &sample;
228 }
229 if (best == nullptr)
230 return; // no usable historical sample (target predates the ring)
231
232 // Save the live capsules (move out, no copy) and copy the
233 // historical sample in. Move-out leaves `inst.capsules`
234 // empty; the assignment from `best->capsules` repopulates
235 // it. The destructor move-restores in reverse.
236 guard.saved_.emplace_back(entity, std::move(inst.capsules));
237 inst.capsules = best->capsules;
238 });
239
240 if (!guard.saved_.empty())
241 guard.registry_ = &registry;
242
243 return guard;
244}
245
246} // namespace systems
Collision shape component — AABB or vertical capsule.
Server-side ring buffer of recent hitbox capsule snapshots per entity.
Skeleton-driven hitbox types: body regions, capsule definitions, damage profiles, and per-entity runt...
Per-shooter rewind target for lag-compensated hitscan.
World-space position component for ECS entities.
Shared hitscan raycasting against world geometry and player hitboxes.
Shared ECS registry type alias for the game engine.
entt::registry Registry
Shared ECS registry type alias.
Definition Registry.hpp:11
Linear velocity component for ECS entities.
RewindHitboxesGuard & operator=(RewindHitboxesGuard &&other) noexcept
Definition LagCompensation.hpp:82
RewindHitboxesGuard(RewindHitboxesGuard &&other) noexcept
Definition LagCompensation.hpp:76
~RewindHitboxesGuard() noexcept
Definition LagCompensation.hpp:71
void restore() noexcept
Walk saved_ and move each stored capsules vector back into its entity's HitboxInstance.
Definition LagCompensation.hpp:97
std::vector< std::pair< entt::entity, std::vector< WorldCapsule > > > saved_
Definition LagCompensation.hpp:112
RewindHitboxesGuard(const RewindHitboxesGuard &)=delete
Registry * registry_
Definition LagCompensation.hpp:111
friend RewindHitboxesGuard rewindHitboxes(Registry &, entt::entity, const glm::vec3 *, const glm::vec3 *, float, float)
Rewind every other player's hitbox capsules to where they were on shooter's screen at fire time.
Definition LagCompensation.hpp:148
RewindHitboxesGuard & operator=(const RewindHitboxesGuard &)=delete
bool raycastAABB(glm::vec3 origin, glm::vec3 direction, const WorldAABB &box, float maxDistance, float &outDistance, glm::vec3 &outNormal)
Ray vs axis-aligned box intersection (slab method).
Definition Raycast.hpp:45
Client-only input sampling system — split into two halves so mouse look can run every iterate() (smoo...
Definition DebugUI.hpp:15
RewindHitboxesGuard rewindHitboxes(Registry &registry, entt::entity shooter, const glm::vec3 *rayOrigin=nullptr, const glm::vec3 *rayDirection=nullptr, float rayMaxDistance=0.0f, float bulletRadius=0.0f)
Rewind every other player's hitbox capsules to where they were on shooter's screen at fire time.
Definition LagCompensation.hpp:148
Collision shape attached to an entity.
Definition CollisionShape.hpp:34
Per-tick capsule snapshot stored in the history ring.
Definition HitboxHistory.hpp:41
uint32_t tick
Server tick when the sample was recorded. 0 = unset.
Definition HitboxHistory.hpp:42
std::vector< WorldCapsule > capsules
Definition HitboxHistory.hpp:43
Ring buffer of recent hitbox snapshots for one entity.
Definition HitboxHistory.hpp:66
std::size_t count
Number of samples written so far, capped at k_capacity.
Definition HitboxHistory.hpp:82
std::array< HitboxHistorySample, k_capacity > ring
Definition HitboxHistory.hpp:74
ECS components.
Definition Hitbox.hpp:108
std::vector< WorldCapsule > capsules
~12 capsules per character.
Definition Hitbox.hpp:109
Server tick to which to rewind other entities' hitboxes when resolving this entity's hitscan.
Definition LagCompTarget.hpp:23
World-space position of an entity, in game units.
Definition Position.hpp:10
Linear velocity of an entity, in game units per second.
Definition Velocity.hpp:10
An axis-aligned box in world space, used as static collision geometry.
Definition SweptCollision.hpp:39