import { NetBase } from "./NetBase"; import { NetPlayer } from "./NetPlayer"; type EventNames> = Extract; type EventParams, Ev extends EventNames> = Ev extends keyof Map ? Map[Ev] extends (...args: any[]) => any ? Parameters : never : never; type StartEventNames, Prefix extends string> = Extract extends infer K ? K extends `${Prefix}${string}` ? K : never : never; type EventParamFrist, Ev extends EventNames> = Ev extends keyof Map ? Map[Ev] extends (...args: infer Params) => any ? Params extends [infer First, ...any[]] ? First : never : never : never; type PickEvent = Pick>; /**网络房间*/ export class NetRoom extends NetBase { public readonly evt = chsdk.get_new_event>(); private _hostId: string; /**游戏主机ID*/public get HostId(): string { return this._hostId }; private _status: boolean = true; /**游戏是否关闭*/public get cloosed(): boolean { return !this._status }; public _own_id: string; private _players: Map> = new Map(); // private _waitSends_mode0: Array<{ type: string, data: any }> = []; private _waitSends_mode1: Array<{ type: string, data: any }> = []; private _waitSends_mode2: Array<{ type: string, data: any }> = []; // constructor(roomData: any, playerData: any) { super(roomData.roomId); this._status = roomData.status; this._hostId = roomData.hostInfo; let ps = Object.keys(playerData).map(key => ({ key, data: playerData[key] })); for (let i = 0; i < ps.length; i++) this.addPlayer(ps[i]); this._players.forEach((v, _) => { (v as any).set_host(v.Id === this._hostId, this.isHost); }); this.initValue(roomData.gameData); } private addPlayer(p: any): void { const id = p.key; const pd = p.data; let player = this._players.get(id); if (!player) { player = new NetPlayer(id); this._players.set(id, player); } player.init(pd); if (player.isOwn) this._own_id = id; } private updatePlayerStatus(id: string, online: boolean): void { const p = this.getPlayer(id); if (p) { (p as any).change_online(online); const location = p.location; const nickName = p.nickName; (this.evt as any)._emit('r_online', id, online, location, nickName); } } private changeHost(id: string): void { this._hostId = id; this._players.forEach((v, _) => { (v as any).set_host(v.Id === this._hostId, this.isHost); }); const p = this.getPlayer(this._hostId); (this.evt as any)._emit('r_host', id, p.location, p.nickName); } /**自己是否是主机*/ public get isHost(): boolean { return this._own_id === this._hostId; } /**自己的所有信息*/ public get own(): NetPlayer { return this._players.get(this._own_id); } /**其它玩家信息*/ public get others(): NetPlayer[] { return Array.from(this._players.values()).filter(player => !player.isOwn); } /**所有ai信息*/ public get ais(): NetPlayer[] { return Array.from(this._players.values()).filter(player => player.isAI); } /**所有玩家*/ public get all(): NetPlayer[] { return this.getAllPlayer(); } /**在线玩家信息*/ public get onlines(): NetPlayer[] { return Array.from(this._players.values()).filter(player => player.online); } /**不在线玩家信息*/ public get outlines(): NetPlayer[] { return Array.from(this._players.values()).filter(player => !player.online); } /**所有玩家*/ public getAllPlayer(): NetPlayer[] { return Array.from(this._players.values()); } /**将玩家按 location 放到一个数组中,并自定义数组长度*/ public getAllPlayersAtLocations(customLength: number): (NetPlayer | null)[] { const locationArray: (NetPlayer | null)[] = Array(customLength).fill(null); this._players.forEach((player) => { if (player.location >= 0 && player.location < customLength) locationArray[player.location] = player; }); return locationArray; } /**中某个玩家的所有信息*/ public getPlayer(id: string): NetPlayer { return this._players.get(id); } /**获取除了某个id的所有玩家*/ public getExPlayer(id: string): NetPlayer[] { return Array.from(this._players.values()).filter(player => player.Id != id); } /**获在线或不在线的所有玩家*/ public getOnlinePlayer(isOnline: boolean): NetPlayer[] { return isOnline ? this.onlines : this.outlines; } /**是否有AI权限*/ public canAiPlayer(player: NetPlayer): boolean { if (!this._status) return false; if (!this.isHost) return false; if (!player || !player.isAI) return false; return true; } /**是否有AI权限Id,返回可以控制的player*/ public canAiPlayerId(id: string): NetPlayer | null { if (!this._status) return null; if (!this.isHost) return null; const player = this._players.get(id); if (!player) return null; if (!player.isAI) return null; return player; } /**是否拥有权限(包括,自己和自己是主机时的ai)*/ public hasPermission(player: NetPlayer): boolean { return player.isOwn || this.canAiPlayer(player); } /**创建obj数据*/ public creatObj(data: T): string { if (!this._status) return; if (!this.isHost) return; let oid = super.getValue('oid') ?? 0; oid++; const key = 'obj_' + oid; super.setValue('oid', oid); super.setValueDirty('oid', oid); super.setValue(key, data); super.setValueDirty(key, data); (this.evt as any)._emit('r_obj', key, data); return key; } /**删除obj数据*/ public getObj(key: string): T { return super.getValue(key); } /**不建议经常更改obj */ public changeObj(key: string, data: T): void { if (!this.isHost) return; let old = super.getValue(key); if (old) { super.setValue(key, data); super.setValueDirty(key, data); (this.evt as any)._emit('r_obj', key, data, old); } } // public deleteObj(key: string) { if (!this.isHost) return; super.setValue(key, null); super.setValueDirty(key, null); } /**修改某个键的值*/ public setValue, T2 extends EventParamFrist>(key: T, data: T2): void { if (!this._status) return; if (!this.isHost) return;//不是主机没有权限 let old = super.getValue(key); if (old) { if (typeof data === "object") { //old = JSON.parse(JSON.stringify(old)); } else if (data === old) { return; } } super.setValue(key, data); super.setValueDirty(key, data); (this.evt as any)._emit(key, data, old); } /**获取数据的值*/ public getValue, T2 extends EventParamFrist>(key: T): T2 { return super.getValue(key); } private server_change(data: { [key: string]: any }): void { if (!this._status) return; if (this.isHost) return; Object.keys(data).forEach(key => { this.change(key, data[key]); }); } private change(key: string, data: any) { const old = super.getValue(key); super.setValue(key, data); if (typeof data !== "object" && data === old) return; if (key === 'oid') { return; } else if (key.startsWith('obj_')) { (this.evt as any)._emit('r_obj', key, data, old); } else { (this.evt as any)._emit(key, data, old); } } private _results: any[] = []; /** * 游戏结算数据 * * @returns {Array} results - 包含游戏结算信息的数组。 * 每个元素表示一个玩家的结算数据,结构如下: * * - Id: {string} 玩家唯一标识符 * - AddScore: {number} 本局游戏中增加的星星数 * - IsFinish: {boolean} 游戏是否已完成 * - Rank: {number} 玩家在当前游戏中的排名 * - Score: {number} 玩家在最后星星数 * - TotalRank: {number} 玩家的总排名 * - Level: {Object} 玩家当前等级的信息 * - LowerRank: {number} 当前段位的等级 * - Rank: {number} 段位 * - Star: {number} 当前段位的等级的星级 * - UserData: {Object} 玩家个人信息 * - gid: {string} 游戏全局唯一标识符 * - head: {string} 玩家头像的 URL * - hid: {number} 玩家省份id * - ip: {string} 玩家 IP 地址 * - loginTime: {number} 玩家登录时间的时间戳 * - nickName: {string} 玩家昵称 * - openId: {string} 玩家在平台上的唯一标识符 * - option: {string} 玩家选择的选项或设置 * - pf: {string} 平台信息 * - registerTime: {number} 玩家注册时间的时间戳 * - userId: {number} 玩家在系统中的用户 ID * - province: {string} 玩家所在的省份 * - Elements: {Array} 包含其他玩家的结算数据 * - Rank: {number} 其他玩家的排名 * - Level: {Object} 其他玩家的等级信息 * - LowerRank: {number} 其他玩家当前段位的等级 * - Rank: {number} 其他玩家的段位 * - Star: {number} 其他玩家段位的等级的星级 * - UserData: {Object} 其他玩家的个人信息(与上面的 UserData 结构相同) */ public get results(): { Id: string, AddScore: number, IsFinish: boolean, Rank: number, Score: number, TotalRank: number, Level: { LowerRank: number, Rank: number, Star: number }, UserData: { gid: string, head: string, hid: number, ip: string, loginTime: number, nickName: string, openId: string, option: string, pf: string, registerTime: number, userId: number, province: string }, Elements: { Rank: number, Level: { LowerRank: number, Rank: number, Star: number }, UserData: { gid: string, head: string, hid: number, ip: string, loginTime: number, nickName: string, openId: string, option: string, pf: string, registerTime: number, userId: number, province: string } }[], }[] { return this._results; } /**获取自己本局获得星星*/ public get own_results_addScore(): number { const rs = this.results; if (!rs) return 0; for (let i = 0; i < rs.length; i++) { if (rs[i].Id === this._own_id) { return rs[i].AddScore; } } return 0; } private closed(data: any): void { this._status = false; this._results.length = 0; const ps = Object.keys(data).map(key => ({ key, data: data[key] })); for (let i = 0; i < ps.length; i++) { const key = ps[i].key; const user = ps[i].data; user.Id = key; const player = this.getPlayer(key); (player as any).set_level(user.Level, user.Rank, user.Score, user.TotalRank); user.UserData = { gid: player.gid, head: player.head, hid: player.hid, ip: player.ip, loginTime: player.loginTime, nickName: player.nickName, openId: player.openId, option: player.option, pf: player.pf, registerTime: player.registerTime, userId: player.userId, province: player.province }; const elements = user.Elements; if (elements) { for (let i = 0; i < elements.length; i++) { const o = elements[i]; o.nickName = o.nickName || '玩家' + o.userId o.hid = Number.parseInt(o.hid); o.loginTime = Number.parseInt(o.loginTime); o.registerTime = Number.parseInt(o.registerTime); o.userId = Number.parseInt(o.userId); o.province = chsdk.provinceCode2Name(o.hid) ?? '其它'; } } this._results.push(user); }; if (this._results.length > 1) { this._results.sort((a, b) => { if (a.Rank === 0 && b.Rank === 0) { return 0; } else if (a.Rank === 0) { return 1; } else if (b.Rank === 0) { return -1; } else { return a.Rank - b.Rank; } }); } // (this.evt as any)._emit('r_closed'); } private finish(pid: string, rank: number): void { const player = this.getPlayer(pid); if (!player) return; (player as any).setFinish(rank); (this.evt as any)._emit('r_finish', pid, rank); } /**向房间所有玩家发消息*/ public sendEvt, T2 extends EventParams>(key: T, ...data: T2): void { if (!this._status) return; this._waitSends_mode0.push({ type: key, data: data }); } /**向房间主机发消息*/ public sendToHost, T2 extends EventParams>(key: T, ...data: T2): void { if (!this._status) return; this._waitSends_mode1.push({ type: key, data: data }); } /**向房间其它玩家*/ public sendToOther, T2 extends EventParams>(key: T, ...data: T2): void { if (!this._status) return; this._waitSends_mode2.push({ type: key, data: data }); } /**处理发送事件*/ private doSendMode(f: (mode0: { type: string, data: any }[], mode1: { type: string, data: any }[], mode2: { type: string, data: any }[]) => void): void { f(this._waitSends_mode0, this._waitSends_mode1, this._waitSends_mode2); this._waitSends_mode0.length = 0; this._waitSends_mode1.length = 0; this._waitSends_mode2.length = 0; } private _chat_msg: string | null = null; private _last_chat_time: number = 0; /**房间里聊天 ,成功返回true,发送频率过快返回false*/ public chat(msg: string): boolean { const now = chsdk.date.now(); if (now - this._last_chat_time < 1000) return false; this._last_chat_time = now; this._chat_msg = msg; return true; } private onChat(id: string, msg: string): void { const p = this.getPlayer(id); (this.evt as any)._emit('r_chat', id, msg, p.location, p.nickName); } private doSendChat(f: (msg: string) => void) { f(this._chat_msg); this._chat_msg = null; } // private _onEvt(key: string, data: any): void { (this.evt as any)._emit(key, ...data); } // public dispose(): void { super.dispose(); this._players.forEach((v, _) => { v.dispose(); }); this._players.clear(); this._status = false; this.evt.clearAll(); } }