• 玩家下线(GS部分)


    玩家下线,之前一直感觉这个过程有点复杂
    else if (stat == link_stat::link_disconnected || stat == link_stat::link_connect_failed )
    {
        GameChannel* pDisconnectGC = m_vecChannel[rPkt.channel_id];
        if(pDisconnectGC)
        {
            //如果进入了地图,保存人物信息时会调用push_freeQueue + 滞空m_Channels[channel_id],
            pDisconnectGC->OnDisconnect();//下线的一些收尾工作
    
            //如果未进地图就下线,直接断开;不用保存角色详细数据,可直接放入释放队列中
            if(!pDisconnectGC->m_pMap)
            {
                //PushFreeQueue(pDisconnectGC);
                //m_vecChannel[rPkt.channel_id] = NULL;
                AutoFreeGC(pDisconnectGC);
            }
        }
        m_LiveMgr.Remove(rPkt.channel_id);//将livemgr的这个位置清零
    }
    下面一步一步看这个过程
    bool GameChannel::OnDisconnect()
    {
        //m_isDisconnect = true;
        OnOffLine();
        return true;
    }
    
    void GameChannel::OnOffLine()
    {
        switch (m_eGameState)
        {
        case eGameState_Login://登录状态
            ClearLoginRcd();//清除登录状态:主要是删除已删除的角色,删除在线用户
            break;
        case eGameState_EnterMap://进入地图状态
            ClearMapRcd();//从map下线,这个获取后面看
            ClearLoginRcd();//清除登录状态
            break;
        case eGameState_InChangeMap:
            //...正在切图就下线了,是不是该延时下线呢?
            m_eGameState = eGameState_DelayDisconnect;
        case eGameState_OnlyConnect:    //未登陆,空连接
        case eGameState_Disconnect:        //已离线
            return;
        default:
            break;
        }
        m_eGameState = eGameState_Disconnect;//将状态切换成离线状态
    }
    
    void GameServer::AutoFreeGC(GameChannel* pGameChannel)
    {
        m_vecChannel[pGameChannel->m_nChannelId] = NULL;
        //channel里面有个m_uDBGetAskRefCount,由于判断数据库是否有返回,加入数据库很慢的时候玩家下线你把这个内存释放了,等数据库返回的时候就访问了野指针
        if (pGameChannel->IsNoDBAsk())//没有数据库访问,可以安全的删除指针了
        {
            delete pGameChannel;//删除玩家指针
            pGameChannel = nullptr;
        }
        else
        {
            PushFreeQueue(pGameChannel);//如果还有数据库访问,就将其放到释放队列中延迟释放
        }
    }
    
    void GameServer::PushFreeQueue(GameChannel* gc)
    {
        FreeChannel fch;
        fch.m_pGameChannel = gc;
        fch.m_uTime = GetTickCount();
        m_FreeQueue.push(fch);
    }
    
    //那这个队列到底什么时候释放的呢?
    在主线程里面初始化了一个定时器
    void GameServer::InitTimer()
    {
        I_TimerFactory* pTimeFactory = NEW(TimerFactory);
        SetPlug("TimerFactory", pTimeFactory);
        m_FreeQueueTimer.reset(pTimeFactory->createTimer());
        m_FreeQueueTimer->regTimer(std::bind(&GameServer::FreeQueueTimer, this));
        m_FreeQueueTimer->setInterval(30 * 1000);//(30 * 1000);
        m_FreeQueueTimer->start();
    }
    
    //之前出现过,函数回调的时候出现错误,当时就是这个问题回调的时候玩家已经下线了,channel已经删除了
    /**
     * 方案一:不直接释放,延迟1分钟,保证数据库不再返回时再释放。(数据库太忙情况不安全,停用)
     * 方案二:不直接释放,看GC标志是否无数据请求。
     */
    void GameServer::FreeQueueTimer()
    {
        if(!m_FreeQueue.size())
            return ;
    
        int now = GetTickCount();
        for (;;)//每次释放完或者到有不能释放的为止
        {
            if(!m_FreeQueue.size())
                return ;
            FreeChannel tmpFreeChannel = m_FreeQueue.front();
            //if(now - tmpFreeChannel.m_uTime > 60 * 1000)
            if (tmpFreeChannel.m_pGameChannel->IsNoDBAsk())//等到数据库没有访问了再释放
            {
                delete tmpFreeChannel.m_pGameChannel;
                m_FreeQueue.pop();
            }
            else
            {
                break;
            }
        }
    }
  • 相关阅读:
    “xmlns:d="http://schemas.microsoft.com/expression/blend/2008"” 命名空间不能被解析
    mysql 连接命令 表管理 ,克隆表,临时表,字符串属性,设定语句间的分隔符
    视图
    NULL值比较,两个列的合并,列值按条件替换。
    词性和 句子成份
    设置用户帐号只能一个地方登录
    数字的格式化,异常处理
    接口,构造器与垃圾回收机制,对像的生命周期。非静态方法与静态方法。
    继承,多态..
    JavaScript —— 常用数据类型隐式转换
  • 原文地址:https://www.cnblogs.com/zzyoucan/p/4132310.html
Copyright © 2020-2023  润新知