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
| Step | Where | What happens |
|---|---|---|
| 1. Broadcast | Server | Gameplay code calls BroadcastNumberPop. Authority is validated; client calls are ignored with a warning. |
| 2. Resolve location | Server | If 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. Send | Server | ServerCullDistance = 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. Receive | Each client | The event reaches UCrimsonNumberPopSubsystem::HandleIncomingEvent - via the GameState component's OnNumberPopReceived delegate (multicast path) or directly (culled path). |
| 5. Filter | Each client | UCrimsonNumberPopPlayerSettings::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. Display | Each client | The request is queued; the tick dequeues it, resolves a style from the registry, formats the value, and starts a pooled widget. |
Bandwidth choices
WorldLocationis aFVector_NetQuantize- quantized to 1 cm, plenty for a floating number and much smaller on the wire.PrefixTextandCustomTexttravel with the event; empty strings cost almost nothing, so pay only for what you use.InstigatorandTargetare 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.