feat(cordis): 接入上下文组合与模组事务热替换
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using ShrinkEventBus;
|
||||
|
||||
namespace ShrinkCommand.Integration
|
||||
{
|
||||
public static class ShrinkCommandEventBusBridge
|
||||
{
|
||||
private sealed class ServiceRegistration
|
||||
{
|
||||
public string ServiceName = ShrinkCommandConstants.DefaultServiceName;
|
||||
public ShrinkCommandService Service = null!;
|
||||
public IShrinkCommandSource? DefaultSource;
|
||||
public Action<ShrinkCommandExecutingInfo>? ExecutingHandler;
|
||||
public Action<ShrinkCommandExecutedInfo>? ExecutedHandler;
|
||||
}
|
||||
|
||||
private static readonly object SyncRoot = new();
|
||||
private static readonly Dictionary<string, ServiceRegistration> RegisteredByName = new(StringComparer.OrdinalIgnoreCase);
|
||||
private static readonly Dictionary<ShrinkCommandService, ServiceRegistration> RegisteredByService = new();
|
||||
private static bool _initialized;
|
||||
|
||||
[UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void ResetStaticState()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
foreach (var registration in RegisteredByService.Values)
|
||||
{
|
||||
if (registration.ExecutingHandler != null)
|
||||
registration.Service.OnCommandExecuting -= registration.ExecutingHandler;
|
||||
if (registration.ExecutedHandler != null)
|
||||
registration.Service.OnCommandExecuted -= registration.ExecutedHandler;
|
||||
}
|
||||
|
||||
RegisteredByService.Clear();
|
||||
RegisteredByName.Clear();
|
||||
_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterService(
|
||||
ShrinkCommandService service,
|
||||
ShrinkCommandEventBusBridgeOptions? options = null)
|
||||
{
|
||||
if (service == null)
|
||||
throw new ArgumentNullException(nameof(service));
|
||||
|
||||
EnsureInitialized();
|
||||
options ??= new ShrinkCommandEventBusBridgeOptions();
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (RegisteredByService.TryGetValue(service, out var existing))
|
||||
{
|
||||
existing.DefaultSource = options.DefaultSource;
|
||||
if (!string.Equals(existing.ServiceName, options.ServiceName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
RegisteredByName.Remove(existing.ServiceName);
|
||||
existing.ServiceName = NormalizeServiceName(options.ServiceName);
|
||||
RegisteredByName[existing.ServiceName] = existing;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var registration = new ServiceRegistration
|
||||
{
|
||||
ServiceName = NormalizeServiceName(options.ServiceName),
|
||||
Service = service,
|
||||
DefaultSource = options.DefaultSource
|
||||
};
|
||||
registration.ExecutingHandler = info => PublishExecuting(registration, info);
|
||||
registration.ExecutedHandler = info => PublishExecuted(registration, info);
|
||||
service.OnCommandExecuting += registration.ExecutingHandler;
|
||||
service.OnCommandExecuted += registration.ExecutedHandler;
|
||||
RegisteredByService[service] = registration;
|
||||
RegisteredByName[registration.ServiceName] = registration;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnregisterService(ShrinkCommandService service)
|
||||
{
|
||||
if (service == null)
|
||||
return;
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (!RegisteredByService.TryGetValue(service, out var registration))
|
||||
return;
|
||||
|
||||
if (registration.ExecutingHandler != null)
|
||||
service.OnCommandExecuting -= registration.ExecutingHandler;
|
||||
if (registration.ExecutedHandler != null)
|
||||
service.OnCommandExecuted -= registration.ExecutedHandler;
|
||||
|
||||
RegisteredByService.Remove(service);
|
||||
RegisteredByName.Remove(registration.ServiceName);
|
||||
}
|
||||
}
|
||||
|
||||
public static async UniTask<ShrinkCommandExecutionResult> RequestCommandAsync(
|
||||
string rawInput,
|
||||
IShrinkCommandSource? source = null,
|
||||
string? serviceName = null)
|
||||
{
|
||||
var requestEvent = new ShrinkCommandExecuteRequestEvent
|
||||
{
|
||||
ServiceName = NormalizeServiceName(serviceName),
|
||||
RawInput = rawInput ?? string.Empty,
|
||||
Source = source
|
||||
};
|
||||
|
||||
await EventBus.TriggerEventAsync(requestEvent);
|
||||
return requestEvent.ExecutionResult ?? ShrinkCommandExecutionResult.Failure("没有命令桥处理该请求。");
|
||||
}
|
||||
|
||||
private static void EnsureInitialized()
|
||||
{
|
||||
if (_initialized)
|
||||
return;
|
||||
|
||||
_initialized = true;
|
||||
EventBus.RegisterEvent<ShrinkCommandExecuteRequestEvent>(HandleExecuteRequestAsync, EventPriority.LOWEST);
|
||||
}
|
||||
|
||||
private static async UniTask HandleExecuteRequestAsync(ShrinkCommandExecuteRequestEvent eventArgs)
|
||||
{
|
||||
if (eventArgs == null || eventArgs.IsHandled || eventArgs.IsCanceled)
|
||||
return;
|
||||
|
||||
ServiceRegistration? registration;
|
||||
lock (SyncRoot)
|
||||
{
|
||||
RegisteredByName.TryGetValue(NormalizeServiceName(eventArgs.ServiceName), out registration);
|
||||
}
|
||||
|
||||
if (registration == null)
|
||||
{
|
||||
eventArgs.IsHandled = true;
|
||||
eventArgs.ExecutionResult = ShrinkCommandExecutionResult.Failure(
|
||||
$"未找到命令服务: {NormalizeServiceName(eventArgs.ServiceName)}");
|
||||
return;
|
||||
}
|
||||
|
||||
var source = eventArgs.Source ?? registration.DefaultSource;
|
||||
if (source == null)
|
||||
{
|
||||
eventArgs.IsHandled = true;
|
||||
eventArgs.ExecutionResult = ShrinkCommandExecutionResult.Failure("命令请求缺少来源对象。");
|
||||
return;
|
||||
}
|
||||
|
||||
eventArgs.ExecutionResult = await registration.Service.ExecuteAsync(source, eventArgs.RawInput);
|
||||
eventArgs.IsHandled = true;
|
||||
}
|
||||
|
||||
private static void PublishExecuting(ServiceRegistration registration, ShrinkCommandExecutingInfo info)
|
||||
{
|
||||
EventBus.TriggerEvent(new ShrinkCommandExecutingEvent
|
||||
{
|
||||
ServiceName = registration.ServiceName,
|
||||
RawInput = info.RawInput,
|
||||
CommandPath = info.Command.Path,
|
||||
SourceId = info.Source.SourceId,
|
||||
SourceDisplayName = info.Source.DisplayName,
|
||||
Arguments = new Dictionary<string, string>(info.Arguments, StringComparer.OrdinalIgnoreCase)
|
||||
});
|
||||
}
|
||||
|
||||
private static void PublishExecuted(ServiceRegistration registration, ShrinkCommandExecutedInfo info)
|
||||
{
|
||||
var commandPath = info.Command?.Path ?? string.Empty;
|
||||
var arguments = new Dictionary<string, string>(info.Arguments, StringComparer.OrdinalIgnoreCase);
|
||||
EventBus.TriggerEvent(new ShrinkCommandExecutedEvent
|
||||
{
|
||||
ServiceName = registration.ServiceName,
|
||||
RawInput = info.RawInput,
|
||||
CommandPath = commandPath,
|
||||
SourceId = info.Source.SourceId,
|
||||
SourceDisplayName = info.Source.DisplayName,
|
||||
Arguments = arguments,
|
||||
IsSuccess = info.Result.IsSuccess,
|
||||
Message = info.Result.Message
|
||||
});
|
||||
|
||||
if (info.Result.IsSuccess)
|
||||
return;
|
||||
|
||||
EventBus.TriggerEvent(new ShrinkCommandFailedEvent
|
||||
{
|
||||
ServiceName = registration.ServiceName,
|
||||
RawInput = info.RawInput,
|
||||
CommandPath = commandPath,
|
||||
SourceId = info.Source.SourceId,
|
||||
SourceDisplayName = info.Source.DisplayName,
|
||||
Arguments = arguments,
|
||||
ErrorMessage = info.Result.Message
|
||||
});
|
||||
}
|
||||
|
||||
private static string NormalizeServiceName(string? serviceName)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(serviceName) ? ShrinkCommandConstants.DefaultServiceName : serviceName.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user