Concept: Event Flow & Replication

One event type (FCrimsonNumberPopEvent) travels one direction: server to clients. Everything visual happens client-side after arrival. Understanding the pipeline explains every behavior in the plugin.

The pipeline

StepWhereWhat happens
1. BroadcastServerGameplay code calls BroadcastNumberPop. Authority is validated; client calls are ignored with a warning.
2. Resolve locationServerIf the caller didn't set WorldLocation (bHasWorldLocation false and location zero), it is resolved from the Target - socket, then offset, then root + 100 cm (How-To: Position Pops).
3. SendServerServerCullDistance = 0: one NetMulticast Unreliable RPC to all clients. Greater than 0: a per-client Client Unreliable RPC to each player whose GetFocalLocation() is in range.
4. ReceiveEach clientThe event reaches UCrimsonNumberPopSubsystem::HandleIncomingEvent - via the GameState component's OnNumberPopReceived delegate (multicast path) or directly (culled path).
5. FilterEach clientUCrimsonNumberPopPlayerSettings::PassesFilter runs, if the component exists: distance cap first (all categories, local-pawn targets exempt), then heals, incoming-damage override, crits-only, own/ally/enemy toggles.
6. DisplayEach clientThe request is queued; the tick dequeues it, resolves a style from the registry, formats the value, and starts a pooled widget.

Bandwidth choices

  • WorldLocation is a FVector_NetQuantize - quantized to 1 cm, plenty for a floating number and much smaller on the wire.
  • PrefixText and CustomText travel with the event; empty strings cost almost nothing, so pay only for what you use.
  • Instigator and Target are serialized as NetGUID references. On a client where the actor is not net-relevant (or not replicated), they resolve to null - the filter then treats the event as third-party.
  • All RPCs are unreliable: pops are cosmetic, so a dropped packet just means one missing number instead of retransmission cost.

Display-only by design

UCrimsonNumberPopSubsystem is never created on dedicated servers or in non-game worlds (editor previews, thumbnails), and every entry point re-checks the net mode lazily - so a PIE world running as a dedicated server also displays nothing. The GameState component self-registers with the local subsystem in BeginPlay (RegisterEventSource), so there is no polling and no manual binding.

Listen servers and singleplayer
A listen server's local player is a client of the multicast like any other, and in singleplayer the 'server' and the only client are the same process - the one GameState-broadcast path covers every net mode. The subsystem's direct RequestNumberPop exists for purely local, no-replication use (offline tools, menus).
Never trust pops as gameplay state
Events are fire-and-forget cosmetics. If a system needs to know damage happened, read it from your authoritative gameplay state - not from a number pop delegate.