Socially Distant OS
  • Docs
  • API
Search Results for

    Show / Hide Table of Contents
    • Accessibility
      • In-Game TTS
    • Development and Modding: Getting started
      • Building from source
      • Contribution guidelines
      • Project structure
      • Code style guide
    • Game scripting (sdsh)
      • The Basics
    • Game Framework
      • Event Bus
      • Playing Audio
    • Story Scripting
      • Career Mode Narrative Objects
      • Narrative Identifiers
      • News Articles
    • User Interface
      • UI Overview
      • Signals
      • List Adapters
      • Optimizing UI for Performance
      • Advanced UI features
        • Visual Styles

    event Bus

    Socially Distant has a bunch of moving parts that make up its game play. The game is split into three main parts - the UI, the game hypervisor, and the network simulation. These three layers need to stay decoupled from each other, but they do need to communicate with each other. In some cases, they need to pass messages along to each other across threads. The Event Bus facilitates all of that.

    Posting an Event

    When you want to tell the rest of the game that your code did something, you do so by posting a new Event to the Event Bus.

    Consider this example.

    You want to fire an event every time a command is executed on a computer.

    First, create a new CommandRunEvent class inheriting DeviceEvent.

    public sealed class CommandRunEvent : DeviceEvent
    {
        public string Name { get;
        public string[] Arguments { get; }
    
        public CommandRunEvent(IComputer computer, string name, string[] args) : base(computer)
        {
            Name = name;
            Arguments = args;
        }
    }
    

    Then, simply create an instance of the CommandRunEvent and then:

    EventBus.Post(myCommandRunEvent);
    

    It's that simple!

    Receiving Events

    Most of the time, you'll be listening to events on the bus.

    To listen for an event, call EventBus.Listen<T>(callback).

    IDisposable EventBus.Listen<T>(Action<T> callback) (where T is any Event)

    This method takes a callback, and a type of Socially Distant event. It creates an event listener that calls the callback any time an event of matching type is posted. The event listener is returned as an IDisposable. When you want to stop receiving events, call Dispose() on the event listener.

    Here's an example of how to receive an event every time a device is pinged on the Internet.

    private void OnPing(PingEvent pingEvent)
    {
        Log.Information("Pong!");
    }
    
    using var listener = EventBus.Listen<PingEvent>(OnPing);
    
    // ...
    
    listener.Dispose();
    

    What are Events?

    An Event is just a plain C# object containing data about something that just happened in the game. Any time something happens in-game, an Event is posted to the Event Bus containing information about what just happened.

    For example, any time the files on an in-game computer are accessed, the game posts a FileSystemEvent object to the Event Bus. In this case, information about what computer was accessed and what happened on disk is sent with the event. Other parts of the game, like the mission system, can then listen for FileSystemEvent events on the Event Bus, and do what they will with the information. This can be anything, like completing an objective when a specific file is deleted.

    Event Hierarchy

    Any object inheriting from Event can be posted to the event bus. You can also inherit subclasses of Event. This forms the Event Hierarchy.

    Consider the following example situation:

    The Mission System fires three kinds of events.

    • MissionFailEvent: when a mission is failed.
    • MissionCompletedEvent: when a mission is completed.
    • MissionAbandonedEvent: When a mission is abandoned.

    These three events may have unique information about what specifically happened. A fail event may carry info about why the mission failed. However, all mission events carry a mission with them.

    Another part of the game, like DocumentAdapter, may want to receive all mission events so it can redraw any mission info boxes in an email. Without Event Hierarchy, DocumentAdapter would need to listen for mission fails, completions, and abandons. This works fine until we add a MissionStartEvent!

    EventBus.Listen<MissionFailEvent>(OnMissionFail);
    EventBus.Listen<MissionCompleteEvent>(OnMissionComplete);
    EventBus.Listen<MissionAbandonEvent>(OnMissionAbandon);
    

    Since we know all types of mission event will have a mission associated with them, we can define a common MissionEvent class they all inherit from.

    public abstract class MissionEvent : Event
    {
        public IMission Mission { get; }
    
        protected MissionEvent(IMission mission)
        {
            Mission = mission;
        }
    }
    

    Then, DocumentAdapter can simplify to just listening for mission events.

    EventBus.Listen<MissionEvent>(OnMissionEvent);
    

    You can use C#'s pattern matching features to see more specific information about an Event instance, including checking what kind of event it is.

    Thread-safety and performance notes

    Posting an event with EventBus.Post() is thread-safe. Events are posted to a ConcurrentQueue to be dispatched on the next available frame.

    All events on the Event Bus are dispatched on the main UI thread. Do not do slow work in an event listener.

    The game only dispatches 129 Event instances per frame, due to how clogged the event bus can get when the player's moving fast.

    In this article
    Back to top Generated by DocFX