You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase onDisconnect仅在玩家重连后生效,如何实现断网即时触发?

解决Firebase onDisconnect断网不即时触发的问题

首先得明确:你用的onDisconnect().setValue(1)本质是客户端预先告知Firebase服务器「如果我和你的连接中断,就执行这个写操作」。但服务器是通过心跳超时判断客户端断联的,默认超时约60秒,所以无法做到即时触发。要实现接近实时的断联检测,得结合客户端心跳+服务器端超时监听的方案。

解决方案步骤

1. 客户端添加心跳机制

客户端定期向数据库写入心跳标记(比如当前时间戳),同时保留onDisconnect作为兜底(处理正常退出、主动断网的场景):

// 初始化玩家心跳引用(替换为你的玩家唯一标识,比如playerId)
DatabaseReference heartbeatRef = FirebaseDatabase.getInstance()
        .getReference("rooms/" + roomName + "/players/" + playerId + "/heartbeat");

// 启动定时任务,每10秒更新一次心跳值
Timer heartbeatTimer = new Timer();
heartbeatTimer.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        // 写入当前时间戳作为心跳
        heartbeatRef.setValue(System.currentTimeMillis());
    }
}, 0, 10000); // 立即执行,之后每10秒执行一次

// 断联时移除心跳标记,同时触发playersLeave累加
heartbeatRef.onDisconnect().removeValue();
DatabaseReference leaveRef = FirebaseDatabase.getInstance()
        .getReference("rooms/" + roomName + "/playersLeave");
// 用transaction替代setValue,避免多玩家断联时数值被覆盖
leaveRef.onDisconnect().transaction(currentValue -> {
    return (currentValue == null ? 0 : currentValue) + 1;
});

// 注意:玩家正常退出游戏时,要取消定时任务
// heartbeatTimer.cancel();

2. 服务器端用云函数监听心跳超时

通过Firebase云函数监听心跳节点的变化,如果超过设定时间(比如20秒)未更新心跳,就判定玩家断联,主动累加playersLeave数值:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

exports.handlePlayerDisconnect = functions.database
    .ref("/rooms/{roomId}/players/{playerId}/heartbeat")
    .onWrite(async (change, context) => {
        const roomId = context.params.roomId;
        const playerId = context.params.playerId;
        const currentTime = Date.now();

        // 如果心跳节点被移除(比如onDisconnect触发),直接处理断联
        if (!change.after.exists()) {
            const leaveRef = admin.database().ref(`/rooms/${roomId}/playersLeave`);
            return leaveRef.transaction(current => (current || 0) + 1);
        }

        // 延迟20秒后检查心跳是否更新
        return new Promise(resolve => {
            setTimeout(async () => {
                const heartbeatSnapshot = await admin.database()
                    .ref(`/rooms/${roomId}/players/${playerId}/heartbeat`)
                    .once("value");
                
                // 如果心跳存在且超时超过20秒,判定为断联
                if (heartbeatSnapshot.exists() && currentTime - heartbeatSnapshot.val() > 20000) {
                    const leaveRef = admin.database().ref(`/rooms/${roomId}/playersLeave`);
                    await leaveRef.transaction(current => (current || 0) + 1);
                    // 顺便清理玩家的心跳节点
                    await heartbeatSnapshot.ref.remove();
                }
                resolve();
            }, 20000);
        });
    });

关键细节说明

  • 心跳间隔与超时时间:可根据需求调整,比如心跳间隔5秒、超时15秒,实时性更强,但会增加数据库写入次数,需平衡成本与体验。
  • transaction的使用:必须用transaction替代setValue(1),否则多个玩家同时断联时,数值会被覆盖,无法正确累加。
  • 定时任务清理:玩家正常退出游戏时,一定要调用heartbeatTimer.cancel()取消心跳任务,避免无效的数据库写入。

内容的提问来源于stack exchange,提问作者SuperMen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 13:55:20