using System; using System.Collections.Generic; using Newtonsoft.Json; using UnityEngine; namespace ShrinkModFramework { public static class ShrinkModNetworkManager { private sealed class HandlerRegistration { public Type PayloadType; public Action Handler; } private static readonly Dictionary Handlers = new(StringComparer.Ordinal); private static IShrinkModNetworkTransport _transport; private static bool _enabled = true; private static bool _verboseLogging; public static bool HasTransport => _transport != null; public static void Configure(bool enabled, bool verboseLogging) { _enabled = enabled; _verboseLogging = verboseLogging; } public static void SetTransport(IShrinkModNetworkTransport transport) { if (_transport != null) _transport.OnEnvelopeReceived -= OnEnvelopeReceived; _transport = transport; if (_transport != null) { _transport.OnEnvelopeReceived += OnEnvelopeReceived; if (_verboseLogging) Debug.Log("[ShrinkModNetwork] 已绑定网络传输层。"); } } public static void RegisterHandler(string modId, string channel, Action handler) { if (string.IsNullOrWhiteSpace(modId)) throw new ArgumentException("模组 ID 不能为空。", nameof(modId)); if (string.IsNullOrWhiteSpace(channel)) throw new ArgumentException("频道名不能为空。", nameof(channel)); if (handler == null) throw new ArgumentNullException(nameof(handler)); var key = BuildKey(modId, channel); if (Handlers.ContainsKey(key)) throw new InvalidOperationException($"网络频道已注册:{key}"); Handlers.Add(key, new HandlerRegistration { PayloadType = typeof(T), Handler = (context, payload) => handler(context, (T)payload) }); } public static void SendToServer(string modId, string channel, T payload, string senderPeerId = null) { SendInternal(modId, channel, payload, senderPeerId, null, ShrinkModNetworkTarget.Server); } public static void SendToAllClients(string modId, string channel, T payload, string senderPeerId = null) { SendInternal(modId, channel, payload, senderPeerId, null, ShrinkModNetworkTarget.AllClients); } public static void SendToClient(string modId, string channel, T payload, string targetPeerId, string senderPeerId = null) { SendInternal(modId, channel, payload, senderPeerId, targetPeerId, ShrinkModNetworkTarget.SpecificClient); } internal static void ResetForDomainReload() { Handlers.Clear(); SetTransport(null); _enabled = true; _verboseLogging = false; } internal static void UnregisterHandlers(string modId) { if (string.IsNullOrWhiteSpace(modId)) return; var prefix = modId.Trim() + "::"; var keys = new List(); foreach (var key in Handlers.Keys) { if (key.StartsWith(prefix, StringComparison.Ordinal)) keys.Add(key); } foreach (var key in keys) Handlers.Remove(key); } private static void SendInternal(string modId, string channel, T payload, string senderPeerId, string targetPeerId, ShrinkModNetworkTarget target) { if (!_enabled) return; if (_transport == null) { Debug.LogWarning($"[ShrinkModNetwork] 当前没有传输层,消息 {modId}/{channel} 被丢弃。"); return; } var envelope = new ShrinkModNetworkEnvelope { ModId = modId, Channel = channel, PayloadJson = JsonConvert.SerializeObject(payload), SenderPeerId = senderPeerId, TargetPeerId = targetPeerId, Target = target }; _transport.Send(envelope); } private static void OnEnvelopeReceived(ShrinkModNetworkEnvelope envelope) { if (!_enabled || envelope == null) return; var key = BuildKey(envelope.ModId, envelope.Channel); if (!Handlers.TryGetValue(key, out var registration)) { if (_verboseLogging) Debug.LogWarning($"[ShrinkModNetwork] 未找到频道处理器:{key}"); return; } try { var payload = JsonConvert.DeserializeObject(envelope.PayloadJson, registration.PayloadType); var context = new ShrinkModNetworkMessageContext( envelope.SenderPeerId, envelope.TargetPeerId, envelope.Target, _transport != null && _transport.IsServer, _transport != null && _transport.IsClient); registration.Handler(context, payload); } catch (Exception ex) { Debug.LogError($"[ShrinkModNetwork] 处理频道 {key} 失败:{ex.Message}"); } } private static string BuildKey(string modId, string channel) { if (string.IsNullOrWhiteSpace(modId)) throw new ArgumentException("模组 ID 不能为空。", nameof(modId)); if (string.IsNullOrWhiteSpace(channel)) throw new ArgumentException("频道名不能为空。", nameof(channel)); return $"{modId.Trim()}::{channel.Trim()}"; } } }