AreaService.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. using DeepCore;
  2. using DeepCore.GameData.Zone;
  3. using DeepCore.Log;
  4. using DeepCrystal.RPC;
  5. using DeepMMO.Protocol;
  6. using DeepMMO.Server.AreaManager;
  7. using DeepMMO.Server.Connect;
  8. using DeepMMO.Server.SystemMessage;
  9. using DeepCore.IO;
  10. using DeepMMO.Server.GameEvent;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Threading.Tasks;
  14. using DeepCore.GameEvent;
  15. using DeepCore.GameEvent.Message;
  16. using DeepCore.Game3D.Host.Instance;
  17. using System.Collections.Concurrent;
  18. using System.IO;
  19. using DeepCore.GameData;
  20. namespace DeepMMO.Server.Area
  21. {
  22. public class AreaService : IService
  23. {
  24. public Random random { get; private set; } = new Random();
  25. public IDisposable sync_state_timer { get; private set; }
  26. public IRemoteService area_manager;
  27. private TypeCodec session_battle_codec;
  28. public BattleCodec BattleClientCodec { get; }
  29. public string ServerGroupId;
  30. public AreaService(ServiceStartInfo start) : base(start)
  31. {
  32. ServerGroupId = start.Config["groupID"];
  33. this.BattleClientCodec = new BattleCodec(RPGServerBattleManager.Templates);
  34. this.session_battle_codec = base.ServerCodec.Factory.GetCodec(typeof(SessionBattleAction));
  35. }
  36. protected override void OnDisposed()
  37. {
  38. }
  39. protected override async Task OnStartAsync()
  40. {
  41. this.area_manager = await ServerNames.GetOrCreateAreaManagerServiceAsync(this, ServerGroupId);
  42. this.sync_state_timer = this.Provider.CreateTimer(timer_SyncState, this,
  43. TimeSpan.FromSeconds(0),
  44. TimeSpan.FromSeconds(TimerConfig.timer_sec_AreaStateNotify));
  45. await area_manager.CallAsync<RegistAreaResponse>(new RegistAreaRequest()
  46. {
  47. areaName = SelfAddress.ServiceName,
  48. areaNode = SelfAddress.ServiceNode,
  49. });
  50. }
  51. protected override async Task OnStopAsync(ServiceStopInfo reason)
  52. {
  53. this.sync_state_timer.Dispose();
  54. foreach (var item in zoneNodes)
  55. {
  56. await item.Value.DoStopAsync();
  57. }
  58. }
  59. public override bool GetState(TextWriter sb)
  60. {
  61. ForEachZones(node =>
  62. {
  63. sb.WriteLine(CUtils.SequenceChar('-', 100));
  64. node.GetStatus(sb);
  65. sb.WriteLine(CUtils.SequenceChar('-', 100));
  66. });
  67. return true;
  68. }
  69. protected virtual void timer_SyncState(object obj)
  70. {
  71. this.area_manager.Invoke(new AreaStateNotify()
  72. {
  73. areaName = SelfAddress.ServiceName,
  74. areaNode = SelfAddress.ServiceNode,
  75. roleCount = this.PlayerCount,
  76. zoneCount = this.ZoneNodeCount,
  77. cpuPercent = 1, // TODO
  78. memoryMB = 1, // TODO
  79. });
  80. this.LogState();
  81. }
  82. //[RpcHandler(typeof(SystemStaticServicesStartedNotify))]
  83. //public virtual void rpc_HandleSystem(SystemStaticServicesStartedNotify shutdown)
  84. //{
  85. // area_manager.CallAsync<RegistAreaResponse>(new RegistAreaRequest()
  86. // {
  87. // areaName = SelfAddress.ServiceName,
  88. // areaNode = SelfAddress.ServiceNode,
  89. // });
  90. //}
  91. //-----------------------------------------------------------------------------------------------------------------------------
  92. [RpcHandler(typeof(ServerGameEventNotify))]
  93. public virtual void rpc_event_notify(ServerGameEventNotify ntf)
  94. {
  95. var emsg = EventMessage.FromBytes(ntf.EventMessageData);
  96. if (ntf.Broadcast)
  97. {
  98. foreach (var node in zoneNodes)
  99. {
  100. node.Value.EventMgr?.OnReceiveMessage(emsg);
  101. }
  102. }
  103. else
  104. {
  105. var mgr = EventManagerFactory.Instance.GetEventManager(ntf.To);
  106. mgr?.OnReceiveMessage(emsg);
  107. }
  108. }
  109. //-----------------------------------------------------------------------------------------------------------------------------
  110. #region __CreateObject__
  111. //--------------------------------------------------------------------------------------------------------------------------------
  112. protected virtual AreaZonePlayer CreateZonePlayer(AreaZoneNode node, RoleEnterZoneRequest enter)
  113. {
  114. return new AreaZonePlayer(this, node, enter);
  115. }
  116. protected virtual AreaZoneNode CreateZoneNode(CreateZoneNodeRequest create, MapTemplateData map)
  117. {
  118. return new AreaZoneNode(this, create, map);
  119. }
  120. public virtual UnitInfo GetDefaultUnitTemplate()
  121. {
  122. return RPGServerBattleManager.DataRoot.Templates.GetUnit(95559);
  123. }
  124. public virtual AreaZoneNode GetDefaultZoneNode()
  125. {
  126. return random.GetRandomInArray(this.ZoneNodes);
  127. }
  128. #endregion
  129. //-----------------------------------------------------------------------------------------------------------------------------
  130. #region __ClientToArea__
  131. //--------------------------------------------------------------------------------------------------------------------------------
  132. /// <summary>
  133. /// Client -> Session -> Area
  134. /// </summary>
  135. [RpcHandler(typeof(SessionBattleAction), true)]
  136. public virtual void client_rpc_Handle(BinaryMessage action)
  137. {
  138. try
  139. {
  140. using (var buffer = MemoryStreamObjectPool.AllocAutoRelease(action.DataSegment))
  141. using (var input = IOStreamObjectPool.AllocInputAutoRelease(base.ServerCodec.Factory, buffer))
  142. {
  143. var roleID = input.GetUTF();
  144. var player = GetPlayer(roleID);
  145. if (player == null)
  146. {
  147. return;
  148. }
  149. //drop 4 for bytes size//
  150. buffer.Position += 4;
  151. player.client_rpc_Handle(buffer);
  152. }
  153. }
  154. catch (Exception e)
  155. {
  156. log.Error(e.Message, e);
  157. }
  158. }
  159. public override void OnWormholeTransported(RemoteAddress from, object message)
  160. {
  161. if (message is BinaryMessage bin)
  162. {
  163. client_rpc_Handle(bin);
  164. }
  165. else if (message is SessionBattleAction ser)
  166. {
  167. var player = GetPlayer(ser.roleID);
  168. if (player == null)
  169. {
  170. player.client_rpc_Handle(ser.clientBattleAction);
  171. }
  172. }
  173. }
  174. #endregion
  175. //-----------------------------------------------------------------------------------------------------------------------------
  176. #region __LogicToArea__
  177. //--------------------------------------------------------------------------------------------------------------------------------
  178. /// <summary>
  179. /// 创建副本
  180. /// </summary>
  181. /// <param name="create"></param>
  182. /// <param name="cb"></param>
  183. [RpcHandler(typeof(CreateZoneNodeRequest), typeof(CreateZoneNodeResponse), ServerNames.AreaManagerType)]
  184. public async Task<CreateZoneNodeResponse> area_manager_rpc_CreateZone(CreateZoneNodeRequest create)
  185. {
  186. try
  187. {
  188. var maptemp = RPGServerTemplateManager.Instance.GetMapTemplate(create.mapTemplateID);
  189. if (maptemp == null) throw new Exception("No MapTemplate : " + create.mapTemplateID);
  190. var zoneUUID = create.managerZoneUUID;
  191. if (zoneNodes.ContainsKey(zoneUUID))
  192. {
  193. throw new Exception(string.Format("node instance id ({0}) already exist!", zoneUUID));
  194. }
  195. AreaZoneNode node = this.CreateZoneNode(create, maptemp);
  196. zoneNodes.TryAdd(zoneUUID, node);
  197. var z = await node.DoStartAsync();
  198. // log.InfoFormat("Scene Started : {0} : {1}", z.UUID, z.Data);
  199. return (new CreateZoneNodeResponse()
  200. {
  201. areaName = SelfAddress.ServiceName,
  202. areaNode = SelfAddress.ServiceNode,
  203. zoneUUID = zoneUUID,
  204. TemplateID = maptemp.zone_template_id,
  205. });
  206. }
  207. catch (Exception e)
  208. {
  209. log.Error("CreateZone failed, error: " + e.Message, e);
  210. return (new CreateZoneNodeResponse()
  211. {
  212. s2c_code = CreateZoneNodeResponse.CODE_ERROR,
  213. s2c_msg = e.Message,
  214. });
  215. }
  216. }
  217. /// <summary>
  218. /// 销毁副本
  219. /// </summary>
  220. /// <param name="stop"></param>
  221. /// <param name="cb"></param>
  222. [RpcHandler(typeof(DestoryZoneNodeRequest), typeof(DestoryZoneNodeResponse), ServerNames.AreaManagerType)]
  223. public async Task<DestoryZoneNodeResponse> area_manager_rpc_DestroyZone(DestoryZoneNodeRequest stop)
  224. {
  225. try
  226. {
  227. if (this.zoneNodes.TryRemove(stop.zoneUUID, out var node))
  228. {
  229. //删除场景实例的所有玩家
  230. node.ZoneNode.ForEachPlayers((p) =>
  231. {
  232. try
  233. {
  234. using (var player = p.Client as AreaZonePlayer)
  235. {
  236. log.Error("DestoryZoneNode Have Player : " + player.RoleUUID);
  237. players.TryRemove(player.RoleUUID, out var pp);
  238. }
  239. }
  240. catch (Exception err)
  241. {
  242. log.Error(err.Message, err);
  243. }
  244. });
  245. await node.DoStopAsync();
  246. return (new DestoryZoneNodeResponse() { s2c_msg = "done" });
  247. }
  248. else
  249. {
  250. return (new DestoryZoneNodeResponse() { s2c_msg = "done" });
  251. }
  252. }
  253. catch (Exception e)
  254. {
  255. log.Error("DestroyZone failed, error: " + e.Message, e);
  256. return (new DestoryZoneNodeResponse() { s2c_code = Response.CODE_ERROR, s2c_msg = e.Message });
  257. }
  258. }
  259. /// <summary>
  260. /// 玩家进入副本
  261. /// </summary>
  262. /// <param name="enter"></param>
  263. /// <param name="cb"></param>
  264. [RpcHandler(typeof(RoleEnterZoneRequest), typeof(RoleEnterZoneResponse), ServerNames.AreaManagerType)]
  265. public async Task<RoleEnterZoneResponse> area_manager_rpc_PlayerEnter(RoleEnterZoneRequest enter)
  266. {
  267. try
  268. {
  269. var node = this.FindZoneNode(enter);
  270. if (node == null)
  271. {
  272. //TODO 分配一个默认场景
  273. node = this.GetDefaultZoneNode();
  274. }
  275. //场景不存在//
  276. if (node == null)
  277. {
  278. return new RoleEnterZoneResponse()
  279. {
  280. s2c_code = RoleEnterZoneResponse.CODE_ZONE_NOT_EXIST,
  281. s2c_msg = $"PlayerEnter: ZoneNotExistException: roleID={enter.roleUUID} zone={enter.expectZoneUUID}"
  282. };
  283. }
  284. if (this.players.TryGetOrCreate(enter.roleUUID, out var player, (uuid) => this.CreateZonePlayer(node, enter)))
  285. {
  286. if (player.ZoneNode == node)
  287. {
  288. var replace = await node.DoPlayerEnterReplace(player, enter);
  289. if (replace.IsSuccess) { return replace; }
  290. this.players.TryGetOrCreate(enter.roleUUID, out player, (uuid) => this.CreateZonePlayer(node, enter));
  291. return await node.DoPlayerEnterAsync(player, enter);
  292. }
  293. else
  294. {
  295. return new RoleEnterZoneResponse()
  296. {
  297. s2c_code = RoleEnterZoneResponse.CODE_ZONE_NOT_EXIST,
  298. s2c_msg = $"PlayerEnter: ZoneNotExistException: roleID={enter.roleUUID} zone={enter.expectZoneUUID}"
  299. };
  300. }
  301. }
  302. else
  303. {
  304. return await node.DoPlayerEnterAsync(player, enter);
  305. }
  306. }
  307. catch (Exception e)
  308. {
  309. log.Error(e.Message, e);
  310. return new RoleEnterZoneResponse() { s2c_code = Response.CODE_ERROR, s2c_msg = e.Message };
  311. }
  312. }
  313. /// <summary>
  314. /// 玩家离开副本
  315. /// </summary>
  316. /// <param name="leave"></param>
  317. /// <param name="cb"></param>
  318. [RpcHandler(typeof(RoleLeaveZoneRequest), typeof(RoleLeaveZoneResponse), ServerNames.AreaManagerType)]
  319. public async Task<RoleLeaveZoneResponse> area_manager_rpc_PlayerLeave(RoleLeaveZoneRequest leave)
  320. {
  321. try
  322. {
  323. if (this.players.TryRemove(leave.roleID, out var player))
  324. {
  325. using (player)
  326. {
  327. AreaZoneNode node = player.ZoneNode;
  328. if (node == null)
  329. {
  330. return (new RoleLeaveZoneResponse()
  331. {
  332. s2c_code = RoleLeaveZoneResponse.CODE_ROLE_NOT_EXIST,
  333. s2c_msg = $"PlayerLeave: ZoneNotExistException: roleID={leave.roleID} zone={player.ZoneUUID}"
  334. });
  335. }
  336. return await node.DoPlayerLeaveAsync(player, leave);
  337. }
  338. }
  339. else
  340. {
  341. return (new RoleLeaveZoneResponse()
  342. {
  343. s2c_code = RoleLeaveZoneResponse.CODE_ROLE_NOT_EXIST,
  344. s2c_msg = $"PlayerLeave: PlayerNotExistException: roleID=roleID={leave.roleID} zone={leave.zoneUUID}"
  345. });
  346. }
  347. }
  348. catch (Exception e)
  349. {
  350. log.Error(e.Message, e);
  351. return (new RoleLeaveZoneResponse() { s2c_code = Response.CODE_ERROR, s2c_msg = e.Message });
  352. }
  353. }
  354. [RpcHandler(typeof(GetRolePositionRequest), typeof(GetRolePositionResponse), ServerNames.AreaManagerType)]
  355. public async Task<GetRolePositionResponse> area_manager_rpc_GetRolePosition(GetRolePositionRequest req)
  356. {
  357. var resp = new GetRolePositionResponse();
  358. if (players.TryGetValue(req.roleUUID, out var role))
  359. {
  360. return await role.ZoneNode.DoGetPlayerPosition(role, req);
  361. }
  362. else
  363. {
  364. resp.s2c_code = GetRolePositionResponse.CODE_ROLE_NOT_EXIST;
  365. return resp;
  366. }
  367. }
  368. //-----------------------------------------------------------------------------------------------------------------------------
  369. /// <summary>
  370. /// 玩家数据改变
  371. /// </summary>
  372. /// <param name="changed"></param>
  373. [RpcHandler(typeof(RoleDataChangedNotify))]
  374. public void logic_rpc_PlayerNetStateChanged(RoleDataChangedNotify changed)
  375. {
  376. AreaZonePlayer player;
  377. AreaZoneNode node;
  378. try
  379. {
  380. if (TryGetPlayer(changed.roleID, out player, out node))
  381. {
  382. player.logic_rpc_Handle(changed.roleData);
  383. }
  384. }
  385. catch (Exception e)
  386. {
  387. log.Error(e.Message, e);
  388. }
  389. }
  390. /// <summary>
  391. /// 玩家断开连接
  392. /// </summary>
  393. /// <param name="disconnect"></param>
  394. [RpcHandler(typeof(SessionDisconnectNotify))]
  395. public void logic_rpc_Handle(SessionDisconnectNotify disconnect)
  396. {
  397. AreaZonePlayer player;
  398. AreaZoneNode node;
  399. try
  400. {
  401. if (TryGetPlayer(disconnect.roleID, out player, out node))
  402. {
  403. node.DoPlayerDisconnect(player);
  404. }
  405. }
  406. catch (Exception e)
  407. {
  408. log.Error(e.Message, e);
  409. }
  410. }
  411. /// <summary>
  412. /// 玩家重新连接
  413. /// </summary>
  414. /// <param name="disconnect"></param>
  415. [RpcHandler(typeof(SessionReconnectNotify))]
  416. public void logic_rpc_Handle(SessionReconnectNotify reconnect)
  417. {
  418. AreaZonePlayer player;
  419. AreaZoneNode node;
  420. try
  421. {
  422. if (TryGetPlayer(reconnect.roleID, out player, out node))
  423. {
  424. node.DoPlayerReconnect(player);
  425. }
  426. }
  427. catch (Exception e)
  428. {
  429. log.Error(e.Message, e);
  430. }
  431. }
  432. [RpcHandler(typeof(SessionBeginLeaveRequest), typeof(SessionBeginLeaveResponse))]
  433. public async Task<SessionBeginLeaveResponse> logic_rpc_Handle(SessionBeginLeaveRequest leave)
  434. {
  435. try
  436. {
  437. if (this.players.TryGetValue(leave.roleID, out var player))
  438. {
  439. var node = player.ZoneNode;
  440. if (node != null)
  441. {
  442. await node.DoPlayerBeginLeaveAsync(player);
  443. return (new SessionBeginLeaveResponse() { s2c_code = Response.CODE_OK });
  444. }
  445. }
  446. return (new SessionBeginLeaveResponse() { s2c_code = Response.CODE_ERROR });
  447. }
  448. catch (Exception e)
  449. {
  450. log.Warn(e.Message, e);
  451. return (new SessionBeginLeaveResponse() { s2c_code = Response.CODE_ERROR, s2c_msg = e.Message });
  452. }
  453. }
  454. #endregion
  455. //-----------------------------------------------------------------------------------------------------------------------------
  456. #region __ZoneAndPlayer__
  457. //--------------------------------------------------------------------------------------------------------------------------------
  458. private ConcurrentDictionary<string, AreaZoneNode> zoneNodes = new ConcurrentDictionary<string, AreaZoneNode>();
  459. private ConcurrentDictionary<string, AreaZonePlayer> players = new ConcurrentDictionary<string, AreaZonePlayer>();
  460. public int PlayerCount
  461. {
  462. get { { return players.Count; } }
  463. }
  464. public int ZoneNodeCount
  465. {
  466. get { { return zoneNodes.Count; } }
  467. }
  468. public List<AreaZoneNode> ZoneNodes
  469. {
  470. get { { return new List<AreaZoneNode>(zoneNodes.Values); } }
  471. }
  472. public List<AreaZonePlayer> ZonePlayers
  473. {
  474. get { { return new List<AreaZonePlayer>(players.Values); } }
  475. }
  476. public void ForEachPlayers(Action<AreaZonePlayer> action)
  477. {
  478. using (var list = CollectionObjectPool<AreaZonePlayer>.AllocList())
  479. {
  480. { list.AddRange(players.Values); }
  481. foreach (var p in list) { action(p); }
  482. }
  483. }
  484. public void ForEachZones(Action<AreaZoneNode> action)
  485. {
  486. using (var list = CollectionObjectPool<AreaZoneNode>.AllocList())
  487. {
  488. { list.AddRange(zoneNodes.Values); }
  489. foreach (var z in list) { action(z); }
  490. }
  491. }
  492. public AreaZoneNode GetZoneNode(string zoneUUID)
  493. {
  494. if (zoneNodes.TryGetValue(zoneUUID, out var ret))
  495. {
  496. return ret;
  497. }
  498. return null;
  499. }
  500. public AreaZoneNode FindZoneNode(RoleEnterZoneRequest enter)
  501. {
  502. {
  503. if (enter.expectZoneUUID != null && zoneNodes.TryGetValue(enter.expectZoneUUID, out var node))
  504. {
  505. return node;
  506. }
  507. if (enter.expectMapTemplateID != 0)
  508. {
  509. foreach (var e in zoneNodes.Values)
  510. {
  511. if (e.MapTemplateID == enter.expectMapTemplateID)
  512. {
  513. return e;
  514. }
  515. }
  516. }
  517. }
  518. return null;
  519. }
  520. public AreaZonePlayer GetPlayer(string roleID)
  521. {
  522. if (roleID != null && players.TryGetValue(roleID, out var ret))
  523. {
  524. return ret;
  525. }
  526. return null;
  527. }
  528. public bool TryGetPlayer(string roleID, out AreaZonePlayer player, out AreaZoneNode node)
  529. {
  530. if (roleID != null && players.TryGetValue(roleID, out player))
  531. {
  532. node = player.ZoneNode;
  533. // if (node == null)
  534. // {
  535. // throw new Exception("PlayerLeave: InstanceNotExistException: " + player.ZoneUUID);
  536. // }
  537. return true;
  538. }
  539. player = null;
  540. node = null;
  541. // else
  542. // {
  543. // //throw new Exception("PlayerLeave: PlayerNotExistException roleID: " + roleID);
  544. // }
  545. return false;
  546. }
  547. /// <summary>
  548. /// 异步执行战斗场景内交互代码
  549. /// </summary>
  550. /// <typeparam name="T"></typeparam>
  551. /// <param name="zoneUUID"></param>
  552. /// <param name="func"></param>
  553. /// <returns></returns>
  554. public Task<T> QueueZoneTaskAsync<T>(string zoneUUID, Func<InstanceZone, T> func)
  555. {
  556. if (zoneNodes.TryGetValue(zoneUUID, out var node))
  557. {
  558. return node.ZoneNode.QueueSceneTaskAsync<T>(func);
  559. }
  560. return Task.FromResult<T>(func(null));
  561. }
  562. /// <summary>
  563. /// 异步执行战斗场景内交互代码
  564. /// </summary>
  565. /// <typeparam name="T"></typeparam>
  566. /// <param name="roleUUID"></param>
  567. /// <param name="func"></param>
  568. /// <returns></returns>
  569. public Task<T> QueuePlayerTaskAsync<T>(string roleUUID, Func<InstancePlayer, T> func)
  570. {
  571. if (roleUUID != null && players.TryGetValue(roleUUID, out var player))
  572. {
  573. var node = player.ZoneNode;
  574. return node.ZoneNode.QueuePlayerTaskAsync<T>(roleUUID, func);
  575. }
  576. return Task.FromResult<T>(func(null));
  577. }
  578. /// <summary>
  579. /// 异步执行战斗场景内交互代码
  580. /// </summary>
  581. /// <typeparam name="T"></typeparam>
  582. /// <param name="zoneUUID"></param>
  583. /// <param name="func"></param>
  584. /// <returns></returns>
  585. public Task<T> QueueZoneTaskAsync<T>(string zoneUUID, Func<AreaZoneNode, InstanceZone, T> func)
  586. {
  587. if (zoneNodes.TryGetValue(zoneUUID, out var node))
  588. {
  589. return node.ZoneNode.QueueSceneTaskAsync<T>(z => func(node, z));
  590. }
  591. return Task.FromResult<T>(func(null, null));
  592. }
  593. /// <summary>
  594. /// 异步执行战斗场景内交互代码
  595. /// </summary>
  596. /// <typeparam name="T"></typeparam>
  597. /// <param name="roleUUID"></param>
  598. /// <param name="func"></param>
  599. /// <returns></returns>
  600. public Task<T> QueuePlayerTaskAsync<T>(string roleUUID, Func<AreaZonePlayer, InstancePlayer, T> func)
  601. {
  602. if (roleUUID != null && players.TryGetValue(roleUUID, out var player))
  603. {
  604. var node = player.ZoneNode;
  605. return node.ZoneNode.QueuePlayerTaskAsync<T>(roleUUID, p => func(player, p));
  606. }
  607. return Task.FromResult<T>(func(null, null));
  608. }
  609. #endregion
  610. //-----------------------------------------------------------------------------------------------------------------------------
  611. }
  612. }