XNet/XNet.Business/GameLoopService.cs
2025-12-25 14:32:23 +08:00

104 lines
4.3 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 Microsoft.Extensions.Hosting;
using System.Diagnostics;
using System.Numerics;
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;
public GameLoopService(NavMeshManager navMeshManager, SceneAgent sceneAgent, WsConnectionManager wsManager)
{
_navMeshManager = navMeshManager;
_sceneAgent = sceneAgent;
_wsManager = wsManager;
}
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 映射
_navMeshManager.CreateInstance(instanceGuid_A, "Map_Forest");
_navMeshManager.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;
// 【核心循环】
// 现在不需要一把大锁锁住全服了
// 1. 如果有来自 WebAPI 的异步命令(如动态创建副本),可以在 NavMeshManager 内部处理
// 或者在这里手动处理队列 (如果使用了 CommandQueue)
// _navMeshManager.ProcessCommands();
// 2. 并行更新所有副本的 Crowd
// 这行代码利用 SceneAgent 内部的 Parallel.ForEach效率极高
_sceneAgent.UpdateAll(deltaTime);
// 2. 收集所有需要同步的Agent状态
List<AgentPositionSyncMsg> allSyncMsgs =
[
// 遍历所有实例可从_navMeshManager获取实例列表或SceneAgent维护
.. _sceneAgent.GetAgentsNeedSync(instanceGuid_A),
.. _sceneAgent.GetAgentsNeedSync(instanceGuid_B),
];
// 3. 异步发送WebSocket消息不阻塞游戏循环
if (allSyncMsgs.Count > 0)
{
_ = _wsManager.SendAgentPositionBatchAsync(allSyncMsgs);
}
// 帧率控制
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 ===");
}
}
}