feat: 事件复制
This commit is contained in:
11 files changed
+546
-116
No files matched your search
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("ShrinkNetwork.Integration.EventBus")]
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43e85f78c24f1d94ba7d06a4fa37e937
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+24
-2
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace ShrinkEventBus
|
||||
{
|
||||
@@ -10,16 +11,26 @@ namespace ShrinkEventBus
|
||||
private bool _isCanceled;
|
||||
private EventResult _result = EventResult.DEFAULT;
|
||||
|
||||
[IgnoreDataMember]
|
||||
public EventHandlerInfo? CurrentHandler { get; internal set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public DateTime EventTime { get; private set; } = DateTime.UtcNow;
|
||||
|
||||
[IgnoreDataMember]
|
||||
public Guid EventId { get; private set; } = Guid.NewGuid();
|
||||
|
||||
[IgnoreDataMember]
|
||||
public bool IsCancelable { get; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public bool HasResult { get; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public EventPriority? Phase { get; private set; }
|
||||
|
||||
internal bool IsInPool { get; set; }
|
||||
internal Action<EventBase> ReleaseAction { get; set; }
|
||||
internal Action<EventBase>? ReleaseAction { get; set; }
|
||||
|
||||
protected EventBase()
|
||||
{
|
||||
@@ -44,6 +55,16 @@ namespace ShrinkEventBus
|
||||
OnReset();
|
||||
}
|
||||
|
||||
internal void PrepareForDispatch()
|
||||
{
|
||||
CurrentHandler = null;
|
||||
Phase = null;
|
||||
EventTime = DateTime.UtcNow;
|
||||
EventId = Guid.NewGuid();
|
||||
_listenerList.Clear();
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public bool IsCanceled
|
||||
{
|
||||
get => _isCanceled;
|
||||
@@ -54,6 +75,7 @@ namespace ShrinkEventBus
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public EventResult Result
|
||||
{
|
||||
get => _result;
|
||||
@@ -82,4 +104,4 @@ namespace ShrinkEventBus
|
||||
ReleaseAction?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
-19
@@ -9,6 +9,8 @@ namespace ShrinkEventBus
|
||||
{
|
||||
public static class EventBus
|
||||
{
|
||||
public static event Action<EventBase, Type> OnEventTriggered;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static bool EnableDebugRecord { get; set; }
|
||||
public static event Action<EventBase, string, string> OnEventTriggeredForEditor;
|
||||
@@ -189,16 +191,11 @@ namespace ShrinkEventBus
|
||||
public static async UniTask<bool> TriggerEventAsync<TEvent>(TEvent eventArgs) where TEvent : EventBase
|
||||
{
|
||||
EnsureAutoManagerInitialized();
|
||||
eventArgs.PrepareForDispatch();
|
||||
var collection = EventCache<TEvent>.List;
|
||||
if (collection == null) return false;
|
||||
|
||||
var handlers = collection.GetHandlers();
|
||||
var handlers = collection?.GetHandlers() ?? Array.Empty<EventHandlerInfo>();
|
||||
var wasHandled = false;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
RecordEditorTrace(handlers, eventArgs, typeof(TEvent));
|
||||
#endif
|
||||
|
||||
for (var i = 0; i < handlers.Length; i++)
|
||||
{
|
||||
var handlerInfo = handlers[i];
|
||||
@@ -225,6 +222,7 @@ namespace ShrinkEventBus
|
||||
}
|
||||
|
||||
eventArgs.CurrentHandler = null;
|
||||
CompleteEventDispatch(eventArgs, typeof(TEvent), handlers);
|
||||
return wasHandled;
|
||||
}
|
||||
|
||||
@@ -232,16 +230,11 @@ namespace ShrinkEventBus
|
||||
public static bool TriggerEvent<TEvent>(TEvent eventArgs) where TEvent : EventBase
|
||||
{
|
||||
EnsureAutoManagerInitialized();
|
||||
eventArgs.PrepareForDispatch();
|
||||
var collection = EventCache<TEvent>.List;
|
||||
if (collection == null) return false;
|
||||
|
||||
var handlers = collection.GetHandlers();
|
||||
var handlers = collection?.GetHandlers() ?? Array.Empty<EventHandlerInfo>();
|
||||
var wasHandled = false;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
RecordEditorTrace(handlers, eventArgs, typeof(TEvent));
|
||||
#endif
|
||||
|
||||
for (var i = 0; i < handlers.Length; i++)
|
||||
{
|
||||
var handlerInfo = handlers[i];
|
||||
@@ -256,7 +249,8 @@ namespace ShrinkEventBus
|
||||
}
|
||||
else if (handlerInfo.Handler is Func<TEvent, UniTask> asyncHandler)
|
||||
{
|
||||
FireAndForgetSafe(() => asyncHandler(eventArgs),
|
||||
var detachedEvent = EventCloneUtility.CloneForDetachedDispatch(eventArgs);
|
||||
FireAndForgetSafe(() => asyncHandler(detachedEvent),
|
||||
$"{handlerInfo.DisplayDeclaringType.Name}.{handlerInfo.DisplayMethodName}");
|
||||
wasHandled = true;
|
||||
}
|
||||
@@ -269,6 +263,7 @@ namespace ShrinkEventBus
|
||||
}
|
||||
|
||||
eventArgs.CurrentHandler = null;
|
||||
CompleteEventDispatch(eventArgs, typeof(TEvent), handlers);
|
||||
return wasHandled;
|
||||
}
|
||||
|
||||
@@ -281,15 +276,26 @@ namespace ShrinkEventBus
|
||||
});
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CompleteEventDispatch(EventBase eventArgs, Type eventType, EventHandlerInfo[] handlers)
|
||||
{
|
||||
OnEventTriggered?.Invoke(eventArgs, eventType);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private static void RecordEditorTrace<TEvent>(EventHandlerInfo[] handlers, TEvent eventArgs, Type eventType)
|
||||
where TEvent : EventBase
|
||||
if (!EnableDebugRecord) return;
|
||||
PrepareEditorTrace(handlers, eventArgs);
|
||||
OnEventTriggeredForEditor?.Invoke(eventArgs, eventType.Name, GetSenderInfo());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private static void PrepareEditorTrace(EventHandlerInfo[] handlers, EventBase eventArgs)
|
||||
{
|
||||
if (!EnableDebugRecord) return;
|
||||
var listenerList = eventArgs.GetListenerList();
|
||||
listenerList.Clear();
|
||||
for (var i = 0; i < handlers.Length; i++)
|
||||
listenerList.Add(handlers[i]);
|
||||
OnEventTriggeredForEditor?.Invoke(eventArgs, eventType.Name, GetSenderInfo());
|
||||
}
|
||||
|
||||
private static string GetSenderInfo()
|
||||
@@ -389,4 +395,4 @@ namespace ShrinkEventBus
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ShrinkEventBus
|
||||
{
|
||||
internal static class EventCloneUtility
|
||||
{
|
||||
private static readonly object CacheLock = new();
|
||||
private static readonly Dictionary<Type, FieldInfo[]> FieldCache = new();
|
||||
|
||||
public static TEvent CloneForDetachedDispatch<TEvent>(TEvent source) where TEvent : EventBase
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
|
||||
if (Activator.CreateInstance(source.GetType()) is not TEvent clone)
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot clone event type {source.GetType().FullName}. A public parameterless constructor is required.");
|
||||
|
||||
CopyFields(source, clone);
|
||||
CopyListenerSnapshot(source, clone);
|
||||
clone.ReleaseAction = null;
|
||||
clone.IsInPool = false;
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static void CopyFields(EventBase source, EventBase target)
|
||||
{
|
||||
var fields = GetCopyableFields(source.GetType());
|
||||
for (var i = 0; i < fields.Length; i++)
|
||||
fields[i].SetValue(target, fields[i].GetValue(source));
|
||||
}
|
||||
|
||||
private static void CopyListenerSnapshot(EventBase source, EventBase target)
|
||||
{
|
||||
var targetListeners = target.GetListenerList();
|
||||
targetListeners.Clear();
|
||||
|
||||
var sourceHandlers = source.GetListenerList().GetHandlers();
|
||||
for (var i = 0; i < sourceHandlers.Length; i++)
|
||||
targetListeners.Add(sourceHandlers[i]);
|
||||
}
|
||||
|
||||
private static FieldInfo[] GetCopyableFields(Type type)
|
||||
{
|
||||
lock (CacheLock)
|
||||
{
|
||||
if (FieldCache.TryGetValue(type, out var cached))
|
||||
return cached;
|
||||
|
||||
var fields = new List<FieldInfo>();
|
||||
var currentType = type;
|
||||
while (currentType != null && currentType != typeof(object))
|
||||
{
|
||||
var declaredFields = currentType.GetFields(BindingFlags.Instance | BindingFlags.Public |
|
||||
BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
|
||||
for (var i = 0; i < declaredFields.Length; i++)
|
||||
{
|
||||
var field = declaredFields[i];
|
||||
if (field.IsStatic)
|
||||
continue;
|
||||
if (ShouldSkipField(field))
|
||||
continue;
|
||||
|
||||
fields.Add(field);
|
||||
}
|
||||
|
||||
currentType = currentType.BaseType;
|
||||
}
|
||||
|
||||
cached = fields.ToArray();
|
||||
FieldCache[type] = cached;
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldSkipField(FieldInfo field)
|
||||
{
|
||||
return field.Name is "_listenerList"
|
||||
or "<ReleaseAction>k__BackingField"
|
||||
or "<IsInPool>k__BackingField";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 876564c111495b2489870b29ab75da5e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user