Colyseus v0.12.0如何在onLeave回调中区分玩家杀进程与网络掉线
问题解决方案
核心实现思路
- 通过前端主动上报+服务端状态标记的方案区分
consented=false下的两种离线场景 - 移动端监听应用即将被销毁的生命周期事件,提前向服务端发送主动退出标记
- 服务端收到标记后存储到对应用户的状态中,
onLeave触发时读取状态判断离线原因
前端实现(以移动端为例)
不管是用Cocos/Unity开发原生应用还是Webview套壳,都可以监听应用退到后台/即将被销毁的事件,触发时发送自定义事件:
// 前端示例代码,Web端直接监听页面销毁事件 window.addEventListener('beforeunload', () => { // 向服务端发送主动杀进程标记 client.send('clientBeforeKill', {}); }); // 原生开发可在iOS的applicationWillTerminate回调、Android的onDestroy回调中触发该消息发送
服务端实现修改
首先在房间初始化的onCreate回调中添加自定义事件监听:
onCreate(options) { // 保留你原有的初始化逻辑 // 新增主动杀进程事件监听 this.onMessage('clientBeforeKill', (client, data) => { // 给该玩家标记主动杀进程状态 if(this.state.players[client.sessionId]) { this.state.players[client.sessionId].isManualKill = true; } }); }
修改后的完整onLeave代码如下:
async onLeave(client, consented) { console.log("===============Leave ================"); console.log(consented, "----Client Left----", client.sessionId); const leavingPlayer = this.state.players[client.sessionId]; console.log(consented, "---------- Left----", leavingPlayer?.name); console.log(consented, "---------- player----", this.ClientUserName); try { if (consented) { // 原有游戏内主动退出的逻辑保持不变 console.log("----consented true----", client.sessionId); console.log("--------ROOM DISOPOSED------consented", this.ourRoomName); if (this.ClientUserName[0] === leavingPlayer.name) { this.playersInRoom = 0; console.log("1111111111111111111111"); this.broadcast( { en: "QuitLobby", value: "host quit the lobby", }, { except: client }); this.disconnect(); this.ClientId = []; this.ClientAvatar = []; this.setState(this.state); } else { this.playersInRoom--; this.playerCount--; console.log(this.users.length) for (let i = 0; i < this.ClientUserName.length; i++) { if (this.ClientUserName[i] === leavingPlayer.name) { this.ClientUserName.splice(i, 1); this.ClientId.splice(i, 1); this.ClientAvatar.splice(i, 1); this.users.splice(i + 1, 1); } } console.log(this.playersInRoom) console.log(this.users.length) this.broadcast({ en: 'playerInfo', playerData: { idArr: this.ClientId, nameArr: this.ClientUserName, avatarArr: this.ClientAvatar } }) } } else { // 新增逻辑:判断是否是主动杀进程 if(leavingPlayer?.isManualKill) { // 主动杀进程逻辑:直接广播计分板、销毁房间 this.broadcast({ en: 'ShowScoreBoard', value: '玩家主动退出游戏' }); this.playersInRoom = 0; this.disconnect(); this.ClientId = []; this.ClientAvatar = []; this.setState(this.state); return; } // 剩下的是网络故障场景,走原有重连逻辑 this.broadcast({ en: 'DisconnectedTemp', value: client.sessionId, }) await this.allowReconnection(client, 20); leavingPlayer.connected = true; console.log("----Client Reconnect----", client.sessionId); } } catch (e) { // 重连超时逻辑:广播计分板、删除离线玩家 this.broadcast({ en: 'ShowScoreBoard', value: '玩家重连超时,游戏结束' }); delete this.state.players[client.sessionId]; } }
补充说明
- 该方案覆盖率可达99%以上,极端场景如果系统强制杀进程没来得及上报,会被判定为网络故障走重连逻辑,属于可接受的降级处理
- 如果不想依赖前端上报,也可以调整Colyseus服务端的
pingInterval(ping包发送间隔,默认3000ms)和pingMaxRetries(最大重试次数,默认2)参数,网络故障触发onLeave的时间会比主动杀进程晚,可通过时间差判断,但准确率低于主动上报方案
内容的提问来源于stack exchange,提问作者Aarju
相关产品推荐
相关产品推荐

