From d0ff0dbfdac0474c7fbb019272b9d4c5c79975ff Mon Sep 17 00:00:00 2001 From: wuyanchen <307378529@qq.com> Date: Fri, 26 Dec 2025 14:10:52 +0800 Subject: [PATCH] ` --- XNet.Business/GameLoopService.cs | 1 - XNet.Business/SceneAgent.cs | 231 ++++++++++++++------------- XNet.Business/WsConnectionManager.cs | 39 ++--- XNet.Business/XNet.Business.csproj | 1 + 4 files changed, 133 insertions(+), 139 deletions(-) diff --git a/XNet.Business/GameLoopService.cs b/XNet.Business/GameLoopService.cs index 06bca05..0fd75ed 100644 --- a/XNet.Business/GameLoopService.cs +++ b/XNet.Business/GameLoopService.cs @@ -13,7 +13,6 @@ namespace XNet.Business private const int TARGET_FPS = 30; private const int FRAME_TIME_MS = 1000 / TARGET_FPS; private readonly List _instanceIds = new List(); - public GameLoopService(NavMeshManager navMeshManager, SceneAgent sceneAgent, WsConnectionManager wsManager) { _navMeshManager = navMeshManager; diff --git a/XNet.Business/SceneAgent.cs b/XNet.Business/SceneAgent.cs index 2296e9b..1cacb95 100644 --- a/XNet.Business/SceneAgent.cs +++ b/XNet.Business/SceneAgent.cs @@ -15,10 +15,10 @@ namespace XNet.Business { public string InstanceId { get; } public DtCrowd Crowd { get; set; } - public object SyncRoot { get; } = new object(); // 线程锁 + //public object SyncRoot { get; } = new object(); // 线程锁 // 新增:存储该实例下所有 Agent 的索引(关键修复) - public List AgentIndices { get; } = new List(); + public ConcurrentDictionary AgentIndices { get; } = new ConcurrentDictionary(); // 构造函数 public CrowdInstance(string instanceId, DtCrowd crowd) @@ -30,22 +30,22 @@ namespace XNet.Business // 辅助:添加 Agent 时自动记录索引 public void AddAgentIndex(int agentIdx) { - lock (SyncRoot) - { - if (!AgentIndices.Contains(agentIdx)) - { - AgentIndices.Add(agentIdx); - } - } + //lock (SyncRoot) + //{ + //if (!AgentIndices.ContainsKey(agentIdx)) + //{ + AgentIndices.TryAdd(agentIdx, true); + //} + //} } // 辅助:移除 Agent 时清理索引 public void RemoveAgentIndex(int agentIdx) { - lock (SyncRoot) - { - AgentIndices.Remove(agentIdx); - } + //lock (SyncRoot) + //{ + AgentIndices.TryRemove(agentIdx, out _); + //} } } @@ -84,45 +84,32 @@ namespace XNet.Business return syncList; } - lock (ci.SyncRoot) + //lock (ci.SyncRoot) + //{ + // 遍历实例内所有Agent索引(修复AgentIndices报错) + foreach (var kv in ci.AgentIndices) { - // 遍历实例内所有Agent索引(修复AgentIndices报错) - foreach (var agentIdx in ci.AgentIndices) + int agentIdx = kv.Key; + var agent = ci.Crowd.GetAgent(agentIdx); + if (agent == null || agent.state == DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) { - var agent = ci.Crowd.GetAgent(agentIdx); - if (agent == null || agent.state == DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) - { - continue; - } + continue; + } - // 获取当前状态 - Vector3 currPos = GetAgentPosition(instanceId, agentIdx); - float currRot = GetAgentRotation(instanceId, agentIdx); + // 获取当前状态 + Vector3 currPos = GetAgentPosition(instanceId, agentIdx); + float currRot = GetAgentRotation(instanceId, agentIdx); - // 对比上一帧状态 - if (lastStates.TryGetValue(agentIdx, out var lastState)) - { - // 判断是否超过阈值 - bool posChanged = Vector3.Distance(currPos, lastState.Position) > POSITION_THRESHOLD; - bool rotChanged = Math.Abs(currRot - lastState.Rotation) > ROTATION_THRESHOLD; + // 对比上一帧状态 + if (lastStates.TryGetValue(agentIdx, out var lastState)) + { + // 判断是否超过阈值 + bool posChanged = Vector3.Distance(currPos, lastState.Position) > POSITION_THRESHOLD; + bool rotChanged = Math.Abs(currRot - lastState.Rotation) > ROTATION_THRESHOLD; - if (posChanged || rotChanged) - { - // 加入同步列表 - syncList.Add(new AgentPositionSyncMsg - { - InstanceId = instanceId, - AgentIdx = agentIdx, - Position = new Vector3Msg { X = currPos.X, Y = currPos.Y, Z = currPos.Z }, - Rotation = currRot - }); - // 更新缓存 - lastStates[agentIdx] = new AgentState { Position = currPos, Rotation = currRot }; - } - } - else + if (posChanged || rotChanged) { - // 首次同步,直接加入并缓存 + // 加入同步列表 syncList.Add(new AgentPositionSyncMsg { InstanceId = instanceId, @@ -130,17 +117,31 @@ namespace XNet.Business Position = new Vector3Msg { X = currPos.X, Y = currPos.Y, Z = currPos.Z }, Rotation = currRot }); - lastStates.TryAdd(agentIdx, new AgentState { Position = currPos, Rotation = currRot }); + // 更新缓存 + lastStates[agentIdx] = new AgentState { Position = currPos, Rotation = currRot }; } } + else + { + // 首次同步,直接加入并缓存 + syncList.Add(new AgentPositionSyncMsg + { + InstanceId = instanceId, + AgentIdx = agentIdx, + Position = new Vector3Msg { X = currPos.X, Y = currPos.Y, Z = currPos.Z }, + Rotation = currRot + }); + lastStates.TryAdd(agentIdx, new AgentState { Position = currPos, Rotation = currRot }); + } } + //} return syncList; } - // ========================================== + // =============================================== // 修复2:重写 GetAgentRotation(通过速度计算朝向) - // ========================================== + // =============================================== public float GetAgentRotation(string instanceId, int agentIdx) { if (!_crowdInstances.TryGetValue(instanceId, out var ci)) @@ -148,28 +149,28 @@ namespace XNet.Business return 0f; } - lock (ci.SyncRoot) + //lock (ci.SyncRoot) + //{ + var agent = ci.Crowd.GetAgent(agentIdx); + if (agent == null || agent.state == DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) { - var agent = ci.Crowd.GetAgent(agentIdx); - if (agent == null || agent.state == DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) - { - return 0f; - } - - // 核心逻辑:通过Agent的速度向量计算绕Y轴的旋转角度(弧度) - // 速度向量 (vx, vz) -> 朝向角度 = atan2(vz, vx) - float vx = agent.vel.X; - float vz = -agent.vel.Z; // 和坐标转换一致,Z轴取反 - float rotation = 0f; - - // 只有速度大于阈值时才计算朝向(避免静止时角度抖动) - if (Math.Abs(vx) > 0.001f || Math.Abs(vz) > 0.001f) - { - rotation = (float)Math.Atan2(vz, vx); // 计算弧度(范围 -π ~ π) - } - - return rotation; + return 0f; } + + // 核心逻辑:通过Agent的速度向量计算绕Y轴的旋转角度(弧度) + // 速度向量 (vx, vz) -> 朝向角度 = atan2(vz, vx) + float vx = agent.vel.X; + float vz = -agent.vel.Z; // 和坐标转换一致,Z轴取反 + float rotation = 0f; + + // 只有速度大于阈值时才计算朝向(避免静止时角度抖动) + if (Math.Abs(vx) > 0.001f || Math.Abs(vz) > 0.001f) + { + rotation = (float)Math.Atan2(vz, vx); // 计算弧度(范围 -π ~ π) + } + + return rotation; + //} } @@ -214,45 +215,45 @@ namespace XNet.Business throw new ArgumentException($"实例 {instanceId} 不存在"); } - lock (ci.SyncRoot) - { - // 创建Agent(原有逻辑) - var agentParams = new DtCrowdAgentParams(); - agentParams.radius = radius; - agentParams.height = height; - agentParams.maxAcceleration = 2.0f; - agentParams.maxSpeed = 3.0f; - agentParams.collisionQueryRange = radius * 8.0f; - agentParams.pathOptimizationRange = radius * 32.0f; - agentParams.separationWeight = 1.0f; + //lock (ci.SyncRoot) + //{ + // 创建Agent(原有逻辑) + var agentParams = new DtCrowdAgentParams(); + agentParams.radius = radius; + agentParams.height = height; + agentParams.maxAcceleration = 2.0f; + agentParams.maxSpeed = 3.0f; + agentParams.collisionQueryRange = radius * 8.0f; + agentParams.pathOptimizationRange = radius * 32.0f; + agentParams.separationWeight = 1.0f; - var rcPos = new RcVec3f(position.X, position.Y, -position.Z); // 坐标转换(和之前一致) - int agentIdx = ci.Crowd.AddAgent(rcPos, agentParams).idx; + var rcPos = new RcVec3f(position.X, position.Y, -position.Z); // 坐标转换(和之前一致) + int agentIdx = ci.Crowd.AddAgent(rcPos, agentParams).idx; - // 关键:记录Agent索引到实例的AgentIndices - ci.AddAgentIndex(agentIdx); + // 关键:记录Agent索引到实例的AgentIndices + ci.AddAgentIndex(agentIdx); - // 初始化状态缓存 - _agentLastState.GetOrAdd(instanceId, _ => new ConcurrentDictionary()) - .TryAdd(agentIdx, new AgentState { Position = position, Rotation = 0f }); + // 初始化状态缓存 + _agentLastState.GetOrAdd(instanceId, _ => new ConcurrentDictionary()) + .TryAdd(agentIdx, new AgentState { Position = position, Rotation = 0f }); - return agentIdx; - } + return agentIdx; + //} } public void RemoveAgent(string instanceId, int agentIdx) { if (!_crowdInstances.TryGetValue(instanceId, out var ci)) return; - lock (ci.SyncRoot) + //lock (ci.SyncRoot) + //{ + var agent = ci.Crowd.GetAgent(agentIdx); + // 【修复】判断 Agent 是否有效,需要检查 state + if (agent != null && agent.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) { - var agent = ci.Crowd.GetAgent(agentIdx); - // 【修复】判断 Agent 是否有效,需要检查 state - if (agent != null && agent.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) - { - ci.Crowd.RemoveAgent(agent); - } + ci.Crowd.RemoveAgent(agent); } + //} } public bool AgentGoto(string instanceId, int agentIdx, Vector3 destination) @@ -267,14 +268,14 @@ namespace XNet.Business if (targetRef == 0) return false; - lock (ci.SyncRoot) + //lock (ci.SyncRoot) + //{ + var agent = ci.Crowd.GetAgent(agentIdx); + if (agent != null && agent.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) { - var agent = ci.Crowd.GetAgent(agentIdx); - if (agent != null && agent.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) - { - return ci.Crowd.RequestMoveTarget(agent, targetRef, realTargetPos); - } + return ci.Crowd.RequestMoveTarget(agent, targetRef, realTargetPos); } + //} return false; } @@ -288,17 +289,17 @@ namespace XNet.Business return Vector3.Zero; } - lock (ci.SyncRoot) + //lock (ci.SyncRoot) + //{ + var agent = ci.Crowd.GetAgent(agentIdx); + if (agent == null || agent.state == DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) { - var agent = ci.Crowd.GetAgent(agentIdx); - if (agent == null || agent.state == DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) - { - return Vector3.Zero; - } - - // 坐标转换(和AddAgent时一致:Z轴取反) - return new Vector3(agent.npos.X, agent.npos.Y, -agent.npos.Z); + return Vector3.Zero; } + + // 坐标转换(和AddAgent时一致:Z轴取反) + return new Vector3(agent.npos.X, agent.npos.Y, -agent.npos.Z); + //} } public void UpdateAll(float deltaTime) @@ -308,10 +309,10 @@ namespace XNet.Business MaxDegreeOfParallelism = Environment.ProcessorCount // 限制并行数=CPU核心数,避免过载 }, ci => { - lock (ci.SyncRoot) - { - ci.Crowd.Update(deltaTime, null); - } + //lock (ci.SyncRoot) + //{ + ci.Crowd.Update(deltaTime, null); + //} }); } } diff --git a/XNet.Business/WsConnectionManager.cs b/XNet.Business/WsConnectionManager.cs index 94ad6e9..70da4ce 100644 --- a/XNet.Business/WsConnectionManager.cs +++ b/XNet.Business/WsConnectionManager.cs @@ -12,7 +12,7 @@ namespace XNet.Business { // ========== 原有核心字段 ========== private readonly ConcurrentDictionary _connections = new(); - private readonly ConcurrentDictionary> _instanceSubscribers = new(); + private readonly ConcurrentDictionary> _instanceSubscribers = new(); private readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; // ========== 新增:对象池配置 ========== @@ -48,10 +48,7 @@ namespace XNet.Business { foreach (var instanceId in _instanceSubscribers.Keys) { - lock (_instanceSubscribers[instanceId]) - { - _instanceSubscribers[instanceId].Remove(connId); - } + _instanceSubscribers[instanceId].TryRemove(connId, out _); } Console.WriteLine($"[WS .NET 10] 连接断开:{connId},当前连接数:{_connections.Count}"); } @@ -65,17 +62,15 @@ namespace XNet.Business return false; } - _instanceSubscribers.GetOrAdd(instanceId, _ => new HashSet()); - lock (_instanceSubscribers[instanceId]) - { - _instanceSubscribers[instanceId].Add(connId); - } + _instanceSubscribers.GetOrAdd(instanceId, _ => new ConcurrentDictionary()); + + _instanceSubscribers[instanceId].TryAdd(connId, true); return true; } public async Task SendAgentPositionBatchAsync(List syncMsgs) { - if(syncMsgs.Count == 0) return; + if (syncMsgs.Count == 0) return; foreach (var group in syncMsgs.GroupBy(m => m.InstanceId)) { @@ -109,7 +104,7 @@ namespace XNet.Business } else { - msgBytes = tempMs.ToArray(); + msgBytes = tempMs.ToArray();// 超出池化数组长度,使用新分配的数组 needReturnPooledBytes = false; } } @@ -130,21 +125,19 @@ namespace XNet.Business var deadConnIds = _deadConnListPool.Get(); try { - lock (subscriberConnIds) + foreach (var connKv in subscriberConnIds) { - foreach (var connId in subscriberConnIds) + if (_connections.TryGetValue(connKv.Key, out var socket)) { - if (_connections.TryGetValue(connId, out var socket)) + if (needReturnPooledBytes) { // 3. 发送时传递有效长度 - if (needReturnPooledBytes) - { - _ = SendToSingleConnAsync(socket, msgBytes, msgBytesLength, connId, deadConnIds); - } - else - { - _ = SendToSingleConnAsync(socket, msgBytes, msgBytes.Length, connId, deadConnIds); - } + _ = SendToSingleConnAsync(socket, msgBytes, msgBytesLength, connKv.Key, deadConnIds); + } + else + { + // 3. 发送整串字节数组 + _ = SendToSingleConnAsync(socket, msgBytes, msgBytes.Length, connKv.Key, deadConnIds); } } } diff --git a/XNet.Business/XNet.Business.csproj b/XNet.Business/XNet.Business.csproj index ed4b1ac..dbfffa4 100644 --- a/XNet.Business/XNet.Business.csproj +++ b/XNet.Business/XNet.Business.csproj @@ -4,6 +4,7 @@ net10.0 enable enable + Library