Entities & Blips — EntityBag and Blips
Burnsie.Common.Entities solves the most common plugin bug there is: leaked peds,
vehicles and blips. The rule it enforces: everything you create goes in a bag, and the
bag is emptied in exactly one place, on every exit path.
EntityBag — lifetime-scoped ownership
One bag per incident/callout/scene. Register everything the scene creates or controls; tear the bag down once, no matter how the scene ends.
var bag = new EntityBag(log);
// Keep() returns its argument, so it chains into creation code:
Ped suspect = bag.Keep(new Ped(model, position, heading));
Blip marker = bag.Keep(new Blip(position) { Color = Blips.DangerColor });
// ... scene runs ...
// EXACTLY ONE of these, on every exit path:
bag.ReleaseAll(); // happy path
bag.DeleteAll(); // abort path
ReleaseAll vs DeleteAll
ReleaseAll() (happy path) | DeleteAll() (abort path) | |
|---|---|---|
| Blips | deleted | deleted |
| Entities | dismissed back to the ambient population | hard-deleted |
| When | scene ended normally in front of the player | teardown must be instant/total (plugin unload, hard abort) |
ReleaseAll never hard-deletes something visible in front of the player. Before
dismissing a ped it hands ambient control back (BlockPermanentEvents = false,
KeepTasks = false) — a ped left with those flags set stays locked in its scripted task
forever instead of resuming ambient life.
Both are idempotent, Exists()-guarded, and must run on a GameFiber. Dispose() is
equivalent to ReleaseAll(), so using-style patterns default to the safe path.
Housekeeping
- Registration is thread-safe (event fibers may
Keepwhile a tracker prunes). - Double-
Keepof the same entity is a no-op. Prune()drops references to entities the game already cleaned up;Countreports tracked entities + blips (diagnostics).
Blips — standard-color blip factory
Implements the community color standard: LightBlue for police units, white for EMS/medical, orange for fire, red for danger/suspects, yellow for routes/objectives. Scale 1.0 for vehicles, 0.75 for peds.
// Flashing red panic blip (flashes forever) with an optional yellow GPS route:
Blip panic = bag.Keep(Blips.Panic(position, "Officer Down", route: true));
// Standard police blip attached to a unit vehicle (tracks it as it moves):
Blip unit = bag.Keep(Blips.PoliceUnit(vehicle, "Responding Unit"));
// Any standard color on an attached unit blip:
Blip ems = bag.Keep(Blips.Unit(ambulance, "EMS", Blips.EmsColor));
Exposed color constants: Blips.PoliceColor, Blips.EmsColor, Blips.FireColor,
Blips.DangerColor, Blips.RouteColor — use them for hand-built blips so your plugin
matches the ecosystem.
Fading blips out
Deleting a dozen unit blips at once pops them off the map; fading reads as "units returning to patrol":
// Single blip:
Blips.FadeOutAndDelete(blip, durationMs: 1600);
// A set, faded together:
Blips.FadeOutAndDeleteMany(blipList, durationMs: 1600);
Both run inline on the calling fiber (they sleep between alpha steps) — call them from a cleanup fiber, not from a path that must return immediately. A blip removed by the game mid-fade is handled silently.
Rules
- One bag per scene; never share a bag between concurrent scenes.
- Entities handed to you by other systems (e.g. LSPDFR-owned backup vehicles) do not belong in your bag — see the Backup dispatcher, which deliberately leaves units LSPDFR-owned.
- Re-validate entities with
.Exists()after any yield, even ones in your bag — the bag guards its own operations, not yours.