GameData.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import { ch } from "../../ch/ch";
  2. import PeriodData, { PeriodDataUpdate } from "./PeriodData";
  3. export interface data_interface {
  4. serialize(): any;
  5. unserialize(data: any): void;
  6. }
  7. /**游戏数据
  8. * 有额外自定义数据可以继承此类
  9. */
  10. export default class GameData<T extends string | number | symbol, DT extends string | number | symbol, WT extends string | number | symbol, MT extends string | number | symbol> {
  11. private _key: string;
  12. private _uid: string;
  13. private _gid: string;
  14. private _save_time: number;
  15. private _ver: string;
  16. private _data: PeriodData<T>;
  17. private _day_data: PeriodData<DT>;
  18. private _week_data: PeriodData<WT>;
  19. private _month_data: PeriodData<MT>;
  20. constructor(gid: string, uid: string, name: string,
  21. limt?: Map<T, { min?: number | null, max?: number | null }>,
  22. day_limt?: Map<DT, { min?: number | null, max?: number | null }>,
  23. week_limt?: Map<WT, { min?: number | null, max?: number | null }>,
  24. month_limt?: Map<MT, { min?: number | null, max?: number | null }>,
  25. ) {
  26. this._key = name;
  27. this._gid = gid;
  28. this._uid = uid;
  29. this._data = new PeriodData<T>(PeriodDataUpdate.none, limt);
  30. this._day_data = new PeriodData<DT>(PeriodDataUpdate.day, day_limt);
  31. this._week_data = new PeriodData<WT>(PeriodDataUpdate.week, week_limt);
  32. this._month_data = new PeriodData<MT>(PeriodDataUpdate.month, month_limt);
  33. }
  34. //
  35. public get data(): PeriodData<T> { return this._data };
  36. public get day_data(): PeriodData<DT> { return this._day_data };
  37. public get week_data(): PeriodData<WT> { return this._week_data };
  38. public get month_data(): PeriodData<MT> { return this._month_data };
  39. public get uid(): string { return this._uid };
  40. //
  41. private _dirty: boolean = false;
  42. public checkNewDay(): void {
  43. let now = ch.date.now();
  44. this._day_data?.check(now);
  45. this._week_data?.check(now);
  46. this._month_data?.check(now);
  47. }
  48. //
  49. private compareVersion(version1: string, version2: string): number {
  50. // 移除前面的 'v' 字符
  51. const v1 = version1.replace(/^V/, '');
  52. const v2 = version2.replace(/^V/, '');
  53. // 分割版本号
  54. const parts1 = v1.split('.').map(Number); // 将版本号分割并转为数字
  55. const parts2 = v2.split('.').map(Number);
  56. // 比较每一部分
  57. for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
  58. const num1 = parts1[i] || 0; // 如果没有该部分,默认为 0
  59. const num2 = parts2[i] || 0;
  60. if (num1 < num2) {
  61. return -1; // version1 < version2
  62. }
  63. if (num1 > num2) {
  64. return 1; // version1 > version2
  65. }
  66. }
  67. return 0; // 两个版本号相等
  68. }
  69. //
  70. private async loadGameDataWithRetry(maxRetries: number = 3, delayMs: number = 1000): Promise<any | null> {
  71. let attempt = 0;
  72. while (attempt < maxRetries) {
  73. const data = await this.load_data();
  74. if (data != null) {
  75. return data ?? null;
  76. } else {
  77. attempt++;
  78. await new Promise(resolve => setTimeout(resolve, delayMs));
  79. }
  80. }
  81. return null;
  82. }
  83. //
  84. private async saveGameDataWithRetry(save_data: { [key: string]: any }, maxRetries: number = 3, delayMs: number = 1000): Promise<boolean> {
  85. let attempt = 0;
  86. while (attempt < maxRetries) {
  87. const ret = await this.save_data(save_data);
  88. if (ret) {
  89. return true;
  90. } else {
  91. attempt++;
  92. await new Promise(resolve => setTimeout(resolve, delayMs));
  93. }
  94. }
  95. return false;
  96. }
  97. /**加载数据*/
  98. public async load(ver?: string | null): Promise<void> {
  99. this.on_load(0);
  100. let load_data = ch.storage.getObject(this._key, this._gid);
  101. if (load_data) {
  102. if (ch.sdk.get_inited() && load_data.uid != this._uid) load_data = null;
  103. }
  104. //
  105. if (!load_data) {
  106. const remote_data = await this.loadGameDataWithRetry();
  107. load_data = remote_data;
  108. }
  109. // } else if (remote_data && this.on_check(load_data, remote_data)) {
  110. // load_data = remote_data;
  111. // }
  112. //
  113. if (!load_data) {
  114. this.on_init();
  115. } else {
  116. this.unserialize(load_data);
  117. }
  118. this.checkVer(ver);
  119. this.checkNewDay();
  120. this.on_load(1);
  121. }
  122. /**远程调用保存,如果不是强制远程保存,必并先设置脏数据*/
  123. public async save(force: boolean = false): Promise<boolean> {
  124. this.on_save(0);
  125. if (force) {
  126. this.setDirty();
  127. } else if (!this._dirty) {
  128. this.on_save(1);
  129. return false;
  130. }
  131. let ret = await this.saveGameDataWithRetry(this.serialize());
  132. this.on_save(1);
  133. if (ret) this._dirty = false;
  134. return ret;
  135. }
  136. /**设置脏数据后保存到本地*/
  137. public setDirty() {
  138. this._dirty = true;
  139. ch.storage.set(this._key, this.serialize(), this._gid);
  140. }
  141. private checkVer(new_v: string | null): void {
  142. if (!new_v) return;
  143. if (!this._ver) {
  144. this.on_ver(true, this._ver, new_v);
  145. }
  146. let k = this.compareVersion(this._ver, new_v);
  147. this.on_ver(k < 0, this._ver, new_v);
  148. this._ver = new_v;
  149. }
  150. /**
  151. * 序列化(数据库存储)
  152. */
  153. private serialize(): { [key: string]: number | string | any } {
  154. const save_data: { [key: string]: number | string } = {};
  155. if (this._data) save_data.data = this._data.serialize();
  156. if (this._day_data) save_data.day_data = this._day_data.serialize();
  157. if (this._week_data) save_data.week_data = this._week_data.serialize();
  158. if (this._month_data) save_data.month_data = this._month_data.serialize();
  159. save_data.uid = this._uid;
  160. save_data.gid = this._gid;
  161. this._save_time = ch.date.now();
  162. save_data.save_time = this._save_time;
  163. if (this._ver) save_data.ver = this._ver;
  164. this.on_serialize(save_data);
  165. return save_data;
  166. }
  167. /**
  168. * 反序列化
  169. */
  170. private unserialize(load_data: { [key: string]: number | string | any }): void {
  171. if (!load_data) return;
  172. this._data?.unserialize(load_data.data);
  173. this._day_data?.unserialize(load_data.day_data);
  174. this._week_data?.unserialize(load_data.week_data);
  175. this._month_data?.unserialize(load_data.month_data);
  176. this._uid = load_data.uid ?? this._uid;
  177. this._gid = load_data.gid ?? this._gid;
  178. this._save_time = load_data.save_time as number ?? ch.date.now();
  179. if (load_data.ver) this._ver = load_data.ver as string;
  180. this.on_unserialize(load_data);
  181. }
  182. /**重写此方法初始自定义数据*/
  183. protected on_init(): void {
  184. }
  185. /**重写此方法检测是否使用远程数据*/
  186. protected on_check(local: any, remote: any): boolean {
  187. return true;
  188. }
  189. /**重写 版本检测数据处理*/
  190. protected on_ver(is_new: boolean, old_v: string, new_v: string): void {
  191. }
  192. /**重写序列化加入自定义的数据*/
  193. protected on_serialize(data: { [key: string]: number | string | any }): void {
  194. }
  195. /**重写反序列化*/
  196. protected on_unserialize(data: { [key: string]: number | string | any }): void {
  197. }
  198. /**保存数据(0开始 1结束)*/
  199. protected on_save(step: 0 | 1): void {
  200. }
  201. /**加载数据(0开始 1结束)*/
  202. protected on_load(step: 0 | 1): void {
  203. }
  204. /**重写成其它加载方式*/
  205. protected async load_data(): Promise<{ [key: string]: any; }> {
  206. const ret = await ch.sdk.loadGameData(this._key);
  207. if (ret.code === 0) {
  208. return ret.data;
  209. } else {
  210. ch.log.warn(`尝试加载数据失败,错误代码: ${ret.code} ${ret.err}`);
  211. }
  212. return null;
  213. }
  214. /**重写成其它保存方式*/
  215. protected async save_data(save_data: { [key: string]: any; }): Promise<boolean> {
  216. const ret = await ch.sdk.saveGameData(this._key, save_data);
  217. if (ret.code == 0) {
  218. return true;
  219. } else {
  220. ch.log.warn(`尝试保存数据失败,错误代码: ${ret.code} ${ret.err}`);
  221. return false;
  222. }
  223. }
  224. }