LogicService.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. using DeepCore;
  2. using DeepCore.GameEvent;
  3. using DeepCore.GameEvent.Message;
  4. using DeepCore.IO;
  5. using DeepCore.Log;
  6. using DeepCore.Statistics;
  7. using DeepCore.Threading;
  8. using DeepCrystal.ORM;
  9. using DeepCrystal.RPC;
  10. using DeepCrystal.Sql;
  11. using DeepMMO.Protocol;
  12. using DeepMMO.Protocol.Client;
  13. using DeepMMO.Server.Area;
  14. using DeepMMO.Server.Connect;
  15. using DeepMMO.Server.GameEvent;
  16. using DeepMMO.Server.Logic.Model;
  17. using System;
  18. using System.Collections.Generic;
  19. using System.Diagnostics;
  20. using System.Threading.Tasks;
  21. namespace DeepMMO.Server.Logic
  22. {
  23. public partial class LogicService : IService
  24. {
  25. public static int SAVE_EXPECT_TIME_LIMIT = 200;
  26. public static int LOAD_EXPECT_TIME_LIMIT = 200;
  27. public static TimeStatisticsRecoder Statistics { get; private set; } =
  28. new TimeStatisticsRecoder("LogicStatistics");
  29. /// <summary>
  30. /// 账号ID.
  31. /// </summary>
  32. public string accountID { get; private set; }
  33. /// <summary>
  34. /// 服务器ID.
  35. /// </summary>
  36. public string serverID { get; private set; }
  37. public string serverGroupID { get; private set; }
  38. public string sessionName { get; private set; }
  39. public string sessionNode { get; private set; }
  40. public string roleID { get; private set; }
  41. public string roleDigitID { get => roleModule.GetRoleData().digitID; }
  42. public ClientInfo clientInfo { get; private set; }
  43. public IRemoteService session { get; private set; }
  44. public LanguageManager Language { get => roleModule.Language; }
  45. public EventManager EventMgr { get; private set; }
  46. public IMappingAdapter DBAdapter { get; private set; }
  47. public event Action OnSessionReconnect;
  48. public event Action OnBeforeSaveData;
  49. public event Action OnClientEntered;
  50. private IDisposable mEventTimer;
  51. private IDisposable mSaveDataTimer;
  52. public bool IsClientEntered { get; private set; }
  53. public override ServiceProperties Properties
  54. {
  55. get
  56. {
  57. var ret = base.Properties;
  58. ret.IsConcurrent = false;
  59. return ret;
  60. }
  61. }
  62. public LogicService(ServiceStartInfo start) : base(start)
  63. {
  64. this.sessionName = start.Config["sessionName"].ToString();
  65. this.sessionNode = start.Config["sessionNode"].ToString();
  66. this.accountID = start.Config["accountID"].ToString();
  67. this.serverID = start.Config["serverID"].ToString();
  68. this.roleID = start.Config["roleID"].ToString();
  69. this.serverGroupID = start.Config["serverGroupID"].ToString();
  70. this.clientInfo = new ClientInfo(start.Config);
  71. this.DBAdapter = ORMFactory.Instance.DefaultAdapter;
  72. }
  73. protected override async Task OnStartAsync()
  74. {
  75. var stopwatch = Stopwatch.StartNew();
  76. try
  77. {
  78. this.session = await base.Provider.GetAsync(new RemoteAddress(sessionName, sessionNode));
  79. this.OnCreateModules();
  80. await this.OnModulesStartAsync();
  81. await this.OnModulesStartedAsync();
  82. this.EventMgr = EventManagerFactory.Instance.CreateEventManager("Player", roleID);
  83. if (EventMgr != null)
  84. {
  85. EventMgr.PutObject("Service", this);
  86. EventMgr.Start();
  87. mEventTimer = Provider.CreateTimer(OnEventManagerTick, this,
  88. TimeSpan.FromSeconds(0),
  89. TimeSpan.FromSeconds(TimerConfig.timer_sec_EventUpdateTime));
  90. }
  91. {
  92. //定期存数据.
  93. int interval = TimerConfig.timer_minute_SaveDataTimer;
  94. interval = Math.Max(5, interval);
  95. this.mSaveDataTimer = Provider.CreateTimer(
  96. OnFlushDataTickAsync,
  97. this,
  98. TimeSpan.FromMinutes(interval));
  99. }
  100. if (stopwatch.ElapsedMilliseconds > LOAD_EXPECT_TIME_LIMIT)
  101. {
  102. log.Warn("LogicService : OnStartAsync Use Time = " + stopwatch.Elapsed);
  103. }
  104. }
  105. catch (Exception hzdsb)
  106. {
  107. hzdsb.PrintStackTrace();
  108. }
  109. finally
  110. {
  111. stopwatch.Stop();
  112. }
  113. }
  114. private void OnEventManagerTick(object state)
  115. {
  116. EventMgr?.Update();
  117. }
  118. private Task OnFlushDataTickAsync(object state)
  119. {
  120. return this.OnModulesSaveDataAsync();
  121. }
  122. protected override async Task OnStopAsync(ServiceStopInfo reason)
  123. {
  124. IsClientEntered = false;
  125. mSaveDataTimer?.Dispose();
  126. await OnModulesStopAsync();
  127. EventMgr?.Dispose();
  128. if (reason.Event != ServiceStopInfo.ShutdownEvent.START_ERROR)
  129. {
  130. await OnModulesSaveDataAsync();
  131. }
  132. await OnModulesStopedAsync();
  133. }
  134. protected override void OnDisposed()
  135. {
  136. this.OnModulesDispose();
  137. OnSessionReconnect = null;
  138. OnBeforeSaveData = null;
  139. OnClientEntered = null;
  140. mEventTimer?.Dispose();
  141. EventMgr = null;
  142. }
  143. //---------------------------------------------------------------------------------------------------
  144. #region Modules
  145. public RoleModule roleModule { get; protected set; }
  146. public AreaModule areaModule { get; protected set; }
  147. private List<ILogicModule> modules = new List<ILogicModule>();
  148. protected virtual void OnCreateModules()
  149. {
  150. this.roleModule = RegistModule(new RoleModule(this));
  151. this.areaModule = RegistModule(new AreaModule(this));
  152. }
  153. protected T RegistModule<T>(T module) where T : ILogicModule
  154. {
  155. modules.Add(module);
  156. return module;
  157. }
  158. private async Task OnModulesStartAsync()
  159. {
  160. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  161. {
  162. foreach (var module in list)
  163. {
  164. try
  165. {
  166. await module.OnStartAsync();
  167. }
  168. catch (Exception err)
  169. {
  170. log.Error(ErrorBaseInfo() + err.Message, err);
  171. throw;
  172. }
  173. }
  174. }
  175. }
  176. private async Task OnModulesStartedAsync()
  177. {
  178. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  179. {
  180. foreach (var module in list)
  181. {
  182. try
  183. {
  184. await module.OnStartedAsync();
  185. }
  186. catch (Exception err)
  187. {
  188. log.Error(err.Message, err);
  189. }
  190. }
  191. }
  192. }
  193. private async Task OnModulesSaveDataAsync()
  194. {
  195. var stopwatch = Stopwatch.StartNew();
  196. try
  197. {
  198. OnBeforeSaveData?.Invoke();
  199. if (ORMFactory.IsTest)
  200. {
  201. var watch_exe = CUtils.TickTimeMS;
  202. //var exe = new SyncTaskExecutor();
  203. var trans = DBAdapter.CreateExecutableObjectTransaction(this);
  204. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  205. {
  206. foreach (var module in list)
  207. {
  208. var watch = CUtils.TickTimeMS;
  209. try
  210. {
  211. module.OnSaveData(trans);
  212. }
  213. catch (Exception err)
  214. {
  215. log.Error(err.Message, err);
  216. }
  217. finally
  218. {
  219. Statistics.LogTime($"{module.GetType().Name} : OnSaveData", CUtils.TickTimeMS - watch);
  220. }
  221. }
  222. }
  223. var test = new TestTransaction(CFiles.CurrentSubDir("/test_orm/"), this.roleID, trans, ORMFactory.Instance.DefaultAdapter, this);
  224. await trans.ExecuteAsync().ContinueWith(t =>
  225. {
  226. Statistics.LogTime($"{GetType().Name} : OnModulesSaveDataAsync", CUtils.TickTimeMS - watch_exe);
  227. });
  228. await test.CheckAsync(false);
  229. }
  230. else
  231. {
  232. var watch_exe = CUtils.TickTimeMS;
  233. var trans = DBAdapter.CreateExecutableObjectTransaction(this);
  234. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  235. {
  236. foreach (var module in list)
  237. {
  238. var watch = CUtils.TickTimeMS;
  239. try
  240. {
  241. module.OnSaveData(trans);
  242. }
  243. catch (Exception err)
  244. {
  245. log.Error(err.Message, err);
  246. }
  247. finally
  248. {
  249. Statistics.LogTime($"{module.GetType().Name} : OnSaveData", CUtils.TickTimeMS - watch);
  250. }
  251. }
  252. }
  253. await trans.ExecuteAsync().ContinueWith(t =>
  254. {
  255. Statistics.LogTime($"{GetType().Name} : OnModulesSaveDataAsync", CUtils.TickTimeMS - watch_exe);
  256. });
  257. }
  258. }
  259. finally
  260. {
  261. stopwatch.Stop();
  262. //if (DeepCore.Log.Logger.SHOW_LOG)
  263. {
  264. if (stopwatch.ElapsedMilliseconds > SAVE_EXPECT_TIME_LIMIT)
  265. {
  266. log.Warn(" Warn LogicService : OnModulesSaveDataAsync Flush Time = " + stopwatch.Elapsed);
  267. }
  268. else
  269. {
  270. log.Debug("Debug LogicService : OnModulesSaveDataAsync Flush Time = " + stopwatch.Elapsed);
  271. }
  272. }
  273. }
  274. }
  275. private async Task OnModulesStopAsync()
  276. {
  277. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  278. {
  279. list.Reverse();
  280. foreach (var module in list)
  281. {
  282. try
  283. {
  284. await module.OnStopAsync();
  285. }
  286. catch (Exception err)
  287. {
  288. log.Error(err.Message, err);
  289. }
  290. }
  291. }
  292. }
  293. private async Task OnModulesStopedAsync()
  294. {
  295. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  296. {
  297. list.Reverse();
  298. foreach (var module in list)
  299. {
  300. try
  301. {
  302. await module.OnStopedAsync();
  303. }
  304. catch (Exception err)
  305. {
  306. log.Error(err.Message, err);
  307. }
  308. }
  309. }
  310. }
  311. private async Task NotifyModulesClientEnterGameAsync()
  312. {
  313. IsClientEntered = true;
  314. OnClientEntered?.Invoke();
  315. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  316. {
  317. foreach (var module in list)
  318. {
  319. try
  320. {
  321. await module.OnClientEnterGameAsync();
  322. }
  323. catch (Exception err)
  324. {
  325. log.Error(err.Message, err);
  326. }
  327. }
  328. }
  329. }
  330. private void OnModulesDispose()
  331. {
  332. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  333. {
  334. list.Reverse();
  335. foreach (var module in list)
  336. {
  337. try
  338. {
  339. module.Dispose();
  340. }
  341. catch (Exception err)
  342. {
  343. log.Error(err.Message, err);
  344. }
  345. }
  346. }
  347. modules.Clear();
  348. }
  349. private void OnModulesSessionReconnect()
  350. {
  351. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  352. {
  353. foreach (var module in list)
  354. {
  355. try
  356. {
  357. module.OnSessionReconnect();
  358. }
  359. catch (Exception err)
  360. {
  361. log.Error(err.Message, err);
  362. }
  363. }
  364. }
  365. }
  366. private void OnModulesSessionDisconnect()
  367. {
  368. using (var list = CollectionObjectPool<ILogicModule>.AllocList(modules))
  369. {
  370. foreach (var module in list)
  371. {
  372. try
  373. {
  374. module.OnSessionDisconnect();
  375. }
  376. catch (Exception err)
  377. {
  378. log.Error(err.Message, err);
  379. }
  380. }
  381. }
  382. }
  383. private string ErrorBaseInfo()
  384. {
  385. return string.Format("ServerID:{0}|AccountID:{1}|RoleID:{2} ", serverID, accountID, roleID);
  386. }
  387. #endregion
  388. //---------------------------------------------------------------------------------------------------
  389. #region Area
  390. /// <summary>
  391. /// Area通知逻辑需要传送操作,一般是踩到场景传送点
  392. /// </summary>
  393. /// <param name="tp"></param>
  394. [RpcHandler(typeof(RoleNeedTransportNotify), ServerNames.AreaServiceType)]
  395. public void area_rpc_Handle(RoleNeedTransportNotify tp)
  396. {
  397. var task = areaModule.RequestTransportAsync(tp);
  398. }
  399. /// <summary>
  400. /// Area通知逻辑服无缝切场景
  401. /// </summary>
  402. /// <param name="tp"></param>
  403. [RpcHandler(typeof(RoleCrossMapNotify), ServerNames.AreaServiceType)]
  404. public void area_rpc_Handle(RoleCrossMapNotify notify)
  405. {
  406. areaModule.RequestCrossMapAsync(notify).NoWait();
  407. }
  408. [RpcHandler(typeof(AreaGameOverNotify), ServerNames.AreaServiceType)]
  409. public void area_rpc_Handle(AreaGameOverNotify notify)
  410. {
  411. areaModule.DoAreaGameOverNotify(notify);
  412. }
  413. #endregion
  414. //---------------------------------------------------------------------------------------------------
  415. #region Session
  416. /// <summary>
  417. /// 玩家断开连接
  418. /// </summary>
  419. /// <param name="disconnect"></param>
  420. [RpcHandler(typeof(SessionDisconnectNotify), ServerNames.SessionServiceType)]
  421. public virtual void session_rpc_Handle(SessionDisconnectNotify disconnect)
  422. {
  423. var area = areaModule.currentArea;
  424. if (area != null)
  425. {
  426. disconnect.roleID = this.roleID;
  427. area.Invoke(disconnect);
  428. }
  429. this.OnModulesSessionDisconnect();;
  430. }
  431. /// <summary>
  432. /// 玩家重新连接
  433. /// </summary>
  434. /// <param name="disconnect"></param>
  435. [RpcHandler(typeof(SessionReconnectNotify), ServerNames.SessionServiceType)]
  436. public virtual void session_rpc_Handle(SessionReconnectNotify reconnect)
  437. {
  438. this.clientInfo = new ClientInfo(reconnect.config);
  439. var area = areaModule.currentArea;
  440. if (area != null)
  441. {
  442. reconnect.roleID = this.roleID;
  443. area.Invoke(reconnect);
  444. }
  445. this.OnModulesSessionReconnect();
  446. OnSessionReconnect?.Invoke();
  447. }
  448. [RpcHandler(typeof(SessionBeginLeaveRequest), typeof(SessionBeginLeaveResponse), ServerNames.SessionServiceType)]
  449. public virtual void session_rpc_Handle(SessionBeginLeaveRequest disconnect, OnRpcReturn<SessionBeginLeaveResponse> cb)
  450. {
  451. var area = areaModule.currentArea;
  452. if (area != null)
  453. {
  454. area.Call(disconnect, cb);
  455. }
  456. else
  457. {
  458. cb(new SessionBeginLeaveResponse() { s2c_code = Response.CODE_ERROR });
  459. }
  460. }
  461. [RpcHandler(typeof(ClientEnterGameRequest), typeof(ClientEnterGameResponse), ServerNames.SessionServiceType)]
  462. public void client_rpc_Handle(ClientEnterGameRequest enter, OnRpcReturn<ClientEnterGameResponse> cb)
  463. {
  464. cb(new ClientEnterGameResponse() { s2c_role = this.roleModule.ToClientRoleData() });
  465. Provider.Execute(NotifyModulesClientEnterGameAsync);
  466. }
  467. /// <summary>
  468. /// 测试客户端Ping,Pong
  469. /// </summary>
  470. [RpcHandler(typeof(ClientPing), typeof(ClientPong))]
  471. public virtual void client_rpc_Handle(ClientPing ping, OnRpcReturn<ClientPong> cb)
  472. {
  473. #if TEST
  474. session.Invoke(new LogicTimeNotify() { index = 0, time = ping.time });
  475. session.Invoke(new LogicTimeNotify() { index = 1, time = ping.time });
  476. cb(new ClientPong()
  477. {
  478. s2c_code = (ping.time.Millisecond % 2 == 0) ? Response.CODE_OK : Response.CODE_ERROR,
  479. s2c_msg = DateTime.Now.ToString(),
  480. time = ping.time
  481. });
  482. session.Invoke(new LogicTimeNotify() { index = 2, time = ping.time });
  483. session.Invoke(new LogicTimeNotify() { index = 3, time = ping.time });
  484. #else
  485. cb(new ClientPong() { s2c_code = Response.CODE_OK, time = ping.time });
  486. #endif
  487. }
  488. [RpcHandler(typeof(ServerGameEventNotify))]
  489. public virtual void rpc_event_notify(ServerGameEventNotify ntf)
  490. {
  491. if (EventMgr != null && (string.IsNullOrEmpty(ntf.ServerGroupID) || ntf.ServerGroupID == serverGroupID))
  492. {
  493. EventMgr.OnReceiveMessage(EventMessage.FromBytes(ntf.EventMessageData));
  494. }
  495. }
  496. [RpcHandler(typeof(ClientGameEventNotify))]
  497. public virtual void client_rpc_notify(ClientGameEventNotify ntf)
  498. {
  499. var msg = EventMessage.FromBytes(ntf.EventMessageData);
  500. if (msg is NamedEventMessage nameMsg)
  501. {
  502. nameMsg.From = EventMgr?.Address;
  503. }
  504. var address = EventManagerAddress.Parse(msg.From);
  505. address = new EventManagerAddress("Client", address.UUID);
  506. msg.From = address.Address;
  507. EventManager.MessageBroker.Publish(ntf.To, EventMgr, msg);
  508. }
  509. [RpcHandler(typeof(ClientGetZoneInfoSnapRequest), typeof(ClientGetZoneInfoSnapResponse))]
  510. public virtual Task<ClientGetZoneInfoSnapResponse> client_rpc_Handle(ClientGetZoneInfoSnapRequest req)
  511. {
  512. return areaModule.DoClientGetZoneInfoSnapRequest(req);
  513. }
  514. [RpcHandler(typeof(ClientChangeZoneLineRequest), typeof(ClientChangeZoneLineResponse))]
  515. public virtual Task<ClientChangeZoneLineResponse> client_rpc_Handle(ClientChangeZoneLineRequest req)
  516. {
  517. return areaModule.DoClientChangeZoneLineRequest(req);
  518. }
  519. #endregion
  520. public class ClientInfo
  521. {
  522. public string Os { get; private set; }
  523. public string Network { get; private set; }
  524. public string AppVersion { get; private set; }
  525. public string AppVersionCode { get; private set; }
  526. public string OsVersion { get; private set; }
  527. public string SdkVersion { get; private set; }
  528. public string DeviceBrand { get; private set; }
  529. public string DeviceModel { get; private set; }
  530. public string DeviceScreen { get; private set; }
  531. public string Mac { get; private set; }
  532. public string Imei { get; private set; }
  533. public string Uuid { get; private set; }
  534. public string PackageName { get; private set; }
  535. public string BuildNumber { get; private set; }
  536. public string Carrier { get; private set; }
  537. public string Iccid { get; private set; }
  538. public string Imsi { get; private set; }
  539. public string Idfa { get; private set; }
  540. public string ClientIp { get; private set; }
  541. public string DeviceID { get; private set; }
  542. public string Channel { get; private set; }
  543. public string SDKName { get; private set; }
  544. public string PlatformAccount { get; private set; }
  545. public ClientInfo(HashMap<string, string> config)
  546. {
  547. this.Os = config["deviceType"]?.ToString();
  548. this.Os = SQLFactory.CurrentFactory.EscapeString(Os);
  549. this.Network = config["network"]?.ToString();
  550. this.Network = SQLFactory.CurrentFactory.EscapeString(Network);
  551. this.AppVersion = config["clientVersion"]?.ToString();
  552. this.AppVersion = SQLFactory.CurrentFactory.EscapeString(AppVersion);
  553. this.AppVersionCode = null;
  554. this.OsVersion = null;
  555. this.SdkVersion = config["sdkVersion"]?.ToString();
  556. this.SdkVersion = SQLFactory.CurrentFactory.EscapeString(SdkVersion);
  557. this.DeviceBrand = null;
  558. this.DeviceModel = config["deviceModel"]?.ToString();
  559. this.DeviceModel = SQLFactory.CurrentFactory.EscapeString(DeviceModel);
  560. this.DeviceScreen = null;
  561. this.Mac = config["deviceId"]?.ToString();
  562. this.Mac = SQLFactory.CurrentFactory.EscapeString(Mac);
  563. this.Imei = null;
  564. this.Uuid = config["roleID"]?.ToString();
  565. this.Uuid = SQLFactory.CurrentFactory.EscapeString(Uuid);
  566. this.PackageName = null;
  567. this.BuildNumber = null;
  568. this.Carrier = null;
  569. this.Iccid = null;
  570. this.Imsi = null;
  571. this.Idfa = config["deviceId"]?.ToString();
  572. this.Idfa = SQLFactory.CurrentFactory.EscapeString(Idfa);
  573. this.ClientIp = config["clientIp"]?.ToString();
  574. this.ClientIp = SQLFactory.CurrentFactory.EscapeString(ClientIp);
  575. this.DeviceID = config["deviceId"]?.ToString();
  576. this.DeviceID = SQLFactory.CurrentFactory.EscapeString(DeviceID);
  577. this.Channel = config["channel"]?.ToString();
  578. this.Channel = SQLFactory.CurrentFactory.EscapeString(Channel);
  579. if (string.IsNullOrEmpty(Channel))
  580. this.Channel = "0";
  581. this.SDKName = config["sdkName"]?.ToString();
  582. this.SDKName = SQLFactory.CurrentFactory.EscapeString(SDKName);
  583. this.PlatformAccount = config["platformAccount"]?.ToString();
  584. }
  585. }
  586. //---------------------------------------------------------------------------------------------------
  587. }
  588. }