Skip to content

Commit

Permalink
Clean up event bus
Browse files Browse the repository at this point in the history
  • Loading branch information
Tornado-Technology committed Jul 12, 2024
1 parent 50ea7ae commit 03c90ce
Show file tree
Hide file tree
Showing 34 changed files with 180 additions and 115 deletions.
2 changes: 1 addition & 1 deletion Hypercube.Client/Graphics/Event/MainWindowClosedEvent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Hypercube.Client.Graphics.Windows;
using Hypercube.Shared.EventBus.Events;
using Hypercube.Shared.EventBus.Events.Events;

namespace Hypercube.Client.Graphics.Event;

Expand Down
3 changes: 2 additions & 1 deletion Hypercube.Client/Graphics/Event/WindowClosed.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using Hypercube.Client.Graphics.Windows;
using Hypercube.Shared.EventBus.Events.Events;

namespace Hypercube.Client.Graphics.Event;

public readonly struct WindowClosedEvent(WindowRegistration registration)
public readonly struct WindowClosedEvent(WindowRegistration registration) : IEventArgs
{
public readonly WindowRegistration Registration = registration;
}
3 changes: 2 additions & 1 deletion Hypercube.Client/Graphics/Event/WindowFocusChangedEvent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Hypercube.Client.Graphics.Windows;
using Hypercube.Shared.EventBus.Events.Events;

namespace Hypercube.Client.Graphics.Event;

public readonly record struct WindowFocusChangedEvent(WindowRegistration Registration, bool Focused);
public readonly record struct WindowFocusChangedEvent(WindowRegistration Registration, bool Focused) : IEventArgs;
4 changes: 1 addition & 3 deletions Hypercube.Client/Runtimes/Runtime.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Hypercube.Client.Graphics;
using Hypercube.Client.Graphics.Event;
using Hypercube.Client.Graphics.Rendering;
using Hypercube.Client.Graphics.Event;
using Hypercube.Client.Runtimes.Loop;
using Hypercube.Shared.Dependency;
using Hypercube.Shared.EventBus;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Hypercube.Shared.EventBus.Events;
using Hypercube.Shared.EventBus.Events.Events;
using Hypercube.Shared.EventBus.Events.Handlers;

namespace Hypercube.Shared.Entities.Realisation.EventBus;

Expand All @@ -14,7 +16,7 @@ public void Subscribe<T>(IEventSubscriber subscriber, EventRefHandler<T> refHand
throw new NotImplementedException();
}

public void Raise(object receiver)
public void Raise(IEventArgs receiver)
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Hypercube.Shared.Entities.Realisation.Components;
using Hypercube.Shared.EventBus.Events.Events;

namespace Hypercube.Shared.Entities.Realisation.Events;

public readonly record struct ComponentAdded(EntityUid EntityUid, IComponent Component);
public readonly record struct ComponentAdded(EntityUid EntityUid, IComponent Component) : IEventArgs;
6 changes: 4 additions & 2 deletions Hypercube.Shared/Entities/Realisation/Events/EntityAdded.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
namespace Hypercube.Shared.Entities.Realisation.Events;
using Hypercube.Shared.EventBus.Events.Events;

public readonly record struct EntityAdded(EntityUid EntityUid);
namespace Hypercube.Shared.Entities.Realisation.Events;

public readonly record struct EntityAdded(EntityUid EntityUid) : IEventArgs;
6 changes: 4 additions & 2 deletions Hypercube.Shared/Entities/Realisation/Events/EntityRemoved.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
namespace Hypercube.Shared.Entities.Realisation.Events;
using Hypercube.Shared.EventBus.Events.Events;

public readonly record struct EntityRemoved(EntityUid EntityUid);
namespace Hypercube.Shared.Entities.Realisation.Events;

public readonly record struct EntityRemoved(EntityUid EntityUid) : IEventArgs;
143 changes: 81 additions & 62 deletions Hypercube.Shared/EventBus/EventBus.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
using System.Runtime.CompilerServices;
using Hypercube.Shared.EventBus.Events;
using Hypercube.Shared.EventBus.Events.Broadcast;
using Hypercube.Shared.EventBus.Events.Events;
using Hypercube.Shared.EventBus.Events.Exceptions;
using Hypercube.Shared.EventBus.Events.Handlers;

namespace Hypercube.Shared.EventBus;

public sealed class EventBus : IEventBus
{
private readonly Dictionary<Type, EventData> _eventData = new();
private readonly Dictionary<Type, EventRegistration> _eventRegistration = new();
private readonly Dictionary<IEventSubscriber, Dictionary<Type, BroadcastRegistration>> _inverseEventSubscriptions = new();

public void Raise<T>(ref T receiver) where T : IEventArgs
{
ProcessEvent(ref Unsafe.As<T, Unit>(ref receiver), typeof(T));
}

public void Raise<T>(T receiver) where T : IEventArgs
{
ProcessEvent(ref Unsafe.As<T, Unit>(ref receiver), typeof(T));
}

public void Raise(IEventArgs eventArgs)
{
var eventType = eventArgs.GetType();
ref var unitRef = ref ExtractUnitRef(ref eventArgs, eventType);

ProcessEvent(ref unitRef, eventType);
}

public void Subscribe<T>(IEventSubscriber subscriber, EventRefHandler<T> refHandler) where T : IEventArgs
{
SubscribeEventCommon<T>(subscriber, (ref Unit ev) =>
Expand All @@ -18,6 +39,8 @@ public void Subscribe<T>(IEventSubscriber subscriber, EventRefHandler<T> refHand
}, refHandler);
}

/// <exception cref="ArgumentNullException">Throws when subscriber is null</exception>
/// <exception cref="InvalidOperationException"></exception>
private void SubscribeEventCommon<T>(IEventSubscriber subscriber, RefHandler refHandler, object equality)
where T : IEventArgs
{
Expand All @@ -26,96 +49,92 @@ private void SubscribeEventCommon<T>(IEventSubscriber subscriber, RefHandler ref
var eventType = typeof(T);
var subscription = new BroadcastRegistration(refHandler, equality);

var subscriptions = GetEventData(eventType);
if (subscriptions.BroadcastRegistrations.Contains(subscription))
throw new InvalidOperationException();

subscriptions.BroadcastRegistrations.Add(subscription);

Dictionary<Type, BroadcastRegistration>? inverseSubs;
if (!_inverseEventSubscriptions.TryGetValue(subscriber, out inverseSubs))
{
inverseSubs = new Dictionary<Type, BroadcastRegistration>();
_inverseEventSubscriptions[subscriber] = inverseSubs;
}
var subscriptions = GetEventRegistration(eventType);
subscriptions.Add(subscription);

if (!inverseSubs.TryAdd(eventType, subscription))
throw new InvalidOperationException();
}

private EventData GetEventData(Type eventType)
{
if (_eventData.TryGetValue(eventType, out var found))
return found;
var inverseSubscriptions = GetEventInverseSubscription(subscriber);
if (inverseSubscriptions.TryAdd(eventType, subscription))
return;

var list = new List<BroadcastRegistration>();
var data = new EventData(list);

return _eventData[eventType] = data;
throw new InvalidOperationException();
}

/// <exception cref="ArgumentNullException">Throws when subscriber is null</exception>
/// <exception cref="InvalidOperationException"></exception>
public void Unsubscribe<T>(IEventSubscriber subscriber) where T : IEventArgs
{
ArgumentNullException.ThrowIfNull(subscriber);

var eventType = typeof(T);

if (_inverseEventSubscriptions.TryGetValue(subscriber, out var inverse)
&& inverse.TryGetValue(eventType, out var tuple))
UnsubscribeEvent(eventType, tuple, subscriber);
}

private void UnsubscribeEvent(Type evType, BroadcastRegistration tuple, IEventSubscriber subscriber)
{
if (_eventData.TryGetValue(evType, out var subs) &&
subs.BroadcastRegistrations.Contains(tuple))
{
subs.BroadcastRegistrations.Remove(tuple);
}
var inverseSubscriptions = GetEventInverseSubscription(subscriber);
if (!inverseSubscriptions.TryGetValue(eventType, out var registration))
throw new InvalidOperationException();

if (_inverseEventSubscriptions.TryGetValue(subscriber, out var inverse) && inverse.ContainsKey(evType))
inverse.Remove(evType);
Unsubscribe(eventType, registration, subscriber);
}

public void Raise(object receiver)
/// <exception cref="UnregisteredEventException"></exception>
/// <exception cref="InvalidOperationException"></exception>
private void Unsubscribe(Type eventType, BroadcastRegistration registration, IEventSubscriber subscriber)
{
ArgumentNullException.ThrowIfNull(receiver);

var evType = receiver.GetType();
ref var unitRef = ref ExtractUnitRef(ref receiver, evType);
var eventRegistration = GetEventRegistration(eventType, false);
eventRegistration.Remove(registration);

ProcessEvent(ref unitRef, evType);
}
public void Raise<T>(ref T receiver) where T : IEventArgs
{
ProcessEvent(ref Unsafe.As<T, Unit>(ref receiver), typeof(T));
}

public void Raise<T>(T receiver) where T : IEventArgs
{
ProcessEvent(ref Unsafe.As<T, Unit>(ref receiver), typeof(T));
var inverseSubscriptions = GetEventInverseSubscription(subscriber, false);
inverseSubscriptions.Remove(eventType);
}

private void ProcessEvent(ref Unit unitRef, Type evType)
private void ProcessEvent(ref Unit unitRef, Type eventType)
{
if (!_eventData!.TryGetValue(evType, out var data))
if (!_eventRegistration.TryGetValue(eventType, out var registration))
return;

ProcessEventCore(ref unitRef, data);
ProcessEventCore(ref unitRef, registration);
}

private void ProcessEventCore(ref Unit unitRef, EventData data)
private void ProcessEventCore(ref Unit unitRef, EventRegistration registration)
{
foreach (var handler in data.BroadcastRegistrations)
foreach (var handler in registration.BroadcastRegistrations)
{
handler.Handler(ref unitRef);
}
}

/// <param name="eventType">Type of event whose registration we want to receive.</param>
/// <param name="autoRegistration">Allows you to control the automatic registration of an event if it does not exist.</param>
/// <exception cref="UnregisteredEventException">If <c>autoRegistration</c> is <c>false</c>, it will throw an exception if registration is not found.</exception>
private EventRegistration GetEventRegistration(Type eventType, bool autoRegistration = true)
{
if (_eventRegistration.TryGetValue(eventType, out var found))
return found;

if (!autoRegistration)
throw new UnregisteredEventException(eventType);

return _eventRegistration[eventType] = new EventRegistration();
}

/// <exception cref="InvalidOperationException"></exception>
private Dictionary<Type, BroadcastRegistration> GetEventInverseSubscription(IEventSubscriber subscriber, bool creating = true)
{
if (_inverseEventSubscriptions.TryGetValue(subscriber, out var subscriptions))
return subscriptions;

if (!creating)
throw new InvalidOperationException();

return _inverseEventSubscriptions[subscriber] = new Dictionary<Type, BroadcastRegistration>();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ref Unit ExtractUnitRef(ref object obj, Type objType)
private static ref Unit ExtractUnitRef(ref IEventArgs eventArgs, Type objType)
{
//return ref Unsafe.As<IEventArgs, Unit>(ref eventArgs);

// Why not only unit?
return ref objType.IsValueType
? ref Unsafe.As<object, UnitBox>(ref obj).Value
: ref Unsafe.As<object, Unit>(ref obj);
? ref Unsafe.As<IEventArgs, UnitBox>(ref eventArgs).Value
: ref Unsafe.As<IEventArgs, Unit>(ref eventArgs);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Hypercube.Shared.EventBus.Events.Broadcast;
using Hypercube.Shared.EventBus.Events.Handlers;

namespace Hypercube.Shared.EventBus.Events.Broadcast;

public sealed class BroadcastRegistration : IEquatable<BroadcastRegistration>
{
Expand Down
13 changes: 0 additions & 13 deletions Hypercube.Shared/EventBus/Events/EventData.cs

This file was deleted.

4 changes: 0 additions & 4 deletions Hypercube.Shared/EventBus/Events/EventHandler.cs

This file was deleted.

31 changes: 31 additions & 0 deletions Hypercube.Shared/EventBus/Events/EventRegistration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Hypercube.Shared.EventBus.Events.Broadcast;

namespace Hypercube.Shared.EventBus.Events;

/// <summary>
/// Saves information about a specific event.
/// </summary>
public readonly struct EventRegistration()
{
private readonly HashSet<BroadcastRegistration> _broadcastRegistrations = new();

public IReadOnlySet<BroadcastRegistration> BroadcastRegistrations => _broadcastRegistrations;

/// <exception cref="InvalidOperationException"></exception>
public void Add(BroadcastRegistration registration)
{
if (_broadcastRegistrations.Contains(registration))
throw new InvalidOperationException();

_broadcastRegistrations.Add(registration);
}

/// <exception cref="InvalidOperationException"></exception>
public void Remove(BroadcastRegistration registration)
{
if (!_broadcastRegistrations.Contains(registration))
throw new InvalidOperationException();

_broadcastRegistrations.Remove(registration);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
namespace Hypercube.Shared.EventBus.Events;

public interface IEventArgs;

public abstract class EventArgs : IEventArgs;
namespace Hypercube.Shared.EventBus.Events.Events;

public abstract class CancellableEventArgs : EventArgs
{
Expand Down
3 changes: 3 additions & 0 deletions Hypercube.Shared/EventBus/Events/Events/EventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Hypercube.Shared.EventBus.Events.Events;

public abstract class EventArgs : IEventArgs;
3 changes: 3 additions & 0 deletions Hypercube.Shared/EventBus/Events/Events/IEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Hypercube.Shared.EventBus.Events.Events;

public interface IEventArgs;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Hypercube.Shared.EventBus.Events.Exceptions;

public sealed class UnregisteredEventException(Type registrationType) :
Exception($"Attempted to resolve unregistered event {registrationType.FullName}.");
3 changes: 3 additions & 0 deletions Hypercube.Shared/EventBus/Events/Handlers/EventRefHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Hypercube.Shared.EventBus.Events.Handlers;

public delegate void EventRefHandler<T>(ref T ev);
3 changes: 3 additions & 0 deletions Hypercube.Shared/EventBus/Events/Handlers/RefHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Hypercube.Shared.EventBus.Events.Handlers;

public delegate void RefHandler(ref Unit ev);
4 changes: 3 additions & 1 deletion Hypercube.Shared/EventBus/IEventBus.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Hypercube.Shared.EventBus.Events;
using Hypercube.Shared.EventBus.Events.Events;
using Hypercube.Shared.EventBus.Events.Handlers;

namespace Hypercube.Shared.EventBus;

Expand All @@ -10,7 +12,7 @@ public interface IEventBus
{
void Raise<T>(ref T receiver) where T : IEventArgs;
void Raise<T>(T receiver) where T : IEventArgs;
void Raise(object receiver);
void Raise(IEventArgs eventArgs);
void Subscribe<T>(IEventSubscriber subscriber, EventRefHandler<T> refHandler) where T : IEventArgs;
void Unsubscribe<T>(IEventSubscriber subscriber) where T : IEventArgs;
}
Loading

0 comments on commit 03c90ce

Please sign in to comment.