Priority events

Invocation

Choose normal, fast, safe, selective, and masked invocation patterns.

  • Invoke
  • InvokeFast
  • InvokeSafe
  • Masks

Choose the invocation shape that matches the path: normal, fast, safe, selective, or masked.

Invoking

Normal Invoke

Invoke visits active slots from 1 through 9.

CombatBus.Tick.Invoke();
CombatBus.HealthChanged.Invoke(currentHealth);

var report = new HitReport(origin, hitPoint, normal, targetId);
CombatBus.HitReported.Invoke(in report);

Invoke is the standard all-slot invocation path. It drains queued off-thread subscription operations before dispatching and handles cancellation options. It does not isolate handler exceptions; a throwing handler can escape the call. Use InvokeSafe when you need ExceptionMode behavior and result details.

InvokeFast

InvokeFast is the optimized hot-path invocation.

CombatBus.Tick.InvokeFast();
CombatBus.HealthChanged.InvokeFast(currentHealth);
CombatBus.HitReported.InvokeFast(in report);

Use it when:

  • you are in a high-frequency path
  • you do not need per-handler cancellation checks
  • you trust the handlers not to throw
  • you want the fastest all-slot dispatch after warmup

InvokeFast uses a direct dispatch path. Do not use it when you need exception isolation or detailed exception results. Prefer Invoke while learning, and move to InvokeFast when profiling says the event is hot enough to matter and the handlers are trusted.

InvokeSafe

InvokeSafe returns an AzInvokeResult.

AzInvokeResult result = CombatBus.HealthChanged.InvokeSafe(currentHealth);

if (!result.Success)
{
    Debug.LogWarning(result.ToDetailedString());
}

Use InvokeSafe for diagnostics, tooling, editor workflows, validation, and systems where you need result details.

Selective Invocation

Priority events can invoke only part of the slot set.

InvokeSlots

InvokeSlots accepts a bitmask. Bit 0 is slot 1; bit 8 is slot 9.

CombatBus.Tick.InvokeSlots(AzPriorityMasks.UpTo(3)); // slots 1..3
CombatBus.Tick.InvokeSlots(AzPriorityMasks.From(7)); // slots 7..9
CombatBus.Tick.InvokeSlots(AzPriorityMasks.Slot5);   // slot 5 only

For payload events, the mask comes first:

CombatBus.HealthChanged.InvokeSlots(AzPriorityMasks.UpTo(3), currentHealth);
CombatBus.HitReported.InvokeSlots(AzPriorityMasks.From(7), in report);

InvokePriorityAtLeast

InvokePriorityAtLeast(minPriority) invokes slots 1..minPriority.

CombatBus.Tick.InvokePriorityAtLeast(3); // slots 1, 2, 3
CombatBus.Tick.InvokePriorityAtLeast(5); // slots 1, 2, 3, 4, 5

This helper exists on the no-payload AzPriorityEvent.

InvokePriorityAtMost

InvokePriorityAtMost(maxPriority) invokes slots maxPriority..9.

CombatBus.Tick.InvokePriorityAtMost(7); // slots 7, 8, 9
CombatBus.Tick.InvokePriorityAtMost(5); // slots 5, 6, 7, 8, 9

This helper exists on the no-payload AzPriorityEvent.

InvokeSlot

InvokeSlot invokes exactly one slot.

CombatBus.Tick.InvokeSlot(5);
CombatBus.HealthChanged.InvokeSlot(5, currentHealth);
CombatBus.HitReported.InvokeSlot(5, in report);

Important: full Invoke and InvokeFast refresh the occupied slot mask and drain queued off-thread operations. Single-slot invocation is best for main-thread-managed subscriptions where you know the slot is already current. If you rely on off-thread subscriptions, prefer Invoke, InvokeFast, or InvokeSlots so queued operations are drained first.

InvokeOnly and InvokeWhere

The no-payload AzPriorityEvent also exposes InvokeOnly and InvokeWhere.

byte[] slots = { 1, 5, 9 };
CombatBus.Tick.InvokeOnly(slots);

CombatBus.Tick.InvokeWhere(slot => slot <= 3 || slot == 9);

Use masks for common fixed patterns. Use InvokeWhere when the slot decision is dynamic and easier to express as a predicate.

AzPriorityMasks

AzPriorityMasks provides constants and helpers for building slot masks.

Constants

AzPriorityMasks.Slot1
AzPriorityMasks.Slot2
AzPriorityMasks.Slot3
AzPriorityMasks.Slot4
AzPriorityMasks.Slot5
AzPriorityMasks.Slot6
AzPriorityMasks.Slot7
AzPriorityMasks.Slot8
AzPriorityMasks.Slot9

AzPriorityMasks.OddSlots
AzPriorityMasks.EvenSlots
AzPriorityMasks.None
AzPriorityMasks.All

Helpers

ushort early = AzPriorityMasks.UpTo(3);      // slots 1..3
ushort late = AzPriorityMasks.From(7);       // slots 7..9
ushort middle = AzPriorityMasks.Build(4, 6); // slots 4..6
ushort custom = AzPriorityMasks.Combine(
    AzPriorityMasks.Slot1,
    AzPriorityMasks.Slot5,
    AzPriorityMasks.Slot9);

bool includesFive = AzPriorityMasks.ContainsSlot(custom, 5);

Build(start, end) is order-agnostic:

AzPriorityMasks.Build(1, 3); // slots 1..3
AzPriorityMasks.Build(3, 1); // also slots 1..3