#nullable enable using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using ShrinkEventBus; namespace ShrinkCommand.Integration { public static class ShrinkCommandEventBusBridge { [ShrinkEventSubscriber(OwnerId = "shrink.command", DefaultBus = "game")] private sealed class CommandRequestSubscriber { [ShrinkSubscribe(Priority = ShrinkEventPriority.Lowest)] private UniTask OnExecuteRequest(ShrinkCommandExecuteRequestEvent eventArgs) { return HandleExecuteRequestAsync(eventArgs); } } private sealed class ServiceRegistration { public string ServiceName = ShrinkCommandConstants.DefaultServiceName; public ShrinkCommandService Service = null!; public IShrinkCommandSource? DefaultSource; public Action? ExecutingHandler; public Action? ExecutedHandler; } private static readonly object SyncRoot = new(); private static readonly Dictionary RegisteredByName = new(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RegisteredByService = new(); private static IDisposable? _requestHandlerBinding; #if UNITY_5_3_OR_NEWER [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] #endif 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(); _requestHandlerBinding?.Dispose(); _requestHandlerBinding = null; } } 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 RequestCommandAsync( string rawInput, IShrinkCommandSource? source = null, string? serviceName = null) { var requestEvent = new ShrinkCommandExecuteRequestEvent { ServiceName = NormalizeServiceName(serviceName), RawInput = rawInput ?? string.Empty, Source = source }; await EventBus.PostAsync(requestEvent); return requestEvent.ExecutionResult ?? ShrinkCommandExecutionResult.Failure("没有命令桥处理该请求。"); } private static void EnsureInitialized() { if (_requestHandlerBinding != null) return; _requestHandlerBinding = EventBus.Attach(new CommandRequestSubscriber()); } 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.Post(new ShrinkCommandExecutingEvent { ServiceName = registration.ServiceName, RawInput = info.RawInput, CommandPath = info.Command.Path, SourceId = info.Source.SourceId, SourceDisplayName = info.Source.DisplayName, Arguments = new Dictionary(info.Arguments, StringComparer.OrdinalIgnoreCase) }); } private static void PublishExecuted(ServiceRegistration registration, ShrinkCommandExecutedInfo info) { var commandPath = info.Command?.Path ?? string.Empty; var arguments = new Dictionary(info.Arguments, StringComparer.OrdinalIgnoreCase); EventBus.Post(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.Post(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(); } } }