Files
RobustToolbox/Robust.Shared/GameObjects/EntityEventBus.Ordering.cs
Pieter-Jan Briers fa0d4da6d1 Implement subscription ordering into EventBus. (#1823)
* Implement subscription ordering into EventBus.

* Topological helpers, rest of code uses shared topological sort, fix bugs.

* Fix tests.

Didn't realize that multi-subscriptions are allowed on input handlers like that??

* Improve and use topological sort helpers more.
2021-06-14 21:34:30 +02:00

96 lines
3.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Utility;
namespace Robust.Shared.GameObjects
{
internal partial class EntityEventBus
{
// TODO: Topological sort is currently done every time an event is emitted.
// This should be fine for low-volume stuff like interactions, but definitely not for anything high volume.
// Not sure if we could pre-cache the topological sort, here.
// Ordered event raising is slow so if this event has any ordering dependencies we use a slower path.
private readonly HashSet<Type> _orderedEvents = new();
private void ProcessSingleEventOrdered(EventSource source, object eventArgs, Type eventType)
{
var found = new List<(EventHandler, OrderingData?)>();
CollectBroadcastOrdered(source, eventType, found);
DispatchOrderedEvents(eventArgs, found);
}
private void CollectBroadcastOrdered(
EventSource source,
Type eventType,
List<(EventHandler, OrderingData?)> found)
{
if (!_eventSubscriptions.TryGetValue(eventType, out var subs))
return;
foreach (var handler in subs)
{
if ((handler.Mask & source) != 0)
found.Add((handler.Handler, handler.Ordering));
}
}
private void RaiseLocalOrdered<TEvent>(
EntityUid uid,
TEvent args,
bool broadcast)
where TEvent : EntityEventArgs
{
var found = new List<(EventHandler, OrderingData?)>();
if (broadcast)
CollectBroadcastOrdered(EventSource.Local, typeof(TEvent), found);
_eventTables.CollectOrdered(uid, typeof(TEvent), found);
DispatchOrderedEvents(args, found);
if (broadcast)
ProcessAwaitingMessages(EventSource.Local, args, typeof(TEvent));
}
private static void DispatchOrderedEvents(object eventArgs, List<(EventHandler, OrderingData?)> found)
{
var nodes = TopologicalSort.FromBeforeAfter(
found.Where(f => f.Item2 != null),
n => n.Item2!.OrderType,
n => n.Item1!,
n => n.Item2!.Before ?? Array.Empty<Type>(),
n => n.Item2!.After ?? Array.Empty<Type>(),
allowMissing: true);
foreach (var handler in TopologicalSort.Sort(nodes))
{
handler(eventArgs);
}
// Go over all handlers that don't have ordering so weren't included in the sort.
foreach (var (handler, orderData) in found)
{
if (orderData == null)
handler(eventArgs);
}
}
private void HandleOrderRegistration(Type eventType, OrderingData? data)
{
if (data == null)
return;
if (data.Before != null || data.After != null)
_orderedEvents.Add(eventType);
}
private sealed record OrderingData(Type OrderType, Type[]? Before, Type[]? After);
}
}