Table of Contents

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 a Dictionary<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.