using Unity.Entities; namespace ProjectM.Simulation { /// /// SERVER-ONLY expiry tracker paired with a by . It is NOT a /// [GhostField] and lives in a SEPARATE buffer so the replicated layout stays /// byte-identical — adding ANY member (even non-ghost) to a [GhostField] buffer element regenerates its /// serializer/stride/hash = an effective ghost re-bake. To grant a TIMED buff, append both a StatModifier and a /// TimedModifier sharing one unique SourceId; TimedModifierExpirySystem removes the matching StatModifier /// when elapses, and that removal replicates for free via the StatModifier [GhostField] /// buffer (OwnerSendType.All), so StatRecomputeSystem reverts the effective stat on both worlds with no change. /// public struct TimedModifier : IBufferElementData { /// Matches the this row governs. public uint SourceId; /// Server tick at which the paired StatModifier expires (0 = no expiry / inert; schedule via TickUtil.NonZero). public uint UntilTick; } /// Pure helpers for removing modifiers by provenance (clear-by-type / timed expiry). Deterministic, no RNG/wall-clock. public static class TimedModifierUtil { /// Remove every row whose SourceId matches (RemoveAtSwapBack). Returns the count removed. public static int RemoveBySourceId(DynamicBuffer mods, uint sourceId) { int removed = 0; for (int j = mods.Length - 1; j >= 0; j--) if (mods[j].SourceId == sourceId) { mods.RemoveAtSwapBack(j); removed++; } return removed; } } }