NetRoom.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. import { NetBase, results, transUserDataform } from "./NetBase";
  2. import { NetPlayer } from "./NetPlayer";
  3. type EventNames<Map extends chsdk.OmitIndex<chsdk.EventsMap>> = Extract<keyof Map, string>;
  4. type EventParams<Map extends chsdk.OmitIndex<chsdk.EventsMap>, Ev extends EventNames<Map>> = Ev extends keyof Map ? Map[Ev] extends (...args: any[]) => any ? Parameters<Map[Ev]> : never : never;
  5. type StartEventNames<Map extends chsdk.OmitIndex<chsdk.EventsMap>, Prefix extends string> = Extract<keyof Map, string> extends infer K ? K extends `${Prefix}${string}` ? K : never : never;
  6. type EventParamFrist<Map extends chsdk.OmitIndex<chsdk.EventsMap>, Ev extends EventNames<Map>> = Ev extends keyof Map
  7. ? Map[Ev] extends (...args: infer Params) => any ? Params extends [infer First, ...any[]] ? First : never : never : never;
  8. type PickEvent<T, Prefix1 extends string, Prefix2 extends string, Prefix3 extends string> = Pick<T, Extract<keyof T, `${Prefix1}${string}` | `${Prefix2}${string}` | `${Prefix3}${string}`>>;
  9. const C2S_TALK = 'c2s_talk';
  10. const C2S_MESSAGE = 'c2s_message';
  11. const C2S_MESSAGE_TO_HOST = 'c2s_message_to_host';
  12. const C2S_MESSAGE_TO_OTHER = 'c2s_message_without_self';
  13. const C2S_FINISH = 'c2s_finish';
  14. const C2S_UPDATE_USER_GAMEDATA = 'c2s_update_user_gameData';
  15. const C2S_UPDATE_ROOM_GAMEDATA = 'c2s_update_room_gameData';
  16. /**网络房间*/
  17. export class NetRoom<GD> extends NetBase {
  18. public readonly evt = chsdk.get_new_event<PickEvent<GD, 'r_', 'r_obj', 'revt_'>>();
  19. private _hostId: string;
  20. /**游戏主机ID*/
  21. public get HostId(): string { return this._hostId };
  22. private _status: boolean = true;
  23. /**游戏是否关闭*/
  24. public get cloosed(): boolean { return !this._status };
  25. private _own_id: string;
  26. public get ownId(): string { return this._own_id };
  27. private _players: Map<string, NetPlayer<GD>> = new Map();
  28. //
  29. private _waitSends_mode0: Array<{ type: string, data: any }> = [];
  30. private _waitSends_mode1: Array<{ type: string, data: any }> = [];
  31. private _waitSends_mode2: Array<{ type: string, data: any }> = [];
  32. private _send: (type: string, data?: any) => void;
  33. /**自己是否是主机*/
  34. public get isHost(): boolean {
  35. return this._own_id === this._hostId;
  36. }
  37. //
  38. constructor(ownId: string, roomData: any, playerData: any, send: (type: string, data?: any) => void) {
  39. super(roomData.roomId);
  40. this._send = send;
  41. this._own_id = ownId;
  42. this._hostId = roomData.hostInfo;
  43. this._status = roomData.status;
  44. let ps = Object.keys(playerData).map(key => ({ key, data: playerData[key] }));
  45. for (let i = 0; i < ps.length; i++) this.addPlayer(ps[i]);
  46. this.initValue(roomData.gameData);
  47. }
  48. private addPlayer(p: any): void {
  49. const id = p.key;
  50. const pd = p.data;
  51. let player = this._players.get(id);
  52. if (!player) {
  53. player = new NetPlayer<GD>(id);
  54. this._players.set(id, player);
  55. }
  56. player.init(pd);
  57. (player as any).set_own(player.Id === this._own_id);
  58. (player as any).set_host(player.Id === this._hostId, this.isHost)
  59. }
  60. private updatePlayerStatus(id: string, online: boolean): void {
  61. const p = this.getPlayer(id);
  62. if (p) {
  63. (p as any).change_online(online);
  64. const location = p.location;
  65. const nickName = p.nickName;
  66. (this.evt as any)._emit('r_online', id, online, location, nickName);
  67. }
  68. }
  69. private changeHost(id: string): void {
  70. this._hostId = id;
  71. this._players.forEach((v, _) => { (v as any).set_host(v.Id === this._hostId, this.isHost); });
  72. const p = this.getPlayer(this._hostId);
  73. (this.evt as any)._emit('r_host', id, p.location, p.nickName);
  74. }
  75. /**自己的所有信息*/
  76. public get own(): NetPlayer<GD> {
  77. return this._players.get(this._own_id);
  78. }
  79. /**其它玩家信息*/
  80. public get others(): NetPlayer<GD>[] {
  81. return Array.from(this._players.values()).filter(player => !player.isOwn);
  82. }
  83. /**所有ai信息*/
  84. public get ais(): NetPlayer<GD>[] {
  85. return Array.from(this._players.values()).filter(player => player.isAI);
  86. }
  87. /**所有玩家*/
  88. public get all(): NetPlayer<GD>[] {
  89. return this.getAllPlayer();
  90. }
  91. /**在线玩家信息*/
  92. public get onlines(): NetPlayer<GD>[] {
  93. return Array.from(this._players.values()).filter(player => player.online);
  94. }
  95. /**不在线玩家信息*/
  96. public get outlines(): NetPlayer<GD>[] {
  97. return Array.from(this._players.values()).filter(player => !player.online);
  98. }
  99. /**所有玩家*/
  100. public getAllPlayer(): NetPlayer<GD>[] {
  101. return Array.from(this._players.values());
  102. }
  103. /**将玩家按 location 放到一个数组中,并自定义数组长度*/
  104. public getAllPlayersAtLocations(customLength: number): (NetPlayer<GD> | null)[] {
  105. const locationArray: (NetPlayer<GD> | null)[] = Array(customLength).fill(null);
  106. this._players.forEach((player) => {
  107. if (player.location >= 0 && player.location < customLength) locationArray[player.location] = player;
  108. });
  109. return locationArray;
  110. }
  111. /**中某个玩家的所有信息*/
  112. public getPlayer(id: string): NetPlayer<GD> {
  113. return this._players.get(id);
  114. }
  115. /**获取除了某个id的所有玩家*/
  116. public getExPlayer(id: string): NetPlayer<GD>[] {
  117. return Array.from(this._players.values()).filter(player => player.Id != id);
  118. }
  119. /**获在线或不在线的所有玩家*/
  120. public getOnlinePlayer(isOnline: boolean): NetPlayer<GD>[] {
  121. return isOnline ? this.onlines : this.outlines;
  122. }
  123. /**是否有AI权限*/
  124. public canAiPlayer(player: NetPlayer<GD>): boolean {
  125. if (!this._status) return false;
  126. if (!this.isHost) return false;
  127. if (!player || !player.isAI) return false;
  128. return true;
  129. }
  130. /**是否有AI权限Id,返回可以控制的player*/
  131. public canAiPlayerId(id: string): NetPlayer<GD> | null {
  132. if (!this._status) return null;
  133. if (!this.isHost) return null;
  134. const player = this._players.get(id);
  135. if (!player) return null;
  136. if (!player.isAI) return null;
  137. return player;
  138. }
  139. /**是否拥有权限(包括,自己和自己是主机时的ai)*/
  140. public hasPermission(player: NetPlayer<GD>): boolean {
  141. return player.isOwn || this.canAiPlayer(player);
  142. }
  143. /**创建自定义obj数据*/
  144. public creatObj<T extends { [key: string]: any }>(data: T): string {
  145. if (!this._status) return;
  146. if (!this.isHost) return;
  147. let oid: number = super.getValue('oid') ?? 0;
  148. oid++;
  149. const key = 'obj_' + oid;
  150. super.setValue('oid', oid);
  151. super.setValueDirty('oid', oid);
  152. super.setValue(key, data);
  153. super.setValueDirty(key, data);
  154. (this.evt as any)._emit('r_obj', key, data);
  155. return key;
  156. }
  157. /**根据key获取obj数据*/
  158. public getObj<T extends { [key: string]: any }>(key: string): T {
  159. return super.getValue(key);
  160. }
  161. /**获取当前所有的obj数据*/
  162. public getAllObj<T extends { [key: string]: any }>(): { key: string, data: T }[] {
  163. const list: { key: string, data: T }[] = [];
  164. const oid = super.getValue('oid') ?? 0;
  165. for (let i = 1; i <= oid; i++) {
  166. const key = 'obj_' + i;
  167. const data = super.getValue(key);
  168. if (data) list.push({ key: key, data: data === 0 ? null : data });
  169. }
  170. return list;
  171. }
  172. /**不建议经常更改obj */
  173. public changeObj<T extends { [key: string]: any }>(key: string, data: T): void {
  174. if (!this.isHost) return;
  175. const old = super.getValue(key);
  176. if (old) {
  177. super.setValue(key, data);
  178. super.setValueDirty(key, data);
  179. (this.evt as any)._emit('r_obj', key, data, old);
  180. }
  181. }
  182. /**删除obj数据*/
  183. public deleteObj<T extends { [key: string]: any } | 0>(key: string, data: T | 0 = 0) {
  184. if (!this.isHost) return;
  185. const old = super.getValue(key);
  186. if (old) {
  187. super.setValue(key, data);
  188. super.setValueDirty(key, data);
  189. (this.evt as any)._emit('r_obj', key, null, old);
  190. }
  191. }
  192. /**修改某个键的值*/
  193. public setValue<T extends StartEventNames<GD, 'r_'>, T2 extends EventParamFrist<GD, T>>(key: T, data: T2): void {
  194. if (!this._status) return;
  195. if (!this.isHost) return;//不是主机没有权限
  196. let old = super.getValue(key);
  197. if (old) {
  198. if (typeof data === "object") {
  199. //old = JSON.parse(JSON.stringify(old));
  200. } else if (data === old) {
  201. return;
  202. }
  203. }
  204. super.setValue(key, data);
  205. super.setValueDirty(key, data);
  206. //
  207. (this.evt as any)._emit(key, data, old);
  208. }
  209. /**获取数据的值*/
  210. public getValue<T extends StartEventNames<GD, 'r_'>, T2 extends EventParamFrist<GD, T>>(key: T): T2 {
  211. return super.getValue(key);
  212. }
  213. private server_change(data: { [key: string]: any }): void {
  214. if (!this._status) return;
  215. if (this.isHost) return;
  216. const evt: { evt: 0 | 1, key: string, data: any, old: any }[] = [];
  217. Object.keys(data).forEach(key => {
  218. evt.push(this.change(key, data[key]));
  219. });
  220. const e = (this.evt as any)
  221. for (let i = 0; i < evt.length; i++) {
  222. const ee = evt[i];
  223. if (!ee) continue;
  224. if (ee.evt === 1) {
  225. e._emit('r_obj', ee.key, ee.data, ee.old);
  226. } else {
  227. e._emit(ee.key, ee.data, ee.old);
  228. }
  229. }
  230. }
  231. private change(key: string, data: any): { evt: 0 | 1, key: string, data: any, old: any } | null {
  232. const old = super.getValue(key);
  233. super.setValue(key, data);
  234. if (typeof data !== "object" && data === old) return null;
  235. if (key === 'oid') {
  236. return null;
  237. } else if (key.startsWith('obj_')) {
  238. return { evt: 1, key: key, data: data === 0 ? null : data, old: old };
  239. } else {
  240. return { evt: 0, key: key, data: data, old: old };
  241. }
  242. }
  243. private _results: any[] = [];
  244. /**
  245. * 游戏结算数据
  246. *
  247. * @returns {Array} results - 包含游戏结算信息的数组。
  248. * 每个元素表示一个玩家的结算数据,结构如下:
  249. *
  250. * - Id: {string} 玩家唯一标识符
  251. * - AddScore: {number} 本局游戏中增加的星星数
  252. * - IsFinish: {boolean} 游戏是否已完成
  253. * - Rank: {number} 玩家在当前游戏中的排名
  254. * - Score: {number} 玩家在最后星星数
  255. * - TotalRank: {number} 玩家的总排名
  256. * - Level: {Object} 玩家当前等级的信息
  257. * - LowerRank: {number} 当前段位的等级
  258. * - Rank: {number} 段位
  259. * - Star: {number} 当前段位的等级的星级
  260. * - UserData: {Object} 玩家个人信息
  261. * - gid: {string} 游戏全局唯一标识符
  262. * - head: {string} 玩家头像的 URL
  263. * - hid: {number} 玩家省份id
  264. * - ip: {string} 玩家 IP 地址
  265. * - loginTime: {number} 玩家登录时间的时间戳
  266. * - nickName: {string} 玩家昵称
  267. * - openId: {string} 玩家在平台上的唯一标识符
  268. * - option: {string} 玩家选择的选项或设置
  269. * - pf: {string} 平台信息
  270. * - registerTime: {number} 玩家注册时间的时间戳
  271. * - userId: {number} 玩家在系统中的用户 ID
  272. * - province: {string} 玩家所在的省份
  273. * - Elements: {Array} 包含其他玩家的结算数据
  274. * - Rank: {number} 其他玩家的排名
  275. * - Level: {Object} 其他玩家的等级信息
  276. * - LowerRank: {number} 其他玩家当前段位的等级
  277. * - Rank: {number} 其他玩家的段位
  278. * - Star: {number} 其他玩家段位的等级的星级
  279. * - UserData: {Object} 其他玩家的个人信息(与上面的 UserData 结构相同)
  280. */
  281. public get results(): results[] {
  282. return this._results;
  283. }
  284. /**获取自己本局结算*/
  285. public get own_results(): results {
  286. const rs = this.results;
  287. if (!rs) return null;
  288. for (let i = 0; i < rs.length; i++) {
  289. if (rs[i].Id === this._own_id) {
  290. return rs[i];
  291. }
  292. }
  293. return null;
  294. }
  295. /**获取自己本局获得星星*/
  296. public get own_results_addScore(): number {
  297. const rs = this.results;
  298. if (!rs) return 0;
  299. for (let i = 0; i < rs.length; i++) {
  300. if (rs[i].Id === this._own_id) {
  301. return rs[i].AddScore;
  302. }
  303. }
  304. return 0;
  305. }
  306. /**获取自己本局获得名次*/
  307. public get own_results_rank(): number {
  308. const rs = this.results;
  309. if (!rs) return 0;
  310. for (let i = 0; i < rs.length; i++) {
  311. if (rs[i].Id === this._own_id) {
  312. return rs[i].Rank;
  313. }
  314. }
  315. return 0;
  316. }
  317. public sort_results<T extends StartEventNames<GD, "p_">>(sort_name: T): void {
  318. if (this._results.length > 1) {
  319. this._results.sort((a, b) => {
  320. if (a.Rank === 0 && b.Rank === 0) {
  321. if (sort_name) {
  322. return this.getPlayer(b.Id).getValue(sort_name) - this.getPlayer(a.Id).getValue(sort_name);
  323. }
  324. return 0;
  325. } else if (a.Rank === 0) {
  326. return 1;
  327. } else if (b.Rank === 0) {
  328. return -1;
  329. } else {
  330. return a.Rank - b.Rank;
  331. }
  332. });
  333. }
  334. }
  335. private closed(data: any): void {
  336. this._status = false;
  337. this._results.length = 0;
  338. const ps = Object.keys(data).map(key => ({ key, data: data[key] }));
  339. for (let i = 0; i < ps.length; i++) {
  340. const key = ps[i].key;
  341. const user = ps[i].data;
  342. user.Id = key;
  343. const player = this.getPlayer(key);
  344. (player as any).set_level(user.Level, user.Rank, user.Score, user.TotalRank);
  345. user.UserData = {
  346. gid: player.gid, head: player.head, hid: player.hid, ip: player.ip, loginTime: player.loginTime,
  347. nickName: player.nickName, openId: player.openId, option: player.option, pf: player.pf, registerTime: player.registerTime, userId: player.userId, province: player.province
  348. };
  349. const elements = user.Elements;
  350. if (elements) {
  351. for (let i = 0; i < elements.length; i++) {
  352. elements[i] = transUserDataform(elements[i]);
  353. }
  354. }
  355. this._results.push(user);
  356. };
  357. if (this._results.length > 1) {
  358. this._results.sort((a, b) => {
  359. if (a.Rank === 0 && b.Rank === 0) {
  360. return 0;
  361. } else if (a.Rank === 0) {
  362. return 1;
  363. } else if (b.Rank === 0) {
  364. return -1;
  365. } else {
  366. return a.Rank - b.Rank;
  367. }
  368. });
  369. }
  370. //
  371. (this.evt as any)._emit('r_closed');
  372. }
  373. private finish(pid: string, rank: number): void {
  374. const player = this.getPlayer(pid);
  375. if (!player) return;
  376. (player as any).setFinish(rank);
  377. (this.evt as any)._emit('r_finish', pid, rank);
  378. }
  379. private exit(pid: string): void {
  380. const player = this.getPlayer(pid);
  381. if (!player) return;
  382. const location = player.location;
  383. const nickName = player.nickName;
  384. (player as any).exit();
  385. (this.evt as any)._emit('r_exit', pid, location, nickName);
  386. }
  387. /**向房间所有玩家发消息*/
  388. public sendEvt<T extends StartEventNames<GD, 'revt_'>, T2 extends EventParams<GD, T>>(key: T, ...data: T2): void {
  389. if (!this._status) return;
  390. this._waitSends_mode0.push({ type: key, data: data });
  391. }
  392. /**向房间主机发消息*/
  393. public sendToHost<T extends StartEventNames<GD, 'revt_'>, T2 extends EventParams<GD, T>>(key: T, ...data: T2): void {
  394. if (!this._status) return;
  395. this._waitSends_mode1.push({ type: key, data: data });
  396. }
  397. /**向房间其它玩家*/
  398. public sendToOther<T extends StartEventNames<GD, 'revt_'>, T2 extends EventParams<GD, T>>(key: T, ...data: T2): void {
  399. if (!this._status) return;
  400. this._waitSends_mode2.push({ type: key, data: data });
  401. }
  402. /**处理发送事件*/
  403. private doSendMode(): void {
  404. if (this._waitSends_mode0.length > 0) {
  405. this._send(C2S_MESSAGE, this._waitSends_mode0);
  406. this._waitSends_mode0.length = 0;
  407. }
  408. if (this._waitSends_mode1.length > 0) {
  409. this._send(C2S_MESSAGE_TO_HOST, this._waitSends_mode1);
  410. this._waitSends_mode1.length = 0;
  411. }
  412. if (this._waitSends_mode2.length > 0) {
  413. this._send(C2S_MESSAGE_TO_OTHER, this._waitSends_mode2);
  414. this._waitSends_mode2.length = 0;
  415. }
  416. }
  417. private _chat_msg: string | null = null;
  418. private _last_chat_time: number = 0;
  419. /**房间里聊天 ,成功返回true,发送频率过快返回false*/
  420. public chat(msg: string): boolean {
  421. const now = chsdk.date.now();
  422. if (now - this._last_chat_time < 1000) return false;
  423. this._last_chat_time = now;
  424. this._chat_msg = msg;
  425. return true;
  426. }
  427. private onChat(id: string, msg: string): void {
  428. const p = this.getPlayer(id);
  429. (this.evt as any)._emit('r_chat', id, msg, p.location, p.nickName);
  430. }
  431. private doSendChat() {
  432. if (this._chat_msg) {
  433. this._send(C2S_TALK, this._chat_msg);
  434. this._chat_msg = null;
  435. }
  436. this.doSendMode();
  437. const ps = this.all;
  438. for (let i = 0; i < ps.length; i++) {
  439. const p = ps[i];
  440. (p as any).doFinishGame(() => { this._send(C2S_FINISH, p.Id); });
  441. }
  442. }
  443. public doSendDirty() {
  444. (this.own as any).doDirtyData((dirty) => {
  445. this._send(C2S_UPDATE_USER_GAMEDATA, { userKey: this.own.Id, data: dirty });
  446. })
  447. if (!this.isHost) return;
  448. (this as any).doDirtyData((dirty) => {
  449. this._send(C2S_UPDATE_ROOM_GAMEDATA, dirty);
  450. })
  451. const ais = this.ais;
  452. for (let i = 0; i < ais.length; i++) {
  453. const ai = ais[i];
  454. (ai as any).doDirtyData((dirty) => {
  455. this._send(C2S_UPDATE_USER_GAMEDATA, { userKey: ai.Id, data: dirty });
  456. })
  457. }
  458. }
  459. //
  460. private _onEvt(key: string, data: any): void {
  461. (this.evt as any)._emit(key, ...data);
  462. }
  463. //
  464. public dispose(): void {
  465. super.dispose();
  466. this._send = null;
  467. this._players.forEach((v, _) => {
  468. v.dispose();
  469. });
  470. this._players.clear();
  471. this._status = false;
  472. this.evt.clearAll();
  473. }
  474. }