# ITapBattleEventHandler

# 接口描述

ITapBattleEventHandler是多人联机SDK的事件处理器接口,用于接收SDK的各种异步事件通知。开发者需要实现此接口,并在Initialize时传入实例。

# 接口定义

public interface ITapBattleEventHandler
{
    // 连接事件
    void OnDisconnected(DisconnectedInfo info);

    // 错误事件
    void OnBattleServiceError(BattleServiceErrorInfo info);

    // 房间属性事件
    void OnRoomPropertiesChanged(RoomPropertiesNotification info);

    // 玩家属性事件
    void OnPlayerCustomPropertiesChanged(PlayerCustomPropertiesNotification info);
    void OnPlayerCustomStatusChanged(PlayerCustomStatusNotification info);

    // 帧同步事件
    void OnFrameSyncStopped(FrameSyncStopInfo info);
    void OnFrameReceived(string frameData);
    void OnFrameSyncStarted(FrameSyncStartInfo info);

    // 玩家事件
    void OnPlayerOffline(PlayerOfflineNotification info);
    void OnPlayerLeft(LeaveRoomNotification info);
    void OnPlayerEntered(EnterRoomNotification info);

    // 消息事件
    void OnCustomMessageReceived(CustomMessageNotification info);

    // 踢人事件
    void OnPlayerKicked(PlayerKickedInfo info);
}

# 事件说明

# 连接事件

# OnDisconnected

触发时机:连接断开时

参数

public class DisconnectedInfo
{
    public string reason;   // 断开原因
    public int code;        // 错误代码
}

# 房间事件

# OnPlayerEntered

触发时机:玩家进入房间时

参数

public class EnterRoomNotification
{
    public string roomId;           // 房间ID
    public PlayerInfo playerInfo;   // 进入的玩家信息
}

# OnPlayerLeft

触发时机:玩家离开房间时

参数

public class LeaveRoomNotification
{
    public string roomId;       // 房间ID
    public string playerId;     // 离开的玩家ID
    public string playerName;   // 离开的玩家名称
}

# OnPlayerKicked

触发时机:玩家被踢出房间时

参数

public class PlayerKickedInfo
{
    public string playerId;     // 被踢的玩家ID
    public string reason;       // 被踢原因
}

# 玩家事件

# OnPlayerOffline

触发时机:玩家掉线时

参数

public class PlayerOfflineNotification
{
    public string playerId;     // 掉线的玩家ID
    public string playerName;   // 掉线的玩家名称
}

# OnPlayerCustomStatusChanged

触发时机:玩家状态变更时

参数

public class PlayerCustomStatusNotification
{
    public string playerId;     // 玩家ID
    public int status;          // 新的自定义状态
}

# OnPlayerCustomPropertiesChanged

触发时机:玩家属性变更时

参数

public class PlayerCustomPropertiesNotification
{
    public string playerId;                     // 玩家ID
    public Dictionary <string, object> properties;   // 新的自定义属性(字典)
}

# 房间属性事件

# OnRoomPropertiesChanged

触发时机:房间属性变更时

参数

public class RoomPropertiesNotification
{
    public string id;                                   // 房间ID
    public string name;                                 // 房间名称
    public Dictionary <string, object> customProperties;  // 自定义属性(字典)
}

# 帧同步事件

# OnFrameSyncStarted

触发时机:帧同步开始时

参数

public class FrameSyncStartInfo
{
    public RoomInfo roomInfo;       // 房间信息
    public int frameSyncId;         // 帧同步会话ID,房间内唯一
    public int seed;                // 随机数种子,用于NewRandomNumberGenerator
    public string serverTime;       // 服务器时间字符串
}

重要说明:帧同步开始后,使用 info.seed 作为参数调用 TapBattleClient.NewRandomNumberGenerator(info.seed) 创建确定性随机数生成器

# OnFrameReceived

触发时机:接收帧同步数据时(持续触发)

参数

public class FrameData
{
    public int id;                      // 帧序号
    public PlayerInputInfo[] inputs;    // 本帧所有玩家的输入
}

public class PlayerInputInfo
{
    public string playerId;     // 玩家ID
    public string data;         // 操作数据(JSON字符串)
    public string serverTime;   // 服务器时间戳
}

使用说明:SDK会自动将JSON反序列化为FrameData对象,可以直接使用

# OnFrameSyncStopped

触发时机:帧同步停止时

参数

public class FrameSyncStopInfo
{
    public string roomId;       // 房间ID
    public int frameSyncId;     // 帧同步会话ID
    public int reason;          // 停止原因: 0=房主主动结束, 1=超时结束(30分钟)
}

# 消息事件

# OnCustomMessageReceived

触发时机:收到自定义消息时

参数

public class CustomMessageNotification
{
    public string fromPlayerId; // 消息发送者玩家ID
    public object message;      // 自定义消息内容
    public int type;            // 消息类型(0-房间内所有玩家,1-队伍内所有玩家)
}

# 错误事件

# OnBattleServiceError

触发时机:多人联机服务发生错误时

参数

public class BattleServiceErrorInfo
{
    public string errorMessage;     // 错误消息
    public int errorCode;           // 错误代码
}

# 实现示例

# 示例1:基础实现

using UnityEngine;

public class MyBattleEventHandler : ITapBattleEventHandler
{
    private RandomNumberGenerator rng;  // 随机数生成器实例

    public void OnDisconnected(DisconnectedInfo info)
    {
        Debug.Log($"连接断开: {info.reason} (代码: {info.code})");
    }

    public void OnPlayerEntered(EnterRoomNotification info)
    {
        Debug.Log($"玩家 {info.playerInfo.id} 进入房间");
    }

    public void OnPlayerLeft(LeaveRoomNotification info)
    {
        Debug.Log($"玩家 {info.playerId} 离开房间");
    }

    public void OnPlayerKicked(PlayerKickedInfo info)
    {
        Debug.Log($"玩家 {info.playerId} 被踢出房间,原因: {info.reason}");
    }

    public void OnPlayerOffline(PlayerOfflineNotification info)
    {
        Debug.Log($"玩家 {info.playerId} ({info.playerName}) 掉线");
    }

    public void OnPlayerCustomStatusChanged(PlayerCustomStatusNotification info)
    {
        Debug.Log($"玩家 {info.playerId} 状态变更: {info.status}");
    }

    public void OnPlayerCustomPropertiesChanged(PlayerCustomPropertiesNotification info)
    {
        Debug.Log($"玩家 {info.playerId} 属性变更");
    }

    public void OnRoomPropertiesChanged(RoomPropertiesNotification info)
    {
        Debug.Log($"房间 {info.id} 属性变更");
    }

    public void OnFrameSyncStarted(FrameSyncStartInfo info)
    {
        Debug.Log($"帧同步开始");
        Debug.Log($"  房间: {info.roomInfo.id}");
        Debug.Log($"  帧同步ID: {info.frameSyncId}");
        Debug.Log($"  随机种子: {info.seed}");
        Debug.Log($"  服务器时间: {info.serverTime}");
        
        // 创建确定性随机数生成器(新API - 返回实例)
        rng = TapBattleClient.NewRandomNumberGenerator(info.seed);
    }

    public void OnFrameReceived(FrameData frameData)
    {
        // 直接使用帧数据对象
        Debug.Log($"收到第 {frameData.id} 帧,包含 {frameData.inputs.Length} 个输入");
        
        foreach (var input in frameData.inputs)
        {
            ProcessPlayerInput(input.playerId, input.data);
        }
        
        // 使用确定性随机数
        if (rng != null)
        {
            int randomValue = rng.RandomInt();
            // 使用randomValue进行游戏逻辑...
        }
    }

    public void OnFrameSyncStopped(FrameSyncStopInfo info)
    {
        Debug.Log($"帧同步停止");
        Debug.Log($"  房间: {info.roomId}");
        Debug.Log($"  帧同步ID: {info.frameSyncId}");
        Debug.Log($"  停止原因: {info.reason}");
        
        // 释放随机数生成器(新API - 使用实例方法)
        if (rng != null)
        {
            rng.Free();
            rng = null;
        }
    }

    public void OnCustomMessageReceived(CustomMessageNotification info)
    {
        Debug.Log($"收到来自 {info.fromPlayerId} 的消息");
    }

    public void OnBattleServiceError(BattleServiceErrorInfo info)
    {
        Debug.LogError($"多人联机服务错误: {info.errorMessage} (代码: {info.errorCode})");
    }

    private void ProcessPlayerInput(string playerId, string data) { }
}

# 最佳实践

  1. 实现所有方法 - 即使某些事件不需要处理,也建议添加日志以便调试
  2. 错误处理 - 务必实现OnBattleServiceError,及时处理服务异常
  3. UI更新 - 事件回调在主线程执行,可以安全地更新UI
  4. 状态同步 - 使用事件更新本地的游戏状态和UI显示
  5. 避免阻塞 - 事件处理方法中不要执行耗时操作

# 相关API