zoukankan      html  css  js  c++  java
  • 天龙源码分析 客户端登录流程

    1 登录状态定义

    //登录状态
    enum PLAYER_LOGIN_STATUS
    {
    LOGIN_DEBUG_SETTING,            //!< — FOR DEBUG 用户参数
    
    LOGIN_SELECT_SERVER,            // 选择服务器界面.
    LOGIN_DISCONNECT,                //!< 尚未登录
    
    LOGIN_CONNECTING,                //!< 连接服务器中…
    LOGIN_CONNECTED_OK,                //!< 成功连接到服务器
    LOGIN_CONNECT_FAILED,            //!< 连接到服务器失败
    
    LOGIN_ACCOUNT_BEGIN_REQUESTING,    // 发送密码之前状态
    LOGIN_ACCOUNT_REQUESTING,        //!< 发送帐号信息数据包到服务器…
    LOGIN_ACCOUNT_OK,                //!< 帐号验证成功
    LOGIN_ACCOUNT_FAILED,            //!< 帐号验证失败
    
    LOGIN_WAIT_FOR_LOGIN,            // 排队进入游戏状态.
    
    LOGIN_FIRST_LOGIN,                // 首次登录
    LOGIN_CHANGE_SCENE,                // 切换场景的重登录
    };

    2 登录流程采用轮回方式,在Tick中判断当前所处状态

    VOID CGamePro_Login::Tick(VOID)
    {
    CGameProcedure::Tick();
    
    switch(m_Status)
    {
    case LOGIN_DEBUG_SETTING:
    {
    if(!CGameProcedure::s_pUISystem)
    {
    SetStatus(CGamePro_Login::LOGIN_DISCONNECT);
    }
    else
    {
    //DO NOTING,WAIT UI…
    }
    }
    break;
    
    case LOGIN_SELECT_SERVER:// 选择服务器状态
    {
    //— for debug
    if(CGameProcedure::s_pVariableSystem->GetAs_Int(“GameServer_ConnectDirect”) == 1)
    {
    //直接切换到Change-Server流程
    CGameProcedure::SetActiveProc((CGameProcedure*)CGameProcedure::s_pProcChangeScene);
    return;
    }
    //— for debug
    break;
    }
    case LOGIN_DISCONNECT:
    {
    s_pGfxSystem->PushDebugString(“Connect to login server %s:%d…”, m_szLoginServerAddr, m_nLoginServerPort);
    
    //开始登录
    SetStatus(LOGIN_CONNECTING);
    CNetManager::GetMe()->ConnectToServer(m_szLoginServerAddr, m_nLoginServerPort);
    
    }
    break;
    
    case LOGIN_CONNECTING:
    
    break;
    
    //连接成功
    case LOGIN_CONNECTED_OK:
    {
    
    // 设置正在验证密码
    // SetStatus(LOGIN_ACCOUNT_REQUESTING);
    
    }
    break;
    
    //连接失败
    case LOGIN_CONNECT_FAILED:
    
    CNetManager::GetMe()->Close();
    SetStatus(LOGIN_SELECT_SERVER);
    break;
    
    // 正在验证用户名和密码.
    case LOGIN_ACCOUNT_REQUESTING:
    {
    
    // 判断是否超时, 超时就提示错误信息.
    break;
    }
    case LOGIN_ACCOUNT_BEGIN_REQUESTING:
    {
    break;
    }
    //登录信息验证成功
    case LOGIN_ACCOUNT_OK:
    {
    // 保存选择的服务器
    CGameProcedure::s_pVariableSystem->SetAs_Int(“Login_Area”,   CGameProcedure::s_pProcLogIn->m_iCurSelAreaId, FALSE);
    CGameProcedure::s_pVariableSystem->SetAs_Int(“Login_Server”, CGameProcedure::s_pProcLogIn->m_iCurSelLoginServerId, FALSE);
    
    if(m_bReLogin)
    {
    //直接进入场景
    CGameProcedure::s_pProcEnter->SetStatus(CGamePro_Enter::ENTERSCENE_READY);
    CGameProcedure::s_pProcEnter->SetEnterType(ENTER_TYPE_FIRST);
    CGameProcedure::SetActiveProc((CGameProcedure*)CGameProcedure::s_pProcEnter);
    }
    else
    {
    // 设置登录状态为首次登录(以区分切换场景的登录状态)
    CGameProcedure::s_pProcLogIn->FirstLogin();
    
    //转入人物选择循环
    CGameProcedure::SetActiveProc((CGameProcedure*)s_pProcCharSel);
    
    }
    }
    break;
    default:
    break;
    }
    }

    游戏初始化时,为LOGIN_SELECT_SERVER状态。
    2 然后,如果用户填了用户名和密码,点击登录,则触发下面事件

    // 连接到login server
    int CGamePro_Login::ConnectToLoginServer(int iAreaIndex, int iLoginServerIndex)
    {
    
    if( iAreaIndex < 0 || iAreaIndex >= m_iAreaCount )
    return 1;
    
    if( iLoginServerIndex >= (int)m_pAreaInfo[iAreaIndex].LoginInfo.size() )
    return 1;
    
    // 设置ip地址和端口号.
    SetIpAddr( m_pAreaInfo[iAreaIndex].LoginInfo[iLoginServerIndex].szIp.c_str() );
    SetPort( m_pAreaInfo[iAreaIndex].LoginInfo[iLoginServerIndex].iPort );
    
    // 设置当前的状态为非连接状态
    SetStatus(LOGIN_DISCONNECT);
    
    // 通知界面显示系统提示信息, 正在连接服务器.
    CGameProcedure::s_pEventSystem->PushEvent( GE_GAMELOGIN_SHOW_SYSTEM_INFO_NO_BUTTON, “正在连接到服务器…..”);
    
    return 0;
    
    }
    
    

    把游戏登录状态设置为LOGIN_DISCONNECT

    3 步骤1中,如果检测到状态为LOGIN_DISCONNECT,则触发Net事件

    case LOGIN_DISCONNECT:
    {
    s_pGfxSystem->PushDebugString(“Connect to login server %s:%d…”, m_szLoginServerAddr, m_nLoginServerPort);
    
    //开始登录
    SetStatus(LOGIN_CONNECTING);
    CNetManager::GetMe()->ConnectToServer(m_szLoginServerAddr, m_nLoginServerPort);
    
    }
    break;
    
    ConnectToServer具体执行代码如下:
    //创建登录线程
    UINT nThreadID;
    m_hConnectThread = (HANDLE)::_beginthreadex(NULL, 0, _ConnectThread, this, CREATE_SUSPENDED|THREAD_QUERY_INFORMATION, &nThreadID );
    if (m_hConnectThread == NULL)
    {
    TDThrow(_T(“(CNetManager::ConnectToServer)Can’t create connect thread!”));
    }
    
    在这里,创建一个登录线程,用于执行连接服务器,具体执行为_ConnectThread的回调函数:
    //连接线程返回值
    // 0  : 尚未连接
    // 1  : 成功连接到服务器
    // -1 : 创建SOCKET发生错误
    // -2 : 无法连接到目的服务器
    // -3 : 超时错误
    INT CNetManager::ConnectThread(VOID)
    {
    //关闭SOCKET
    m_Socket.close();
    //创建新的SOCKET
    if(!m_Socket.create())
    {
    return -1;
    }
    
    //连接到服务器
    if(!m_Socket.connect( m_strServerAddr.c_str(), m_nServerPort ))
    {
    m_Socket.close();
    return -2 ;
    }
    //成功连接
    return 1;
    }

     

    执行上面代码后,客户端等服务端返回了,监视是否连接成功,是在Net的Tick里面采用轮回查询方式

    // Tick 游戏登录流程.
    VOID    CNetManager::TickGameLoginProcedure()
    {
    switch(CGameProcedure::s_pProcLogIn->GetStatus())
    {
    case CGamePro_Login::LOGIN_DEBUG_SETTING:
    {
    
    break;
    }
    
    case CGamePro_Login::LOGIN_SELECT_SERVER:
    {
    
    break;
    }
    
    //尚未登录,准备状态
    case CGamePro_Login::LOGIN_DISCONNECT:
    {
    break;
    }
    
    //SOCKET连接中…
    case CGamePro_Login::LOGIN_CONNECTING:
    {
    WaitConnecting();
    break;
    }
    
    WaitConnecting()的代码如下:
    VOID CNetManager::WaitConnecting(VOID)
    {
    //监测登录线程是否结束
    int nExitCode = 0;
    
    if(::GetExitCodeThread(m_hConnectThread, (DWORD*)&nExitCode))
    {
    
    }
    
    //登录线程未结束
    if( STILL_ACTIVE == nExitCode)
    {
    //检查是否超时
    UINT dwTimeNow = CGameProcedure::s_pTimeSystem->GetTimeNow();
    UINT dwUsed =  CGameProcedure::s_pTimeSystem->CalSubTime(m_timeConnectBegin, dwTimeNow);
    //超时
    if(dwUsed >= MAX_CONNECT_TIME)
    {
    //强制结束登录线程
    TerminateThread(m_hConnectThread, 0);
    nExitCode = -3;
    }
    //继续等待
    else
    {
    return;
    }
    }
    
    //登录线程已经结束 关闭句柄
    if(CloseHandle(m_hConnectThread))
    {
    m_hConnectThread = NULL;
    }
    
    //登录过程中发生错误
    if(nExitCode < 0)
    {
    //LPCTSTR szErrorDesc;
    switch(nExitCode)
    {
    case -1:
    {
    SetNetStatus(CONNECT_FAILED_CREATE_SOCKET_ERROR);
    CGameProcedure::s_pEventSystem->PushEvent(GE_NET_CLOSE, “创建网络连接失败!”);
    break;
    }
    case -2:
    {
    SetNetStatus(CONNECT_FAILED_CONNECT_ERROR);
    CGameProcedure::s_pEventSystem->PushEvent(GE_NET_CLOSE, “目的服务器可能关闭!”);
    break;
    }
    case -3:
    {
    SetNetStatus(CONNECT_FAILED_TIME_OUT);
    CGameProcedure::s_pEventSystem->PushEvent(GE_NET_CLOSE, “连接超时!”);
    break;
    }
    default:
    {
    SetNetStatus(CONNECT_FAILED_CONNECT_ERROR);
    CGameProcedure::s_pEventSystem->PushEvent(GE_NET_CLOSE, “未知错误!”);
    break;
    }
    
    }
    
    this->Close();
    return;
    }
    
    //连接成功后设置为非阻塞模式和设置Linger参数
    if(!m_Socket.setNonBlocking() || !m_Socket.setLinger(0))
    {
    SetNetStatus(CONNECT_FAILED_CONNECT_ERROR);
    TDThrow(_T(“(CNetManager::Tick)SetSocket Error”));
    return;
    }
    
    //通知登录流程,SOCKET连接成功
    
    if(CGameProcedure::GetActiveProcedure() == (CGameProcedure*)CGameProcedure::s_pProcLogIn)
    {
    CGameProcedure::s_pProcLogIn->SendClConnectMsg();
    SetNetStatus(CONNECT_SUCESS);//
    }
    else if(CGameProcedure::GetActiveProcedure() == (CGameProcedure*)CGameProcedure::s_pProcChangeScene)
    {
    SetNetStatus(CONNECT_SUCESS);//
    }
    
    return;
    }

    代码很清晰,先判断登录线程的返回结果。如果登录线程没结束,退出循环;如果线程已经退出,则检查退出状态,登录成功,则设置登录状态为CONNECT_SUCESS

    5 不过,从登录界面切换到人物选择界面,判断登录状态是否为LOGIN_ACCOUNT_OK。但客户端未查到更新到这个状态的地方,猜测是服务器更新这个状态的

    //登录信息验证成功
    case LOGIN_ACCOUNT_OK:
    {
    // 保存选择的服务器
    CGameProcedure::s_pVariableSystem->SetAs_Int(“Login_Area”,   CGameProcedure::s_pProcLogIn->m_iCurSelAreaId, FALSE);
    CGameProcedure::s_pVariableSystem->SetAs_Int(“Login_Server”, CGameProcedure::s_pProcLogIn->m_iCurSelLoginServerId, FALSE);
    
    if(m_bReLogin)
    {
    //直接进入场景
    CGameProcedure::s_pProcEnter->SetStatus(CGamePro_Enter::ENTERSCENE_READY);
    CGameProcedure::s_pProcEnter->SetEnterType(ENTER_TYPE_FIRST);
    CGameProcedure::SetActiveProc((CGameProcedure*)CGameProcedure::s_pProcEnter);
    }
    else
    {
    // 设置登录状态为首次登录(以区分切换场景的登录状态)
    CGameProcedure::s_pProcLogIn->FirstLogin();
    
    //转入人物选择循环
    CGameProcedure::SetActiveProc((CGameProcedure*)s_pProcCharSel);
    
    }
    }
    break;

    这样就进入角色选择界面了
    (另外一种可能进入角色选择界面的方式见《选择角色流程分析》)

  • 相关阅读:
    uva558 Wormholes SPFA 求是否存在负环
    管理经验(一)——怎样当好一个管理者
    41. 百度面试题:字符串的排列(字符串)
    24L01/SI24R1调试笔记
    中英文对照 —— 学术概念
    matlab 稀疏矩阵(sparse matrix)
    matlab 稀疏矩阵(sparse matrix)
    matlab 可变参数与默认参数设置
    matlab 可变参数与默认参数设置
    卷积、卷积矩阵(Convolution matrix)与核(Kernel)
  • 原文地址:https://www.cnblogs.com/dieangel/p/3326936.html
Copyright © 2011-2022 走看看