EventBus & EventBridge
Namespaces: RisingV.Shared.Events
, RisingV.Shared
Assemblies: RisingV.Shared
The EventBus system provides a high‑performance, type‑safe pub‑sub
back‑bone for the entire RisingV framework.
Every plugin, manager, engine, or game system can raise events without any
compile‑time coupling to listeners.
EventBridge
is a static façade that surfaces all built‑in game events
(V‑Rising server hooks, file changes, weapon lifecycle, etc.) and exposes
one‑line helper methods for subscription and publication.
1. Architectural Overview
┌────────────┐ Publish ┌──────────────┐
│ Producer │ ───────────────► │ EventBus │
└────────────┘ └──────┬───────┘
│ Dispatch
Subscribe / Unsubscribe │
▼
┌──────────────┐
│ Consumers │
└──────────────┘
- EventBus – a singleton message hub (
Application.EventBus
) that keeps aDictionary<Type, List<Handler>>
. - EventBridge – a file with hundreds of strongly‑typed record structs that represent game events and helper extension methods to talk to the bus.
- Producers – game systems: Engines, Managers, Persistence layer, Unity hooks, etc.
- Consumers – plugins or subsystems that subscribe to specific event types.
2. Event Contracts
All events implement IEvent
(marker) and derive from Event<T>
to inherit
equality, ToString()
, and metadata.
public abstract record Event<TEvent> : IEvent
where TEvent : IEvent;
Concrete events are tiny record structs – zero allocation, immutable, and carry only the data you need.
Example (player downed):
public record PlayerDownedEvent(Entity Victim, Entity Killer)
: Event<PlayerDownedEvent>;
Because events are records, pattern‑matching and deconstruction work out‑of‑the‑box.
3. Using EventBridge
Subscribing
// inside your plugin / engine
private IDisposable? _sub;
public override void Ready()
{
// async handler
_sub = EventBridge.Subscribe<PlayerDownedEvent>(
this,
async evt =>
{
await GrantAchievementAsync(evt.Victim, "NoPainNoGain");
});
}
Options:
Overload | Handler Type |
---|---|
Subscribe(owner, Action<T>) |
Synchronous |
Subscribe(owner, Func<T,ValueTask>) |
Async |
Subscribe(owner, EventHandler<T>) |
Async + cancellation token |
owner
tags the subscription so you can unsubscribe all of them in one call.
Publishing
EventBridge.PublishSafe(new PlayerDownedEvent(victim, killer));
Method | Behaviour |
---|---|
Publish |
Sync, exceptions propagate back to caller. |
PublishSafe |
Sync, exceptions caught and logged. |
PublishAsync |
Async ValueTask , propagate errors. |
PublishSafeAsync |
Async, swallow/log errors. |
Unsubscribing
_sub?.Dispose(); // via IDisposable
EventBridge.Unsubscribe(this); // or remove all by owner
4. Built‑In Event Categories
EventBridge
organises events with #region
blocks:
Region | Highlights |
---|---|
Initialization | OnGameDataInitialized , ServerStartupSuccessful |
File Change | OnFilesChanged for hot‑reload pipelines |
Connection | UserConnect , UserDisconnect |
Player | Buffing, achievements, admin auth, kick/ban |
Unit | Buff spawn, downed, buffed |
Stats & Damage | StatChanged* , Damaged* (boss, player, unit, etc.) |
Death | Typed death events (DeathPlayer , DeathBoss , …) |
VBlood | VBloodConsumed |
Equipment / Weapon / Armor | Spawn/despawn, equip/unequip |
Save & Spawn | OnSave , generic SpawnEvent , script spawns |
(Reserved) | InputSystem events for future expansions |
For the full list inspect EventBridge.cs
in your IDE.
5. Thread‑Safety & Performance
- Lock‑free dispatch – the bus copies the handler list once per publish, so handlers can add/remove in other threads without deadlocks.
- Handlers run sequentially in subscription order; use async publish if you need parallelism.
- Minimise allocations: prefer record events over classes; pass structs by
in
when heavy.
6. Advanced Features
Handler Hash
Subscribe(owner, handler, handlerHash)
lets you supply a stable int key so
duplicate subscriptions are ignored – handy when hot‑reloading code.
Safe Publish for Mods
Third‑party mods should always call PublishSafe
/ PublishSafeAsync
to
avoid breaking the game if another mod’s handler throws.
7. Quick‑Start Cheat‑Sheet
// Subscribe
IDisposable sub = EventBridge.Subscribe<WeaponSpawnedEvent>(
this, evt => Logger.Info($"Weapon spawned: {evt.WeaponEntity}"));
// Publish
EventBridge.PublishSafe(new WeaponSpawnedEvent(entity, level));
// Unsubscribe
sub.Dispose(); // or EventBridge.Unsubscribe(this);
8. Troubleshooting
Symptom | Cause | Remedy |
---|---|---|
No handlers called | Forgot to keep IDisposable alive OR Owner was GC’d |
Store the subscription reference or use EventBridge.Subscribe overload without owner. |
Handler runs twice | Duplicate subscription | Supply handlerHash or unsubscribe correctly. |
“Unhandled exception in handler” logs | PublishSafe* swallowed but logged an error |
Fix logic or switch to Publish during debugging. |
TL;DR
- EventBus = zero‑friction, type‑safe messaging.
- EventBridge = curated catalogue of game‑level events + helper APIs.
- Subscribe‑Publish‑Unsubscribe in 3 lines, with async and safe variants.
Use it to keep your RisingV code loose‑coupled and hot‑reload‑friendly.