ilikenba 发表于 2005-1-31 11:52

[分享]MazeServer源码示例

<DIV class=HtmlCode>
<P>// File: mazeserver.cpp
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#define STRICT
#define D3D_OVERLOADS
#include &lt;windows.h&gt;
#include &lt;d3dx.h&gt;
#include &lt;stdio.h&gt;
#include &lt;math.h&gt;
#include &lt;mmsystem.h&gt;
#include &lt;dplay8.h&gt;
#include &lt;dpaddr.h&gt;
#include &lt;dxerr8.h&gt;
#include "DXUtil.h"
#include "MazeServer.h"
#include "Packets.h"
#include "Maze.h"
#include &lt;malloc.h&gt;
#include &lt;tchar.h&gt;</P>

<P>//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CMazeServer::CMazeServer()
{
    m_dwPlayerCount         = 0;
   
    m_wActiveThreadCount   = 0;
    m_wMaxThreadCount      = 0;
    m_fAvgThreadCount      = 0;
    m_fAvgThreadTime       = 0;
    m_fMaxThreadTime       = 0;</P>
<P>    m_dwServerReliableRate  = 15;
    m_dwServerTimeout       = 150;
    m_dwLogLevel            = 2;
    m_pMaze                 = NULL;</P>
<P>    m_ClientNetConfig.ubReliableRate = 15;
    m_ClientNetConfig.wUpdateRate    = 150;
    m_ClientNetConfig.wTimeout       = 150;</P>
<P>    m_ClientNetConfig.dwThreadWait = 0;</P>
<P>    m_ClientNetConfig.ubClientPackIndex = 0;
    m_ClientNetConfig.ubServerPackIndex = 0;
    for(WORD x = 0; x &lt; PACK_ARRAY_SIZE; x++)
    {
        m_ClientNetConfig.wClientPackSizeArray = 0;
        m_ClientNetConfig.wServerPackSizeArray = 0;
    }
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeServer::Init( BOOL bLocalLoopback, const CMaze* pMaze )
{
    m_bLocalLoopback = bLocalLoopback;
    m_pMaze = pMaze;
    if( m_pMaze == NULL )
        return DXTRACE_ERR( TEXT("Param"), E_FAIL );</P>
<P>    // Grab height and width of maze
    m_dwWidth = m_pMaze-&gt;GetWidth();
    m_dwHeight = m_pMaze-&gt;GetHeight();</P>
<P>    m_ClientNetConfig.dwMazeWidth  = m_dwWidth;
    m_ClientNetConfig.dwMazeHeight = m_dwHeight;</P>
<P>    // Validate size. Must be a power-of-2 times LOCK_GRID_SIZE. Compute the shifts.
    if( m_dwWidth &gt; SERVER_MAX_WIDTH || m_dwHeight &gt; SERVER_MAX_HEIGHT )
        return DXTRACE_ERR( TEXT("Maze height and width need to be less than 128"), E_INVALIDARG );
    if( (m_dwWidth % LOCK_GRID_SIZE) != 0 || (m_dwHeight % LOCK_GRID_SIZE) != 0 )
        return DXTRACE_ERR( TEXT("Maze height and width need to be divisable by 16"), E_INVALIDARG );</P>
<P>    DWORD scale = m_dwWidth / LOCK_GRID_SIZE;
    m_dwMazeXShift = 0;
    while ( (scale &gt;&gt;= 1) )
        m_dwMazeXShift++;</P>
<P>    scale = m_dwHeight / LOCK_GRID_SIZE;
    m_dwMazeYShift = 0;
    while ( (scale &gt;&gt;= 1) )
        m_dwMazeYShift++;</P>
<P>    if( ((DWORD(LOCK_GRID_SIZE) &lt;&lt; m_dwMazeXShift) != m_dwWidth) ||
        ((DWORD(LOCK_GRID_SIZE) &lt;&lt; m_dwMazeYShift) != m_dwHeight) )
        return DXTRACE_ERR( TEXT("Maze height and width need to be power of 2"), E_INVALIDARG );</P>
<P>    // Initialise the player list
    ZeroMemory( m_PlayerDatas, sizeof(m_PlayerDatas) );
    m_pFirstActivePlayerData = NULL;
    m_pFirstFreePlayerData = m_PlayerDatas;
    for( DWORD i = 1; i &lt; MAX_PLAYER_OBJECTS-1; i++ )
    {
        m_PlayerDatas.pNext = &amp;m_PlayerDatas;
        m_PlayerDatas.pPrevious = &amp;m_PlayerDatas;
    }</P>
<P>    m_PlayerDatas.pNext = &amp;m_PlayerDatas;
    m_PlayerDatas.pPrevious = &amp;m_PlayerDatas;
    m_dwActivePlayerDataCount = 0;
    m_dwPlayerDataUniqueValue = 0;</P>
<P>    // Initialise the cells
    ZeroMemory( m_Cells, sizeof(m_Cells) );
    ZeroMemory( &amp;m_OffMapCell, sizeof(m_OffMapCell) );</P>
<P>    return S_OK;
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::Shutdown()
{
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::LockRange( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
{
    m_LockGrid.LockRange( x1&gt;&gt;m_dwMazeXShift, y1&gt;&gt;m_dwMazeYShift ,
                          x2&gt;&gt;m_dwMazeXShift, y2&gt;&gt;m_dwMazeYShift );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnlockRange( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
{
    m_LockGrid.UnlockRange( x1&gt;&gt;m_dwMazeXShift, y1&gt;&gt;m_dwMazeYShift ,
                            x2&gt;&gt;m_dwMazeXShift, y2&gt;&gt;m_dwMazeYShift );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::LockCell( DWORD x, DWORD y )
{
    if( x == 0xffff )
        m_OffMapLock.Enter();
    else
        m_LockGrid.LockCell(x&gt;&gt;m_dwMazeXShift,y&gt;&gt;m_dwMazeYShift);
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnlockCell( DWORD x, DWORD y )
{
    if( x == 0xffff )
        m_OffMapLock.Leave();
    else
        m_LockGrid.UnlockCell(x&gt;&gt;m_dwMazeXShift,y&gt;&gt;m_dwMazeYShift);
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::LockCellPair( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
{
    if( x1 == x2 &amp;&amp; y1 == y2 )
    {
        if( x1 != 0xffff &amp;&amp; y2 != 0xffff )
            LockCell( x1, y1 );
        else
            m_OffMapLock.Enter();</P>
<P>        return;
    }</P>
<P>    DWORD x1shift = x1&gt;&gt;m_dwMazeXShift;
    DWORD x2shift = x2&gt;&gt;m_dwMazeXShift;
    DWORD y1shift = y1&gt;&gt;m_dwMazeYShift;
    DWORD y2shift = y2&gt;&gt;m_dwMazeYShift;</P>
<P>    if( x1 == 0xffff )
    {
        m_OffMapLock.Enter();
        m_LockGrid.LockCell(x2shift,y2shift);
    }
    else if( x2 == 0xffff )
    {
        m_OffMapLock.Enter();
        m_LockGrid.LockCell(x1shift,y1shift);
    }
    else
    {
        m_LockGrid.LockCellPair(x1shift,y1shift,x2shift,y2shift);
    }
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnlockCellPair( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
{
    if( x1 == x2 &amp;&amp; y1 == y2 )
    {
        if( x1 != 0xffff &amp;&amp; y2 != 0xffff )
            UnlockCell( x1, y1 );
        else
            m_OffMapLock.Leave();</P>
<P>        return;
    }</P>
<P>    DWORD x1shift = x1&gt;&gt;m_dwMazeXShift;
    DWORD x2shift = x2&gt;&gt;m_dwMazeXShift;
    DWORD y1shift = y1&gt;&gt;m_dwMazeYShift;
    DWORD y2shift = y2&gt;&gt;m_dwMazeYShift;</P>
<P>    if( x1 == 0xffff )
    {
        m_LockGrid.UnlockCell(x2shift,y2shift);
        m_OffMapLock.Leave();
    }
    else if( x2 == 0xffff )
    {
        m_LockGrid.UnlockCell(x1shift,y1shift);
        m_OffMapLock.Leave();
    }
    else
    {
        m_LockGrid.UnlockCellPair(x1shift,y1shift,x2shift,y2shift);
    }
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::OnAddConnection( DWORD id )
{
    m_AddRemoveLock.Enter();</P>
<P>    // Increment our count of players
    m_dwPlayerCount++;
    if( m_dwLogLevel &gt; 0 )
    {
        ConsolePrintf( SLINE_LOG, TEXT("Adding player DPNID %0.8x"), id );
        ConsolePrintf( SLINE_LOG, TEXT("Players connected = %d"), m_dwPlayerCount );
    }</P>
<P>    if( m_dwPlayerCount &gt; m_dwPeakPlayerCount )
        m_dwPeakPlayerCount = m_dwPlayerCount;</P>
<P>    // Create a player for this client
    PlayerData* pPlayerData = CreatePlayerData();
    if( pPlayerData == NULL )
    {
        ConsolePrintf( SLINE_LOG, TEXT("ERROR! Unable to create new PlayerData for client!") );
        DXTRACE_ERR( TEXT("CreatePlayerData"), E_FAIL );
        m_AddRemoveLock.Leave();
        return;
    }</P>
<P>    // Store that pointer as local player data
    SetPlayerDataForID( id, pPlayerData );</P>
<P>    // Grab net config into to send to client
    m_ClientNetConfigLock.Enter();
    ServerConfigPacket packet( m_ClientNetConfig );
    m_ClientNetConfigLock.Leave();</P>
<P>    // Send it
    SendPacket( id, &amp;packet, sizeof(packet), TRUE, 0 );</P>
<P>    m_AddRemoveLock.Leave();
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::OnRemoveConnection( DWORD id )
{
    m_AddRemoveLock.Enter();</P>
<P>    // Decrement count of players
    m_dwPlayerCount--;</P>
<P>    if( m_dwLogLevel &gt; 0 )
    {
        ConsolePrintf( SLINE_LOG, TEXT("Removing player DPNID %0.8x"), id );
        ConsolePrintf( SLINE_LOG, TEXT("Players connected = %d"), m_dwPlayerCount );
    }</P>
<P>    // Find playerdata for this client
    PlayerData* pPlayerData = GetPlayerDataForID( id );
    if( pPlayerData != NULL )
    {
        // Destroy it
        RemovePlayerDataID( pPlayerData );
        DestroyPlayerData( pPlayerData );
    }</P>
<P>    m_AddRemoveLock.Leave();
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeServer::OnPacket( DWORD dwFrom, void* pData, DWORD size )
{
    BOOL fFoundSize = FALSE;</P>
<P>    // Increment the number of thread we have in this process.
    m_csThreadCountLock.Enter();</P>
<P>    //Get the start time of when we entered the message handler.
    FLOAT   fStartTime = DXUtil_Timer( TIMER_GETAPPTIME );</P>
<P>    m_wActiveThreadCount++;
    if(m_wActiveThreadCount &gt; m_wMaxThreadCount)
        m_wMaxThreadCount = m_wActiveThreadCount;
   
    // Calculate and average.
    FLOAT fdiff = m_wActiveThreadCount - m_fAvgThreadCount;
    m_fAvgThreadCount += fdiff/32;
   
    m_csThreadCountLock.Leave();</P>
<P>
    ClientPacket* pClientPack = (ClientPacket*)pData;
    switch( pClientPack-&gt;wType )
    {
        case PACKETTYPE_CLIENT_POS:
            
            // Check to see if the packet has a valid size. Including
            // the custom pack size.
            if( size &lt; sizeof(ClientPosPacket))
                fFoundSize = FALSE;
            else if( ! IsValidPackSize(size - sizeof(ClientPosPacket)))
                fFoundSize = FALSE;
            else
                fFoundSize = TRUE;</P>
<P>            // If valid sized packet, handle the position.
            if(fFoundSize)
                HandleClientPosPacket( dwFrom, (ClientPosPacket*)pClientPack );
            else
                m_pNet-&gt;RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );</P>
<P>            break;</P>
<P>        case PACKETTYPE_CLIENT_VERSION:
            if( size == sizeof(ClientVersionPacket) )
                HandleClientVersionPacket( dwFrom, (ClientVersionPacket*)pClientPack );
            else
                m_pNet-&gt;RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
            break;</P>
<P>        case PACKETTYPE_SERVER_CONFIG:</P>
<P>            // Server config packet sent to all users (including server). Just ignore and continue.</P>
<P>            break;
        default:
            HandleUnknownPacket( dwFrom, pClientPack, size );
            break;
    }</P>
<P>    //If the user wants to hold the thread, Sleep for given amount of time.
    if ( m_dwServerThreadWait &gt; 0 )
    {
        Sleep( m_dwServerThreadWait );
    }
   
    // Retrieve thread data for this process.
    m_csThreadCountLock.Enter();</P>
<P>    m_wActiveThreadCount--;</P>
<P>    FLOAT fDiffTime = (DXUtil_Timer( TIMER_GETAPPTIME ) - fStartTime) - m_fAvgThreadTime;
    m_fAvgThreadTime += fDiffTime/32;</P>
<P>    //Get the Max time in the thread.
    if ( fDiffTime &gt; m_fMaxThreadTime )
    {
        m_fMaxThreadTime = fDiffTime;
    }</P>
<P>    m_csThreadCountLock.Leave();</P>
<P>    return S_OK;
}</P>

<P>//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
BOOL CMazeServer::IsValidPackSize( DWORD dwSize )
{
    BOOL fFoundSize = FALSE;
    BYTE ubPackLocation = m_ClientNetConfig.ubClientPackIndex;
   
    // Check through the array of valid pack sizes.
    if(dwSize != m_ClientNetConfig.wClientPackSizeArray)
    {
        for( --ubPackLocation; ubPackLocation != m_ClientNetConfig.ubClientPackIndex; ubPackLocation--)
        {
            if(dwSize == m_ClientNetConfig.wClientPackSizeArray)
            {
                // Found valid size in the array.
                fFoundSize = TRUE;
                break;
            }
            if(ubPackLocation &gt;= PACK_ARRAY_SIZE)  ubPackLocation = PACK_ARRAY_SIZE;  //Wrap the array.
        }
    }
    else
    {
        fFoundSize = TRUE;
    }</P>
<P>    return fFoundSize;
}</P>
<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::OnSessionLost( DWORD dwReason )
{
    ConsolePrintf( SLINE_LOG, TEXT("ERROR! Session was lost") );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
PlayerData* CMazeServer::CreatePlayerData()
{
    m_PlayerDataListLock.Enter();</P>
<P>    // Grab first free player in the list
    PlayerData* pPlayerData = m_pFirstFreePlayerData;</P>
<P>    if( pPlayerData )
    {
        LockPlayerData( pPlayerData );</P>
<P>        // Got one, so remove it from the free list
        if( pPlayerData-&gt;pPrevious )
            pPlayerData-&gt;pPrevious-&gt;pNext = pPlayerData-&gt;pNext;
        if( pPlayerData-&gt;pNext )
            pPlayerData-&gt;pNext-&gt;pPrevious = pPlayerData-&gt;pPrevious;
        m_pFirstFreePlayerData = pPlayerData-&gt;pNext;</P>
<P>        // Add it to the active list
        if( m_pFirstActivePlayerData )
            m_pFirstActivePlayerData-&gt;pPrevious = pPlayerData;
        pPlayerData-&gt;pNext = m_pFirstActivePlayerData;
        pPlayerData-&gt;pPrevious = NULL;
        m_pFirstActivePlayerData = pPlayerData;</P>
<P>        // Update count of players
        m_dwActivePlayerDataCount++;</P>
<P>        // Generate the ID for this player
        m_dwPlayerDataUniqueValue++;
        pPlayerData-&gt;dwID = (DWORD) ((pPlayerData-m_PlayerDatas)|(m_dwPlayerDataUniqueValue&lt;&lt;PLAYER_OBJECT_SLOT_BITS));</P>
<P>        pPlayerData-&gt;pNextInIDHashBucket = NULL;
        pPlayerData-&gt;NetID = 0;
        pPlayerData-&gt;dwNumNearbyPlayers = 0;</P>
<P>        // Insert into the "off-map" cell
        pPlayerData-&gt;fPosX = pPlayerData-&gt;fPosY = -1;
        pPlayerData-&gt;wCellX = pPlayerData-&gt;wCellY = 0xffff;
        m_OffMapLock.Enter();
        pPlayerData-&gt;pNextInCell = m_OffMapCell.pFirstPlayerData;
        m_OffMapCell.pFirstPlayerData = pPlayerData;
        m_OffMapLock.Leave();</P>
<P>        // Mark as active
        pPlayerData-&gt;bActive = TRUE;</P>
<P>        UnlockPlayerData( pPlayerData );
    }</P>
<P>    m_PlayerDataListLock.Leave();</P>
<P>    return pPlayerData;
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::DestroyPlayerData( PlayerData* pPlayerData )
{
    m_PlayerDataListLock.Enter();
    LockPlayerData( pPlayerData );</P>
<P>    // Remove the player from its cell
    RemovePlayerDataFromCell( pPlayerData );</P>
<P>    // Mark as inactive
    pPlayerData-&gt;bActive = FALSE;</P>
<P>    // Remove player from active list
    if( pPlayerData-&gt;pPrevious )
        pPlayerData-&gt;pPrevious-&gt;pNext = pPlayerData-&gt;pNext;
    if( pPlayerData-&gt;pNext )
        pPlayerData-&gt;pNext-&gt;pPrevious = pPlayerData-&gt;pPrevious;</P>
<P>    if( m_pFirstActivePlayerData == pPlayerData )
        m_pFirstActivePlayerData = pPlayerData-&gt;pNext;</P>
<P>    // Add it to the free list
    if( m_pFirstFreePlayerData )
        m_pFirstFreePlayerData-&gt;pPrevious = pPlayerData;
    pPlayerData-&gt;pNext = m_pFirstFreePlayerData;
    pPlayerData-&gt;pPrevious = NULL;
    m_pFirstFreePlayerData = pPlayerData;</P>
<P>    // Update count of players
    m_dwActivePlayerDataCount--;</P>
<P>    UnlockPlayerData( pPlayerData );
    m_PlayerDataListLock.Leave();
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::RemovePlayerDataFromCell( PlayerData* pPlayerData )
{
    // Lock the player
    LockPlayerData( pPlayerData );</P>
<P>    // Lock the cell the player is in
    ServerCell* pCell;
    if( pPlayerData-&gt;wCellX == 0xffff )
    {
        m_OffMapLock.Enter();
        pCell = &amp;m_OffMapCell;
    }
    else
    {
        LockCell( pPlayerData-&gt;wCellX, pPlayerData-&gt;wCellY );
        pCell = &amp;m_Cells;
    }</P>
<P>    // Remove it from the cell
    PlayerData* pPt = pCell-&gt;pFirstPlayerData;
    PlayerData* pPrev = NULL;
    while ( pPt )
    {
        if( pPt == pPlayerData )
        {
            if( pPrev )
                pPrev-&gt;pNextInCell = pPlayerData-&gt;pNextInCell;
            else
                pCell-&gt;pFirstPlayerData = pPlayerData-&gt;pNextInCell;</P>
<P>            pPlayerData-&gt;pNextInCell = NULL;
            break;
        }
        pPrev = pPt;
        pPt = pPt-&gt;pNextInCell;
    }</P>
<P>    // Unlock the cell
    if( pPlayerData-&gt;wCellX == 0xffff )
        m_OffMapLock.Leave();
    else
        UnlockCell( pPlayerData-&gt;wCellX, pPlayerData-&gt;wCellY );</P>
<P>    // Unlock the player
    UnlockPlayerData( pPlayerData );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnsafeRemovePlayerDataFromCell( PlayerData* pPlayerData )
{
    ServerCell* pCell = GetCell( pPlayerData );
    PlayerData* pPt  = pCell-&gt;pFirstPlayerData;
    PlayerData* pPrev = NULL;
    while ( pPt )
    {
        if( pPt == pPlayerData )
        {
            if( pPrev )
                pPrev-&gt;pNextInCell = pPlayerData-&gt;pNextInCell;
            else
                pCell-&gt;pFirstPlayerData = pPlayerData-&gt;pNextInCell;
            pPlayerData-&gt;pNextInCell = NULL;
            break;
        }
        pPrev = pPt;
        pPt = pPt-&gt;pNextInCell;
    }
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnsafeAddPlayerDataToCell( PlayerData* pPlayerData )
{
    ServerCell* pCell   = GetCell( pPlayerData );
    pPlayerData-&gt;pNextInCell = pCell-&gt;pFirstPlayerData;
    pCell-&gt;pFirstPlayerData = pPlayerData;
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::HandleClientPosPacket( DWORD dwFrom, ClientPosPacket* pClientPosPack )
{
    // Grab player for this client and lock it
    PlayerData* pFromPlayer = GetPlayerDataForID( dwFrom );
    if( pFromPlayer == NULL )
    {
        if( m_dwLogLevel &gt; 1 )
            ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Could not find data structure for this player"), pFromPlayer-&gt;NetID );
        return;
    }</P>
<P>    LockPlayerData( pFromPlayer );</P>
<P>    if( FALSE == pFromPlayer-&gt;bAllow )
    {
        if( m_dwLogLevel &gt; 0 )
            ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Got position packet from bad client.  Rejecting client"), pFromPlayer-&gt;NetID );</P>
<P>        m_pNet-&gt;RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
        UnlockPlayerData( pFromPlayer );
        return;
    }</P>
<P>    // Compute the cell the player should be in now
    DWORD newcellx = int(pClientPosPack-&gt;fX);
    DWORD newcelly = int(pClientPosPack-&gt;fY);
    DWORD oldcellx = pFromPlayer-&gt;wCellX;
    DWORD oldcelly = pFromPlayer-&gt;wCellY;</P>
<P>    // Have we moved cell?
    if( newcellx != oldcellx || newcelly != oldcelly )
    {
        // Yes, so lock the pair of cells in question
        LockCellPair( oldcellx, oldcelly, newcellx, newcelly );</P>
<P>        // Remove from old cell and add to new cell
        UnsafeRemovePlayerDataFromCell( pFromPlayer );
        pFromPlayer-&gt;wCellX = WORD(newcellx); pFromPlayer-&gt;wCellY = WORD(newcelly);
        UnsafeAddPlayerDataToCell( pFromPlayer );</P>
<P>        // Unlock cells
        UnlockCellPair( oldcellx, oldcelly, newcellx, newcelly );
    }</P>
<P>    // Update player position
    pFromPlayer-&gt;fPosX      = pClientPosPack-&gt;fX;
    pFromPlayer-&gt;fPosY      = pClientPosPack-&gt;fY;
    pFromPlayer-&gt;aCameraYaw = pClientPosPack-&gt;aCameraYaw;</P>
<P>    // Allocate space to build the reply packet, and fill in header
    DWORD dwAllocSize;
    ServerAckPacket* pSvrAckPack = NULL;</P>
<P>    // Begin by allocating a buffer sized according to
    // the current number of nearby players + 4.  This will give
    // a little room for more players to come 'near' without resize
    // the buffer.
    DWORD dwMaxPlayerStatePackets = pFromPlayer-&gt;dwNumNearbyPlayers + 4;</P>
<P>    dwAllocSize = sizeof(ServerAckPacket) + dwMaxPlayerStatePackets*sizeof(PlayerStatePacket);
    pSvrAckPack = (ServerAckPacket*) realloc( pSvrAckPack, dwAllocSize );
    if( NULL == pSvrAckPack )
    {
        // Out of mem.  Cleanup and return
        UnlockPlayerData( pFromPlayer );
        return;      
    }
    ZeroMemory( pSvrAckPack, dwAllocSize );</P>
<P>    *pSvrAckPack = ServerAckPacket(m_dwPlayerCount);
    pSvrAckPack-&gt;wPlayerStatePacketCount = 0;
    PlayerStatePacket* pChunk = (PlayerStatePacket*)(pSvrAckPack+1);</P>
<P>    // Compute range of cells we're going to scan for players to send
    DWORD minx = (newcellx &gt; 7) ? (newcellx - 7) : 0;
    DWORD miny = (newcelly &gt; 7) ? (newcelly - 7) : 0;
    DWORD maxx = (newcellx+7 &gt;= m_dwWidth) ? m_dwWidth-1 : newcellx+7;
    DWORD maxy = (newcelly+7 &gt;= m_dwHeight) ? m_dwHeight-1 : newcelly+7;</P>
<P>    // Lock that range of cells
    LockRange( minx, miny, maxx, maxy );</P>
<P>    // Scan through the cells, tagging player data onto the end of
    // our pSvrAckPacket until we run out of room
    for( DWORD y = miny; y &lt;= maxy; y++ )
    {
        for( DWORD x = minx; x &lt;= maxx; x++ )
        {
            PlayerData* pCurPlayerData = m_Cells.pFirstPlayerData;
            while ( pCurPlayerData )
            {
                if( pCurPlayerData != pFromPlayer )
                {
                    if( pSvrAckPack-&gt;wPlayerStatePacketCount &gt;= dwMaxPlayerStatePackets )
                    {
                        // Make sure pChunk is where we think it is
                        assert( (BYTE*) pChunk == (BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack-&gt;wPlayerStatePacketCount*sizeof(PlayerStatePacket)) );</P>
<P>                        // There are more than just 4 new nearby players, so resize the
                        // buffer pSvrAckPack to allow 16 more PlayerStatePacket's.
                        dwMaxPlayerStatePackets += 16;
                        dwAllocSize = sizeof(ServerAckPacket) + dwMaxPlayerStatePackets*sizeof(PlayerStatePacket);
                        ServerAckPacket* pNewSvrAckPack = NULL;
                        pNewSvrAckPack = (ServerAckPacket*) realloc( pSvrAckPack, dwAllocSize );
                        if( NULL == pNewSvrAckPack )
                        {
                            // Out of mem.  Cleanup and return
                            free( pSvrAckPack );
                            UnlockRange( minx, miny, maxx, maxy );
                            UnlockPlayerData( pFromPlayer );
                            return;      
                        }</P>
<P>                        pSvrAckPack = pNewSvrAckPack;
                        pChunk = (PlayerStatePacket*) ((BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack-&gt;wPlayerStatePacketCount*sizeof(PlayerStatePacket) ) );</P>
<P>                        // Make sure pChunk is still where its supposed to be
                        assert( (BYTE*) pChunk == (BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack-&gt;wPlayerStatePacketCount*sizeof(PlayerStatePacket)) );
                    }</P>
<P>                    pChunk-&gt;dwID       = pCurPlayerData-&gt;dwID;
                    pChunk-&gt;fX         = pCurPlayerData-&gt;fPosX;
                    pChunk-&gt;fY         = pCurPlayerData-&gt;fPosY;
                    pChunk-&gt;aCameraYaw = pCurPlayerData-&gt;aCameraYaw;
                    pChunk++;
                    pSvrAckPack-&gt;wPlayerStatePacketCount++;
                }
                pCurPlayerData = pCurPlayerData-&gt;pNextInCell;
            }
        }
    }</P>
<P>    // Update the dwNumNearbyPlayers for this player
    pFromPlayer-&gt;dwNumNearbyPlayers = pSvrAckPack-&gt;wPlayerStatePacketCount;</P>
<P>    // Unlock range of cells
    UnlockRange( minx, miny, maxx, maxy );</P>
<P>    if( m_dwLogLevel &gt; 2 )
    {
        ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Position is (%0.2f,%0.2f)"), pFromPlayer-&gt;NetID, pFromPlayer-&gt;fPosX, pFromPlayer-&gt;fPosY );
    }
    else if( m_dwLogLevel == 2 )
    {
        FLOAT fTime = DXUtil_Timer( TIMER_GETAPPTIME );
        if( fTime - pFromPlayer-&gt;fLastDisplayTime &gt; 60.0f )
        {
            ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Position is (%0.2f,%0.2f)"), pFromPlayer-&gt;NetID, pFromPlayer-&gt;fPosX, pFromPlayer-&gt;fPosY );
            pFromPlayer-&gt;fLastDisplayTime = fTime;
        }
    }</P>
<P>    // Unlock the playerdata
    UnlockPlayerData( pFromPlayer );</P>
<P>    // Send acknowledgement back to client, including list of nearby players
    DWORD acksize = sizeof(ServerAckPacket) + (pSvrAckPack-&gt;wPlayerStatePacketCount * sizeof(PlayerStatePacket));

    // Pack the buffer with dummy data.
    if(m_ClientNetConfig.wServerPackSizeArray &gt; 0)
    {
        DWORD   dwBufferSize = acksize + m_ClientNetConfig.wServerPackSizeArray;
        VOID*   pTempBuffer = 0;</P>
<P>        pTempBuffer = malloc(dwBufferSize);
        if( NULL == pTempBuffer )
        {
            //Out of memory
            DXTRACE_ERR_NOMSGBOX( TEXT("System out of Memory!"), E_OUTOFMEMORY );
            free( pSvrAckPack );
            return;
        }</P>
<P>        FillMemory(pTempBuffer, dwBufferSize, 'Z');
        memcpy(pTempBuffer, pSvrAckPack, acksize);</P>
<P>        SendPacket( dwFrom, pTempBuffer, dwBufferSize, FALSE, m_dwServerTimeout );
   
        free(pTempBuffer);
    }   
    else
    {
        SendPacket( dwFrom, pSvrAckPack, acksize, FALSE, m_dwServerTimeout );
    }</P>
<P>    free( pSvrAckPack );</P>
<P>}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::HandleClientVersionPacket( DWORD dwFrom, ClientVersionPacket* pClientVersionPack )
{
    // Grab playerdata for this client and lock it
    PlayerData* pPlayerData = GetPlayerDataForID( dwFrom );
    if( pPlayerData == NULL )
        return;
    LockPlayerData( pPlayerData );</P>
<P>    // Record the version number
    pPlayerData-&gt;dwVersion = pClientVersionPack-&gt;dwVersion;</P>
<P>    if( m_bLocalLoopback )
        pPlayerData-&gt;bAllow = TRUE;
    else
        pPlayerData-&gt;bAllow = IsClientVersionSupported( pClientVersionPack-&gt;dwVersion );</P>
<P>    if( m_dwLogLevel &gt; 0 )
        ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Client version=%d (%s)"), pPlayerData-&gt;NetID, pPlayerData-&gt;dwVersion, pPlayerData-&gt;bAllow ? TEXT("Accepted") : TEXT("Rejected") );</P>
<P>    if( FALSE == pPlayerData-&gt;bAllow )
    {
        if( m_dwLogLevel &gt; 0 )
            ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Rejecting client"), pPlayerData-&gt;NetID );</P>
<P>        m_pNet-&gt;RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
        UnlockPlayerData( pPlayerData );
        return;
    }</P>
<P>    // Unlock the playerdata
    UnlockPlayerData( pPlayerData );</P>
<P>    // Send acknowledgement to client that the client was either accepted or rejected
    ServerAckVersionPacket packet( pPlayerData-&gt;bAllow, dwFrom );
    SendPacket( dwFrom, &amp;packet, sizeof(packet), TRUE, 0 );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
BOOL CMazeServer::IsClientVersionSupported( DWORD dwClientVersion )
{
    switch( dwClientVersion )
    {
        case 107: // only v107 is supported
            return TRUE;
        default:
            return FALSE;
    }
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::HandleUnknownPacket( DWORD dwFrom, ClientPacket* pClientPack, DWORD size )
{
    if( m_dwLogLevel &gt; 1 )
        ConsolePrintf( SLINE_LOG, TEXT("ERROR! Unknown %d byte packet from player %0.8x"), size, dwFrom );</P>
<P>    m_pNet-&gt;RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD   CMazeServer::IDHash( DWORD id )
{
    DWORD   hash = ((id) + (id&gt;&gt;8) + (id&gt;&gt;16) + (id&gt;&gt;24)) &amp; (NUM_ID_HASH_BUCKETS-1);
    return hash;
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::RemovePlayerDataID( PlayerData* pPlayerData )
{
    // Hash the ID to a bucket number
    DWORD   bucket = IDHash( pPlayerData-&gt;NetID );</P>
<P>    // Lock that hash bucket
    const   DWORD   buckets_per_lock = NUM_ID_HASH_BUCKETS / NUM_ID_HASH_BUCKET_LOCKS;
    m_IDHashBucketLocks.Enter();</P>
<P>    // Loop though players in bucket until we find the right one
    PlayerData* pPt = m_pstIDHashBucket;
    PlayerData* pPrev = NULL;
    while( pPt )
    {
        if( pPt == pPlayerData )
            break;
        pPrev = pPt;
        pPt = pPt-&gt;pNextInIDHashBucket;
    }</P>
<P>    if( pPt )
    {
        if( pPrev )
            pPrev-&gt;pNextInIDHashBucket = pPt-&gt;pNextInIDHashBucket;
        else
            m_pstIDHashBucket = pPt-&gt;pNextInIDHashBucket;
        pPt-&gt;pNextInIDHashBucket = NULL;
    }</P>
<P>    // Unlock the hash bucket
    m_IDHashBucketLocks.Leave();
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::SetPlayerDataForID( DWORD id, PlayerData* pPlayerData )
{
    // Make sure this player isn't added twice to the m_pstIDHashBucket[]
    // otherwise there will be a circular reference
    PlayerData* pSearch = GetPlayerDataForID( id );
    if( pSearch != NULL )
        return;</P>
<P>    // Hash the ID to a bucket number
    DWORD   bucket = IDHash( id );</P>
<P>    // Lock that hash bucket
    const   DWORD   buckets_per_lock = NUM_ID_HASH_BUCKETS / NUM_ID_HASH_BUCKET_LOCKS;
    m_IDHashBucketLocks.Enter();</P>
<P>    // Add player onto hash bucket chain
    pPlayerData-&gt;pNextInIDHashBucket = m_pstIDHashBucket;
    m_pstIDHashBucket = pPlayerData;</P>
<P>    // Store net id in player
    pPlayerData-&gt;NetID = id;</P>
<P>    // Unlock the hash bucket
    m_IDHashBucketLocks.Leave();
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
PlayerData* CMazeServer::GetPlayerDataForID( DWORD id )
{
    // Hash the ID to a bucket number
    DWORD   bucket = IDHash( id );</P>
<P>    // Lock that hash bucket
    const   DWORD   buckets_per_lock = NUM_ID_HASH_BUCKETS / NUM_ID_HASH_BUCKET_LOCKS;
    m_IDHashBucketLocks.Enter();</P>
<P>    // Loop though players in bucket until we find the right one
    PlayerData* pPlayerData = m_pstIDHashBucket;
    while ( pPlayerData )
    {
        if( pPlayerData-&gt;NetID == id )
            break;
        pPlayerData = pPlayerData-&gt;pNextInIDHashBucket;
    }</P>
<P>    // Unlock the hash bucket
    m_IDHashBucketLocks.Leave();</P>
<P>    // Return the player we found (will be NULL if we couldn't find it)
    return pPlayerData;
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc: calls DisplayConnectionInfo for each connection in a round-robin manner
//-----------------------------------------------------------------------------
void CMazeServer::DisplayNextConnectionInfo()
{
    if( m_pNet )
    {
        // Find the player that was displayed the longest time ago, and display it.
        FLOAT fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
        PlayerData* pOldestPlayerData = NULL;
        FLOAT fOldestTime = 0.0f;</P>
<P>        m_PlayerDataListLock.Enter();</P>
<P>        PlayerData* pPlayerData = m_pFirstActivePlayerData;
        while ( pPlayerData )
        {
            if( fCurTime - pPlayerData-&gt;fLastCITime &gt; fOldestTime )
            {
                fOldestTime  = fCurTime - pPlayerData-&gt;fLastCITime;
                pOldestPlayerData = pPlayerData;
            }</P>
<P>            pPlayerData = pPlayerData-&gt;pNext;
        }</P>
<P>        // Display the player with the oldest CI field, and update its CI field.
        if( pOldestPlayerData )
        {
            ConsolePrintf( SLINE_LOG, TEXT("Displaying connection info for next player") );
            DisplayConnectionInfo( pOldestPlayerData-&gt;NetID );
            pOldestPlayerData-&gt;fLastCITime = fCurTime;
        }
        else
        {
            ConsolePrintf( SLINE_LOG, TEXT("No players found") );
        }</P>
<P>        m_PlayerDataListLock.Leave();
    }
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::PrintStats()
{
    ConsolePrintf( SLINE_LOG, TEXT("Thread count: Active=%d Avg=%.2f Max=%d"),
                                    m_wActiveThreadCount, m_fAvgThreadCount, m_wMaxThreadCount );
    ConsolePrintf( SLINE_LOG, TEXT("Thread Time: Avg=%.4f Max=%.4f(s)"),
                                    m_fAvgThreadTime, m_fMaxThreadTime );
    ConsolePrintf( SLINE_LOG, TEXT("Players online (not including server player): %d"), m_dwPlayerCount );
    ConsolePrintf( SLINE_LOG, TEXT("Peak player count: %d"), m_dwPeakPlayerCount );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::DisplayConnectionInfo( DWORD dwID )
{
    TCHAR strInfo;
    TCHAR* strEndOfLine;
    TCHAR* strStartOfLine;</P>
<P>    // Query the IOutboudNet for info about the connection to this user
    m_pNet-&gt;GetConnectionInfo( dwID, strInfo );</P>
<P>    ConsolePrintf( SLINE_LOG, TEXT("Displaying connection info for %0.8x"), dwID );
    ConsolePrintf( SLINE_LOG, TEXT("(Key: G=Guaranteed NG=Non-Guaranteed B=Bytes P=Packets)") );</P>
<P>    // Display each line seperately
    strStartOfLine = strInfo;
    while( TRUE )
    {
        strEndOfLine = _tcschr( strStartOfLine, '\n' );
        if( strEndOfLine == NULL )
            break;</P>
<P>        *strEndOfLine = 0;
        ConsolePrintf( SLINE_LOG, strStartOfLine );
        strStartOfLine = strEndOfLine + 1;
    }
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeServer::SendPacket( DWORD to, void* pData,
                                 DWORD size, BOOL reliable, DWORD dwTimeout )
{
    // Chance of forcing any packet to be delivered reliably
    if( m_Rand.Get( 100 ) &lt; m_dwServerReliableRate )
        reliable = TRUE;</P>
<P>    return m_pNet-&gt;SendPacket( to, pData, size, reliable, dwTimeout );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::SendConfigPacketToAll( ServerConfigPacket* pPacket )
{
    // If we're up and running, then send this new information to all clients
    if( m_pNet )
    {
        //Use the AllPlayers ID
        SendPacket( DPNID_ALL_PLAYERS_GROUP, pPacket, sizeof(ServerConfigPacket), TRUE, 0 );
    }
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::SetClientReliableRate( DWORD percent )
{
    // Update client config, and build packet containing that data
    m_ClientNetConfigLock.Enter();
    m_ClientNetConfig.ubReliableRate = BYTE(percent);
    ServerConfigPacket packet( m_ClientNetConfig );
    m_ClientNetConfigLock.Leave();</P>
<P>    SendConfigPacketToAll( &amp;packet );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::SetClientUpdateRate( DWORD rate )
{
    // Update client config, and build packet containing that data
    m_ClientNetConfigLock.Enter();
    m_ClientNetConfig.wUpdateRate = WORD(rate);
    ServerConfigPacket  packet( m_ClientNetConfig );
    m_ClientNetConfigLock.Leave();</P>
<P>    SendConfigPacketToAll( &amp;packet );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::SetClientTimeout( DWORD timeout )
{
    // Update client config, and build packet containing that data
    m_ClientNetConfigLock.Enter();
    m_ClientNetConfig.wTimeout = WORD(timeout);
    ServerConfigPacket  packet( m_ClientNetConfig );
    m_ClientNetConfigLock.Leave();</P>
<P>    SendConfigPacketToAll( &amp;packet );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::SetClientPackSize( DWORD size )
{
    // Update client config, and build packet containing that data
    m_ClientNetConfigLock.Enter();
   
    m_ClientNetConfig.ubClientPackIndex++; //Increase index and verify location in array.
    if(m_ClientNetConfig.ubClientPackIndex &gt;= PACK_ARRAY_SIZE)   
        m_ClientNetConfig.ubClientPackIndex = 0;</P>
<P>    m_ClientNetConfig.wClientPackSizeArray = WORD(size);
    ServerConfigPacket packet( m_ClientNetConfig );
    m_ClientNetConfigLock.Leave();</P>
<P>    SendConfigPacketToAll( &amp;packet );
}</P>

<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::SetServerPackSize( DWORD size )
{
    // Update client config, and build packet containing that data
    m_ClientNetConfigLock.Enter();
   
    m_ClientNetConfig.ubServerPackIndex++; //Increase index and verify location in array.
    if(m_ClientNetConfig.ubServerPackIndex &gt;= PACK_ARRAY_SIZE)   
        m_ClientNetConfig.ubServerPackIndex = 0;</P>
<P>    m_ClientNetConfig.wServerPackSizeArray = WORD(size);
    ServerConfigPacket packet( m_ClientNetConfig );
    m_ClientNetConfigLock.Leave();</P>
<P>    SendConfigPacketToAll( &amp;packet );
}</P>
<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::SetClientThreadWait( DWORD dwThreadWait )
{
    // Update client config, and build packet containing that data
    m_ClientNetConfigLock.Enter();
   
    m_ClientNetConfig.dwThreadWait = dwThreadWait;
    ServerConfigPacket packet( m_ClientNetConfig );
    m_ClientNetConfigLock.Leave();</P>
<P>    SendConfigPacketToAll( &amp;packet );
}</P></DIV>
页: [1]
查看完整版本: [分享]MazeServer源码示例