RPGServerPersistenceManager.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. using DeepCore.Log;
  2. using DeepCore.ORM;
  3. using DeepCore.Reflection;
  4. using DeepCore.Threading;
  5. using DeepCrystal;
  6. using DeepCrystal.ORM;
  7. using DeepCrystal.ORM.Generic;
  8. using DeepCrystal.ORM.Query;
  9. using DeepCrystal.RPC;
  10. using DeepCrystal.Threading;
  11. using DeepMMO.Data;
  12. using DeepMMO.Protocol.Client;
  13. using DeepMMO.Server.Logic;
  14. using System;
  15. using System.Text.RegularExpressions;
  16. using System.Threading.Tasks;
  17. namespace DeepMMO.Server
  18. {
  19. public class RPGServerPersistenceManager : DeepCore.Disposable
  20. {
  21. //--------------------------------------------------------------------------------------------------------------------------
  22. #region Singleton
  23. private static readonly object lock_init = new object();
  24. private static bool init_done = false;
  25. private static RPGServerPersistenceManager instance;
  26. public static bool IsInitDone { get { return init_done; } }
  27. public static RPGServerPersistenceManager Instance
  28. {
  29. get
  30. {
  31. if (instance == null)
  32. {
  33. lock (lock_init)
  34. {
  35. if (!init_done)
  36. {
  37. var config = IService.GlobalConfig;
  38. instance = ReflectionUtil.CreateInterface<RPGServerPersistenceManager>(GlobalConfig.RPGServerPersistenceManager);
  39. instance.Init();
  40. init_done = true;
  41. }
  42. }
  43. }
  44. return instance;
  45. }
  46. }
  47. #endregion
  48. //--------------------------------------------------------------------------------------------------------------------------
  49. private DeepCrystal.ORM.IMappingHash mappingNameToUUID;
  50. private DeepCrystal.ORM.IMappingHash mappingUUIDToName;
  51. private DeepCrystal.ORM.IMappingHash mappingDigitToUUID;
  52. private DeepCrystal.ORM.IMappingHash mappingUUIDToDigit;
  53. public DateTime ServerInitTimeUTC { get; private set; }
  54. public RPGServerPersistenceManager()
  55. {
  56. instance = this;
  57. }
  58. public virtual void Init()
  59. {
  60. Task.Run(async () =>
  61. {
  62. using (var start = ORMFactory.Instance.DefaultAdapter.GetHash("SERVER_INIT", null))
  63. {
  64. var now = DateTime.UtcNow;
  65. if (await start.SetAsync(nameof(ServerInitTimeUTC), now, When.NotExists))
  66. {
  67. ServerInitTimeUTC = now;
  68. }
  69. else
  70. {
  71. ServerInitTimeUTC = await start.GetAsync<DateTime>(nameof(ServerInitTimeUTC));
  72. }
  73. }
  74. this.mappingNameToUUID = DeepCrystal.ORM.ORMFactory.Instance.DefaultAdapter.GetHash("Mapping:NameToUUID", null);
  75. this.mappingUUIDToName = DeepCrystal.ORM.ORMFactory.Instance.DefaultAdapter.GetHash("Mapping:UUIDToName", null);
  76. this.mappingDigitToUUID = DeepCrystal.ORM.ORMFactory.Instance.DefaultAdapter.GetHash("Mapping:DigitToUUID", null);
  77. this.mappingUUIDToDigit = DeepCrystal.ORM.ORMFactory.Instance.DefaultAdapter.GetHash("Mapping:UUIDToDigit", null);
  78. }).Wait();
  79. }
  80. protected override void Disposing()
  81. {
  82. mappingNameToUUID?.Dispose();
  83. mappingUUIDToName?.Dispose();
  84. mappingDigitToUUID?.Dispose();
  85. mappingUUIDToDigit?.Dispose();
  86. this.mappingNameToUUID = null;
  87. this.mappingUUIDToName = null;
  88. this.mappingDigitToUUID = null;
  89. this.mappingUUIDToDigit = null;
  90. }
  91. //--------------------------------------------------------------------------------------------------------------------------
  92. #region RoleNameMapping
  93. //--------------------------------------------------------------------------------------------------------------------------
  94. protected virtual string GenDigitID(string roleUUID)
  95. {
  96. var duration = DateTime.UtcNow - ServerInitTimeUTC;
  97. var prifix = ((long)duration.TotalMilliseconds);
  98. var suffix = ((int)roleUUID[0]) % 10;
  99. return $"{prifix}{suffix}";
  100. }
  101. public virtual Task<string> GetRoleNameByUUIDAsync(string roleUUID, ITaskExecutor svc)
  102. {
  103. return svc.Execute(mappingUUIDToName.GetAsync(roleUUID).ContinueWith<string>(t => t.GetResultToString()));
  104. }
  105. public virtual Task<IConvertible[]> GetRoleNameByUUIDAsync(string[] roleUUID, ITaskExecutor svc)
  106. {
  107. return svc.Execute(mappingUUIDToName.GetAsync(roleUUID));
  108. }
  109. public virtual Task<string> GetRoleUUIDByNameAsync(string roleName, ITaskExecutor svc)
  110. {
  111. return svc.Execute(mappingNameToUUID.GetAsync(roleName).ContinueWith<string>(t => t.GetResultToString()));
  112. }
  113. public virtual Task<string> GetRoleDigitByUUIDAsync(string roleUUID, ITaskExecutor svc)
  114. {
  115. return svc.Execute(mappingUUIDToDigit.GetAsync(roleUUID).ContinueWith<string>(t => t.GetResultToString()));
  116. }
  117. public virtual Task<string[]> GetRoleUUIDByDigitAsync(string digit, ITaskExecutor svc)
  118. {
  119. return svc.Execute(mappingDigitToUUID.GetAsync(digit).ContinueWith<string[]>(t =>
  120. {
  121. if (t.IsCompleted)
  122. {
  123. var exist = t.GetResultToString();
  124. if (exist != null)
  125. {
  126. return exist.Split(',');
  127. }
  128. }
  129. return null;
  130. }));
  131. }
  132. public virtual Task<string> TryRegistRoleNameMappingAsync(string roleUUID, string roleName, ITaskExecutor svc)
  133. {
  134. return svc.Execute(async () =>
  135. {
  136. if (await mappingNameToUUID.SetAsync(roleName, roleUUID, When.NotExists))
  137. {
  138. await mappingUUIDToName.SetAsync(roleUUID, roleName);
  139. var digitID = Instance.GenDigitID(roleUUID);
  140. if (await mappingDigitToUUID.SetAsync(digitID, roleUUID, When.NotExists) == false)
  141. {
  142. var exist = await mappingDigitToUUID.GetAsync(digitID);
  143. await mappingDigitToUUID.SetAsync(digitID, $"{exist},{roleUUID}");
  144. }
  145. await mappingUUIDToDigit.SetAsync(roleUUID, digitID);
  146. return digitID;
  147. }
  148. return null;
  149. });
  150. }
  151. public virtual Task<bool> RoleChangeNameMappingAsync(string roleUUID, string newName, string curName, ITaskExecutor svc)
  152. {
  153. return svc.Execute(async () =>
  154. {
  155. if (await mappingNameToUUID.SetAsync(newName, roleUUID, When.NotExists))
  156. {
  157. //删除旧的名字.
  158. await mappingNameToUUID.DeleteAsync(curName);
  159. //删除旧的UUID关联.
  160. await mappingUUIDToName.DeleteAsync(roleUUID);
  161. //设置新的UUID关联.
  162. await mappingUUIDToName.SetAsync(roleUUID, newName);
  163. return true;
  164. }
  165. return false;
  166. });
  167. }
  168. public virtual Task<bool> RoleNameExist(string roleName,ITaskExecutor svc)
  169. {
  170. return svc.Execute(async () =>
  171. {
  172. return await mappingNameToUUID.ExistsAsync(roleName);
  173. });
  174. }
  175. #endregion
  176. //--------------------------------------------------------------------------------------------------------------------------
  177. #region NameChecking
  178. //匹配中文,英文字母和数字及_:
  179. private Regex roleNamePattern = new Regex(@"^[\u4e00-\u9fa5_a-zA-Z0-9]+$");
  180. /// <summary>
  181. /// 检查角色名是否合法
  182. /// </summary>
  183. /// <param name="roleName"></param>
  184. /// <returns></returns>
  185. public virtual bool CheckRoleName(string roleName)
  186. {
  187. if (roleNamePattern.IsMatch(roleName))
  188. {
  189. return true;
  190. }
  191. return false;
  192. }
  193. #endregion
  194. //--------------------------------------------------------------------------------------------------------------------------
  195. public const string TYPE_ACCOUNT_DATA = "Account:";
  196. //账号下所有角色数据.
  197. public const string TYPE_ACCOUNT_ROLE_SNAP_DATA = "Account:AccountRoleSnap:";
  198. public const string TYPE_ACCOUNT_EXT_DATA = "Account:AccountExt";
  199. public const string TYPE_ROLE_DATA = "Role:";
  200. public const string TYPE_RANK_DATA = "Rank:";
  201. public const string TYPE_ROLE_RANK_SNAP_DATA = "Role_Rank:";
  202. public const string TYPE_RANK_ENTIRE_DATA = "RankingEntireData:{0}";
  203. public const string TYPE_RANK_MAPPING_SORT = "Mapping:RankSort:Group{0}:Main{1}_Sub{2}";
  204. public const string TYPE_ROLE_SNAP_DATA = "RoleSnap:";
  205. public const string TYPE_ROLE_SNAP_EXT_DATA = "RoleSnapExt:";
  206. public const string TYPE_ROLEHERO_DATA = "RoleHero:";
  207. public const string TYPE_ROLEItem_DATA = "RoleItem:";
  208. public const string TYPE_ROLE_SNAP_EXT_BIN_DATA = "RoleSnapBinExt:";
  209. //同区内每个服记录.
  210. public const string TYPE_SERVER_RECORD_DATA = "ServerRecord";
  211. //账号封停数据.
  212. public const string TYPE_ROLE_DATA_STATUS_SNAP_DATA = "RoleDataStatusSnap:";
  213. /// <summary>
  214. /// 创建角色,由Session.Roled调用ORM.
  215. /// </summary>
  216. /// <typeparam name="T"></typeparam>
  217. /// <param name="data"></param>
  218. /// <returns></returns>
  219. public virtual async Task<RoleSnap> CreateRoleDataAsync(ServerRoleData data, ITaskExecutor svc)
  220. {
  221. var roleMapping = new MappingReference<ServerRoleData>(TYPE_ROLE_DATA, data.uuid, svc);
  222. await roleMapping.SaveDataAsync(data);
  223. var snapData = InitRoleSnap(data, new RoleSnap());
  224. // Snap数据映射
  225. var snapMapping = new MappingReference<RoleSnap>(TYPE_ROLE_SNAP_DATA, data.uuid, svc);
  226. await snapMapping.SaveDataAsync(snapData);
  227. return snapData;
  228. }
  229. public virtual async Task DeleteRoleDataAsync(string c2s_role_uuid, ITaskExecutor svc)
  230. {
  231. var snapMapping = new MappingReference<RoleSnap>(TYPE_ROLE_SNAP_DATA, c2s_role_uuid, svc);
  232. var roleSnap = await snapMapping.LoadDataAsync();
  233. // TODO
  234. }
  235. public virtual async Task<AccountData> GetOrCreateAccountDataAsync(MappingReference<AccountData> saveAcc, string accountName, string accountToken)
  236. {
  237. if (await saveAcc.EnterLockAsync(out var token))
  238. {
  239. try
  240. {
  241. var accountData = await saveAcc.LoadOrCreateDataAsync(() =>
  242. {
  243. var ret = new AccountData();
  244. ret.uuid = accountName;
  245. ret.token = accountToken;
  246. return ret;
  247. });
  248. return accountData;
  249. }
  250. finally
  251. {
  252. await saveAcc.ExitLockAsync(token);
  253. }
  254. }
  255. return null;
  256. }
  257. public virtual QueryMappingReference<T> GetQueryReference<T>(string typeName, ITaskExecutor svc, IMappingAdapter db = null) where T : IObjectMapping
  258. {
  259. /* TEST
  260. Task.Run(async () =>
  261. {
  262. var trans = ORMFactory.Instance.CreateTransaction(ORMFactory.Instance.DefaultAdapter);
  263. trans.AddCondition(ORMFactory.Instance.Conditions.HashEqual("key", "fieldA", 12345));
  264. trans.AddCondition(ORMFactory.Instance.Conditions.HashNotEqual("key", "fieldB", 12345));
  265. using (var save = trans.GetHash("key", svc))
  266. {
  267. await save.SetAsync("fieldA", 1);
  268. await save.SetAsync("fieldB", 2);
  269. await save.SetAsync("fieldC", "ccc");
  270. await trans.ExecuteAsync(svc);
  271. }
  272. });
  273. */
  274. return new QueryMappingReference<T>(typeName, svc, db);
  275. }
  276. protected virtual RoleSnap InitRoleSnap(ServerRoleData roleData, RoleSnap ret)
  277. {
  278. ret.uuid = roleData.uuid;
  279. ret.digitID = roleData.digitID;
  280. ret.name = roleData.name;
  281. ret.account_uuid = roleData.account_uuid;
  282. ret.role_template_id = roleData.role_template_id;
  283. ret.unit_template_id = roleData.unit_template_id;
  284. ret.level = roleData.Level;
  285. ret.create_time = roleData.create_time;
  286. ret.last_login_time = roleData.last_login_time;
  287. ret.server_id = roleData.server_id;
  288. ret.privilege = roleData.privilege;
  289. return ret;
  290. }
  291. //--------------------------------------------------------------------------------------------------------------------------
  292. #region ServerRoleIDMapping.
  293. public class ServerRoleIDMappingSet
  294. {
  295. private const string TYPE_SERVER_ROLEID_DATA = "ServerID:{0}:RoleID:";
  296. private readonly DeepCrystal.ORM.IMappingSet mappingSet;
  297. public ServerRoleIDMappingSet(ITaskExecutor svc, string serverID)
  298. {
  299. string key = string.Format(TYPE_SERVER_ROLEID_DATA, serverID);
  300. this.mappingSet = DeepCrystal.ORM.ORMFactory.Instance.DefaultAdapter.GetSet(key, svc);
  301. }
  302. public Task AddRoleIDAsync(string playerUUID)
  303. {
  304. return mappingSet.AddAsync(playerUUID);
  305. }
  306. public Task<string[]> GetRoleIDsAsync()
  307. {
  308. return mappingSet.MembersAsync().ContinueWith(t =>
  309. {
  310. var rst = t.GetResultAs();
  311. if (rst != null) return Array.ConvertAll(rst, (s) => s.ToString());
  312. return null;
  313. });
  314. }
  315. }
  316. public virtual ServerRoleIDMappingSet GetServerRoleIDMappingSet(ITaskExecutor svc, string serverid)
  317. {
  318. return new ServerRoleIDMappingSet(svc, serverid);
  319. }
  320. #endregion
  321. //--------------------------------------------------------------------------------------------------------------------------
  322. public virtual void SaveBICreateRoleInfo(Logger log, ServerRoleData data, string channel, IRemoteService logService)
  323. {
  324. }
  325. }
  326. }