Skip to content

Inter addon communication

Extremelyd1 edited this page Apr 15, 2022 · 2 revisions

While you can use the already described INetServer interfaces for the client and server to communicate across the network (Client -> Server) it's impractical to use network functionality to communicate between addons on the same machine (Addon -> Addon). This sort of eventing is instead provided using the IEventAggregator interface provided through both your IClientApi and IServerApi interfaces.

Usage information:

  • Publish-subscribe event pattern
  • Eliminates the need for static referencing between mods
  • Perfect for leveraging mod reuse with one mod keeping track of a bunch of complicated data and multiple mods subscribing to its event bus
  • Segregated between client and server, so client addons only get client events and server addons only get server events
  • Requires shared types for payloads so that "magic strings" are not required

Implementation example

Let's create a set of toy mods which are simply going to broadcast their name and version to another mod on the server. For this example we will create 4 projects (Broadcast1, Broadcast2, Listener, and SharedEvents).

First let's setup the shared event. This will live in a library alongside all other shared event types you want to publish and make available for consumers of your mod. For the purposes of this example we'll say the mod developer made Listener and published SharedEvents for people to reference so they can talk to the addon.

Let's define our event in our shared library:

 public class SomeKindOfStringCarryingEvent : PubSubEvent<string> { }

That's it. No more code required. We've defined a PubSubEvent with a payload of string. Pretty easy.

Now let's setup our Listener addon:

public class ListenerAddon : ServerAddon {
    public override void Initialize(IServerApi serverApi) {
        serverApi.EventAggregator.GetEvent<SomeKindOfStringCarryingEvent>().Subscribe(s => {
            Logger.Info(this, s);
        });
        Logger.Info(this, "Listener Initialized");
    }

    protected override string Name => "Listener";
    protected override string Version => "0.0.1";
    public override bool NeedsNetwork => false;
}

This addon is simply an initialize method with a request to the EventAggregator to get a handle to our pre-defined event which we subscribe to with a lambda.

Now we play the part of other mod developers and make some new addon which use this functionality.

Broadcast1 addon:

public class Broadcast1Addon : ServerAddon {
    public override void Initialize(IServerApi serverApi) {
        Task.Run(async () => {
            await Task.Delay(5000);
            serverApi.EventAggregator.GetEvent<SomeKindOfStringCarryingEvent>().Publish($"{Name}@{Version}");
        });
        Logger.Info(this, "Broadcast1 Initialized");
    }

    protected override string Name => "Broadcast1";
    protected override string Version => "0.0.1";
    public override bool NeedsNetwork => false;
}

Broadcast2 addon:

public class Broadcast2Addon : ServerAddon {
    public override void Initialize(IServerApi serverApi) {
        Task.Run(async () => {
            await Task.Delay(5000);
            serverApi.EventAggregator.GetEvent<SomeKindOfStringCarryingEvent>().Publish($"{Name}@{Version}");
        });
        Logger.Info(this, "Broadcast2 Initialized");
    }

    protected override string Name => "Broadcast2";
    protected override string Version => "0.0.2";
    public override bool NeedsNetwork => false;
}

For the purposes of this example the mods really do the same thing but with different names. They initialize, wait 5 seconds, then both will request a handle to the previously defined event in our shared library and publish to the event with their name and version.

If you were to run these addons, you would see something like this:

...
[INFO]:[Listener] Listener Initialized
...
[INFO]:[Broadcast1Addon] Broadcast1 Initialized
...
[INFO]:[Broadcast2Addon] Broadcast2 Initialized
...
[INFO]:[Listener] Broadcast1@0.0.1
[INFO]:[Listener] Broadcast2@0.0.2
...

Now this isn't super exciting but it shows the basic functionality. Imagine if instead you listened for players to enter an area or take damage in a client-side addon, sent that data via packet to your server addon, then made that data available for consumers to listen for in the EventAggregator. Suddenly the other addons aren't taking up network space with packets, or even figuring out how to get that information out of the Hollow Knight modding API in the first place. This makes the functionality reusable for other developers very easily.

Clone this wiki locally