Files
Client/Library/dxx8/samples/Multimedia/DirectPlay/Maze/MazeCommon/MazeServer.cpp
LGram16 e067522598 Initial commit: ROW Client source code
Game client codebase including:
- CharacterActionControl: Character and creature management
- GlobalScript: Network, items, skills, quests, utilities
- RYLClient: Main client application with GUI and event handlers
- Engine: 3D rendering engine (RYLGL)
- MemoryManager: Custom memory allocation
- Library: Third-party dependencies (DirectX, boost, etc.)
- Tools: Development utilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 16:24:34 +09:00

1320 lines
40 KiB
C++

//----------------------------------------------------------------------------
// 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>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CMazeServer::CMazeServer()
{
m_dwPlayerCount = 0;
m_wActiveThreadCount = 0;
m_wMaxThreadCount = 0;
m_fAvgThreadCount = 0;
m_fAvgThreadTime = 0;
m_fMaxThreadTime = 0;
m_dwServerReliableRate = 15;
m_dwServerTimeout = 150;
m_dwLogLevel = 2;
m_pMaze = NULL;
m_ClientNetConfig.ubReliableRate = 15;
m_ClientNetConfig.wUpdateRate = 150;
m_ClientNetConfig.wTimeout = 150;
m_ClientNetConfig.dwThreadWait = 0;
m_ClientNetConfig.ubClientPackIndex = 0;
m_ClientNetConfig.ubServerPackIndex = 0;
for(WORD x = 0; x < PACK_ARRAY_SIZE; x++)
{
m_ClientNetConfig.wClientPackSizeArray[x] = 0;
m_ClientNetConfig.wServerPackSizeArray[x] = 0;
}
}
//-----------------------------------------------------------------------------
// 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 );
// Grab height and width of maze
m_dwWidth = m_pMaze->GetWidth();
m_dwHeight = m_pMaze->GetHeight();
m_ClientNetConfig.dwMazeWidth = m_dwWidth;
m_ClientNetConfig.dwMazeHeight = m_dwHeight;
// 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 );
DWORD scale = m_dwWidth / LOCK_GRID_SIZE;
m_dwMazeXShift = 0;
while ( (scale >>= 1) )
m_dwMazeXShift++;
scale = m_dwHeight / LOCK_GRID_SIZE;
m_dwMazeYShift = 0;
while ( (scale >>= 1) )
m_dwMazeYShift++;
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 );
// 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[i].pNext = &m_PlayerDatas[i+1];
m_PlayerDatas[i].pPrevious = &m_PlayerDatas[i-1];
}
m_PlayerDatas[0].pNext = &m_PlayerDatas[1];
m_PlayerDatas[MAX_PLAYER_OBJECTS-1].pPrevious = &m_PlayerDatas[MAX_PLAYER_OBJECTS-2];
m_dwActivePlayerDataCount = 0;
m_dwPlayerDataUniqueValue = 0;
// Initialise the cells
ZeroMemory( m_Cells, sizeof(m_Cells) );
ZeroMemory( &m_OffMapCell, sizeof(m_OffMapCell) );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::Shutdown()
{
}
//-----------------------------------------------------------------------------
// 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 );
}
//-----------------------------------------------------------------------------
// 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 );
}
//-----------------------------------------------------------------------------
// 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);
}
//-----------------------------------------------------------------------------
// 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);
}
//-----------------------------------------------------------------------------
// 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();
return;
}
DWORD x1shift = x1>>m_dwMazeXShift;
DWORD x2shift = x2>>m_dwMazeXShift;
DWORD y1shift = y1>>m_dwMazeYShift;
DWORD y2shift = y2>>m_dwMazeYShift;
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);
}
}
//-----------------------------------------------------------------------------
// 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();
return;
}
DWORD x1shift = x1>>m_dwMazeXShift;
DWORD x2shift = x2>>m_dwMazeXShift;
DWORD y1shift = y1>>m_dwMazeYShift;
DWORD y2shift = y2>>m_dwMazeYShift;
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);
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::OnAddConnection( DWORD id )
{
m_AddRemoveLock.Enter();
// 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 );
}
if( m_dwPlayerCount > m_dwPeakPlayerCount )
m_dwPeakPlayerCount = m_dwPlayerCount;
// 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;
}
// Store that pointer as local player data
SetPlayerDataForID( id, pPlayerData );
// Grab net config into to send to client
m_ClientNetConfigLock.Enter();
ServerConfigPacket packet( m_ClientNetConfig );
m_ClientNetConfigLock.Leave();
// Send it
SendPacket( id, &packet, sizeof(packet), TRUE, 0 );
m_AddRemoveLock.Leave();
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::OnRemoveConnection( DWORD id )
{
m_AddRemoveLock.Enter();
// Decrement count of players
m_dwPlayerCount--;
if( m_dwLogLevel > 0 )
{
ConsolePrintf( SLINE_LOG, TEXT("Removing player DPNID %0.8x"), id );
ConsolePrintf( SLINE_LOG, TEXT("Players connected = %d"), m_dwPlayerCount );
}
// Find playerdata for this client
PlayerData* pPlayerData = GetPlayerDataForID( id );
if( pPlayerData != NULL )
{
// Destroy it
RemovePlayerDataID( pPlayerData );
DestroyPlayerData( pPlayerData );
}
m_AddRemoveLock.Leave();
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeServer::OnPacket( DWORD dwFrom, void* pData, DWORD size )
{
BOOL fFoundSize = FALSE;
// Increment the number of thread we have in this process.
m_csThreadCountLock.Enter();
//Get the start time of when we entered the message handler.
FLOAT fStartTime = DXUtil_Timer( TIMER_GETAPPTIME );
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();
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;
// If valid sized packet, handle the position.
if(fFoundSize)
HandleClientPosPacket( dwFrom, (ClientPosPacket*)pClientPack );
else
m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
break;
case PACKETTYPE_CLIENT_VERSION:
if( size == sizeof(ClientVersionPacket) )
HandleClientVersionPacket( dwFrom, (ClientVersionPacket*)pClientPack );
else
m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
break;
case PACKETTYPE_SERVER_CONFIG:
// Server config packet sent to all users (including server). Just ignore and continue.
break;
default:
HandleUnknownPacket( dwFrom, pClientPack, size );
break;
}
//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();
m_wActiveThreadCount--;
FLOAT fDiffTime = (DXUtil_Timer( TIMER_GETAPPTIME ) - fStartTime) - m_fAvgThreadTime;
m_fAvgThreadTime += fDiffTime/32;
//Get the Max time in the thread.
if ( fDiffTime > m_fMaxThreadTime )
{
m_fMaxThreadTime = fDiffTime;
}
m_csThreadCountLock.Leave();
return S_OK;
}
//-----------------------------------------------------------------------------
// 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[ubPackLocation])
{
for( --ubPackLocation; ubPackLocation != m_ClientNetConfig.ubClientPackIndex; ubPackLocation--)
{
if(dwSize == m_ClientNetConfig.wClientPackSizeArray[ubPackLocation])
{
// Found valid size in the array.
fFoundSize = TRUE;
break;
}
if(ubPackLocation >= PACK_ARRAY_SIZE) ubPackLocation = PACK_ARRAY_SIZE; //Wrap the array.
}
}
else
{
fFoundSize = TRUE;
}
return fFoundSize;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::OnSessionLost( DWORD dwReason )
{
ConsolePrintf( SLINE_LOG, TEXT("ERROR! Session was lost") );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
PlayerData* CMazeServer::CreatePlayerData()
{
m_PlayerDataListLock.Enter();
// Grab first free player in the list
PlayerData* pPlayerData = m_pFirstFreePlayerData;
if( pPlayerData )
{
LockPlayerData( pPlayerData );
// 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;
// Add it to the active list
if( m_pFirstActivePlayerData )
m_pFirstActivePlayerData->pPrevious = pPlayerData;
pPlayerData->pNext = m_pFirstActivePlayerData;
pPlayerData->pPrevious = NULL;
m_pFirstActivePlayerData = pPlayerData;
// Update count of players
m_dwActivePlayerDataCount++;
// Generate the ID for this player
m_dwPlayerDataUniqueValue++;
pPlayerData->dwID = (DWORD) ((pPlayerData-m_PlayerDatas)|(m_dwPlayerDataUniqueValue<<PLAYER_OBJECT_SLOT_BITS));
pPlayerData->pNextInIDHashBucket = NULL;
pPlayerData->NetID = 0;
pPlayerData->dwNumNearbyPlayers = 0;
// 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();
// Mark as active
pPlayerData->bActive = TRUE;
UnlockPlayerData( pPlayerData );
}
m_PlayerDataListLock.Leave();
return pPlayerData;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::DestroyPlayerData( PlayerData* pPlayerData )
{
m_PlayerDataListLock.Enter();
LockPlayerData( pPlayerData );
// Remove the player from its cell
RemovePlayerDataFromCell( pPlayerData );
// Mark as inactive
pPlayerData->bActive = FALSE;
// Remove player from active list
if( pPlayerData->pPrevious )
pPlayerData->pPrevious->pNext = pPlayerData->pNext;
if( pPlayerData->pNext )
pPlayerData->pNext->pPrevious = pPlayerData->pPrevious;
if( m_pFirstActivePlayerData == pPlayerData )
m_pFirstActivePlayerData = pPlayerData->pNext;
// Add it to the free list
if( m_pFirstFreePlayerData )
m_pFirstFreePlayerData->pPrevious = pPlayerData;
pPlayerData->pNext = m_pFirstFreePlayerData;
pPlayerData->pPrevious = NULL;
m_pFirstFreePlayerData = pPlayerData;
// Update count of players
m_dwActivePlayerDataCount--;
UnlockPlayerData( pPlayerData );
m_PlayerDataListLock.Leave();
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::RemovePlayerDataFromCell( PlayerData* pPlayerData )
{
// Lock the player
LockPlayerData( pPlayerData );
// 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[pPlayerData->wCellY][pPlayerData->wCellX];
}
// 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;
pPlayerData->pNextInCell = NULL;
break;
}
pPrev = pPt;
pPt = pPt->pNextInCell;
}
// Unlock the cell
if( pPlayerData->wCellX == 0xffff )
m_OffMapLock.Leave();
else
UnlockCell( pPlayerData->wCellX, pPlayerData->wCellY );
// Unlock the player
UnlockPlayerData( pPlayerData );
}
//-----------------------------------------------------------------------------
// 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;
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::UnsafeAddPlayerDataToCell( PlayerData* pPlayerData )
{
ServerCell* pCell = GetCell( pPlayerData );
pPlayerData->pNextInCell = pCell->pFirstPlayerData;
pCell->pFirstPlayerData = pPlayerData;
}
//-----------------------------------------------------------------------------
// 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;
}
LockPlayerData( pFromPlayer );
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 );
m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
UnlockPlayerData( pFromPlayer );
return;
}
// 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;
// Have we moved cell?
if( newcellx != oldcellx || newcelly != oldcelly )
{
// Yes, so lock the pair of cells in question
LockCellPair( oldcellx, oldcelly, newcellx, newcelly );
// Remove from old cell and add to new cell
UnsafeRemovePlayerDataFromCell( pFromPlayer );
pFromPlayer->wCellX = WORD(newcellx); pFromPlayer->wCellY = WORD(newcelly);
UnsafeAddPlayerDataToCell( pFromPlayer );
// Unlock cells
UnlockCellPair( oldcellx, oldcelly, newcellx, newcelly );
}
// Update player position
pFromPlayer->fPosX = pClientPosPack->fX;
pFromPlayer->fPosY = pClientPosPack->fY;
pFromPlayer->aCameraYaw = pClientPosPack->aCameraYaw;
// Allocate space to build the reply packet, and fill in header
DWORD dwAllocSize;
ServerAckPacket* pSvrAckPack = NULL;
// 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;
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 );
*pSvrAckPack = ServerAckPacket(m_dwPlayerCount);
pSvrAckPack->wPlayerStatePacketCount = 0;
PlayerStatePacket* pChunk = (PlayerStatePacket*)(pSvrAckPack+1);
// 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;
// Lock that range of cells
LockRange( minx, miny, maxx, maxy );
// 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[y][x].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)) );
// 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;
}
pSvrAckPack = pNewSvrAckPack;
pChunk = (PlayerStatePacket*) ((BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack->wPlayerStatePacketCount*sizeof(PlayerStatePacket) ) );
// Make sure pChunk is still where its supposed to be
assert( (BYTE*) pChunk == (BYTE*) ((BYTE*)pSvrAckPack + sizeof(ServerAckPacket) + pSvrAckPack->wPlayerStatePacketCount*sizeof(PlayerStatePacket)) );
}
pChunk->dwID = pCurPlayerData->dwID;
pChunk->fX = pCurPlayerData->fPosX;
pChunk->fY = pCurPlayerData->fPosY;
pChunk->aCameraYaw = pCurPlayerData->aCameraYaw;
pChunk++;
pSvrAckPack->wPlayerStatePacketCount++;
}
pCurPlayerData = pCurPlayerData->pNextInCell;
}
}
}
// Update the dwNumNearbyPlayers for this player
pFromPlayer->dwNumNearbyPlayers = pSvrAckPack->wPlayerStatePacketCount;
// Unlock range of cells
UnlockRange( minx, miny, maxx, maxy );
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;
}
}
// Unlock the playerdata
UnlockPlayerData( pFromPlayer );
// 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[m_ClientNetConfig.ubServerPackIndex] > 0)
{
DWORD dwBufferSize = acksize + m_ClientNetConfig.wServerPackSizeArray[m_ClientNetConfig.ubServerPackIndex];
VOID* pTempBuffer = 0;
pTempBuffer = malloc(dwBufferSize);
if( NULL == pTempBuffer )
{
//Out of memory
DXTRACE_ERR_NOMSGBOX( TEXT("System out of Memory!"), E_OUTOFMEMORY );
free( pSvrAckPack );
return;
}
FillMemory(pTempBuffer, dwBufferSize, 'Z');
memcpy(pTempBuffer, pSvrAckPack, acksize);
SendPacket( dwFrom, pTempBuffer, dwBufferSize, FALSE, m_dwServerTimeout );
free(pTempBuffer);
}
else
{
SendPacket( dwFrom, pSvrAckPack, acksize, FALSE, m_dwServerTimeout );
}
free( pSvrAckPack );
}
//-----------------------------------------------------------------------------
// 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 );
// Record the version number
pPlayerData->dwVersion = pClientVersionPack->dwVersion;
if( m_bLocalLoopback )
pPlayerData->bAllow = TRUE;
else
pPlayerData->bAllow = IsClientVersionSupported( pClientVersionPack->dwVersion );
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") );
if( FALSE == pPlayerData->bAllow )
{
if( m_dwLogLevel > 0 )
ConsolePrintf( SLINE_LOG, TEXT("DPNID %0.8x: Rejecting client"), pPlayerData->NetID );
m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
UnlockPlayerData( pPlayerData );
return;
}
// Unlock the playerdata
UnlockPlayerData( pPlayerData );
// Send acknowledgement to client that the client was either accepted or rejected
ServerAckVersionPacket packet( pPlayerData->bAllow, dwFrom );
SendPacket( dwFrom, &packet, sizeof(packet), TRUE, 0 );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
BOOL CMazeServer::IsClientVersionSupported( DWORD dwClientVersion )
{
switch( dwClientVersion )
{
case 107: // only v107 is supported
return TRUE;
default:
return FALSE;
}
}
//-----------------------------------------------------------------------------
// 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 );
m_pNet->RejectClient( dwFrom, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD CMazeServer::IDHash( DWORD id )
{
DWORD hash = ((id) + (id>>8) + (id>>16) + (id>>24)) & (NUM_ID_HASH_BUCKETS-1);
return hash;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::RemovePlayerDataID( PlayerData* pPlayerData )
{
// Hash the ID to a bucket number
DWORD bucket = IDHash( pPlayerData->NetID );
// Lock that hash bucket
const DWORD buckets_per_lock = NUM_ID_HASH_BUCKETS / NUM_ID_HASH_BUCKET_LOCKS;
m_IDHashBucketLocks[bucket/buckets_per_lock].Enter();
// Loop though players in bucket until we find the right one
PlayerData* pPt = m_pstIDHashBucket[bucket];
PlayerData* pPrev = NULL;
while( pPt )
{
if( pPt == pPlayerData )
break;
pPrev = pPt;
pPt = pPt->pNextInIDHashBucket;
}
if( pPt )
{
if( pPrev )
pPrev->pNextInIDHashBucket = pPt->pNextInIDHashBucket;
else
m_pstIDHashBucket[bucket] = pPt->pNextInIDHashBucket;
pPt->pNextInIDHashBucket = NULL;
}
// Unlock the hash bucket
m_IDHashBucketLocks[bucket/buckets_per_lock].Leave();
}
//-----------------------------------------------------------------------------
// 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;
// Hash the ID to a bucket number
DWORD bucket = IDHash( id );
// Lock that hash bucket
const DWORD buckets_per_lock = NUM_ID_HASH_BUCKETS / NUM_ID_HASH_BUCKET_LOCKS;
m_IDHashBucketLocks[bucket/buckets_per_lock].Enter();
// Add player onto hash bucket chain
pPlayerData->pNextInIDHashBucket = m_pstIDHashBucket[bucket];
m_pstIDHashBucket[bucket] = pPlayerData;
// Store net id in player
pPlayerData->NetID = id;
// Unlock the hash bucket
m_IDHashBucketLocks[bucket/buckets_per_lock].Leave();
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
PlayerData* CMazeServer::GetPlayerDataForID( DWORD id )
{
// Hash the ID to a bucket number
DWORD bucket = IDHash( id );
// Lock that hash bucket
const DWORD buckets_per_lock = NUM_ID_HASH_BUCKETS / NUM_ID_HASH_BUCKET_LOCKS;
m_IDHashBucketLocks[bucket/buckets_per_lock].Enter();
// Loop though players in bucket until we find the right one
PlayerData* pPlayerData = m_pstIDHashBucket[bucket];
while ( pPlayerData )
{
if( pPlayerData->NetID == id )
break;
pPlayerData = pPlayerData->pNextInIDHashBucket;
}
// Unlock the hash bucket
m_IDHashBucketLocks[bucket/buckets_per_lock].Leave();
// Return the player we found (will be NULL if we couldn't find it)
return pPlayerData;
}
//-----------------------------------------------------------------------------
// 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;
m_PlayerDataListLock.Enter();
PlayerData* pPlayerData = m_pFirstActivePlayerData;
while ( pPlayerData )
{
if( fCurTime - pPlayerData->fLastCITime > fOldestTime )
{
fOldestTime = fCurTime - pPlayerData->fLastCITime;
pOldestPlayerData = pPlayerData;
}
pPlayerData = pPlayerData->pNext;
}
// 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") );
}
m_PlayerDataListLock.Leave();
}
}
//-----------------------------------------------------------------------------
// 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 );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeServer::DisplayConnectionInfo( DWORD dwID )
{
TCHAR strInfo[5000];
TCHAR* strEndOfLine;
TCHAR* strStartOfLine;
// Query the IOutboudNet for info about the connection to this user
m_pNet->GetConnectionInfo( dwID, strInfo );
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)") );
// Display each line seperately
strStartOfLine = strInfo;
while( TRUE )
{
strEndOfLine = _tcschr( strStartOfLine, '\n' );
if( strEndOfLine == NULL )
break;
*strEndOfLine = 0;
ConsolePrintf( SLINE_LOG, strStartOfLine );
strStartOfLine = strEndOfLine + 1;
}
}
//-----------------------------------------------------------------------------
// 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;
return m_pNet->SendPacket( to, pData, size, reliable, dwTimeout );
}
//-----------------------------------------------------------------------------
// 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 );
}
}
//-----------------------------------------------------------------------------
// 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();
SendConfigPacketToAll( &packet );
}
//-----------------------------------------------------------------------------
// 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();
SendConfigPacketToAll( &packet );
}
//-----------------------------------------------------------------------------
// 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();
SendConfigPacketToAll( &packet );
}
//-----------------------------------------------------------------------------
// 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;
m_ClientNetConfig.wClientPackSizeArray[m_ClientNetConfig.ubClientPackIndex] = WORD(size);
ServerConfigPacket packet( m_ClientNetConfig );
m_ClientNetConfigLock.Leave();
SendConfigPacketToAll( &packet );
}
//-----------------------------------------------------------------------------
// 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;
m_ClientNetConfig.wServerPackSizeArray[m_ClientNetConfig.ubServerPackIndex] = WORD(size);
ServerConfigPacket packet( m_ClientNetConfig );
m_ClientNetConfigLock.Leave();
SendConfigPacketToAll( &packet );
}
//-----------------------------------------------------------------------------
// 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();
SendConfigPacketToAll( &packet );
}