[分享]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 <windows.h>
#include <d3dx.h>
#include <stdio.h>
#include <math.h>
#include <mmsystem.h>
#include <dplay8.h>
#include <dpaddr.h>
#include <dxerr8.h>
#include "DXUtil.h"
#include "MazeServer.h"
#include "Packets.h"
#include "Maze.h"
#include <malloc.h>
#include <tchar.h></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 < 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->GetWidth();
m_dwHeight = m_pMaze->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 > SERVER_MAX_WIDTH || m_dwHeight > 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 >>= 1) )
m_dwMazeXShift++;</P>
<P> scale = m_dwHeight / LOCK_GRID_SIZE;
m_dwMazeYShift = 0;
while ( (scale >>= 1) )
m_dwMazeYShift++;</P>
<P> if( ((DWORD(LOCK_GRID_SIZE) << m_dwMazeXShift) != m_dwWidth) ||
((DWORD(LOCK_GRID_SIZE) << 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 < MAX_PLAYER_OBJECTS-1; i++ )
{
m_PlayerDatas.pNext = &m_PlayerDatas;
m_PlayerDatas.pPrevious = &m_PlayerDatas;
}</P>
<P> m_PlayerDatas.pNext = &m_PlayerDatas;
m_PlayerDatas.pPrevious = &m_PlayerDatas;
m_dwActivePlayerDataCount = 0;
m_dwPlayerDataUniqueValue = 0;</P>
<P> // Initialise the cells
ZeroMemory( m_Cells, sizeof(m_Cells) );
ZeroMemory( &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>>m_dwMazeXShift, y1>>m_dwMazeYShift ,
x2>>m_dwMazeXShift, y2>>m_dwMazeYShift );
}</P>
<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnlockRange( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
{
m_LockGrid.UnlockRange( x1>>m_dwMazeXShift, y1>>m_dwMazeYShift ,
x2>>m_dwMazeXShift, y2>>m_dwMazeYShift );
}</P>
<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::LockCell( DWORD x, DWORD y )
{
if( x == 0xffff )
m_OffMapLock.Enter();
else
m_LockGrid.LockCell(x>>m_dwMazeXShift,y>>m_dwMazeYShift);
}</P>
<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnlockCell( DWORD x, DWORD y )
{
if( x == 0xffff )
m_OffMapLock.Leave();
else
m_LockGrid.UnlockCell(x>>m_dwMazeXShift,y>>m_dwMazeYShift);
}</P>
<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::LockCellPair( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
{
if( x1 == x2 && y1 == y2 )
{
if( x1 != 0xffff && y2 != 0xffff )
LockCell( x1, y1 );
else
m_OffMapLock.Enter();</P>
<P> return;
}</P>
<P> DWORD x1shift = x1>>m_dwMazeXShift;
DWORD x2shift = x2>>m_dwMazeXShift;
DWORD y1shift = y1>>m_dwMazeYShift;
DWORD y2shift = y2>>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 && y1 == y2 )
{
if( x1 != 0xffff && y2 != 0xffff )
UnlockCell( x1, y1 );
else
m_OffMapLock.Leave();</P>
<P> return;
}</P>
<P> DWORD x1shift = x1>>m_dwMazeXShift;
DWORD x2shift = x2>>m_dwMazeXShift;
DWORD y1shift = y1>>m_dwMazeYShift;
DWORD y2shift = y2>>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 > 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 > 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, &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 > 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 > 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->wType )
{
case PACKETTYPE_CLIENT_POS:
// Check to see if the packet has a valid size. Including
// the custom pack size.
if( size < 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->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->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 > 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 > 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 >= 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->pPrevious )
pPlayerData->pPrevious->pNext = pPlayerData->pNext;
if( pPlayerData->pNext )
pPlayerData->pNext->pPrevious = pPlayerData->pPrevious;
m_pFirstFreePlayerData = pPlayerData->pNext;</P>
<P> // Add it to the active list
if( m_pFirstActivePlayerData )
m_pFirstActivePlayerData->pPrevious = pPlayerData;
pPlayerData->pNext = m_pFirstActivePlayerData;
pPlayerData->pPrevious = NULL;
m_pFirstActivePlayerData = pPlayerData;</P>
<P> // Update count of players
m_dwActivePlayerDataCount++;</P>
<P> // Generate the ID for this player
m_dwPlayerDataUniqueValue++;
pPlayerData->dwID = (DWORD) ((pPlayerData-m_PlayerDatas)|(m_dwPlayerDataUniqueValue<<PLAYER_OBJECT_SLOT_BITS));</P>
<P> pPlayerData->pNextInIDHashBucket = NULL;
pPlayerData->NetID = 0;
pPlayerData->dwNumNearbyPlayers = 0;</P>
<P> // Insert into the "off-map" cell
pPlayerData->fPosX = pPlayerData->fPosY = -1;
pPlayerData->wCellX = pPlayerData->wCellY = 0xffff;
m_OffMapLock.Enter();
pPlayerData->pNextInCell = m_OffMapCell.pFirstPlayerData;
m_OffMapCell.pFirstPlayerData = pPlayerData;
m_OffMapLock.Leave();</P>
<P> // Mark as active
pPlayerData->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->bActive = FALSE;</P>
<P> // Remove player from active list
if( pPlayerData->pPrevious )
pPlayerData->pPrevious->pNext = pPlayerData->pNext;
if( pPlayerData->pNext )
pPlayerData->pNext->pPrevious = pPlayerData->pPrevious;</P>
<P> if( m_pFirstActivePlayerData == pPlayerData )
m_pFirstActivePlayerData = pPlayerData->pNext;</P>
<P> // Add it to the free list
if( m_pFirstFreePlayerData )
m_pFirstFreePlayerData->pPrevious = pPlayerData;
pPlayerData->pNext = m_pFirstFreePlayerData;
pPlayerData->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->wCellX == 0xffff )
{
m_OffMapLock.Enter();
pCell = &m_OffMapCell;
}
else
{
LockCell( pPlayerData->wCellX, pPlayerData->wCellY );
pCell = &m_Cells;
}</P>
<P> // Remove it from the cell
PlayerData* pPt = pCell->pFirstPlayerData;
PlayerData* pPrev = NULL;
while ( pPt )
{
if( pPt == pPlayerData )
{
if( pPrev )
pPrev->pNextInCell = pPlayerData->pNextInCell;
else
pCell->pFirstPlayerData = pPlayerData->pNextInCell;</P>
<P> pPlayerData->pNextInCell = NULL;
break;
}
pPrev = pPt;
pPt = pPt->pNextInCell;
}</P>
<P> // Unlock the cell
if( pPlayerData->wCellX == 0xffff )
m_OffMapLock.Leave();
else
UnlockCell( pPlayerData->wCellX, pPlayerData->wCellY );</P>
<P> // Unlock the player
UnlockPlayerData( pPlayerData );
}</P>
<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnsafeRemovePlayerDataFromCell( PlayerData* pPlayerData )
{
ServerCell* pCell = GetCell( pPlayerData );
PlayerData* pPt = pCell->pFirstPlayerData;
PlayerData* pPrev = NULL;
while ( pPt )
{
if( pPt == pPlayerData )
{
if( pPrev )
pPrev->pNextInCell = pPlayerData->pNextInCell;
else
pCell->pFirstPlayerData = pPlayerData->pNextInCell;
pPlayerData->pNextInCell = NULL;
break;
}
pPrev = pPt;
pPt = pPt->pNextInCell;
}
}</P>
<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnsafeAddPlayerDataToCell( PlayerData* pPlayerData )
{
ServerCell* pCell = GetCell( pPlayerData );
pPlayerData->pNextInCell = pCell->pFirstPlayerData;
pCell->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 > 1 )
ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Could not find data structure for this player"), pFromPlayer->NetID );
return;
}</P>
<P> LockPlayerData( pFromPlayer );</P>
<P> if( FALSE == pFromPlayer->bAllow )
{
if( m_dwLogLevel > 0 )
ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Got position packet from bad client. Rejecting client"), pFromPlayer->NetID );</P>
<P> m_pNet->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->fX);
DWORD newcelly = int(pClientPosPack->fY);
DWORD oldcellx = pFromPlayer->wCellX;
DWORD oldcelly = pFromPlayer->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->wCellX = WORD(newcellx); pFromPlayer->wCellY = WORD(newcelly);
UnsafeAddPlayerDataToCell( pFromPlayer );</P>
<P> // Unlock cells
UnlockCellPair( oldcellx, oldcelly, newcellx, newcelly );
}</P>
<P> // Update player position
pFromPlayer->fPosX = pClientPosPack->fX;
pFromPlayer->fPosY = pClientPosPack->fY;
pFromPlayer->aCameraYaw = pClientPosPack->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->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->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 > 7) ? (newcellx - 7) : 0;
DWORD miny = (newcelly > 7) ? (newcelly - 7) : 0;
DWORD maxx = (newcellx+7 >= m_dwWidth) ? m_dwWidth-1 : newcellx+7;
DWORD maxy = (newcelly+7 >= 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 <= maxy; y++ )
{
for( DWORD x = minx; x <= maxx; x++ )
{
PlayerData* pCurPlayerData = m_Cells.pFirstPlayerData;
while ( pCurPlayerData )
{
if( pCurPlayerData != pFromPlayer )
{
if( pSvrAckPack->wPlayerStatePacketCount >= dwMaxPlayerStatePackets )
{
// Make sure pChunk is where we think it is
assert( (BYTE*) pChunk == (BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack->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->wPlayerStatePacketCount*sizeof(PlayerStatePacket) ) );</P>
<P> // Make sure pChunk is still where its supposed to be
assert( (BYTE*) pChunk == (BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack->wPlayerStatePacketCount*sizeof(PlayerStatePacket)) );
}</P>
<P> pChunk->dwID = pCurPlayerData->dwID;
pChunk->fX = pCurPlayerData->fPosX;
pChunk->fY = pCurPlayerData->fPosY;
pChunk->aCameraYaw = pCurPlayerData->aCameraYaw;
pChunk++;
pSvrAckPack->wPlayerStatePacketCount++;
}
pCurPlayerData = pCurPlayerData->pNextInCell;
}
}
}</P>
<P> // Update the dwNumNearbyPlayers for this player
pFromPlayer->dwNumNearbyPlayers = pSvrAckPack->wPlayerStatePacketCount;</P>
<P> // Unlock range of cells
UnlockRange( minx, miny, maxx, maxy );</P>
<P> if( m_dwLogLevel > 2 )
{
ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Position is (%0.2f,%0.2f)"), pFromPlayer->NetID, pFromPlayer->fPosX, pFromPlayer->fPosY );
}
else if( m_dwLogLevel == 2 )
{
FLOAT fTime = DXUtil_Timer( TIMER_GETAPPTIME );
if( fTime - pFromPlayer->fLastDisplayTime > 60.0f )
{
ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Position is (%0.2f,%0.2f)"), pFromPlayer->NetID, pFromPlayer->fPosX, pFromPlayer->fPosY );
pFromPlayer->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->wPlayerStatePacketCount * sizeof(PlayerStatePacket));
// Pack the buffer with dummy data.
if(m_ClientNetConfig.wServerPackSizeArray > 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->dwVersion = pClientVersionPack->dwVersion;</P>
<P> if( m_bLocalLoopback )
pPlayerData->bAllow = TRUE;
else
pPlayerData->bAllow = IsClientVersionSupported( pClientVersionPack->dwVersion );</P>
<P> if( m_dwLogLevel > 0 )
ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Client version=%d (%s)"), pPlayerData->NetID, pPlayerData->dwVersion, pPlayerData->bAllow ? TEXT("Accepted") : TEXT("Rejected") );</P>
<P> if( FALSE == pPlayerData->bAllow )
{
if( m_dwLogLevel > 0 )
ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Rejecting client"), pPlayerData->NetID );</P>
<P> m_pNet->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->bAllow, dwFrom );
SendPacket( dwFrom, &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 > 1 )
ConsolePrintf( SLINE_LOG, TEXT("ERROR! Unknown %d byte packet from player %0.8x"), size, dwFrom );</P>
<P> m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
}</P>
<P>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD CMazeServer::IDHash( DWORD id )
{
DWORD hash = ((id) + (id>>8) + (id>>16) + (id>>24)) & (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->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->pNextInIDHashBucket;
}</P>
<P> if( pPt )
{
if( pPrev )
pPrev->pNextInIDHashBucket = pPt->pNextInIDHashBucket;
else
m_pstIDHashBucket = pPt->pNextInIDHashBucket;
pPt->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->pNextInIDHashBucket = m_pstIDHashBucket;
m_pstIDHashBucket = pPlayerData;</P>
<P> // Store net id in player
pPlayerData->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->NetID == id )
break;
pPlayerData = pPlayerData->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->fLastCITime > fOldestTime )
{
fOldestTime = fCurTime - pPlayerData->fLastCITime;
pOldestPlayerData = pPlayerData;
}</P>
<P> pPlayerData = pPlayerData->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->NetID );
pOldestPlayerData->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->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 ) < m_dwServerReliableRate )
reliable = TRUE;</P>
<P> return m_pNet->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( &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( &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( &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 >= PACK_ARRAY_SIZE)
m_ClientNetConfig.ubClientPackIndex = 0;</P>
<P> m_ClientNetConfig.wClientPackSizeArray = WORD(size);
ServerConfigPacket packet( m_ClientNetConfig );
m_ClientNetConfigLock.Leave();</P>
<P> SendConfigPacketToAll( &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 >= PACK_ARRAY_SIZE)
m_ClientNetConfig.ubServerPackIndex = 0;</P>
<P> m_ClientNetConfig.wServerPackSizeArray = WORD(size);
ServerConfigPacket packet( m_ClientNetConfig );
m_ClientNetConfigLock.Leave();</P>
<P> SendConfigPacketToAll( &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( &packet );
}</P></DIV>
页:
[1]