NetRoom.ts 16 KB

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