Spatial

Triggers

Spatial enter/exit/dwell volumes over the world. Server-only.

Server
services.AddCrossplayTriggers();
Depends on
world
Shape
server-only · zero wire

Seams you implement 1

Crossplay calls these; your game supplies them. Each ships an inert or permissive default, so register yours before services.AddCrossplayTriggers(); and it wins.

ITriggerListener game SPI

SPI the game implements to give volumes meaning — the plug-in point of this piece. The framework does the geometry + the enter/leave/dwell bookkeeping; the rules decide what a capture point or a damage floor does. How to implement. Register your listener in DI BEFORE AddCrossplayTriggers (it is a TryAdd seam). Read Data to tell one volume's purpose from another (capture point A vs. a lava pool), and apply your effect in OnEnter / OnExit / OnDwell. All three fire on the world tick (poll thread).

public sealed class LavaFloorListener : ITriggerListener
{
    private readonly IStatService _stats;
    public LavaFloorListener(IStatService stats) => _stats = stats;

    public void OnEnter(TriggerVolume v, WorldEntity e) { }
    public void OnExit (TriggerVolume v, WorldEntity e) { }
    public void OnDwell(TriggerVolume v, WorldEntity e, float dt)
        => _stats.Add(e.Id, StatId.Health, -10 * dt); // damage per second while standing in it
}
  • void OnDwell(TriggerVolume volume, WorldEntity entity, float intervalSeconds)

    An entity is STILL inside, fired once per DwellIntervalSeconds.

  • void OnEnter(TriggerVolume volume, WorldEntity entity)

    An entity crossed INTO a volume this tick.

  • void OnExit(TriggerVolume volume, WorldEntity entity)

    An entity that was inside has LEFT (moved out, changed zone, or despawned).

Services you call 1

Crossplay implements these. Resolve them from DI and call them from your own systems.

ITriggerService

Server-side API for placing volumes.

  • long Add(string zone, IBoundsArea area, byte[] data = null)

    Registers a spatial volume; occupancy tracking begins on the next tick.

  • IReadOnlyCollection<long> Occupants(long triggerId)

    The entity ids currently inside a volume (empty when the id is unknown).

  • bool Remove(long triggerId)

    Removes a volume (fires OnExit for anyone inside).

Configuration 1

Every tunable lives in an options object — there are no magic numbers to hunt for.

TriggerOptions

Configurable trigger parameters (defaults here; never inlined in logic).

  • float DwellIntervalSeconds { get; set; }

    Seconds between dwell callbacks while an entity stays inside a volume (0 = no dwell).