玩家下线(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;
        }
    }
}

 

posted @ 2014-11-30 00:28  zzyoucan  阅读(190)  评论(0编辑  收藏  举报