2025-12-23 16:42:04 +08:00

709 lines
22 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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<string> {
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<string, PlayerInfo> = new Map();
private playerShots: Map<string, PlayerShot> = 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<Vec3>
// 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
);
}
}
}