玩家下线(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;
        }
    }
}
时间: 2024-11-17 07:47:10

玩家下线(GS部分)的相关文章

GS踢玩家下线功能

GS踢玩家下线功能 //key:userId, val:nChannelId (当前在线用户) std::map<int, int> m_mapOnLineUserByUid; ///< 当前在线用户,用户Id,对应ChannelId //所有在线的玩家通道 std::vector<GameChannel*> m_vecChannel; ///< 所有在线玩家连接 主要就是通过这两个容器实现 if(0 == strncmp(User.m_szUserPwd, m_pUs

GS与数据库打交道

GS与数据库打交道 link_stat stat = (link_stat)rPkt.size; if (stat == link_stat::link_connected) { GameChannel* pNewGC = new GameChannel(); pNewGC->m_nChannelId = rPkt.channel_id;//客户端唯一标识 pNewGC->m_pDataLayer = m_spDataLayer.get();//GC直接发包到客户端 pNewGC->m_

再看GS线程

再看GS线程 void GameServer::ProcessThreadTry() { int nCount = 0; packet rcvPkt; rcvPkt.data = new char[1024 * 100]; //该事件工厂主要创建了两个定时器1.livemgr的检测(即是否是有效的连接)2.道具帮会差异更新数据的获取即定时从道具帮会容器中获取差异需要保存到数据库的这个数据 //3.释放队列的处理,现在玩家下线不是直接把channel删除掉而是放到释放队列中等没有数据库访问时在删除

GS与网络打交道

与网络打交道 在GS,GC,Share都与网络打交道,但还是GC最多 GC打交道过程 send_stat BaseChannel::SendCmdTry() { if (!m_queCmd.size()) return send_stat::send_succeed; for(;;) { if (m_queCmd.empty()) break; DataPkt pkt = m_queCmd.front(); { send_stat hr = m_pDataLayer->SendData(m_nC

GS LiveMgr心跳管理类

struct LiveMgr { private: int m_nCount; ///< 管理数量 std::vector<int> m_vecChannels; ///< 所有channel std::shared_ptr<I_Timer> m_spTimer; ///< 定时器 public: std::function<void(int channel)> m_fnTimeOutDisconnect; void Init(int nMaxGcNu

玩家游戏状态

玩家游戏状态变化 enum GameState//玩家游戏状态 { eGameState_OnlyConnect = 0, ///< 仅连接 eGameState_Login = 1, ///< 登录 eGameState_EnterMap = 2, ///< 进入地图 eGameState_Disconnect = 3, ///< 下线 }; 1.当libevent收到连接事件的时候 GameChannel::GameChannel() { //... m_eGameState

项目数据库逻辑

项目数据库逻辑 数据库主要逻辑模块分为asynDBCenter,DBNet,DBSvr asynDBCenter与DBNet之间通过共享内存通信(缓存进程为了防止GS崩溃导致玩家数据丢失,并且在DBNet里面可以做定时存储) 这里的共享内存有点复杂 1.发包类型的,就是跟GS与Net之间的相似带回收类型的 2.不带回收的,每个玩家独占一个小块,只有玩家独占,当服务器蹦了玩家数据可以存储数据库 3.对于道具由于玩家身上的道具数量众多,不可能有那么多内存分给,当玩家装备属性发生变化时将其放到共享内存

项目分析(map复习)

有段时间没看map里面的东西了,刚才看发现功能上增加了一些,在来复习了一次流程初始化每个map建立线程,这个线程有两个功能,1.处理GS发过来的包 2.驱动map里面的定时器GS发过来的包是存在m_gs2msPkts2这个无锁的单消费者,单生产者队列中,只要是GS中不处理的包都要发到MAP中然后每个线程thrTransData::thread()就从m_gs2msPkts2中取包调用Map::process_pkt由于map继承了thrTransData,然后根据绑定调用对于的函数那如果map里

从游戏开发到应用开发的转变

从2011年大学毕业到2014年年底,这段时间我的软件开发生涯是在游戏圈子里渡过的,虽然终究并没有做出什么名堂来,但是至少也是混到了服务器主程的位置:游戏开发的那些日子,大概也是进步最快的日子,回忆起来自己的博客里面比较有质量的文章都是那段时间写的,尤其是面对上线和大并发的时候,对于软件和代码的理解有了不同于刚毕业那会的理解,我想大概也是项目推动成长吧! 时间来到了2014年年底,随着自己对于游戏开发的兴趣骤减和于策划方案冲突的急剧加深,慢慢产生了离开这个行业的想法,于是就在那个契机,自己放下了