Last active
June 21, 2024 21:48
-
-
Save LambdaSix/b916b09175808b6a44304dc21d1cc20c to your computer and use it in GitHub Desktop.
Generic EventBus for Godot, allow generic registration and emitting of event data while remaining nicely typed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Records are nice for use with events, just change EventRecord to EventArgs if you'd | |
// rather use the traditional EventArgs class. | |
public record EventRecord | |
{ | |
public static readonly EventRecord Empty = new EventRecord(); | |
} | |
public class EventBus | |
{ | |
public static readonly EventBus Instance = new EventBus(); | |
private Dictionary<Type, List<EventHandler<EventRecord>>> channels = new(); | |
public class Subscription : IDisposable | |
{ | |
private EventHandler<EventRecord> _handler; | |
private Type _channel; | |
public bool IsDisposed { get; private set; } = false; | |
public Subscription(Type channel, EventHandler<EventRecord> handler) | |
{ | |
_handler = handler; | |
_channel = channel; | |
} | |
public void Dispose() | |
{ | |
if (IsDisposed) | |
return; | |
IsDisposed = true; | |
if (Instance.channels.TryGetValue(_channel, out var handlers)) | |
handlers.Remove(_handler); | |
} | |
} | |
public Subscription Subscribe<T>(EventHandler<T> handler) where T : EventRecord | |
{ | |
var channelType = typeof(T); | |
List<EventHandler<EventRecord>> listeners; | |
if (channels.TryGetValue(channelType, out var channel)) | |
listeners = channel; | |
else | |
channels.Add(channelType, listeners = new()); | |
listeners.Add(handler as EventHandler<EventRecord>); | |
return new Subscription(channelType, handler as EventHandler<EventRecord>); | |
} | |
public int Publish<T>(object sender, T arg) where T: EventRecord | |
{ | |
if (!channels.TryGetValue(arg.GetType(), out var handlers)) return 0; | |
foreach (var handler in handlers) | |
{ | |
handler(sender, arg); | |
} | |
return handlers.Count; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The EventBus is arranged by 'channels', which are an EventRecord, this encourages/forces | |
// usage of proper objects for passing events, it's not compatable with | |
// Godot's Signal system out of the box though. | |
// Declare your data container, I prefer using Records but EventArgs work as well (with modifying EventBus) | |
public record EntityUpdateEventArgs(Node Entity, Vector2 Position) : EventRecord; | |
public class EventBusExample | |
{ | |
void Example() | |
{ | |
// Subscriptions are done via the type of EventArgs used, you get the same | |
// type back as your arg, along with the identify of who sent the message. | |
EventBus.Instance.Subscribe<EntityUpdateEventArgs>((sender, arg) => | |
{ | |
Console.WriteLine($"Hello from EntityUpdateHandler! {arg.Entity}"); | |
}); | |
// Call Publish to notify any subscribers of an event, providing an appropriate instance of | |
// the subscribed type. All subscribers to that message type will be notified in no particular | |
// order | |
EventBus.Instance.Publish(this, new EntityUpdateEventArgs(new Node2D(), Vector2.Zero)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment