状态同步模式下的游戏掉线重连

  状态同步模型下,所有的游戏逻辑、数据都会保存在服务端。对于开房间式的游戏(比如5V5这类),玩家的状态都可以在每一个房间room内维护着。
  

local room = {
       room_id = -1,                  --房间id
       offline_player_num = 0,        --房间内的掉线人数
       frame_id = 0,                  --帧序号
       players = players,             --玩家列表(这是存储玩家类的表)
       frame_msg_list = {},           --帧数据列表(一局游戏的所有帧数据)
       tmp_frame_msg_list = {},       --临时的帧数据列表
       atk_list = {},                 --维护战斗道具的列表(如飞行中的子弹
       frame_time_id = -1,            --帧消息定时器id
       colider_time_id = -1,          --碰撞计算定时器id
 }

  当检测到有玩家触发掉线事件时,首先根据玩家客户端连接的tcp连接号(client_id)去索引到所在的房间,然后从房间内维护的所有玩家的信息列表(players)中找到该玩家,这时候不要将改玩家对象从players中移除,而是采取标记的方式,不如修改player对象的一个状态值为下线状态。这样子设计的好处在于,如果玩家重新连接,可以快速找到掉线前所在的房间,而且不需要再new一个player对象加入到players中去,只需要修改下players中对应的player的在线状态即可。在该player掉线的过程中,room内所有的player一样在进行正常的逻辑运算,如果这时候掉线的player被打死(状态信息发送了变化),等玩家重新连接后边会直接看到被改变后的状态。
  但是,这样子会存在一个问题,就是每次断开连接再次连接后,服务端收到的同一个玩家的tcp连接好是发生了变化的。所以,对于player类的设计需要特别注意,我们需要给player设计一个上下线都不变的fixed_client_id,这个值只在第一次刚开房间的时候赋值,之后都不会被改变。之所以需要维护这么一个固定的id号,是因为这类游戏,在客户端也往往会维持这么一份所有玩家的信息表,一般都可以采用不同玩家不同的client_id来索引到客户端中每个敌方单位。但是每次玩家掉线重连后由于更新了client_id,服务端会按照最新的client_id将rpc消息转发给对应的客户端,但是客户端方面存储敌方单位的client_id并没有被更新,这样子往往就无法把敌方单位的操作表现出来。这时候fixed_client_id的作用就体现出来了。在服务器做状态运算时,在修改敌方单位状态的时候,需要把敌方单位的fixed_client_id传给客户端,但是在转发rpc消息时要采用最新的client_id。如此便解决了掉线重连的问题。
  

    local player = {
        client_id = client_id,               --唯一id(掉线重连后会更新)
        fixed_client_id = client_id,            --固定的id(只在第一次开房间时和client_id同时赋值,在掉线重连后也不更新)
        account = account,                     --用户名
        log = Log:new("boe.player_" .. account),
        status = 1,                --玩家状态  1:在线  -1:离线  0:掉线
        hp = INIT_HP,               --血量
    }
--玩家重新上线处理器
function M.on_reconnection(client_id,room)
    M[client_id] = room
end

-- 玩家下线事件处理器
function M.on_disconnected(client_id)
    local room = M.get(client_id)
    if room then
        room:on_offline(client_id)   --通知该房间内client_id的玩家掉线了
        M[client_id] = nil     --移除掉线玩家client_id所索引的房间(因为重连后的clien_id是变了的)
        log:info("room.offline_players's num = " .. room.offline_player_num)
        if room.offline_player_num < 2 then return end                          --没有全部掉线则不销毁房间

        timer_queue:erase(room.frame_time_id)   --清空房间定时器
        timer_queue:erase(room.colider_time_id)
        M.erase(room)
        room:on_disconnected()         --房间内存清理
        if room ~= nil then
            room = nil               --内存清理
        end
        log:info("room_mgr:on_disconnected")
    end
    -- if room
end  -- on_disconnected()
发布了47 篇原创文章 · 获赞 8 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/it_wjw/article/details/76795992