using ProjectM.Simulation; using Unity.Burst; using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using Unity.NetCode; using Unity.Transforms; namespace ProjectM.Server { /// /// LANTERN "light is territory" relevancy prototype (Build Spec Phase 1 step 8): per connection, marks every /// ENEMY ghost OUTSIDE that player's lamp radius IRRELEVANT — so monsters in the dark aren't replicated (the /// gamma test: "nothing to see"). Players + untagged/global ghosts stay relevant for free (SetIsIrrelevant). /// /// SHARED-SET DISCIPLINE: is the SOLE clearer of the GhostRelevancy /// set — its OnUpdate runs every tick (requires only GhostRelevancy), setting the mode + clearing + /// adding its region hides. This system runs [UpdateAfter(RegionRelevancySystem)] and only ADDS light /// hides (never clears) via TryAdd (defensive against a ghost hidden by BOTH region and light for one /// connection — a duplicate Add would throw). Gated on so it runs ONLY in the /// LANTERN gym; the legacy base/expedition game keeps region relevancy alone. Runs in /// before GhostSendSystem reads the set. Per-player dim/bright (a /// variable radius) is the tuning follow-up; the prototype uses . /// /// [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)] [UpdateInGroup(typeof(GhostSimulationSystemGroup))] [UpdateAfter(typeof(RegionRelevancySystem))] public partial struct LightRelevancySystem : ISystem { [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate(); state.RequireForUpdate(); // prototype: light-as-territory runs only in the LANTERN gym } [BurstCompile] public void OnUpdate(ref SystemState state) { // Each in-game connection's player position (per-player lamp radius = the dim/bright follow-up; fixed here). var connPos = new NativeHashMap(8, Allocator.Temp); foreach (var (owner, lt) in SystemAPI.Query, RefRO>().WithAll()) connPos[owner.ValueRO.NetworkId] = lt.ValueRO.Position; if (connPos.IsEmpty) { connPos.Dispose(); return; } // The set is already mode=SetIsIrrelevant + cleared + region-populated THIS tick by RegionRelevancySystem. ref var relevancy = ref SystemAPI.GetSingletonRW().ValueRW; var set = relevancy.GhostRelevancySet; var conns = connPos.GetKeyValueArrays(Allocator.Temp); float rSq = LightRelevancyMath.LampRadiusDefault * LightRelevancyMath.LampRadiusDefault; foreach (var (ghost, lt) in SystemAPI.Query, RefRO>().WithAll()) { int ghostId = ghost.ValueRO.ghostId; if (ghostId == 0) continue; // ghost id not assigned yet this tick float3 gp = lt.ValueRO.Position; for (int i = 0; i < conns.Keys.Length; i++) { if (LightRelevancyMath.IsHidden(conns.Values[i], gp, rSq)) set.TryAdd(new RelevantGhostForConnection { Connection = conns.Keys[i], Ghost = ghostId }, 1); } } conns.Dispose(); connPos.Dispose(); } } }