The event bus is a design pattern, it serves the purpose of a "global event system" as it can be invoked from anyone, anywhere and can be listened by anyone, anywhere.
The implementation we use was created by the CS Framework and we use it to create new event buses. To understand further on the design pattern itself, you can read this article.
We use these events when we want something that will be used in more than one place, without having to care about object relation.
We usually want events to be fired by systems when we want to inform other objects something has happened, like a round timer being updated, a player that spawned, an object that was picked up.
Creating
To create a new event, we use partial structs that inherit from the IEvent interface. The reason for the partial keyword is that the CS Framework uses the Roslyn code generation tools to create the rest of the necessary logic on that struct.
Pretty straight-forward isn't it? Well, now for the usage.
Invoking
Invoking is the easiest part. You just need to know what you want to invoke and when. The code below creates a new instance of our event, using the constructor we just defined and then invokes it. We also have to specify who called that event, in our case we use the this keyword to say that the class's instance that called it.
base.Awake() calls the parent class's Awake function. Then we add the method that will be called when the event is invoked. We'll declare the method as follows:
The EventContext is usually unused but it gives us information about the invoking itself. Then we have the event we created, it is the struct that we previously created.
The ref and in keywords are there for optimization reasons but they are required, so always add them if your IDE doesn't autocomplete correctly.
One thing to note here is that you have to remove the listeners eventually, you have to do it on the OnDestroyed callback, but if you're using any class that inherits from Actor or NetworkActor, it already unsubscribes from it if you use the AddHandle method when listening to the events.
One thing we have to keep in mind is that, with networking, we have to use a data-driven design, as we cannot depend on one-time of events only, a user has to be able to disconnect and reconnect back later with no issues, if we did not use data-driven, the user would lose that information.
So, we can have the best of both worlds, FishNet already has callbacks when SyncVars are changed, and they are changed to the correct value when the user enters the server.
All this trajectory is to ensure our client doesn't lose anything when he rejoins a game and can load stuff back correctly, not to mention this is way easier than doing RPCs all over the code.
Anything in the codebase can listen to that event, in that case we use for updating the UI, that changes the player name's color if they are ready or not.