XNet/XNet.Business/GameLoopService.cs
2025-12-28 17:28:40 +08:00

115 lines
4.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Diagnostics;
using System.Numerics;
using XNet.Business.Net;
using XNet.Business.PathNavigation;
namespace XNet.Business
{
public class GameLoopService : BackgroundService
{
private readonly NavMeshManager _navMeshManager;
private readonly SceneAgent _sceneAgent;
private readonly WsConnectionManager _wsManager; // 新增WebSocket管理器
private const int TARGET_FPS = 30;
private const int FRAME_TIME_MS = 1000 / TARGET_FPS;
private readonly List<string> _instanceIds = new List<string>();
public GameLoopService(NavMeshManager navMeshManager, SceneAgent sceneAgent, WsConnectionManager wsManager)
{
_navMeshManager = navMeshManager;
_sceneAgent = sceneAgent;
_wsManager = wsManager;
}
public void CreateInstance(string instanceId, string mapId)
{
_navMeshManager.CreateInstance(instanceId, mapId);
_instanceIds.Add(instanceId);
}
public void RemoveInstance(string instanceId)
{
_navMeshManager.RemoveInstance(instanceId);
_instanceIds.Remove(instanceId);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("=== Server Initializing Resources... ===");
// 1. 加载所有静态地图资源 (Templates)
// 这里假设路径固定,实际可从配置表读取
_navMeshManager.LoadTemplate("Map_Forest", @"D:\NavMeshExport.obj");
//_navMeshManager.LoadTemplate("Map_Dungeon", @"D:\NavMeshExport.obj");
// 2. 模拟创建副本逻辑 (实际应由 WebAPI 或 MatchService 触发)
// 假设现在有两个队伍分别开启了森林副本
string instanceGuid_A = "Instance_TeamA_" + Guid.NewGuid();
string instanceGuid_B = "Instance_TeamB_" + Guid.NewGuid();
// 注册 NavMesh 映射
CreateInstance(instanceGuid_A, "Map_Forest");
CreateInstance(instanceGuid_B, "Map_Forest"); // 复用同一份 NavMesh 内存
// 为这两个副本创建独立的物理/避障模拟器
_sceneAgent.CreateCrowdForInstance(instanceGuid_A);
_sceneAgent.CreateCrowdForInstance(instanceGuid_B);
// 添加一些测试怪物
int monsterA = _sceneAgent.AddAgent(instanceGuid_A, new Vector3(-4, 0, -4), 0.05f, 1);
_sceneAgent.AgentGoto(instanceGuid_A, monsterA, new Vector3(5, 0, 3));
Console.WriteLine("=== Server Game Loop Started ===");
var stopwatch = new Stopwatch();
stopwatch.Start();
long lastTime = stopwatch.ElapsedMilliseconds;
while (!stoppingToken.IsCancellationRequested)
{
long currentTime = stopwatch.ElapsedMilliseconds;
long elapsedMs = currentTime - lastTime;
// 防止调试断点导致 delta 过大,限制最大单帧时间
if (elapsedMs > 100) elapsedMs = 100;
float deltaTime = elapsedMs / 1000.0f;
lastTime = currentTime;
// 【核心循环】
// 2. 并行更新所有副本的 Crowd
// 这行代码利用 SceneAgent 内部的 Parallel.ForEach效率极高
_sceneAgent.UpdateAll(deltaTime);
// 2. 异步收集+推送消息IO密集型提交到专用线程池不阻塞主循环
_ = Task.Run(async () =>
{
List<AgentPositionSyncMsg> allSyncMsgs = [];
foreach (var instanceId in _instanceIds)
{
var syncMsgs = _sceneAgent.GetAgentsNeedSync(instanceId);
allSyncMsgs.AddRange(syncMsgs);
}
if (allSyncMsgs.Count > 0)
{
await _wsManager.SendAgentPositionBatchAsync(allSyncMsgs);
}
}, stoppingToken);
// 帧率控制
long frameWorkTime = stopwatch.ElapsedMilliseconds - currentTime;
int delay = (int)(FRAME_TIME_MS - frameWorkTime);
if (delay > 0)
{
await Task.Delay(delay, stoppingToken);
}
}
Console.WriteLine("=== Server Game Loop Stopped ===");
}
}
}