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
114 friend RewindHitboxesGuard rewindHitboxes(Registry&, entt::entity, const glm::vec3*, const glm::vec3*, float);
115};
116
148 entt::entity shooter,
149 const glm::vec3* rayOrigin = nullptr,
150 const glm::vec3* rayDirection = nullptr,
151 float rayMaxDistance = 0.0f)
152{
154
155 const auto* target = registry.try_get<LagCompTarget>(shooter);
156 if (target == nullptr || target->targetServerTick == 0)
157 return guard; // no-op: no rewind requested for this shooter
158
159 const uint32_t targetTick = target->targetServerTick;
160 const bool haveFilter = rayOrigin != nullptr && rayDirection != nullptr && rayMaxDistance > 0.0f;
161
162 // PR-24 (broad-phase fix): how far back in time we're rewinding,
163 // in seconds. The pre-PR-24 broad-phase filter tested ray vs the
164 // entity's LIVE AABB (live position ± live halfExtents). At
165 // sprint speed (~700 u/s) and 100 ms RTT the entity has moved
166 // ~70 u between the rewind tick and now — well outside the 32 u
167 // X/Z width of the default player AABB. Result: fast-moving
168 // targets whose live position was OFF the ray but whose
169 // historical position was ON the ray got broad-phase REJECTED.
170 // Their capsules stayed LIVE (no rewind), so the raycast tested
171 // against the unrewound capsules and reported a miss; the user
172 // saw the shot-debug visualizer's red capsules at the LIVE
173 // position, identical to "no rollback at all". Original PR-5
174 // assumed 72 u half-extent — that's our Y, but X/Z is only 16 u.
175 //
176 // Fix: dilate the broad-phase AABB by `|velocity| × lagWindow` on
177 // each axis to cover the full range of positions the entity could
178 // have occupied during the rewind window. Cheap (one mul + one
179 // add) and rarely false-rejects even at sprint+200ms.
180 const float lagWindowSec = static_cast<float>(target->lagTicks) / 128.0f;
181
182 auto view = registry.view<HitboxInstance, HitboxHistory>();
183 view.each([&](entt::entity entity, HitboxInstance& inst, const HitboxHistory& hist) {
184 if (entity == shooter)
185 return;
186
187 // PR-5/PR-24: ray-AABB broad-phase pre-filter, dilated by
188 // possible motion over the rewind window so fast-moving
189 // entities aren't false-rejected (see comment above).
190 if (haveFilter) {
191 const auto* pos = registry.try_get<Position>(entity);
192 const auto* shape = registry.try_get<CollisionShape>(entity);
193 if (pos != nullptr && shape != nullptr) {
194 glm::vec3 expand = shape->halfExtents;
195 if (const auto* vel = registry.try_get<Velocity>(entity)) {
196 expand += glm::abs(vel->value) * lagWindowSec;
197 }
198 const physics::WorldAABB bounds{
199 .min = pos->value - expand,
200 .max = pos->value + expand,
201 };
202 float aabbDist = rayMaxDistance;
203 glm::vec3 aabbNormal{0.0f};
204 if (!physics::raycastAABB(*rayOrigin, *rayDirection, bounds, rayMaxDistance, aabbDist, aabbNormal)) {
205 return; // skip rewind for this player — ray doesn't reach their motion-extruded AABB
206 }
207 }
208 }
209
210 // Linear scan over the ring (≤ 32 slots). Find the sample
211 // with the latest tick that's still ≤ targetTick. Empty slots
212 // (tick == 0) and slots newer than targetTick are skipped.
213 const HitboxHistorySample* best = nullptr;
214 for (std::size_t i = 0; i < hist.count; ++i) {
215 const auto& sample = hist.ring[i];
216 if (sample.tick == 0)
217 continue;
218 if (sample.tick > targetTick)
219 continue;
220 if (best == nullptr || sample.tick > best->tick)
221 best = &sample;
222 }
223 if (best == nullptr)
224 return; // no usable historical sample (target predates the ring)
225
226 // Save the live capsules (move out, no copy) and copy the
227 // historical sample in. Move-out leaves `inst.capsules`
228 // empty; the assignment from `best->capsules` repopulates
229 // it. The destructor move-restores in reverse.
230 guard.saved_.emplace_back(entity, std::move(inst.capsules));
231 inst.capsules = best->capsules;
232 });
233
234 if (!guard.saved_.empty())
235 guard.registry_ = &registry;
236
237 return guard;
238}
239
240} // namespace systems
AABB collision shape component for physics entities.
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
friend RewindHitboxesGuard rewindHitboxes(Registry &, entt::entity, const glm::vec3 *, const glm::vec3 *, float)
Rewind every other player's hitbox capsules to where they were on shooter's screen at fire time.
Definition LagCompensation.hpp:147
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
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:44
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)
Rewind every other player's hitbox capsules to where they were on shooter's screen at fire time.
Definition LagCompensation.hpp:147
Axis-aligned bounding box defined by half-extents from the entity's Position.
Definition CollisionShape.hpp:16
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:32