121 lines
4.7 KiB
C#
121 lines
4.7 KiB
C#
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;
|
||
public GameLoopService(NavMeshManager navMeshManager, SceneAgent sceneAgent, WsConnectionManager wsManager)
|
||
{
|
||
_navMeshManager = navMeshManager;
|
||
_sceneAgent = sceneAgent;
|
||
_wsManager = wsManager;
|
||
}
|
||
|
||
|
||
private async Task SendSyncLocationMessage()
|
||
{
|
||
List<RoomMsg<AgentLocationSyncMsg>> allSyncMsgs = [];
|
||
foreach (var instanceIdKv in _sceneAgent.InstanceIds)
|
||
{
|
||
var syncMsgs = _sceneAgent.GetAgentsNeedSync(instanceIdKv.Key);
|
||
|
||
////Debug 输出查看
|
||
//foreach (var msg in syncMsgs)
|
||
//{
|
||
// Console.WriteLine($"[Sync] Instance: {instanceIdKv.Key}, AgentIdx: {msg.Data!.AgentIdx}, Pos: {msg.Data.Position?.X},{msg.Data.Position?.Y},{msg.Data.Position?.Z}");
|
||
//}
|
||
|
||
allSyncMsgs.AddRange(syncMsgs);
|
||
}
|
||
if (allSyncMsgs.Count > 0)
|
||
{
|
||
await _wsManager.SendMessageToRoomBatchAsync(allSyncMsgs);
|
||
}
|
||
}
|
||
|
||
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 映射
|
||
_sceneAgent.CreateInstance(instanceGuid_A, "Map_Forest");
|
||
_sceneAgent.CreateInstance(instanceGuid_B, "Map_Forest"); // 复用同一份 NavMesh 内存
|
||
|
||
// 为这两个副本创建独立的物理/避障模拟器
|
||
_sceneAgent.CreateCrowdForInstance(instanceGuid_A);
|
||
_sceneAgent.CreateCrowdForInstance(instanceGuid_B);
|
||
|
||
//// 添加一些测试怪物
|
||
//for(int i = 0; i < 1000; i++)
|
||
//{
|
||
// int monsterA = _sceneAgent.AddAgent(instanceGuid_A, new Vector3(-4, 0, -4), 0.05f, 1);
|
||
// _sceneAgent.AgentGoto(instanceGuid_A, monsterA, new Vector3(5, 0, 3));
|
||
//}
|
||
|
||
//// 添加一些测试怪物
|
||
//for (int i = 0; i < 9000; i++)
|
||
//{
|
||
// int monsterB = _sceneAgent.AddAgent(instanceGuid_B, new Vector3(-4, 0, -4), 0.05f, 1);
|
||
// _sceneAgent.AgentGoto(instanceGuid_B, monsterB, 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(SendSyncLocationMessage, 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 ===");
|
||
}
|
||
}
|
||
} |