Files
Client/Library/dxx8/samples/Multimedia/DirectPlay/Maze/MazeCommon/MazeClient.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

946 lines
30 KiB
C++

//----------------------------------------------------------------------------
// File: mazecient.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 <dplay8.h>
#include <dpaddr.h>
#include <dxerr8.h>
#include "DXUtil.h"
#include "SyncObjects.h"
#include "IMazeGraphics.h"
#include "DummyConnector.h"
#include "MazeApp.h"
#include "MazeClient.h"
#include "Packets.h"
#include "DXUtil.h"
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
#define NORTH_ANGLE 0x8000
#define EAST_ANGLE 0xc000
#define SOUTH_ANGLE 0x0000
#define WEST_ANGLE 0x4000
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CMazeClient::CMazeClient()
{
ZeroMemory( m_pctCells, sizeof(m_pctCells) );
m_hReady = CreateEvent( NULL, TRUE, FALSE, NULL );
m_hGotFirstConfig = CreateEvent( NULL, TRUE, FALSE, NULL );
m_pNet = NULL;
m_bAutopilot = FALSE;
m_bEngageAutopilot = TRUE;
m_dwNumNearbyPlayers = 0;
m_NetConfig.ubReliableRate = 0;
m_NetConfig.wUpdateRate = 150;
m_NetConfig.wTimeout = 150;
m_NetConfig.dwMazeWidth = 0;
m_NetConfig.dwMazeHeight = 0;
m_NetConfig.dwThreadWait = 0;
m_NetConfig.ubClientPackIndex = 0;
m_NetConfig.ubServerPackIndex = 0;
for(WORD x = 0; x < PACK_ARRAY_SIZE; x++)
{
m_NetConfig.wClientPackSizeArray[x] = 0;
m_NetConfig.wServerPackSizeArray[x] = 0;
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CMazeClient::~CMazeClient()
{
CloseHandle( m_hGotFirstConfig );
CloseHandle( m_hReady );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeClient::Init( CMazeApp* pMazeApp, IMazeGraphics* pMazeGraphics )
{
m_pMazeApp = pMazeApp;
m_pMazeGraphics = pMazeGraphics;
m_aCameraYaw = 0;
m_fLastOutboundTime = DXUtil_Timer( TIMER_GETAPPTIME );
m_bHaveInputFocus = TRUE;
Reset();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeClient::Reset()
{
m_dwNumPlayers = 0;
m_bAutopilot = FALSE;
SetFirstConfig( FALSE );
SetMazeReady( FALSE );
ZeroMemory( m_pctCells, sizeof(m_pctCells) );
PlayerObject* pPlayerObject = m_PlayerObjects;
for( DWORD i = 0; i < MAX_PLAYER_OBJECTS; i++, pPlayerObject++ )
{
pPlayerObject->dwID = 0;
pPlayerObject->wCellX = pPlayerObject->wCellY = 0xffff;
pPlayerObject->pNext = NULL;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::Shutdown()
{
// Destroy the maze
Reset();
m_Maze.Empty();
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::Update( FLOAT fElapsed )
{
// Don't do anyPlayerObject until we get a server config packet
if( !IsMazeReady() )
return;
if( m_bAutopilot )
DoAutopilot( fElapsed );
else
DoManualPilot( fElapsed );
// See if it's time to send a packet to the server with our updated coordinates
FLOAT fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
if( (fCurTime - m_fLastOutboundTime)*1000.0f > m_NetConfig.wUpdateRate )
{
ClientPosPacket packet( m_vCameraPos.x, m_vCameraPos.z, m_aCameraYaw );
// Pack the buffer with dummy data for testing.
if(m_NetConfig.wClientPackSizeArray[m_NetConfig.ubClientPackIndex] > 0)
{
WORD wBufferSize = sizeof(packet) + m_NetConfig.wClientPackSizeArray[m_NetConfig.ubClientPackIndex];
VOID* pTempBuffer = 0;
pTempBuffer = malloc(wBufferSize);
if( NULL == pTempBuffer )
{
//Out of memory, just bail
DXTRACE_ERR_NOMSGBOX( TEXT("System out of Memory!"), E_OUTOFMEMORY );
return;
}
FillMemory(pTempBuffer, wBufferSize, 'Z');
memcpy(pTempBuffer, &packet, sizeof(packet));
SendPacket( (ClientPacket*)pTempBuffer, wBufferSize, FALSE, m_NetConfig.wTimeout );
free(pTempBuffer);
}
else
{
SendPacket( &packet, sizeof(packet), FALSE, m_NetConfig.wTimeout );
}
m_fLastOutboundTime = fCurTime;
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::DoManualPilot( FLOAT fElapsed )
{
// Check if we have the input focus
if( !m_bHaveInputFocus )
return;
// Do rotations
if( GetAsyncKeyState( VK_LEFT ) & 0x8000 )
m_aCameraYaw += (DWORD) ((fElapsed*1000.0f) * 40.0f);
if( GetAsyncKeyState( VK_RIGHT ) & 0x8000 )
m_aCameraYaw -= (DWORD) ((fElapsed*1000.0f) * 40.0f);
float e = fElapsed*1000.0f;
// Compute new position based key input
D3DXVECTOR3 pos = m_vCameraPos;
if( GetAsyncKeyState( VK_UP ) & 0x8000 )
{
pos.x -= Sin(m_aCameraYaw) * 0.002f * e;
pos.z += Cos(m_aCameraYaw) * 0.002f * e;
}
if( GetAsyncKeyState( VK_DOWN ) & 0x8000 )
{
pos.x += Sin(m_aCameraYaw) * 0.002f * e;
pos.z -= Cos(m_aCameraYaw) * 0.002f * e;
}
// Ensure that we have stayed within the maze boundaries
if( pos.x < 0 ) pos.x = 0.1f;
if( pos.x >= m_Maze.GetWidth() ) pos.x = m_Maze.GetWidth() - 0.1f;
if( pos.z < 0 ) pos.z = 0.1f;
if( pos.z >= m_Maze.GetHeight() ) pos.z = m_Maze.GetHeight() - 0.1f;
m_vCameraPos = pos;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::DoAutopilot( FLOAT fElapsed )
{
// While there is still time to use up...
while( fElapsed )
{
// See if we need to turn
if( m_aAutopilotTargetAngle != m_aCameraYaw )
{
SHORT diff = SHORT((m_aAutopilotTargetAngle - m_aCameraYaw)&TRIG_ANGLE_MASK);
FLOAT fNeeded = abs(diff)/40.0f;
if( fNeeded/1000.0f <= fElapsed )
{
m_aCameraYaw = m_aAutopilotTargetAngle;
fElapsed -= fNeeded/1000.0f;
}
else
{
if( diff < 0 )
m_aCameraYaw -= (DWORD) ((fElapsed*1000.0f) * 40.0f);
else
m_aCameraYaw += (DWORD) ((fElapsed*1000.0f) * 40.0f);
fElapsed = 0;
}
}
else
{
// Ensure vAutopilotTarget is inside the maze boundry
if( m_vAutopilotTarget.x < 0 ||
m_vAutopilotTarget.x >= m_Maze.GetWidth() ||
m_vAutopilotTarget.z < 0 ||
m_vAutopilotTarget.z >= m_Maze.GetHeight() )
{
ZeroMemory( m_AutopilotVisited, sizeof(m_AutopilotVisited) );
m_AutopilotStack.Empty();
PickAutopilotTarget();
return;
}
// Facing right way, so now compute distance to target
D3DXVECTOR3 diff = m_vAutopilotTarget - m_vCameraPos;
float fRange = float(sqrt((diff.x*diff.x)+(diff.z*diff.z)));
// Are we there yet?
if( fRange > 0 )
{
// No, so compute how long we'd need
FLOAT fNeeded = fRange / 0.002f;
//Ensure we never leave the boundary of the Maze.
D3DXVECTOR3 pos = m_vCameraPos;
// Do we have enough time this frame?
if( fNeeded/1000.0f <= fElapsed )
{
// Yes, so just snap us there
pos.x = m_vAutopilotTarget.x;
pos.z = m_vAutopilotTarget.z;
fElapsed -= fNeeded/1000.0f;
}
else
{
// No, so move us as far as we can
pos.x -= Sin(m_aCameraYaw) * 0.002f * fElapsed*1000.0f;
pos.z += Cos(m_aCameraYaw) * 0.002f * fElapsed*1000.0f;
fElapsed = 0;
}
// Ensure that we have stayed within the maze boundaries
if( pos.x < 0 ) pos.x = 0.1f;
if( pos.x >= m_Maze.GetWidth() ) pos.x = m_Maze.GetWidth() - 0.1f;
if( pos.z < 0 ) pos.z = 0.1f;
if( pos.z >= m_Maze.GetHeight() ) pos.z = m_Maze.GetHeight() - 0.1f;
// Assign our new values back to our globals.
m_vCameraPos = pos;
}
else
{
// Reached target, so pick another
PickAutopilotTarget();
}
}
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::EngageAutopilot( BOOL bEngage )
{
m_bEngageAutopilot = bEngage;
if( !IsMazeReady() )
return;
BOOL bPrevious = m_bAutopilot;
m_bAutopilot = bEngage;
// If we weren't on autopilot before and are are autopilot now then need to init autopilot
if( m_bAutopilot && !bPrevious )
{
// First of all, snap us to the centre of the current cell
int cellx = int(m_vCameraPos.x);
int cellz = int(m_vCameraPos.z);
m_vCameraPos.x = cellx + 0.5f;
m_vCameraPos.z = cellz + 0.5f;
// Ensure we're within the maze boundaries
if( cellx < 0 ) m_vCameraPos.x = 0.5f;
if( cellx >= int(m_Maze.GetWidth()) ) m_vCameraPos.x = m_Maze.GetWidth() - 0.5f;
if( cellz < 0 ) m_vCameraPos.z = 0.5f;
if( cellz >= int(m_Maze.GetHeight()) ) m_vCameraPos.z = m_Maze.GetHeight() - 0.5f;
// Clear the visited array and stack
ZeroMemory( m_AutopilotVisited, sizeof(m_AutopilotVisited) );
m_AutopilotStack.Empty();
// Pick the next target cell
PickAutopilotTarget();
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::PickAutopilotTarget()
{
// Get current cell and mark as visited
DWORD currentx = DWORD(m_vCameraPos.x);
DWORD currentz = DWORD(m_vCameraPos.z);
m_AutopilotVisited[currentz][currentx] = 1;
// Figure out which directions are allowed. We're allowed to go in any direction
// where there isn't a wall in the way and that takes us to a cell we've visited before.
BYTE cell = m_Maze.GetCell(currentx,currentz);
ANGLE alloweddirs[5];
DWORD dwAllowed = 0;
if( !(cell & MAZE_WALL_NORTH) && !m_AutopilotVisited[currentz-1][currentx] )
alloweddirs[dwAllowed++] = NORTH_ANGLE;
if( !(cell & MAZE_WALL_WEST) && !m_AutopilotVisited[currentz][currentx-1] )
alloweddirs[dwAllowed++] = WEST_ANGLE;
if( !(cell & MAZE_WALL_EAST) && !m_AutopilotVisited[currentz][currentx+1] )
alloweddirs[dwAllowed++] = EAST_ANGLE;
if( !(cell & MAZE_WALL_SOUTH) && !m_AutopilotVisited[currentz+1][currentx] )
alloweddirs[dwAllowed++] = SOUTH_ANGLE;
/*
printf( "Walls: ") );
if( (cell & MAZE_WALL_NORTH) )
printf( "N ") );
if( (cell & MAZE_WALL_WEST) )
printf( "W ") );
if( (cell & MAZE_WALL_EAST) )
printf( "E ") );
if( (cell & MAZE_WALL_SOUTH) )
printf( "S ") );
printf( "\n") );
*/
// Is there anywhere to go?
if( dwAllowed == 0 )
{
// Nope. Can we backtrack?
if( m_AutopilotStack.GetCount() > 0 )
{
// Yes, so pop cell off the stack
AutopilotCell cell(m_AutopilotStack.Pop());
m_vAutopilotTarget.x = float(cell.x) + 0.5f;
m_vAutopilotTarget.z = float(cell.y) + 0.5f;
if( cell.x < currentx )
m_aAutopilotTargetAngle = WEST_ANGLE;
else if( cell.x > currentx )
m_aAutopilotTargetAngle = EAST_ANGLE;
else if( cell.y > currentz )
m_aAutopilotTargetAngle = SOUTH_ANGLE;
else
m_aAutopilotTargetAngle = NORTH_ANGLE;
}
else
{
// No, so we have explored entire maze and must start again
ZeroMemory( m_AutopilotVisited, sizeof(m_AutopilotVisited) );
m_AutopilotStack.Empty();
PickAutopilotTarget();
}
}
else
{
// See if we can continue in current direction
BOOL bPossible = FALSE;
for( DWORD i = 0; i < dwAllowed; i++ )
{
if( alloweddirs[i] == m_aCameraYaw )
{
bPossible = TRUE;
break;
}
}
// If it's allowed to go forward, then have 1 in 2 chance of doing that anyway, otherwise pick randomly from
// available alternatives
if( bPossible && (rand() & 0x1000) )
m_aAutopilotTargetAngle = m_aCameraYaw;
else
m_aAutopilotTargetAngle = alloweddirs[ (rand() % (dwAllowed<<3) ) >>3 ];
m_vAutopilotTarget.z = float(currentz) + 0.5f;
m_vAutopilotTarget.x = float(currentx) + 0.5f;
switch( m_aAutopilotTargetAngle )
{
case SOUTH_ANGLE:
m_vAutopilotTarget.z += 1.0f;
break;
case WEST_ANGLE:
m_vAutopilotTarget.x -= 1.0f;
break;
case EAST_ANGLE:
m_vAutopilotTarget.x += 1.0f;
break;
case NORTH_ANGLE:
m_vAutopilotTarget.z -= 1.0f;
break;
}
// Push current cell onto stack
m_AutopilotStack.Push( AutopilotCell(BYTE(currentx),BYTE(currentz)) );
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeClient::OnPacket( DWORD dwFrom, void* dwData, DWORD dwSize )
{
HRESULT hr = DPN_OK;
BOOL fFoundSize = FALSE;
DWORD dwReqSize = 0;
DWORD dwSRand = 0;
ServerPacket* pPacket = (ServerPacket*)dwData;
switch( pPacket->wType )
{
case PACKETTYPE_SERVER_CONFIG:
{
if( dwSize != sizeof(ServerConfigPacket) )
{
m_pMazeApp->SetDisconnectNow( TRUE );
m_pMazeApp->SetOutOfDateClient( TRUE );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Disconnected because MazeClient is out of date.") );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Please get updated version") );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("from http://msdn.microsoft.com/directx/") );
break;
}
m_NetConfig = ((ServerConfigPacket*)pPacket)->Config;
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Got MazeServer config settings") );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Maze Size=(%d,%d) ReliableRate=%d%%"),
m_NetConfig.dwMazeWidth, m_NetConfig.dwMazeHeight,
DWORD(m_NetConfig.ubReliableRate) );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("UpdateRate=%dms Timeout=%d"),
m_NetConfig.wUpdateRate, m_NetConfig.wTimeout );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("ThreadWait=%dms"),
m_NetConfig.dwThreadWait );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("ClientPackSize=%d ServerPackSize=%d (bytes)"),
m_NetConfig.wClientPackSizeArray[m_NetConfig.ubClientPackIndex],
m_NetConfig.wServerPackSizeArray[m_NetConfig.ubServerPackIndex]);
// See if we have gotten our fist config. If not, send version info.
if(! GotFirstConfig()) //If first time in maze.
{
// The client expects the server to send a ServerConfigPacket packet first,
// then the client sends a ClientVersionPacket, and then the server sends a
// ServerAckVersionPacket packet and the game begins
ClientVersionPacket packet( MAZE_CLIENT_VERSION );
SendPacket( &packet, sizeof(packet), TRUE, 0 );
SetFirstConfig( TRUE );
}
break;
}
case PACKETTYPE_SERVER_ACKVERSION:
{
if( dwSize != sizeof(ServerAckVersionPacket) )
{
m_pMazeApp->SetDisconnectNow( TRUE );
m_pMazeApp->SetOutOfDateClient( TRUE );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Disconnected because MazeClient is out of date.") );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Please get updated version") );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("from http://msdn.microsoft.com/directx/") );
break;
}
ServerAckVersionPacket* pAckVersionPacket = (ServerAckVersionPacket*)pPacket;
// Record the dpnid that the server uses for to talk to us.
// This is just done so that we can record this number in the
// logs to help match server side logs with client side logs.
m_dwLocalClientID = pAckVersionPacket->dwClientID;
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Server assigned ID: 0x%0.8x"), m_dwLocalClientID );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Server accepted client version") );
hr = m_Maze.Init( m_NetConfig.dwMazeWidth,
m_NetConfig.dwMazeHeight, DEFAULT_SEED );
if( FAILED(hr) )
DXTRACE_ERR( TEXT("Init"), hr );
//Seed the random number generator.
dwSRand = (DWORD) (DXUtil_Timer( TIMER_GETABSOLUTETIME ) * (DWORD)GetCurrentThreadId() );
srand( dwSRand );
// Set random start location
m_vCameraPos = D3DXVECTOR3( rand() % m_Maze.GetWidth() + 0.5f, 0.5,
rand() % m_Maze.GetHeight() + 0.5f );
SetMazeReady( TRUE );
EngageAutopilot( TRUE );
break;
}
case PACKETTYPE_SERVER_ACKPOS:
//Make sure we at least have a ServerAckPacket.
if( dwSize < sizeof(ServerAckPacket) )
{
fFoundSize = FALSE;
}
else
{
//Size of our required packet. Does not include custom pack data.
dwReqSize = (sizeof(ServerAckPacket) +
(sizeof(PlayerStatePacket) *
((ServerAckPacket*)pPacket)->wPlayerStatePacketCount));
//Check to see if we have a valid packet size.
if (dwSize < dwReqSize)
fFoundSize = FALSE;
else if ( !IsValidPackSize( dwSize - dwReqSize ))
fFoundSize = FALSE;
else
fFoundSize = TRUE;
}
//If we did not find a correct packet size. Exit.
if( !fFoundSize )
{
m_pMazeApp->SetDisconnectNow( TRUE );
m_pMazeApp->SetOutOfDateClient( TRUE );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Disconnected because MazeClient is out of date.") );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Please get updated version") );
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("from http://msdn.microsoft.com/directx/") );
break;
}
//Found correct packet size. Update clients.
SetPlayerStats( ((ServerAckPacket*)pPacket)->wPlayerCount,
((ServerAckPacket*)pPacket)->wPlayerStatePacketCount );
if( ((ServerAckPacket*)pPacket)->wPlayerStatePacketCount )
HandlePlayerObjectsInAckPacket( (ServerAckPacket*)pPacket );
break;
default:
m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Received unknown %d byte packet from server"), dwSize );
break;
};
//If the server has given us a custom wait time, Let's sleep for that amount of time.
if( m_NetConfig.dwThreadWait > 0 )
{
Sleep( m_NetConfig.dwThreadWait );
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
BOOL CMazeClient::IsValidPackSize( DWORD dwSize )
{
BOOL fFoundSize = FALSE;
BYTE ubPackLocation = m_NetConfig.ubServerPackIndex;
// Look for valid Client pack size in PackArray.
// If found, return TRUE, otherwise FALSE.
if(dwSize != m_NetConfig.wServerPackSizeArray[ubPackLocation])
{
for( --ubPackLocation; ubPackLocation != m_NetConfig.ubServerPackIndex; ubPackLocation--)
{
if(dwSize == m_NetConfig.wServerPackSizeArray[ubPackLocation])
{
fFoundSize = TRUE;
break;
}
if(ubPackLocation >= PACK_ARRAY_SIZE) ubPackLocation = PACK_ARRAY_SIZE; //Wrap the array.
}
}
else
{
fFoundSize = TRUE;
}
return fFoundSize;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::OnSessionLost( DWORD dwReason )
{
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::SendPacket( ClientPacket* pPacket, DWORD dwSize,
BOOL bGuaranteed, DWORD dwTimeout )
{
if( m_NetConfig.ubReliableRate > m_NetRandom.Get( 100 ) )
bGuaranteed = TRUE;
m_pNet->SendPacket( pPacket, dwSize, bGuaranteed, dwTimeout );
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD CMazeClient::GetRoundTripLatencyMS()
{
return m_pNet->GetRoundTripLatencyMS();
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD CMazeClient::GetThroughputBPS()
{
return m_pNet->GetThroughputBPS();
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::HandlePlayerObjectsInAckPacket( ServerAckPacket* pPacket )
{
PlayerStatePacket* pClientInfo = (PlayerStatePacket*)(pPacket+1);
if( !IsMazeReady() )
return;
// Lock the world database
LockWorld();
// Loop though the PlayerObject chunks
for( DWORD count = pPacket->wPlayerStatePacketCount; count; count--, pClientInfo++ )
{
// Get the PlayerObject we think this is
PlayerObject* pPlayerObject = &m_PlayerObjects[pClientInfo->dwID & PLAYER_OBJECT_SLOT_MASK];
// Does the ID match the one we have?
if( pPlayerObject->dwID != pClientInfo->dwID )
{
// No, so the PlayerObject we have needs to be deleted (server reused the same slot
// number, so old PlayerObject must be toast)
RemovePlayerObjectFromCells( pPlayerObject );
// Set the ID to the new ID
pPlayerObject->dwID = pClientInfo->dwID;
pPlayerObject->wCellX = WORD(pClientInfo->fX);
pPlayerObject->wCellY = WORD(pClientInfo->fY);
// Insert into the appropriate cell list
AddPlayerObjectToCells( pPlayerObject );
}
else
{
// Yes, compute the new cell coordinates
DWORD newcellx = DWORD(pClientInfo->fX);
DWORD newcelly = DWORD(pClientInfo->fY);
// Are they the same as the ones we already have?
if( newcellx != pPlayerObject->wCellX || newcelly != pPlayerObject->wCellY )
{
// No, so need to remove from old cell and add to new one
RemovePlayerObjectFromCells( pPlayerObject );
pPlayerObject->wCellX = WORD(newcellx);
pPlayerObject->wCellY = WORD(newcelly);
AddPlayerObjectToCells( pPlayerObject );
}
}
// Update timestamp and position
pPlayerObject->vPos.x = pClientInfo->fX;
pPlayerObject->vPos.y = 0.5f;
pPlayerObject->vPos.z = pClientInfo->fY;
pPlayerObject->aCameraYaw = pClientInfo->aCameraYaw;
pPlayerObject->fLastValidTime = DXUtil_Timer( TIMER_GETAPPTIME );
}
// Unlock world database
UnlockWorld();
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::AddPlayerObjectToCells( PlayerObject* pPlayerObject )
{
if( pPlayerObject->wCellX == 0xffff )
return;
if( FALSE == IsPlayerObjectInCell( pPlayerObject->wCellX, pPlayerObject->wCellY, pPlayerObject ) )
{
PlayerObject** ppCell = &m_pctCells[pPlayerObject->wCellY][pPlayerObject->wCellX];
pPlayerObject->pNext = *ppCell;
*ppCell = pPlayerObject;
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeClient::RemovePlayerObjectFromCells( PlayerObject* pPlayerObject )
{
if( pPlayerObject->wCellX == 0xffff )
return;
PlayerObject** ppCell = &m_pctCells[pPlayerObject->wCellY][pPlayerObject->wCellX];
PlayerObject* pCur = *ppCell;
PlayerObject* pPrev = NULL;
while( pCur )
{
if( pCur == pPlayerObject )
{
pCur = pPlayerObject->pNext;
// Found pPlayerObject, so remove pPlayerObject from the m_pctCells linked list
if( pPrev )
pPrev->pNext = pPlayerObject->pNext;
else
*ppCell = pPlayerObject->pNext;
pPlayerObject->pNext = NULL;
// Update pPlayerObject so that it is marked as removed
pPlayerObject->wCellX = pPlayerObject->wCellY = 0xffff;
// Continue searching, and remove any other instances of
// pPlayerObject from list (there shouldn't be, however)
}
else
{
pPrev = pCur;
pCur = pCur->pNext;
}
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
PlayerObject* CMazeClient::GetFirstPlayerObjectInCell( DWORD x, DWORD z )
{
if( !IsMazeReady() )
return NULL;
FLOAT fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
// Remove any PlayerObjects which are out of date (since they're probably not really in this
// cell any more, but the server has just stopped telling us about them)
PlayerObject** ppCell = &m_pctCells[z][x];
PlayerObject* pCur = m_pctCells[z][x];
PlayerObject* pPrev = NULL;
PlayerObject* pPlayerObject = NULL;
while( pCur )
{
// Too old?
if( (fCurTime - pCur->fLastValidTime) > 5.0f )
{
pPlayerObject = pCur;
pCur = pCur->pNext;
// pPlayerObject is too old, so remove pPlayerObject from the m_pctCells linked list
if( pPrev )
pPrev->pNext = pPlayerObject->pNext;
else
*ppCell = pPlayerObject->pNext;
pPlayerObject->pNext = NULL;
// Update pPlayerObject so that it is marked as removed
pPlayerObject->wCellX = pPlayerObject->wCellY = 0xffff;
// Continue searching, and remove any other old instances from list
}
else
{
pPrev = pCur;
pCur = pCur->pNext;
}
}
// Now return first remaining PlayerObject in the cell (if any)
return m_pctCells[z][x];
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
BOOL CMazeClient::IsPlayerObjectInCell( DWORD wCellX, DWORD wCellY, PlayerObject* pPlayerObject )
{
PlayerObject* pPlayerObjectTmp = m_pctCells[wCellY][wCellX];
while( pPlayerObjectTmp )
{
if( pPlayerObjectTmp == pPlayerObject )
return TRUE;
pPlayerObjectTmp = pPlayerObjectTmp->pNext;
}
return FALSE;
}