import { NetBase, results, transUserDataform } 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>; const C2S_TALK = 'c2s_talk'; const C2S_MESSAGE = 'c2s_message'; const C2S_MESSAGE_TO_HOST = 'c2s_message_to_host'; const C2S_MESSAGE_TO_OTHER = 'c2s_message_without_self'; const C2S_FINISH = 'c2s_finish'; const C2S_UPDATE_USER_GAMEDATA = 'c2s_update_user_gameData'; const C2S_UPDATE_ROOM_GAMEDATA = 'c2s_update_room_gameData'; /**网络房间*/ 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 }; private _own_id: string; public get ownId(): string { return this._own_id }; 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 }> = []; private _send: (type: string, data?: any) => void; /**自己是否是主机*/ public get isHost(): boolean { return this._own_id === this._hostId; } // constructor(ownId: string, roomData: any, playerData: any, send: (type: string, data?: any) => void) { super(roomData.roomId); this._send = send; this._own_id = ownId; this._hostId = roomData.hostInfo; this._status = roomData.status; let ps = Object.keys(playerData).map(key => ({ key, data: playerData[key] })); for (let i = 0; i < ps.length; i++) this.addPlayer(ps[i]); 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); (player as any).set_own(player.Id === this._own_id); (player as any).set_host(player.Id === this._hostId, this.isHost) } 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 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: number = 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; } /**根据key获取obj数据*/ public getObj(key: string): T { return super.getValue(key); } /**获取当前所有的obj数据*/ public getAllObj(): { key: string, data: T }[] { const list: { key: string, data: T }[] = []; const oid = super.getValue('oid') ?? 0; for (let i = 1; i <= oid; i++) { const key = 'obj_' + i; const data = super.getValue(key); if (data) list.push({ key: key, data: data === 0 ? null : data }); } return list; } /**不建议经常更改obj */ public changeObj(key: string, data: T): void { if (!this.isHost) return; const old = super.getValue(key); if (old) { super.setValue(key, data); super.setValueDirty(key, data); (this.evt as any)._emit('r_obj', key, data, old); } } /**删除obj数据*/ public deleteObj(key: string, data: T | 0 = 0) { if (!this.isHost) return; const old = super.getValue(key); if (old) { super.setValue(key, data); super.setValueDirty(key, data); (this.evt as any)._emit('r_obj', key, null, old); } } /**修改某个键的值*/ 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; const evt: { evt: 0 | 1, key: string, data: any, old: any }[] = []; Object.keys(data).forEach(key => { evt.push(this.change(key, data[key])); }); const e = (this.evt as any) for (let i = 0; i < evt.length; i++) { const ee = evt[i]; if (!ee) continue; if (ee.evt === 1) { e._emit('r_obj', ee.key, ee.data, ee.old); } else { e._emit(ee.key, ee.data, ee.old); } } } private change(key: string, data: any): { evt: 0 | 1, key: string, data: any, old: any } | null { const old = super.getValue(key); super.setValue(key, data); if (typeof data !== "object" && data === old) return null; if (key === 'oid') { return null; } else if (key.startsWith('obj_')) { return { evt: 1, key: key, data: data === 0 ? null : data, old: old }; } else { return { evt: 0, key: key, data: data, old: old }; } } private _results: any[] = []; /** * 游戏结算数据 * * @returns {Array} results - 包含游戏结算信息的数组。 * 每个元素表示一个玩家的结算数据,结构如下: * * - Id: {string} 玩家唯一标识符 * - AddScore: {number} 本局游戏中增加的星星数 * - IsFinish: {boolean} 游戏是否已完成 * - Rank: {number} 玩家在当前游戏中的排名 * - Score: {number} 玩家在最后星星数 * - TotalRank: {number} 玩家的总排名 * - LevelCtrl: {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} 其他玩家的排名 * - LevelCtrl: {Object} 其他玩家的等级信息 * - LowerRank: {number} 其他玩家当前段位的等级 * - Rank: {number} 其他玩家的段位 * - Star: {number} 其他玩家段位的等级的星级 * - UserData: {Object} 其他玩家的个人信息(与上面的 UserData 结构相同) */ public get results(): results[] { return this._results; } /**获取自己本局结算*/ public get own_results(): results { const rs = this.results; if (!rs) return null; for (let i = 0; i < rs.length; i++) { if (rs[i].Id === this._own_id) { return rs[i]; } } return null; } /**获取自己本局获得星星*/ 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; } /**获取自己本局获得名次*/ public get own_results_rank(): 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].Rank; } } return 0; } public sort_results>(sort_name: T): void { if (this._results.length > 1) { this._results.sort((a, b) => { if (a.Rank === 0 && b.Rank === 0) { if (sort_name) { return this.getPlayer(b.Id).getValue(sort_name) - this.getPlayer(a.Id).getValue(sort_name); } return 0; } else if (a.Rank === 0) { return 1; } else if (b.Rank === 0) { return -1; } else { return a.Rank - b.Rank; } }); } } 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++) { elements[i] = transUserDataform(elements[i]); } } 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); } private exit(pid: string): void { const player = this.getPlayer(pid); if (!player) return; const location = player.location; const nickName = player.nickName; (player as any).exit(); (this.evt as any)._emit('r_exit', pid, location, nickName); } /**向房间所有玩家发消息*/ 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(): void { if (this._waitSends_mode0.length > 0) { this._send(C2S_MESSAGE, this._waitSends_mode0); this._waitSends_mode0.length = 0; } if (this._waitSends_mode1.length > 0) { this._send(C2S_MESSAGE_TO_HOST, this._waitSends_mode1); this._waitSends_mode1.length = 0; } if (this._waitSends_mode2.length > 0) { this._send(C2S_MESSAGE_TO_OTHER, this._waitSends_mode2); 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() { if (this._chat_msg) { this._send(C2S_TALK, this._chat_msg); this._chat_msg = null; } this.doSendMode(); const ps = this.all; for (let i = 0; i < ps.length; i++) { const p = ps[i]; (p as any).doFinishGame(() => { this._send(C2S_FINISH, p.Id); }); } } public doSendDirty() { (this.own as any).doDirtyData((dirty) => { this._send(C2S_UPDATE_USER_GAMEDATA, { userKey: this.own.Id, data: dirty }); }) if (!this.isHost) return; (this as any).doDirtyData((dirty) => { this._send(C2S_UPDATE_ROOM_GAMEDATA, dirty); }) const ais = this.ais; for (let i = 0; i < ais.length; i++) { const ai = ais[i]; (ai as any).doDirtyData((dirty) => { this._send(C2S_UPDATE_USER_GAMEDATA, { userKey: ai.Id, data: dirty }); }) } } // private _onEvt(key: string, data: any): void { (this.evt as any)._emit(key, ...data); } // public dispose(): void { super.dispose(); this._send = null; this._players.forEach((v, _) => { v.dispose(); }); this._players.clear(); this._status = false; this.evt.clearAll(); } }