// import "core-js"; import "core-js/actual/url/index.js"; import "core-js/actual/url-search-params/index.js"; import { _decorator, Component, director, Game, game, v3, Vec3 } from "cc"; // import "url-polyfill"; // 据报道,微信的JavaScript运行时不支持通过WebSocket直接发送或数据类型。为了绕过这个问题,你可以对这些数据类型进行猴子补丁,将这些数据类型转换成微信能处理的格式。Uint8ArrayArrayWebSocket.send const WebSocket_send = WebSocket.prototype.send; WebSocket.prototype.send = function (data) { if (data instanceof Uint8Array) { WebSocket_send.call(this, data.slice().buffer); } else if (Array.isArray(data)) { WebSocket_send.call(this, new Uint8Array(data).buffer); } else { WebSocket_send.call(this, data); } }; import Colyseus, { RoomAvailable } from "db://colyseus-sdk/colyseus.js"; import { XNetEventKeys } from "../net/XNetEventKeys"; import { Global } from "../Global"; const { ccclass, property } = _decorator; import msgpackpure from "@msgpack/msgpack"; import { CalcTool } from "../tool/CalcTool"; import { PlayerShot } from "../player/PlayerShot"; import { PlayerAnimation } from "../player/PlayerAnimation"; import { PlayerInfo } from "../player/PlayerInfo.js"; import { CharacterMovement } from "../../plugin/kylins_easy_controller/CharacterMovement"; import { PathSmoothTool } from "../tool/PathSmoothTool"; import { StringTool } from "../tool/StringTool"; import { PlayerTool } from "../player/PlayerTool"; import { WebNetLogic } from "../net/WebNetLogic"; const { encode, decode } = msgpackpure; @ccclass("ColyseusManager") export class ColyseusManager extends Component { @property hostname = "localhost"; @property port = 2567; @property useSSL = false; private roomName = "Tank"; private lobbyRoomName = "Lobby"; private client!: Colyseus.Client; private _room!: Colyseus.Room; private _lobbyRoom!: Colyseus.Room; public get room(): Colyseus.Room { return this._room; } public connectCallback: Function | null = null; //() => { } public disConnectCallback: Function | null = null; //() => { } public gameOverCallback: Function | null = null; //() => { } public playerRemove: Function | null = null; //() => { } // public checkStartGameCallback: Function | null = null; //() => { } public receiveCallback = (data) => {}; public receiveFunc = (data) => {}; public getRoomFunc = (roomId) => {}; public getStartGameRoomFunc = (roomId) => {}; public getLobbyRoomFunc = (data) => {}; private static gameHideFunc = () => {}; private static gameShowFunc = () => {}; private _isCanReconnect = true; private _reconnectTimerId: number | null = null; public get isCanReconnect() { return this._isCanReconnect; } // private roomName = "GameRoom"; // 房间名称,服务端定义。一个房间名称对应对个房间,根据实际情况修改,roomId 是唯一的 private _isConnecting = false; //是否正在连接 private _isConnected = false; // 是否连接成功 private _isCreateOnly = false; // 是否只创建房间,不加入房间 public get isCreateOnly() { return this._isCreateOnly; } public set isCreateOnly(value) { this._isCreateOnly = value; } private static _instance: ColyseusManager | null = null; public static get instance(): ColyseusManager | null { return ColyseusManager._instance; } public get isConnected() { return this._isConnected; } public connectCallbackOnce: Function = null; public connectFail: Function = null; private async gameHide() { // console.log('gameHide,关闭连接') // await this.leave(); } public async leave() { //如果还有其他玩家,则马上断开连接,服务端下发AI控制权给其他同房间玩家(这里不用了,AI由服务端生成) // if (Global.players.size - Global.aiPlayersCount > 1) { this.resetTags(); await this.leaveOnly(); // } } public async leaveOnly() { if (this._room != null) { this._isCanReconnect = false; await this._room.leave(); this._room = null; } } public resetTags() { this._isCanReconnect = true; this._isConnecting = false; this._isConnected = false; Global.roomId = null; } private async gameShow() { // console.log('gameShow,重新连接') if (Global.isGameStart && !this._isConnected) { this._isCanReconnect = true; await this.connect(this._isCreateOnly, Global.roomId, true); } } protected onLoad(): void { if (ColyseusManager._instance == null) { director.addPersistRootNode(this.node); ColyseusManager._instance = this; WebNetLogic.init(this); if (!this.client) { this.client = new Colyseus.Client(Global.wsUrl); } } } async start() { console.log("ColyseusManager start"); game.off(Game.EVENT_HIDE, ColyseusManager.gameHideFunc); game.off(Game.EVENT_SHOW, ColyseusManager.gameShowFunc); // game.on( // Game.EVENT_HIDE, // (ColyseusManager.gameHideFunc = this.gameHide.bind(this)) // ); // game.on( // Game.EVENT_SHOW, // (ColyseusManager.gameShowFunc = this.gameShow.bind(this)) // ); // Instantiate Colyseus Client // connects into (ws|wss)://hostname[:port] // if (DEBUG) { // this.client = new Colyseus.Client(`${this.useSSL ? "wss" : "ws"}://${this.hostname}${([443, 80].indexOf(this.port) !== -1 || this.useSSL) ? "" : `:${this.port}`}`); // } // else { // } } public sendCommonMessage(msgType: number, args: any) { // const encodedData = { // msgType: msgType, // connectionId: Global.connectionId, // senderId: Global.selfPlayerId, // receiverOrRoomId: Global.roomId, // msgContent: encode(args) // }; //msgpack 编码数据 // const sendBytes = encode(args) if (this._isConnected && this._room != null) { this._room.send(msgType, args); } } public disconnect() { if (this._room != null) { this._room.leave(); this._room = null; } } // public sendRoomOtherMessage(msg: any) { // const sendBytes = encode(msg) // this.room.sendBytes(XNetEventKeys.ROOM_MSG_OTHER, sendBytes) // } // private receiveData(buffer: any) { // const data = decode(buffer) as any; //msgpack 解码数据 // WebNetLogic.Instance.excecuteEvent(data.msgType.toString(), data) // } public async checkConnect(callback: Function) { if (this._isConnected) { if (callback != null) { callback(this._room.roomId); } } else { if (!this._isConnecting && this._reconnectTimerId == null) { await this.connect(this._isCreateOnly, Global.roomId, true); if (callback != null && this._room != null) { callback(this._room.roomId); } } else { this.connectCallbackOnce = callback; } } } public async initLobbyRoom() { if (!this.client) { this.client = new Colyseus.Client(Global.wsUrl); } try { if (this._lobbyRoom == null) { this._lobbyRoom = await this.client.joinOrCreate(this.lobbyRoomName, { filter: { name: this.roomName, }, }); // console.log(this._lobbyRoom) } this.initLobbyEvents(); } catch (err) { setTimeout( (async () => { this._lobbyRoom = null; await this.initLobbyRoom(); }).bind(this), 3000 ); } } public initLobbyEvents(): boolean { if (this._lobbyRoom != null) { this._lobbyRoom.removeAllListeners(); let allRooms: RoomAvailable[] = []; this._lobbyRoom.onMessage("rooms", (rooms) => { allRooms = rooms; }); this._lobbyRoom.onMessage("+", ([roomId, room]) => { const roomIndex = allRooms.findIndex((room) => room.roomId === roomId); if (roomIndex !== -1) { allRooms[roomIndex] = room; } else { allRooms.push(room); } }); this._lobbyRoom.onMessage("-", (roomId) => { allRooms = allRooms.filter((room) => room.roomId !== roomId); }); this._lobbyRoom.onError((error) => { console.error("LobbyRoom error:", error); }); this._lobbyRoom.onLeave( ((code) => { // console.log("code:", code) setTimeout( (async () => { this._lobbyRoom = null; await this.initLobbyRoom(); }).bind(this), 3000 ); }).bind(this) ); this._lobbyRoom.onMessage( XNetEventKeys.GET_ROOM_FOR_COUNT, this.getRoomFunc ); this._lobbyRoom.onMessage( XNetEventKeys.GET_ROOM_FOR_START, this.getStartGameRoomFunc ); this._lobbyRoom.onMessage( XNetEventKeys.GET_LOBBY_ROOM_MESSAGE, this.getLobbyRoomFunc ); this._lobbyRoom.onMessage("__playground_message_types", (message) => { // console.log(message) }); return true; } return false; } /** * 搜索房间 * @param count 搜索条件:房间人数不能大于count */ public searchRoom(count: number = 5) { if (this._lobbyRoom) { this._lobbyRoom.send(XNetEventKeys.GET_ROOM_FOR_COUNT, { roomName: this.roomName, count: count, roomId: this.room != null ? this.room.roomId : null, }); } } /** * 搜索开始游戏要加入房间 * @param count 搜索条件:房间人数不能大于count */ public searchStartGameRoom(count: number = 5) { if (this._lobbyRoom) { this._lobbyRoom.send(XNetEventKeys.GET_ROOM_FOR_START, { roomName: this.roomName, count: count, roomId: this.room != null ? this.room.roomId : null, }); } } /** * 搜索开始游戏要加入房间 * @param count 搜索条件:房间人数不能大于count */ public sendLobbyRoomMessage(msgType: number, data) { if (this._lobbyRoom) { this._lobbyRoom.send(msgType, data); } } public async connect( isCreateOnly: boolean, roomId: string = null, isForceReconnect: boolean = false, isConnectForRoomIdParameter: boolean = false ): Promise { if (!this.client) { this.client = new Colyseus.Client(Global.wsUrl, {}); } if (this._isConnecting) { return null; } // if (this._isConnected) { // return this.room.roomId // } this._isConnecting = true; this._isConnected = false; this._isCanReconnect = true; this._isCreateOnly = isCreateOnly; // if (isCreateOnly) { // await this.leave() // } try { if (!isConnectForRoomIdParameter) { if (this._room != null) { roomId = this._room.roomId; } else if (StringTool.isNotEmpty(Global.roomId)) { roomId = Global.roomId; } } if (StringTool.isNotEmpty(roomId)) { try { this._room = await this.client.joinById(roomId); } catch (err) { console.log(err); this._isConnecting = false; this._isConnected = false; Global.roomId = null; // if (this._room != null) { // await this._room.leave() this._room = null; // } //4212 :房间锁定 if ( (err != null && err.name == "MatchMakeError") || err.code === 4212 || err.code === 1006 ) { // if (this.client != null) { // this.client = null // } // this._room = null //自动重连,房间已释放,游戏结束 if (this.gameOverCallback) { this.gameOverCallback(); this.gameOverCallback = null; } if (isForceReconnect) { this.reconnect(this.isCreateOnly); } return null; // } else { // this.reconnect(isCreateOnly) // return null // } } else { if (isForceReconnect) { // this._room = null this.reconnect(isCreateOnly); return null; } } } } else { if (isCreateOnly) { this._room = await this.client.create(this.roomName); } else { this._room = await this.client.joinOrCreate(this.roomName); } } this._isConnected = true; this._isConnecting = false; // if (Global.roomId != this._room.roomId) { // // console.log("clearPlayers") // this.clearPlayers()//debug // } Global.roomId = this._room.roomId; // console.log("joined successfully!"); // console.log("user's sessionId:", this.room.sessionId); this._room.removeAllListeners(); this._room.onError( ((code, reason) => { console.log("onError:", code, reason); this.forceReconnect(); }).bind(this) ); // this.room.onMessage("*", (type: string | number, message: any) => { console.log("ReceivedMessage *", message); this.receiveCallback(message) }) this._room.onMessage(XNetEventKeys.TO_SELF, this.receiveCallback); this._room.onMessage(XNetEventKeys.PRIVATGE, this.receiveCallback); this._room.onMessage(XNetEventKeys.ROOM_MSG, this.receiveCallback); this._room.onMessage(XNetEventKeys.ROOM_MSG_OTHER, this.receiveCallback); this._room.onMessage(XNetEventKeys.REQUEST_INIT, this.receiveCallback); this._room.onMessage("__playground_message_types", (message) => { // console.log(message) }); // this.room.onStateChange((state) => { // // console.log("onStateChange: ", state); // // PlayerNet.Instance.receiveSyncPlayerMessage(state) // }); this.setRoomStateChangeListen(); this._room.onLeave( ((code) => { console.log("onLeave:", code); // Global.roomId = null // this._room = null this._isConnected = false; this._isConnecting = false; if (this.disConnectCallback) { this.disConnectCallback(); } // this._isCanReconnect = true this.reconnect(this.isCreateOnly); }).bind(this) ); if (this._reconnectTimerId != null) { clearTimeout(this._reconnectTimerId); } if (this.connectCallback != null) { this.connectCallback(this._room.roomId); } if (this.connectCallbackOnce != null) { this.connectCallbackOnce(this.room.roomId); this.connectCallbackOnce = null; } return this._room.roomId; } catch (e) { if (this.connectFail != null) { this.connectFail(e); } this.forceReconnect(); console.log(e); return null; } } private forceReconnect() { this._isConnected = false; this._isConnecting = false; this._isCanReconnect = true; this.reconnect(this.isCreateOnly); } private players: Map = new Map(); private playerShots: Map = new Map(); public clearPlayers() { this.players.clear(); this.playerShots.clear(); } private setRoomStateChangeListen() { const $ = Colyseus.getStateCallbacks(this._room); const players = $(this._room.state).players; players.onRemove( ((player, sessionId) => { if (this.playerRemove != null) { this.playerRemove(sessionId); } }).bind(this) ); const getPlayerInfo = (playerId: string): PlayerInfo => { let playerInfo: PlayerInfo = null; if (this.players.has(playerId)) { playerInfo = this.players.get(playerId); } if (playerInfo == null) { // console.log("Global.players.get") playerInfo = Global.players.get(playerId); this.players.set(playerId, playerInfo); } return playerInfo; }; const getPlayerShot = (playerId: string): PlayerShot => { let playerShot: PlayerShot = null; if (this.playerShots.has(playerId)) { playerShot = this.playerShots.get(playerId); } if (playerShot == null) { playerShot = getPlayerInfo(playerId)?.getComponent(PlayerShot)!; this.playerShots.set(playerId, playerShot); } return playerShot; }; players.onAdd( ((player, sessionId) => { // console.log("onAdd") if (StringTool.isNotEmpty(player.playerId)) { if (this.players.has(player.playerId)) { this.players.delete(player.playerId); } if (this.playerShots.has(player.playerId)) { this.playerShots.delete(player.playerId); } } // let playerInfo: PlayerInfo = getPlayerInfo(beforeValue) // if (playerInfo != null) { // if (playerInfo.isValid) { // if (playerInfo.isSelfControl) { // playerInfo.isCanControl = true//player 获取成功,说明在房间可以成功操作,设置成可控 // } // if (Global.players.has(playerInfo.playerId)) { // Global.players.delete(playerInfo.playerId) // } // } // if (StringTool.isNotEmpty(value)) { // playerInfo.playerId = value; // Global.players.set(player.playerId, playerInfo) // } // } // ... // console.log("onAdd:", posRot, posRot.playerId); // let playerInfo: PlayerInfo = null //Global.players.get(posRot.playerId)! // let playerShot: PlayerShot = null // playerInfo.getComponent(PlayerShot) // let playerAnimation: PlayerAnimation = null; //playerInfo.getComponent(PlayerAnimation) // let movement: CharacterMovement = null; //playerInfo.getComponent(PlayerAnimation) // const getPlayerAnimation = (playerId: string) => { // if (playerAnimation == null) { // playerAnimation = // getPlayerInfo(playerId)?.getComponent(PlayerAnimation)!; // } // return playerAnimation; // }; // const getMovement = (playerId: string) => { // if (movement == null) { // movement = // getPlayerInfo(playerId)?.getComponent(CharacterMovement)!; // } // return movement; // }; // $(player).pathes.onChange(() => { // const playerInfo = getPlayerInfo(player.playerId); // if (playerInfo == null || !playerInfo.isValid) { // return; // } // const movement = getMovement(player.playerId); // if (movement == null || !movement.isValid) { // return; // } // // if (movement.isInTheAir) { // // return; // // } // playerInfo.isCanControl = true // // playerInfo.isCanSetLocation = true; // if (player.pathes.length > 0) { // playerInfo.netToPoses.length = 0 // // const tmpToPoses = new Array // for (let i = 0; i < player.pathes.length; i++) { // const pos = v3() // CalcTool.integerToFloatForMulVec3ForTmp( // player.pathes[i], // pos // ); // playerInfo.netToPoses.push(pos) // } // // playerInfo.netToPoses = CalcTool.smoothBezier(tmpToPoses, 5) // playerInfo.netToPos = null // } // }); $(player).pos.onChange(() => { // if (!Global.isGameStart) { // return; // } // console.log("pos onChange:", player.playerId) const playerInfo = getPlayerInfo(player.playerId); if (playerInfo == null || !playerInfo.isValid) { return; } // if (!playerInfo.isCanControl) { // return; // } // CalcTool.integerToFloatForMulVec3ForTmp( // player.pos, // playerInfo.netToPos // ); // if (!playerInfo.isCanControl) { // return; // } const pos = CalcTool.integerToFloatForMulVec3(player.pos); // if (playerInfo.isCanControl) { // // // if (!pos.equals(Vec3.ZERO)) { // // playerInfo.setLocation(pos, playerInfo.node.eulerAngles); // // // } // } playerInfo.netToPos.set(pos); }); // $(player).euler.onChange(() => { // const playerInfo = getPlayerInfo(player.playerId); // if (playerInfo == null || !playerInfo.isValid) { // return; // } // if (!playerInfo.isCanControl) { // return // } // const euler = CalcTool.integerToFloatForMulVec3(player.euler) // playerInfo.netToEulers.push(euler) // playerInfo.isCanControl = true // }); $(player).listen("lockedPlayerId", (value, beforeValue) => { const playerShot = getPlayerShot(player.playerId); if (playerShot == null || !playerShot.isValid) { return; } const lockPlayerInfo = Global.players.get(value); if (lockPlayerInfo != null && lockPlayerInfo.isValid) { if (lockPlayerInfo.isValid && lockPlayerInfo.node.active) { playerShot.lockOtherPlayer(value); } else { playerShot.lockOtherPlayer(null); } } }); // $(player).listen("isMoving", (value, beforeValue) => { // // console.log("isMoving onChange:", value, player.playerId); // const playerInfo = getPlayerInfo(player.playerId); // if (playerInfo == null || !playerInfo.isValid) { // return; // } // playerInfo.isMoving = value; // const playerAnimation = getPlayerAnimation(player.playerId); // if (playerAnimation == null || !playerAnimation.isValid) { // return; // } // if (playerInfo.isMoving) { // playerAnimation!.toMoveAnim(); // } else { // playerAnimation!.toIdleAnim(); // } // }); }).bind(this) ); } private reconnect(isCreateOnly: boolean, delayMs = 2000) { if (this._isCanReconnect) { if (this._reconnectTimerId != null) { clearTimeout(this._reconnectTimerId); } this._reconnectTimerId = setTimeout( (async () => { await this.connect(true, Global.roomId, true); }).bind(this), delayMs ); } } }