/* * @Author: 白哉 * @Description: * @Date: 2025年04月03日 星期四 11:04:31 * @Modify: */ using System.Collections.Generic; using Logic.AI; using Logic.CrashSight; using MemoryPack; using TH1_Logic.Core; using TH1_Logic.Net; using UnityEngine; namespace RuntimeData { public enum NetMode { None, Single, Multi, } public class PlayerConfirmData { public ulong MemberId; public float ConfirmTime; public float GameConfirmTime; public bool NeedForceUpdate; public PlayerConfirmData(ulong id) { MemberId = id; ConfirmTime = Time.time; GameConfirmTime = Time.time; NeedForceUpdate = false; } } // 网络信息 [MemoryPackable] public partial class NetData { // 网络模式 public NetMode Mode; // 当前操作的玩家 public uint CurPlayerId; // 地图哈希 [MemoryPackIgnore] public string MapHash; // SteamId => PlayerId 的索引 public Dictionary Players; // 所有玩家行为的序列 public List Actions; // 随机数种子 开始游戏时由房主进行初始化,整局游戏不可变 public int RandomSeed; private System.Random _random; // 玩家开始时间记录 [MemoryPackIgnore] public float PlayerStartTime; [MemoryPackIgnore] public Dictionary PlayerConfirm; [MemoryPackConstructor] public NetData() { Mode = NetMode.None; CurPlayerId = 0; Players = new Dictionary(); Actions = new List(); // 生成确定性种子(由房主生成并广播) RandomSeed = System.Environment.TickCount; PlayerConfirm = new Dictionary(); } // 这里单纯是因为深拷贝目前只用于 AI 演算,故不对 Players 和 Actions 赋值 public NetData(NetData copyData) { Mode = copyData.Mode; CurPlayerId = copyData.CurPlayerId; MapHash = copyData.MapHash; RandomSeed = copyData.RandomSeed; Players = new Dictionary(); Actions = new List(); PlayerConfirm = new Dictionary(); } // 这里单纯是因为深拷贝目前只用于 AI 演算,故不对 Players 和 Actions 赋值 public void DeepCopy(NetData copyData) { Mode = copyData.Mode; CurPlayerId = copyData.CurPlayerId; MapHash = copyData.MapHash; RandomSeed = copyData.RandomSeed; Players = new Dictionary(); Actions = new List(); PlayerConfirm = new Dictionary(); } // 只有 Action 的逻辑可以使用这个 Random, 因为要保证计数一致 public System.Random GetRandom(MapData map) { if (map != Main.MapData) return new System.Random(RandomSeed); if (_random == null) _random = new System.Random(RandomSeed); return _random; } public void RefreshPlayerNet(MapData mapData) { if (Mode != NetMode.Multi) return; if (LobbyManager.Instance.Lobby.IsLobbyOwner()) { // 创建 Player 的时候会分配一次 PlayerId,这里直接使用 foreach (var memberCiv in mapData.MapConfig.MultiCivs) { if (memberCiv.PlayerId == 0) continue; if (Players.ContainsKey(memberCiv.MemberId)) continue; Players[memberCiv.MemberId] = memberCiv.PlayerId; } // 添加其他人 foreach (var memberId in LobbyManager.Instance.Lobby.GetAllMemberIds()) { if (Players.ContainsKey(memberId)) continue; foreach (var player in mapData.PlayerMap.PlayerDataList) { if (Players.ContainsValue(player.Id)) continue; Players[memberId] = player.Id; break; } } } var selfMemberId = LobbyManager.Instance.Lobby.GetSelfMemberId(); if (Players.TryGetValue(selfMemberId, out var id)) mapData.PlayerMap.SelfPlayerId = id; PlayerConfirm.Clear(); foreach (var kv in Players) PlayerConfirm[kv.Key] = new PlayerConfirmData(kv.Key); } public void RefreshMapNet(MapData mapData) { // 地图哈希 MapHash = GetMapDataHash(mapData); } public uint GetActionVersion() { if (Actions.Count == 0) return 0; return Actions[^1].Version + 1; } public ulong GetmemberIdId(uint playerId) { foreach (var kv in Players) { if (kv.Value == playerId) return kv.Key; } return 0; } public void CompareEqual(NetData net) { var differences = new List(); // 使用序列化比较各个组件 MapData.CompareComponent(Mode, net.Mode, "Mode", differences); MapData.CompareComponent(CurPlayerId, net.CurPlayerId, "CurPlayerId", differences); MapData.CompareComponent(Players, net.Players, "Players", differences); MapData.CompareComponent(Actions, net.Actions, "Actions", differences); // 输出结果 foreach (var diff in differences) LogSystem.LogWarning($" - {diff}"); } public static string GetMapDataHash(MapData mapData) { byte[] bytes = MemoryPackSerializer.Serialize(mapData); // 使用 Unity 的 Hash128(性能很好且稳定) var hash128 = new Hash128(); hash128.Append(bytes); return hash128.ToString(); } } }