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>
This commit is contained in:
2025-11-29 16:24:34 +09:00
commit e067522598
5135 changed files with 1745744 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
//----------------------------------------------------------------------------
// File: dummyconnector.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _DUMMY_CONNECTOR_H
#define _DUMMY_CONNECTOR_H
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
#include "NetAbstract.h"
#include <tchar.h>
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
class CDummyConnectorServer : public IOutboundServer
{
public:
CDummyConnectorServer() : m_pTarget(NULL),m_dwID(0) {};
void SetTarget( INetClient* ptarget )
{
m_pTarget = ptarget;
};
// From IOutboundServer
virtual HRESULT SendPacket( DWORD /*to*/ , void* data , DWORD size , BOOL /*guaranteed*/, DWORD /*dwTimeout*/ )
{
if ( m_pTarget )
return m_pTarget->OnPacket( m_dwID , data , size );
return S_OK;
};
virtual HRESULT GetConnectionInfo( DWORD dwID, TCHAR* strConnectionInfo )
{
_tcscpy( strConnectionInfo, TEXT("") );
return S_OK;
}
virtual HRESULT RejectClient( DWORD dwID, HRESULT hrReason )
{
return S_OK;
}
private:
INetClient* m_pTarget;
DWORD m_dwID;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
class CDummyConnectorClient : public IOutboundClient
{
public:
CDummyConnectorClient() : m_pTarget(NULL),m_dwID(0) {};
void SetTarget( INetServer* ptarget )
{
m_pTarget = ptarget;
};
// Connect (sends an "add connection" message to target)
void Connect( DWORD id )
{
m_dwID = id;
if ( m_pTarget )
m_pTarget->OnAddConnection( id );
};
// Disconnect (sends a "remove connection" message to target
void Disconnect( DWORD id )
{
if ( m_pTarget )
m_pTarget->OnRemoveConnection( m_dwID );
}
// From IOutboundClient
virtual HRESULT SendPacket( void* data , DWORD size , BOOL /*guaranteed*/, DWORD /*dwTimeout*/ )
{
if ( m_pTarget )
return m_pTarget->OnPacket( m_dwID , data , size );
return S_OK;
};
virtual DWORD GetThroughputBPS()
{
return 0;
}
virtual DWORD GetRoundTripLatencyMS()
{
return 0;
}
virtual BOOL IsSessionLost() { return FALSE; };
virtual DWORD GetSessionLostReason() { return 0; };
virtual HRESULT GetConnectionInfo( TCHAR* strConnectionInfo )
{
_tcscpy( strConnectionInfo, TEXT("") );
return S_OK;
}
private:
INetServer* m_pTarget;
DWORD m_dwID;
};
#endif

View File

@@ -0,0 +1,12 @@
//----------------------------------------------------------------------------
// File:
//
// Desc:
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _IDPCLIENT_H
#define _IDPCLIENT_H
#endif

View File

@@ -0,0 +1,395 @@
//----------------------------------------------------------------------------
// File: maze.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 <malloc.h>
#include <dplay8.h>
#include <dpaddr.h>
#include <dxerr8.h>
#include "DXUtil.h"
#include "Maze.h"
#include "MazeServer.h"
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CMaze::CMaze()
{
m_dwWidth = m_dwHeight = m_dwSeed = m_dwSize = 0;
m_pMaze = NULL;
m_dwMaxView = 15;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CMaze::~CMaze()
{
if( m_pMaze != NULL )
DXTRACE( TEXT("Warning: Destructing CMaze object without calling Empty()\n") );
Empty();
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMaze::Init( DWORD dwWidth, DWORD dwHeight, DWORD dwSeed )
{
HRESULT hr;
CellNode* pCells = NULL;
CellNode* pCellNode = NULL;
DWORD dwNumWalls;
WallNode* pWalls = NULL;
WallNode* pWallNode = NULL;
WallNode tempWall;
DWORD dwIndex;
DWORD i;
m_Random.Reset( dwSeed );
// Empty out any old data
Empty();
// Store parameters and compute number of cells in the maze
m_dwWidth = dwWidth;
m_dwHeight = dwHeight;
m_dwSeed = dwSeed;
m_dwSize = m_dwWidth * m_dwHeight;
// Must be non-zero
if( m_dwSize == 0 )
{
hr = E_INVALIDARG;
DXTRACE_ERR( TEXT("Maze height and width need must be greater than 0"), E_INVALIDARG );
goto LFail;
}
// Validate maze size
if( m_dwWidth > SERVER_MAX_WIDTH || m_dwHeight > SERVER_MAX_HEIGHT )
{
hr = E_INVALIDARG;
DXTRACE_ERR( TEXT("Maze height and width must be less than 128"), E_INVALIDARG );
goto LFail;
}
if( (m_dwWidth % LOCK_GRID_SIZE) != 0 || (m_dwHeight % LOCK_GRID_SIZE) != 0 )
{
hr = E_INVALIDARG;
DXTRACE_ERR( TEXT("Maze height and width need to be divisable by 16"), E_INVALIDARG );
goto LFail;
}
// Allocate maze, and initially make all walls solid
m_pMaze = new BYTE[m_dwSize];
if( m_pMaze == NULL )
{
hr = E_OUTOFMEMORY;
DXTRACE_ERR( TEXT("new"), hr );
goto LFail;
}
memset( m_pMaze, MAZE_WALL_ALL, m_dwSize );
// Okay, now we're going to generate the maze. We use Kruskal's algorithm, which
// works by walking through the list of walls in a random order, removing a wall
// if it would connect two previously (path-)disconnected cells. This guarantees
// a fully connected maze (i.e. you can reach any cell from any other).
// Allocate and initialize temporary cell list
pCells = new CellNode[m_dwSize];
if( pCells == NULL )
{
hr = E_OUTOFMEMORY;
DXTRACE_ERR( TEXT("new"), hr );
goto LFail;
}
pCellNode = pCells;
for( i = 0; i < m_dwSize; i++ )
{
pCellNode->pNext = NULL;
pCellNode->pPartition = pCellNode;
pCellNode++;
}
// Create list of walls
dwNumWalls = ((m_dwWidth-1)*m_dwHeight)+((m_dwHeight-1)*m_dwWidth);
pWalls = new WallNode[dwNumWalls];
if( pWalls == NULL )
{
hr = E_OUTOFMEMORY;
DXTRACE_ERR( TEXT("new"), hr );
goto LFail;
}
pWallNode = pWalls;
for( i = 1; i < m_dwWidth; i++ )
{
for( DWORD j = 0; j < m_dwHeight; j++, pWallNode++ )
{
pWallNode->dwX = i;
pWallNode->dwY = j;
pWallNode->dwType = MAZE_WALL_WEST;
}
}
for( i = 0; i < m_dwWidth; i++ )
{
for( DWORD j = 1; j < m_dwHeight; j++, pWallNode++ )
{
pWallNode->dwX = i;
pWallNode->dwY = j;
pWallNode->dwType = MAZE_WALL_NORTH;
}
}
// Randomly permute the wall list
for( i = dwNumWalls-1; i > 0; i-- )
{
dwIndex = m_Random.Get(i);
tempWall = pWalls[dwIndex];
pWalls[dwIndex] = pWalls[i];
pWalls[i] = tempWall;
}
// Walk through all the walls
pWallNode = pWalls;
for( i = 0; i < dwNumWalls; i++, pWallNode++ )
{
// Determine the cells either side of the wall
DWORD dwCellA = pWallNode->dwX + (pWallNode->dwY * m_dwWidth);
DWORD dwCellB = dwCellA;
if( pWallNode->dwType == MAZE_WALL_NORTH )
dwCellB -= m_dwWidth;
else
dwCellB--;
// Are they already connected (partitions equal)?
CellNode* pCellA = pCells+dwCellA;
CellNode* pCellB = pCells+dwCellB;
if( pCellA->pPartition != pCellB->pPartition )
{
// Nope, so let's take out that wall. First, connect the partition lists
while ( pCellA->pNext )
pCellA = pCellA->pNext;
pCellB = pCellB->pPartition;
pCellA->pNext = pCellB;
while ( pCellB )
{
pCellB->pPartition = pCellA->pPartition;
pCellB = pCellB->pNext;
}
// Now remove the walls in our maze array
if( pWallNode->dwType == MAZE_WALL_NORTH )
{
m_pMaze[dwCellA] &= ~MAZE_WALL_NORTH;
m_pMaze[dwCellB] &= ~MAZE_WALL_SOUTH;
}
else
{
m_pMaze[dwCellA] &= ~MAZE_WALL_WEST;
m_pMaze[dwCellB] &= ~MAZE_WALL_EAST;
}
}
}
// Free temporary wall and cell lists
delete[] pWalls;
delete[] pCells;
return S_OK;
LFail:
SAFE_DELETE_ARRAY( pCells );
SAFE_DELETE_ARRAY( pWalls );
SAFE_DELETE_ARRAY( m_pMaze );
return hr;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMaze::Empty()
{
if( m_pMaze != NULL )
SAFE_DELETE_ARRAY( m_pMaze );
m_dwWidth = m_dwHeight = m_dwSeed = m_dwSize = 0;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD CMaze::GetVisibleCells( const D3DXVECTOR2& pos, const D3DXVECTOR2& dir ,
float fov, MazeCellRef* plist, DWORD maxlist )
{
// Check we have a maze, and that we were passed reasonable parameters
if( m_pMaze == NULL || plist == NULL || maxlist == 0 )
return 0;
// Check bounds of given viewpoint, must be inside maze
if( pos.x < 0.0f || pos.y < 0.0f ||
pos.x >= float(m_dwWidth) || pos.y >= float(m_dwHeight) )
return 0;
// State data for the algorithm
VisState state;
// Figure out which cell the viewpoint is in
state.dwPosX = DWORD(pos.x);
state.dwPosY = DWORD(pos.y);
state.vPos = pos;
// Compute view boundaries
float c = float(cos(fov*0.5f));
float s = float(sin(fov*0.5f));
D3DXVECTOR2 left,right;
left.x = (dir.x*c)+(dir.y*s);
left.y = (dir.y*c)-(dir.x*s);
right.x = (dir.x*c)-(dir.y*s);
right.y = (dir.y*c)+(dir.x*s);
// Store view direction (for near plane clip)
state.vDir = dir;
// Figure out boundary of area we're prepared to look at (view cutoff)
state.dwMinX = (state.dwPosX > m_dwMaxView) ? state.dwPosX - m_dwMaxView : 0;
state.dwMaxX = ((state.dwPosX + m_dwMaxView) > m_dwWidth) ? m_dwWidth : state.dwPosX + m_dwMaxView;
state.dwMinY = (state.dwPosY > m_dwMaxView) ? state.dwPosY - m_dwMaxView : 0;
state.dwMaxY = ((state.dwPosY + m_dwMaxView) > m_dwHeight) ? m_dwHeight : state.dwPosY + m_dwMaxView;
state.dwArrayPitch = state.dwMaxX-state.dwMinX+1;
// Allocate a temporary buffer which we'll use to mark visited cells
DWORD array_size = state.dwArrayPitch * (state.dwMaxY-state.dwMinY+1);
state.pArray = (BYTE*)_alloca( array_size );
ZeroMemory( state.pArray, array_size );
state.ppVisList = &plist;
state.dwMaxList = maxlist;
state.dwListLen = 0;
// Recurse through cells
RecurseCheckCellVis( state, state.dwPosX, state.dwPosY, left, right );
return state.dwListLen;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMaze::RecurseCheckCellVis( VisState& state, DWORD x, DWORD y,
D3DXVECTOR2 left, D3DXVECTOR2 right )
{
// Fall out if we've overrun list length
if( state.dwListLen >= state.dwMaxList )
return;
// If cell is outside the maximum view bounds, then it's not visible
if( x < state.dwMinX || x > state.dwMaxX ||
y < state.dwMinY || y > state.dwMaxY )
return;
// If cell is already marked, then we don't visit it either
if( state.pArray[x-state.dwMinX+((y-state.dwMinY)*state.dwArrayPitch)] )
return;
// Mark cell as visited
state.pArray[x-state.dwMinX+((y-state.dwMinY)*state.dwArrayPitch)] = 1;
// Compute visibility flags
D3DXVECTOR2 offset;
offset.x = float(x)-state.vPos.x;
offset.y = float(y)-state.vPos.y;
BYTE flags[4];
flags[0] = ComputeVisFlags( state.vDir, left, right, offset );
offset.x += 1.0f;
flags[1] = ComputeVisFlags( state.vDir, left, right, offset );
offset.y += 1.0f;
flags[2] = ComputeVisFlags( state.vDir, left, right, offset );
offset.x -= 1.0f;
flags[3] = ComputeVisFlags( state.vDir, left, right, offset );
offset.y -= 1.0f;
// If there is an edge which clips all points, then the cell isn't in frustrum
if( flags[0]&flags[1]&flags[2]&flags[3] )
return;
// Cell is visible, so add it to list
(*state.ppVisList)->x = x;
(*state.ppVisList)->y = y;
(*state.ppVisList)++;
state.dwListLen++;
// Recurse into adjoining cells. Can move into an adjacent cell only if
// there is a 'portal' (i.e. hole in the wall) that is not clipped and
// that lies on the correct side of the viewport.
BYTE cell = GetCell(x,y);
D3DXVECTOR2 se = offset + D3DXVECTOR2(1,1);
if( !(cell & MAZE_WALL_NORTH) && offset.y < 0 && !(flags[0]&flags[1]) )
RecurseCheckCellVis( state, x, y-1, left, right );
if( !(cell & MAZE_WALL_SOUTH) && se.y > 0 && !(flags[2]&flags[3]) )
RecurseCheckCellVis( state, x, y+1, left, right );
if( !(cell & MAZE_WALL_WEST) && offset.x < 0 && !(flags[3]&flags[0]) )
RecurseCheckCellVis( state, x-1, y, left, right );
if( !(cell & MAZE_WALL_EAST) && se.x > 0 && !(flags[1]&flags[2]) )
RecurseCheckCellVis( state, x+1, y, left, right );
return;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
BYTE CMaze::ComputeVisFlags( const D3DXVECTOR2& dir, const D3DXVECTOR2& left,
const D3DXVECTOR2& right, const D3DXVECTOR2& offset )
{
BYTE flag = (D3DXVec2Dot(&offset, &dir) >= 0) ? 0 : 1;
if( D3DXVec2CCW(&offset,&left) > 0 )
flag |= 2;
if( D3DXVec2CCW(&offset,&right) < 0 )
flag |= 4;
return flag;
}

View File

@@ -0,0 +1,140 @@
//----------------------------------------------------------------------------
// File: maze.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _MAZE_H
#define _MAZE_H
//-----------------------------------------------------------------------------
// Name:
// Desc: Client IDs are 32-bit values that refer to a particular Client. They are
// broken up into two bitfields, one of which can be used into an index
// of a list of Client 'slots', the other bitfield is a "uniqueness" value
// that is incremented each time a new Client is created. Hence, although
// the same slot may be reused by different Clients are different times,
// it's possible to distinguish between the two by comparing uniqueness
// values (you can just compare the whole 32-bit id).
//-----------------------------------------------------------------------------
typedef DWORD ClientID;
#define PLAYER_OBJECT_SLOT_BITS 13
#define MAX_PLAYER_OBJECTS (1<<PLAYER_OBJECT_SLOT_BITS)
#define PLAYER_OBJECT_SLOT_MASK (MAX_PLAYER_OBJECTS-1)
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
#include <assert.h>
#include "Random.h"
const BYTE MAZE_WALL_NORTH = (1<<0);
const BYTE MAZE_WALL_EAST = (1<<1);
const BYTE MAZE_WALL_SOUTH = (1<<2);
const BYTE MAZE_WALL_WEST = (1<<3);
const BYTE MAZE_WALL_ALL = MAZE_WALL_NORTH|MAZE_WALL_EAST|
MAZE_WALL_SOUTH|MAZE_WALL_WEST;
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
struct MazeCellRef
{
DWORD x,y;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
class CMaze
{
public:
CMaze();
~CMaze();
HRESULT Init( DWORD width , DWORD height , DWORD seed );
void Empty();
DWORD GetWidth() const { return m_dwWidth; };
DWORD GetHeight() const { return m_dwHeight; };
DWORD GetSize() const { return m_dwWidth*m_dwHeight; };
BYTE GetCell( DWORD x , DWORD y ) const { assert(m_pMaze!=NULL); return *(m_pMaze+x+(y*m_dwWidth)); };
BOOL CanGoNorth( DWORD x , DWORD y ) const { return !(GetCell(x,y)&MAZE_WALL_NORTH); };
BOOL CanGoEast( DWORD x , DWORD y ) const { return !(GetCell(x,y)&MAZE_WALL_EAST); };
BOOL CanGoSouth( DWORD x , DWORD y ) const { return !(GetCell(x,y)&MAZE_WALL_SOUTH); };
BOOL CanGoWest( DWORD x , DWORD y ) const { return !(GetCell(x,y)&MAZE_WALL_WEST); };
// Get list of visible cells from the given position. Fills out the list pointed to
// be plist, and stops if it blows maxlist. Returns number of visible cells
DWORD GetVisibleCells( const D3DXVECTOR2& pos , const D3DXVECTOR2& dir ,
float fov , MazeCellRef* plist , DWORD maxlist );
// Get/set max view distance (used by GetVisibleCells)
DWORD GetMaxView() const { return m_dwMaxView; };
void SetMaxView() { m_dwMaxView = m_dwMaxView; };
BYTE* m_pMaze;
protected:
DWORD m_dwWidth;
DWORD m_dwHeight;
DWORD m_dwSize;
DWORD m_dwSeed;
DWORD m_dwMaxView;
CRandom m_Random;
// Local types for the maze generation algorithm
struct CellNode
{
CellNode* pPartition;
CellNode* pNext;
};
struct WallNode
{
DWORD dwX,dwY;
DWORD dwType;
};
// Local type for visibilty alg state
struct VisState
{
DWORD dwPosX,dwPosY; // Cell containing view position
D3DXVECTOR2 vPos; // View position
D3DXVECTOR2 vDir; // View direction
BYTE* pArray; // Array in which cell visits are marked
DWORD dwMinX,dwMaxX; // Extents to consider (also array bounds)
DWORD dwMinY,dwMaxY;
DWORD dwArrayPitch; // 'Pitch' of array (width)
MazeCellRef** ppVisList; // Pointer to vis list pointer
DWORD dwMaxList; // Maximum length of vis list
DWORD dwListLen; // Current length of vis list
};
void RecurseCheckCellVis( VisState& state , DWORD x , DWORD y , D3DXVECTOR2 left , D3DXVECTOR2 right );
BYTE ComputeVisFlags( const D3DXVECTOR2& dir , const D3DXVECTOR2& left , const D3DXVECTOR2& right , const D3DXVECTOR2& offset );
private:
CMaze( const CMaze& );
void operator=( const CMaze& );
};
#endif

View File

@@ -0,0 +1,901 @@
//----------------------------------------------------------------------------
// File: mazeapp.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 <tchar.h>
#include <math.h>
#include <process.h>
#include <dxerr8.h>
#include <dplay8.h>
#include "DXUtil.h"
#include "SyncObjects.h"
#include "DummyConnector.h"
#include "DPlay8Client.h"
#include "MazeClient.h"
#include "IMazeGraphics.h"
#include "MazeApp.h"
static CMazeApp* s_pMazeApp = NULL;
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CMazeApp::CMazeApp()
{
s_pMazeApp = this;
m_hOutputMsgThread = NULL;
m_bQuitThread = NULL;
m_hOutputMsgEvent = NULL;
m_dwNextOutputMsg = 0;
m_bLocalLoopback = TRUE;
m_bAllowConnect = FALSE;
m_bConnectNow = TRUE;
m_bSaveSettings = TRUE;
m_lQueueSize = 0;
m_dwNextFreeOutputMsg = 0;
m_hLogFile = NULL;
m_bLocalLoopbackInitDone = FALSE;
m_bInitDone = FALSE;
// Create an event object to flag pending output messages
m_hOutputMsgEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
//Initialize our Config structure to 0.
ZeroMemory(&m_Config, sizeof(m_Config));
//Must init buffer size to -1, since 0 is valid.
m_Config.dwSPBufferSize = 0xffffffff;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CMazeApp::~CMazeApp()
{
CloseHandle( m_hOutputMsgEvent );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeApp::Create( IMazeGraphics* pMazeGraphics )
{
m_pMazeGraphics = pMazeGraphics;
if( m_pMazeGraphics == NULL )
return E_FAIL;
m_pMazeGraphics->Init( this, &m_DP8Client, &m_MazeClient );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
int CMazeApp::Run( HINSTANCE hInst )
{
if( NULL == m_pMazeGraphics )
return 0;
// Start the timer and init the random seed
DXUtil_Timer( TIMER_START );
DWORD dwSRand = (DWORD) (DXUtil_Timer( TIMER_GETABSOLUTETIME ) * UINT_MAX * (DWORD)GetCurrentThreadId() );
srand( dwSRand );
// Tell OS's that have power management to not
// sleep, since this app will be using the
// network connection and need very little user input
SuspendPowerManagement();
// Initialize COM
CoInitializeEx( NULL, COINIT_MULTITHREADED );
// Extract configuration settings from the registry
ReadConfig();
if( m_Config.bFileLogging )
CreateTempLogFile();
if( SUCCEEDED( m_pMazeGraphics->Create( hInst ) ) )
{
ConsolePrintf( LINE_LOG, TEXT("DirectPlayMaze client started.") );
// Spin up a thread to record/display the output
UINT dwOutputMsgThreadID;
m_hOutputMsgThread = (HANDLE)_beginthreadex( NULL, 0, StaticOutputMsgThread,
NULL, 0, &dwOutputMsgThreadID );
// Initialize maze client object - basically just build the maze
m_MazeClient.Init( this, m_pMazeGraphics );
m_pMazeGraphics->Run();
// Wait for threads to shutdown
DXUtil_Trace( TEXT("Quiting\n") );
m_bQuitThread = TRUE;
SetEvent( m_hOutputMsgEvent );
WaitForSingleObject( m_hOutputMsgThread, INFINITE );
CloseHandle( m_hOutputMsgThread );
// Write configuration settings to registry
WriteConfig();
m_pMazeGraphics->Shutdown();
}
// Clean up
DXUtil_Trace( TEXT("Shutting down client\n") );
m_MazeClient.Shutdown();
DXUtil_Trace( TEXT("Shutting down dp8\n") );
m_DP8Client.Shutdown();
CoUninitialize();
return 0;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeApp::SuspendPowerManagement()
{
TCHAR szPath[MAX_PATH];
HINSTANCE hInstKernel32 = NULL;
typedef EXECUTION_STATE (WINAPI* LPSETTHREADEXECUTIONSTATE)( EXECUTION_STATE esFlags );
LPSETTHREADEXECUTIONSTATE pSetThreadExecutionState = NULL;
GetSystemDirectory(szPath, MAX_PATH);
// SetThreadExecutionState() isn't availible on some old OS's,
// so do a LoadLibrary to get to it.
lstrcat(szPath, TEXT("\\kernel32.dll"));
hInstKernel32 = LoadLibrary(szPath);
if (hInstKernel32 != NULL)
{
pSetThreadExecutionState = (LPSETTHREADEXECUTIONSTATE)GetProcAddress(hInstKernel32, "SetThreadExecutionState");
if( pSetThreadExecutionState != NULL )
{
// Tell OS's that have power management to not
// sleep, since this app will be using the
// network connection and need very little user input
pSetThreadExecutionState( ES_SYSTEM_REQUIRED | ES_CONTINUOUS );
}
FreeLibrary(hInstKernel32);
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeApp::FrameMove( FLOAT fElapsedTime )
{
HRESULT hr;
FLOAT fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
static FLOAT s_fLastConnect = INT_MIN;
static FLOAT s_fStartEnumTime = INT_MIN;
static FLOAT s_fStopEnumTime = INT_MIN;
static FLOAT s_fEnumStarted = FALSE;
if( m_DP8Client.IsSessionLost() )
{
if( FALSE == m_bLocalLoopbackInitDone )
{
if( m_bInitDone )
{
ConsolePrintf( LINE_LOG, TEXT("Disconnected from server") );
if( m_DP8Client.GetSessionLostReason() == DISCONNNECT_REASON_CLIENT_OUT_OF_DATE )
{
ConsolePrintf( LINE_LOG, TEXT("Disconnected because MazeClient is out of date.") );
ConsolePrintf( LINE_LOG, TEXT("Please get updated version") );
ConsolePrintf( LINE_LOG, TEXT("from http://msdn.microsoft.com/directx/") );
m_bOutOfDateClient = TRUE;
}
// Disconnected, so retry in 10 seconds
s_fStopEnumTime = fCurTime - m_Config.dwNetworkRetryDelay * 60.0f + 10.0f;
}
else
{
// If just starting up, then retry immediately
m_bInitDone = TRUE;
}
m_MazeClient.LockWorld();
m_MazeClient.Reset();
// Now that the session is lost we need to
// restart DirectPlay by calling Close()
// and Init() on m_pDPlay
m_DP8Client.Shutdown();
//Pass in the our structure in order to get out configuration data.
m_DP8Client.Init(GetConfig());
if( m_bAllowLoopback )
InitServerForLoopback();
m_MazeClient.UnlockWorld();
m_bLocalLoopbackInitDone = TRUE;
}
if( ( !s_fEnumStarted && fCurTime - s_fStopEnumTime > m_Config.dwNetworkRetryDelay * 60.0f || m_bConnectNow )
&& m_bAllowConnect && !m_bOutOfDateClient )
{
m_bConnectNow = FALSE;
if( SUCCEEDED( hr = StartSessionEnum() ) )
{
// DirectPlay host enumeration started
ConsolePrintf( LINE_LOG, TEXT("Starting DirectPlay host enumeration") );
s_fStartEnumTime = fCurTime;
s_fEnumStarted = TRUE;
}
else
{
//If we are in the stress connect, try the next connection.
//When we get back to 1, then we have gone through the list, so let's wait.
if(m_Config.dwStressConnect > 1)
{
//We are going through the list of machines to attempt connection. So lets try the next right away.
ConsolePrintf( LINE_LOG, TEXT("Host not found. Trying local subnet.") );
m_bConnectNow = TRUE; //Try the enum right away with the next item in the list.
}
else
{
ConsolePrintf( LINE_LOG, TEXT("DirectPlay host enumeration failed to start.") );
ConsolePrintf( LINE_LOG, TEXT("Will try again in %d minutes."), m_Config.dwNetworkRetryDelay );
}
// DirectPlay host enumeration failed to start
// Will try again in m_Config.dwNetworkRetryDelay minutes
s_fStopEnumTime = fCurTime;
s_fEnumStarted = FALSE;
}
}
if( s_fEnumStarted && fCurTime - s_fStartEnumTime > 5.0f * 60.0f )
{
//If we are in the stress connect, try the next connection.
//When we get back to 1, then we have gone through the list, so let's start over.
if(m_Config.dwStressConnect > 1)
{
//We are going through the list of machines to attempt connection. So lets try the next right away.
ConsolePrintf( LINE_LOG, TEXT("Host not found. Trying local subnet.") );
m_bConnectNow = TRUE; //Try the enum right away with the next item in the list.
}
else
{
ConsolePrintf( LINE_LOG, TEXT("No host found. Stopping DirectPlay host enumeration") );
ConsolePrintf( LINE_LOG, TEXT("Will try again in %d minutes."), m_Config.dwNetworkRetryDelay );
}
// Stop enumeration
m_DP8Client.StopSessionEnum();
s_fStopEnumTime = fCurTime;
s_fEnumStarted = FALSE;
}
if( s_fEnumStarted && fCurTime - s_fLastConnect > 0.5f )
{
if( TRUE == TryToConnect() )
{
// Connect successful
ConsolePrintf( LINE_LOG, TEXT("Connected to server. Host enumeration stopped.") );
m_bLocalLoopback = FALSE;
s_fEnumStarted = FALSE;
m_bLocalLoopbackInitDone = FALSE;
}
s_fLastConnect = fCurTime;
}
m_bDisconnectNow = FALSE;
}
else
{
m_bLocalLoopbackInitDone = FALSE;
if( m_Config.dwLogLevel > 1 )
{
// Display position every so often
static float fLastPosUpdate = fCurTime;
if( fCurTime - fLastPosUpdate > 10.0f )
{
D3DXVECTOR3 vPos = m_MazeClient.GetCameraPos();
DWORD dwNumPlayers, dwNumNearbyPlayers;
m_MazeClient.GetPlayerStats( &dwNumPlayers, &dwNumNearbyPlayers );
ConsolePrintf( LINE_LOG, TEXT("Position: (%5.1f,%5.1f), Players: %d, Nearby Players: %d"),
vPos.x, vPos.z, dwNumPlayers, dwNumNearbyPlayers );
fLastPosUpdate = fCurTime;
}
}
// Display connection info every so often
static float fLastLogUpdate = fCurTime;
if( m_Config.dwAutoPrintStats > 0 &&
fCurTime - fLastLogUpdate > m_Config.dwAutoPrintStats * 60.0f )
{
D3DXVECTOR3 vPos = m_MazeClient.GetCameraPos();
DWORD dwNumPlayers, dwNumNearbyPlayers;
m_MazeClient.GetPlayerStats( &dwNumPlayers, &dwNumNearbyPlayers );
ConsolePrintf( LINE_LOG, TEXT("Position: (%5.1f,%5.1f), Players: %d, Nearby Players: %d"),
vPos.x, vPos.z, dwNumPlayers, dwNumNearbyPlayers );
TCHAR strInfo[5000];
TCHAR* strEndOfLine;
TCHAR* strStartOfLine;
// Query the IOutboudNet for info about the connection to this user
m_DP8Client.GetConnectionInfo( strInfo );
ConsolePrintf( LINE_LOG, TEXT("Displaying connection info for 0x%0.8x"), m_MazeClient.GetLocalClientID() );
ConsolePrintf( LINE_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( LINE_LOG, strStartOfLine );
strStartOfLine = strEndOfLine + 1;
}
fLastLogUpdate = fCurTime;
}
// If we are testing connect/disconnect, break after so many iterations.
if( m_Config.bAutoDisconnnect )
{
// Disconnect between 5-25seconds
static float fDisconnectCountdown = 10.0f;
fDisconnectCountdown -= fElapsedTime;
if( fDisconnectCountdown < 0.0f )
{
fDisconnectCountdown = (float)(rand() % 20000 + 5000 ) / 1000.0f;
ConsolePrintf( LINE_LOG, TEXT("Intentional disconnect. Connecting again for %0.0f seconds..."), fDisconnectCountdown );
m_DP8Client.Shutdown();
m_MazeClient.Shutdown();
}
}
if( m_bDisconnectNow )
{
m_bDisconnectNow = FALSE;
ConsolePrintf( LINE_LOG, TEXT("Intentional disconnect.") );
m_DP8Client.Shutdown();
m_MazeClient.Shutdown();
if( m_bAllowLoopback )
InitServerForLoopback();
}
}
// Update state of client
m_MazeClient.Update( fElapsedTime );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeApp::StartSessionEnum()
{
HRESULT hr;
// If we're not the preview, then enum sessions
if( m_pMazeGraphics->IsPreview() )
return S_OK;
// Start enumerating available sessions at specified IP address.
if( m_Config.bConnectToMicrosoftSite )
{
ConsolePrintf( LINE_LOG, TEXT("Connecting to DirectPlayMaze.rte.microsoft.com") );
hr = m_DP8Client.StartSessionEnum( MICROSOFT_SERVER );
}
else if( m_Config.bConnectToLocalServer )
{
ConsolePrintf( LINE_LOG, TEXT("Connecting to local server (searches the local subnet)") );
hr = m_DP8Client.StartSessionEnum( TEXT("") );
}
else if( m_Config.bConnectToRemoteServer )
{
ConsolePrintf( LINE_LOG, TEXT("Connecting to remote server at '%s'"), m_Config.szIPAddress );
hr = m_DP8Client.StartSessionEnum( m_Config.szIPAddress );
}
// If users wants Stress Connect, go through the sequence they have requested.
else if( m_Config.dwStressConnect )
{
if( m_Config.dwStressConnect == 1 )
{
// Point to local subnet for next enum.
m_Config.dwStressConnect = 2;
ConsolePrintf( LINE_LOG, TEXT("Connecting to remote server at '%s'"), m_Config.szIPAddress );
hr = m_DP8Client.StartSessionEnum( m_Config.szIPAddress );
}
else
{
// Point back at remote server for next enum.
m_Config.dwStressConnect = 1;
// Must equal 2 or something higher. Try the local subnet.
ConsolePrintf( LINE_LOG, TEXT("Connecting to local server (searches the local subnet)") );
hr = m_DP8Client.StartSessionEnum( TEXT("") );
}
}
return hr;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
BOOL CMazeApp::TryToConnect()
{
if( m_DP8Client.GetNumSessions() > 0 )
{
m_MazeClient.Reset();
m_MazeClient.SetOutboundClient( m_DP8Client.GetOutboundClient() );
m_DP8Client.SetClient( &m_MazeClient );
// Loop through the available sessions and attempt to connect
for( DWORD i = 0; i < m_DP8Client.GetNumSessions(); i++ )
{
if( SUCCEEDED(m_DP8Client.JoinSession( i ) ) )
{
return TRUE;
}
}
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMazeApp::InitServerForLoopback()
{
HRESULT hr;
m_bLocalLoopback = TRUE;
#define LOOPBACK_MAZE_WIDTH 16
#define LOOPBACK_MAZE_HEIGHT 16
// Initalize maze and server objects for loopback mode
if( FAILED( hr = m_MazeClient.m_Maze.Init( LOOPBACK_MAZE_WIDTH,
LOOPBACK_MAZE_HEIGHT,
DEFAULT_SEED ) ) )
{
return DXTRACE_ERR( TEXT("m_Maze.Init"), hr );
}
// Initialize maze server object - hook up to the maze object in the client
m_MazeServer.Init( m_bLocalLoopback, &m_MazeClient.m_Maze );
m_DummyClientConnection.SetTarget( &m_MazeServer );
m_MazeClient.SetOutboundClient( &m_DummyClientConnection );
m_DummyServerConnection.SetTarget( &m_MazeClient );
m_MazeServer.SetOutboundServer( &m_DummyServerConnection );
m_DummyClientConnection.Connect( 2 );
m_MazeClient.EngageAutopilot( TRUE );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
UINT WINAPI CMazeApp::StaticOutputMsgThread( LPVOID pParam )
{
return s_pMazeApp->OutputMsgThread( pParam );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
UINT WINAPI CMazeApp::OutputMsgThread( LPVOID pParam )
{
#define MAX_LOG_LINES 100
TCHAR szLogBuffer[MAX_PATH];
DWORD dwNumProcessed = 0;
while( 1 )
{
// Wait for output to be added to the queue or the quit flag to be set
WaitForSingleObject( m_hOutputMsgEvent, INFINITE );
if( m_bQuitThread )
break;
// Update the time stamp
UpdateTimeStamp();
// Lock output queue
m_OutputMsgQueueLock.Enter();
dwNumProcessed = 0;
// While we have there are messages to print and we
// have display'ed less than 5 messages
while ( m_lQueueSize > 0 && dwNumProcessed < 5 )
{
switch( m_EnumLineType[m_dwNextOutputMsg] )
{
case LINE_LOG:
{
// Add m_szOutputMsgBuffer[m_dwNextOutputMsg] to szLogBuffer array,
// and redisplay the array on the top half of the screen
_stprintf( szLogBuffer, TEXT("%s %s"),
m_strTimeStamp, m_szOutputMsgBuffer[m_dwNextOutputMsg] );
#ifdef _DEBUG
OutputDebugString( szLogBuffer );
OutputDebugString( TEXT("\n") );
#endif
if( m_hLogFile )
{
DWORD dwWritten;
WriteFile( m_hLogFile, szLogBuffer,
lstrlen( szLogBuffer ), &dwWritten, NULL );
TCHAR strEOL = TEXT('\r');
WriteFile( m_hLogFile, &strEOL,
sizeof(TCHAR), &dwWritten, NULL );
strEOL = TEXT('\n');
WriteFile( m_hLogFile, &strEOL,
sizeof(TCHAR), &dwWritten, NULL );
static float s_fLastFlushTime = DXUtil_Timer( TIMER_GETAPPTIME );
float fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
if( fCurTime - s_fLastFlushTime > 0.2f )
{
FlushFileBuffers( m_hLogFile );
s_fLastFlushTime = fCurTime;
}
}
break;
}
default:
_tcscpy( szLogBuffer, m_szOutputMsgBuffer[m_dwNextOutputMsg] );
break;
}
m_pMazeGraphics->HandleOutputMsg( m_EnumLineType[m_dwNextOutputMsg], szLogBuffer );
m_dwNextOutputMsg++;
if( m_dwNextOutputMsg == MAX_OUTPUT_QUEUE )
m_dwNextOutputMsg = 0;
m_lQueueSize--;
dwNumProcessed++;
}
// Unlock output queue
m_OutputMsgQueueLock.Leave();
if( m_hLogFile )
FlushFileBuffers( m_hLogFile );
// Yield time to other threads
Sleep( 10 );
// If there are still messages left, then signal the event
if( m_lQueueSize > 0 )
SetEvent( m_hOutputMsgEvent );
}
if( m_hLogFile )
{
CloseHandle( m_hLogFile );
m_hLogFile = NULL;
}
return 0;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeApp::ConsolePrintf( EnumLineType enumLineType, const TCHAR* fmt, ... )
{
// Format the message into a buffer
TCHAR buffer[512];
_vstprintf( buffer, fmt, (CHAR*) ((&fmt)+1) );
// Lock the output queue
m_OutputMsgQueueLock.Enter();
// Find free spot
if( m_lQueueSize != MAX_OUTPUT_QUEUE )
{
// Format message into the buffer
_vstprintf( m_szOutputMsgBuffer[m_dwNextFreeOutputMsg], fmt, (CHAR*)((&fmt)+1) );
m_EnumLineType[m_dwNextFreeOutputMsg] = enumLineType;
// Increment output pointer and wrap around
m_dwNextFreeOutputMsg++;
if( m_dwNextFreeOutputMsg == MAX_OUTPUT_QUEUE )
m_dwNextFreeOutputMsg = 0;
// Increment message count
m_lQueueSize++;
}
// Unlock output queue
m_OutputMsgQueueLock.Leave();
// Signal event so the output thread empties the queue
SetEvent( m_hOutputMsgEvent );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeApp::UpdateTimeStamp()
{
static float s_fTimeStampUpdateCountdown = -10.0f;
float fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
if( fCurTime - s_fTimeStampUpdateCountdown > 1.0f )
{
SYSTEMTIME sysTime;
GetLocalTime( &sysTime );
_stprintf( m_strTimeStamp, TEXT("[%02d-%02d-%02d %02d:%02d:%02d]"),
sysTime.wMonth, sysTime.wDay, sysTime.wYear % 100,
sysTime.wHour, sysTime.wMinute, sysTime.wSecond );
// Compute how many milliseconds until the next second change
s_fTimeStampUpdateCountdown = fCurTime;
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeApp::CreateTempLogFile()
{
BOOL bSuccess;
TCHAR strTempFileName[MAX_PATH];
TCHAR strTime[MAX_PATH];
DWORD dwCount;
GetTempPath( MAX_PATH, m_strLogDir );
lstrcat( m_strLogDir, TEXT("DirectPlayMaze\\") );
// Create the directory if it doesn't exist
if( GetFileAttributes( m_strLogDir ) == -1 )
{
bSuccess = CreateDirectory( m_strLogDir, NULL );
if( !bSuccess )
{
ConsolePrintf( LINE_LOG, TEXT("Could not create create temp directory '%s'"), m_strLogDir );
goto LFail;
}
}
ConsolePrintf( LINE_LOG, TEXT("Log Directory: '%s'"), m_strLogDir );
SYSTEMTIME sysTime;
GetLocalTime( &sysTime );
_stprintf( strTime, TEXT("client-%04d-%02d-%02d-"),
sysTime.wYear, sysTime.wMonth, sysTime.wDay );
dwCount = 0;
while(TRUE)
{
wsprintf( m_strLogFile, TEXT("%s%05d.log"), strTime, dwCount );
lstrcpy( strTempFileName, m_strLogDir );
lstrcat( strTempFileName, m_strLogFile );
DWORD dwResult = GetFileAttributes( strTempFileName );
if( dwResult == -1 )
break;
dwCount++;
}
if( m_hLogFile )
{
CloseHandle( m_hLogFile );
m_hLogFile = NULL;
}
m_hLogFile = CreateFile( strTempFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
if( m_hLogFile == INVALID_HANDLE_VALUE )
{
ConsolePrintf( LINE_LOG, TEXT("Could not create create temp file '%s'"), strTempFileName );
goto LFail;
}
ConsolePrintf( LINE_LOG, TEXT("Logging to temp file: '%s'"), m_strLogFile );
return;
LFail:
ConsolePrintf( LINE_LOG, TEXT("File logging disabled") );
m_Config.bFileLogging = FALSE;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeApp::CloseTempLogFile()
{
CloseHandle( m_hLogFile );
m_hLogFile = NULL;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeApp::ReadConfig()
{
HKEY hKey = NULL;
RegOpenKeyEx( HKEY_CURRENT_USER, MAZE_REGKEYNAME, 0, KEY_READ, &hKey );
DXUtil_ReadBoolRegKey( hKey, TEXT("ConnectToMicrosoftSite"), &m_Config.bConnectToMicrosoftSite, TRUE );
DXUtil_ReadBoolRegKey( hKey, TEXT("ConnectToLocalServer"), &m_Config.bConnectToLocalServer, FALSE );
DXUtil_ReadBoolRegKey( hKey, TEXT("ConnectToRemoteServer"), &m_Config.bConnectToRemoteServer, FALSE );
DXUtil_ReadIntRegKey( hKey, TEXT("NetworkRetryDelay"), &m_Config.dwNetworkRetryDelay, 30 );
DXUtil_ReadBoolRegKey( hKey, TEXT("FileLogging"), &m_Config.bFileLogging, TRUE );
DXUtil_ReadStringRegKey( hKey, TEXT("IPAddress"), m_Config.szIPAddress, sizeof(m_Config.szIPAddress), TEXT("\0") );
DXUtil_ReadBoolRegKey( hKey, TEXT("ShowFramerate"), &m_Config.bShowFramerate, TRUE );
DXUtil_ReadBoolRegKey( hKey, TEXT("ShowIndicators"), &m_Config.bShowIndicators, TRUE );
DXUtil_ReadBoolRegKey( hKey, TEXT("DrawMiniMap"), &m_Config.bDrawMiniMap, TRUE );
DXUtil_ReadBoolRegKey( hKey, TEXT("FullScreen"), &m_Config.bFullScreen, TRUE );
DXUtil_ReadBoolRegKey( hKey, TEXT("Reflections"), &m_Config.bReflections, FALSE );
DXUtil_ReadBoolRegKey( hKey, TEXT("AutoDisconnnect"), &m_Config.bAutoDisconnnect, FALSE );
DXUtil_ReadBoolRegKey( hKey, TEXT("AutoConnnect"), &m_Config.bAutoConnnect, FALSE );
DXUtil_ReadIntRegKey( hKey, TEXT("LogLevel"), &m_Config.dwLogLevel, 2 );
DXUtil_ReadIntRegKey( hKey, TEXT("AutoPrintStats"), &m_Config.dwAutoPrintStats, 10 );
RegCloseKey( hKey );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CMazeApp::WriteConfig()
{
HKEY hKey;
DWORD dwDisposition;
if( !m_bSaveSettings )
return;
RegCreateKeyEx( HKEY_CURRENT_USER, MAZE_REGKEYNAME, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
&hKey, &dwDisposition );
DXUtil_WriteBoolRegKey( hKey, TEXT("ConnectToMicrosoftSite"), m_Config.bConnectToMicrosoftSite );
DXUtil_WriteBoolRegKey( hKey, TEXT("ConnectToLocalServer"), m_Config.bConnectToLocalServer );
DXUtil_WriteBoolRegKey( hKey, TEXT("ConnectToRemoteServer"), m_Config.bConnectToRemoteServer );
DXUtil_WriteIntRegKey( hKey, TEXT("NetworkRetryDelay"), m_Config.dwNetworkRetryDelay );
DXUtil_WriteBoolRegKey( hKey, TEXT("FileLogging"), m_Config.bFileLogging );
DXUtil_WriteStringRegKey( hKey, TEXT("IPAddress"), m_Config.szIPAddress );
DXUtil_WriteBoolRegKey( hKey, TEXT("ShowFramerate"), m_Config.bShowFramerate );
DXUtil_WriteBoolRegKey( hKey, TEXT("ShowIndicators"), m_Config.bShowIndicators );
DXUtil_WriteBoolRegKey( hKey, TEXT("DrawMiniMap"), m_Config.bDrawMiniMap );
DXUtil_WriteBoolRegKey( hKey, TEXT("FullScreen"), m_Config.bFullScreen );
DXUtil_WriteBoolRegKey( hKey, TEXT("Reflections"), m_Config.bReflections );
DXUtil_WriteBoolRegKey( hKey, TEXT("AutoDisconnnect"), m_Config.bAutoDisconnnect );
DXUtil_WriteBoolRegKey( hKey, TEXT("AutoConnnect"), m_Config.bAutoConnnect );
DXUtil_WriteIntRegKey( hKey, TEXT("LogLevel"), m_Config.dwLogLevel );
DXUtil_WriteIntRegKey( hKey, TEXT("AutoPrintStats"), m_Config.dwAutoPrintStats );
RegCloseKey( hKey );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void ConsolePrintf( ServerBufferType enumLineType, const TCHAR* fmt , ... )
{
// Format the message into a buffer
TCHAR buffer[512];
_vstprintf( buffer, fmt, (CHAR*) ((&fmt)+1) );
s_pMazeApp->ConsolePrintf( LINE_LOG, buffer );
}

View File

@@ -0,0 +1,150 @@
//----------------------------------------------------------------------------
// File: mazeapp.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _MAZE_APP_H
#define _MAZE_APP_H
#include "DPlay8Client.h"
#include "MazeClient.h"
#include "IMazeGraphics.h"
class CDPlay8Client;
//-----------------------------------------------------------------------------
// Defines, and constants
//-----------------------------------------------------------------------------
#define MAX_OUTPUT_QUEUE 256
#define MICROSOFT_SERVER TEXT("DirectPlayMaze.rte.microsoft.com")
#define MAZE_REGKEYNAME TEXT("Software\\Microsoft\\DirectPlayMaze\\MazeClient")
//-----------------------------------------------------------------------------
// Dplay Defines, and constants
//-----------------------------------------------------------------------------
#define MIN_SP_THREADS 1
#define MAX_SP_THREADS 128
#define MAX_SP_BUFFER 1024000
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
struct MazeConfig
{
BOOL bConnectToMicrosoftSite;
BOOL bConnectToLocalServer;
BOOL bConnectToRemoteServer;
DWORD dwNetworkRetryDelay;
BOOL bShowFramerate;
BOOL bShowIndicators;
BOOL bDrawMiniMap;
BOOL bFullScreen;
BOOL bReflections;
BOOL bFileLogging;
DWORD dwSPThreads;
DWORD dwSPBufferSize;
DWORD dwStressConnect;
TCHAR szIPAddress[64];
DWORD dwLogLevel;
DWORD dwAutoPrintStats;
BOOL bAutoDisconnnect;
BOOL bAutoConnnect;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
class CMazeApp
{
public:
CMazeApp();
~CMazeApp();
HRESULT Create( IMazeGraphics* pMazeGraphics );
int Run( HINSTANCE hInst );
HRESULT FrameMove( FLOAT fElapsedTime );
void ConsolePrintf( EnumLineType EnumLineType, const TCHAR* fmt, ... );
void ReadConfig();
void WriteConfig();
void CreateTempLogFile();
void CloseTempLogFile();
MazeConfig* GetConfig() {return &m_Config; };
TCHAR* GetLogDir() { return m_strLogDir; };
TCHAR* GetLogFile() { return m_strLogFile; };
BOOL IsOutOfDateClient() { return m_bOutOfDateClient; };
VOID SetOutOfDateClient( BOOL bOutOfDateClient ) { m_bOutOfDateClient = bOutOfDateClient; };
VOID SetAllowConnect( BOOL bAllowConnect ) { m_bAllowConnect = bAllowConnect; };
VOID SetSaveSettings( BOOL bSaveSettings ) { m_bSaveSettings = bSaveSettings; };
VOID SetConnectNow( BOOL bConnectNow ) { m_bConnectNow = bConnectNow; };
VOID SetDisconnectNow( BOOL bDisconnectNow ) { m_bDisconnectNow = bDisconnectNow; };
VOID SetAllowLoopback( BOOL bAllowLoopback ) { m_bAllowLoopback = bAllowLoopback; };
protected:
static UINT WINAPI StaticOutputMsgThread( LPVOID pParam );
static UINT WINAPI StaticClientThread( LPVOID pParam );
UINT WINAPI OutputMsgThread( LPVOID pParam );
UINT WINAPI ClientThread( LPVOID pParam );
void SuspendPowerManagement();
BOOL ParseCommandLine();
HRESULT StartSessionEnum();
HRESULT InitServerForLoopback();
BOOL TryToConnect();
void UpdateTimeStamp();
IMazeGraphics* m_pMazeGraphics;
CDPlay8Client m_DP8Client;
CMazeClient m_MazeClient;
CMazeServer m_MazeServer;
CDummyConnectorServer m_DummyServerConnection;
CDummyConnectorClient m_DummyClientConnection;
MazeConfig m_Config;
HANDLE m_hOutputMsgThread;
BOOL m_bQuitThread;
HANDLE m_hClientThread;
DWORD m_dwNextOutputMsg;
BOOL m_bLocalLoopbackInitDone;
BOOL m_bInitDone;
BOOL m_bAllowLoopback;
BOOL m_bLocalLoopback;
BOOL m_bAllowConnect;
BOOL m_bConnectNow;
BOOL m_bDisconnectNow;
BOOL m_bOutOfDateClient;
TCHAR m_strTimeStamp[50];
BOOL m_bSaveSettings;
CCriticalSection m_OutputMsgQueueLock;
LONG m_lQueueSize;
TCHAR m_szOutputMsgBuffer[MAX_OUTPUT_QUEUE][256];
EnumLineType m_EnumLineType[MAX_OUTPUT_QUEUE];
DWORD m_dwNextFreeOutputMsg;
HANDLE m_hOutputMsgEvent;
HANDLE m_hLogFile;
TCHAR m_strLogFile[MAX_PATH];
TCHAR m_strLogDir[MAX_PATH];
};
#endif

View File

@@ -0,0 +1,945 @@
//----------------------------------------------------------------------------
// 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;
}

View File

@@ -0,0 +1,200 @@
//----------------------------------------------------------------------------
// File: mazeclient.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _MAZE_CLIENT_H
#define _MAZE_CLIENT_H
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
#include "DXUtil.h"
#include "Maze.h"
#include "NetAbstract.h"
#include "Packets.h"
#include "Trig.h"
#include "SimpleStack.h"
#include "Packets.h"
#include "MazeServer.h"
#include "IMazeGraphics.h"
class CMazeApp;
// The MAZE_CLIENT_VERSION should be rev'ed whenever the client exposes
// new functionality that the server expects. This number is sent to
// the server so the server can accept or reject the client based on its version.
#define MAZE_CLIENT_VERSION 107
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
struct PlayerObject
{
DWORD dwID;
D3DXVECTOR3 vPos;
ANGLE aCameraYaw;
WORD wCellX;
WORD wCellY;
FLOAT fLastValidTime;
PlayerObject* pNext;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
#define MAZE_WIDTH 128
#define MAZE_HEIGHT 128
#define MAZE_SIZE (MAZE_WIDTH*MAZE_HEIGHT)
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
class CMazeClient : public INetClient
{
public:
CMazeClient();
~CMazeClient();
void SetApp( CMazeApp* pMazeApp ) { m_pMazeApp = pMazeApp; };
// INetClient
virtual HRESULT OnPacket( DWORD from , void* data , DWORD size );
virtual void OnSessionLost( DWORD dwReason );
// Connect an outbound network provider
void SetOutboundClient( IOutboundClient* poutnet ) { m_pNet = poutnet; };
HRESULT Init( CMazeApp* pMazeApp, IMazeGraphics* pMazeGraphics );
HRESULT Reset();
void Shutdown();
void Update( FLOAT elapsed );
// Lock and unlock the world database
void LockWorld() { m_WorldLock.Enter(); };
void UnlockWorld() { m_WorldLock.Leave(); };
// Lock and unlock the world database
void SetMazeReady( BOOL bReady ) { if( bReady ) SetEvent( m_hReady ); else ResetEvent( m_hReady ); };
BOOL IsMazeReady() { if( WaitForSingleObject( m_hReady, 0 ) == WAIT_OBJECT_0 ) return TRUE; else return FALSE; };
// Check to see if we have received first Connect Config Packet.
void SetFirstConfig( BOOL bReady ) { if( bReady ) SetEvent( m_hGotFirstConfig ); else ResetEvent( m_hGotFirstConfig ); };
BOOL GotFirstConfig() { if( WaitForSingleObject( m_hGotFirstConfig, 0 ) == WAIT_OBJECT_0 ) return TRUE; else return FALSE; };
// Get data useful for the engine (current position, etc. etc.)
D3DXVECTOR3 GetCameraPos() const { return m_vCameraPos; };
ANGLE GetCameraYaw() const { return m_aCameraYaw; };
DWORD GetNumPlayerObjects() const { return m_dwNumPlayerObjects; };
// Get first engine PlayerObject is a cell
// NOTE: The engine must lock the world DB before traversing the cells
PlayerObject* GetFirstPlayerObjectInCell( DWORD x, DWORD z );
// The layout of the maze (engine needs this to draw)
CMaze m_Maze;
// Get network stats
DWORD GetThroughputBPS();
DWORD GetRoundTripLatencyMS();
void GetPlayerStats( DWORD* pdwNumPlayers, DWORD* pdwNumNearbyPlayers ) { m_StatLock.Enter(); *pdwNumPlayers = m_dwNumPlayers; *pdwNumNearbyPlayers = m_dwNumNearbyPlayers; m_StatLock.Leave(); };
void SetPlayerStats( DWORD dwNumPlayers, DWORD dwNumNearbyPlayers ) { m_StatLock.Enter(); m_dwNumPlayers = dwNumPlayers; m_dwNumNearbyPlayers = dwNumNearbyPlayers; m_StatLock.Leave(); };
DWORD GetLocalClientID() const { return m_dwLocalClientID; };
// Autopilot
void EngageAutopilot( BOOL engage );
void SetAutopilot(BOOL engage) { m_bEngageAutopilot = engage; };
BOOL IsAutopilot() const { return m_bAutopilot; };
// Set whether or not we have input focus
void SetInputFocus( BOOL havefocus ) { m_bHaveInputFocus = havefocus; };
protected:
CMazeApp* m_pMazeApp;
IMazeGraphics* m_pMazeGraphics;
D3DXVECTOR3 m_vCameraPos;
ANGLE m_aCameraYaw;
DWORD m_dwNumPlayerObjects;
CRandom m_Rand;
IOutboundClient* m_pNet;
FLOAT m_fLastOutboundTime;
DWORD m_dwNumPlayers;
DWORD m_dwNumNearbyPlayers;
DWORD m_dwLocalClientID;
BOOL m_bHaveInputFocus;
void SendPacket( ClientPacket* packet , DWORD size , BOOL guaranteed, DWORD dwTimeout );
BOOL IsValidPackSize( DWORD dwSize );
// Arrays of cells and PlayerObjects (this consitutes the world DB), with associated lock
PlayerObject* m_pctCells[SERVER_MAX_HEIGHT][SERVER_MAX_WIDTH];
PlayerObject m_PlayerObjects[MAX_PLAYER_OBJECTS];
CCriticalSection m_WorldLock;
CCriticalSection m_StatLock;
HANDLE m_hReady;
// Autopilot stuff
struct AutopilotCell
{
AutopilotCell() {};
AutopilotCell( BYTE X , BYTE Y ) : x(X),y(Y) {};
BYTE x,y;
};
SimpleStack<AutopilotCell,MAZE_SIZE> m_AutopilotStack;
BYTE m_AutopilotVisited[MAZE_HEIGHT][MAZE_WIDTH];
BOOL m_bAutopilot;
BOOL m_bEngageAutopilot;
D3DXVECTOR3 m_vAutopilotTarget;
ANGLE m_aAutopilotTargetAngle;
void DoAutopilot( FLOAT elapsed );
void DoManualPilot( FLOAT elapsed );
void PickAutopilotTarget();
void HandlePlayerObjectsInAckPacket( ServerAckPacket* ppacket );
void AddPlayerObjectToCells( PlayerObject* pPlayerObject );
void RemovePlayerObjectFromCells( PlayerObject* pPlayerObject );
BOOL IsPlayerObjectInCell( DWORD wCellX, DWORD wCellY, PlayerObject* pPlayerObject );
HANDLE m_hGotFirstConfig;
ClientNetConfig m_NetConfig;
CRandom m_NetRandom;
BOOL m_bDoneInit;
private:
CMazeClient( const CMazeClient& );
void operator=( const CMazeClient& );
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,275 @@
//----------------------------------------------------------------------------
// File: mazeserver.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _MAZESERVER_H
#define _MAZESERVER_H
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
#include "NetAbstract.h"
#include "SyncObjects.h"
#include "Maze.h"
#include "Random.h"
#include "Packets.h"
#include "Trig.h"
class CMaze;
struct ClientPosPacket;
#define SERVER_MAX_WIDTH 128
#define SERVER_MAX_HEIGHT 128
#define DEFAULT_MAZE_WIDTH 16
#define DEFAULT_MAZE_HEIGHT 16
#define DEFAULT_SEED 314159
#define LOCK_GRID_SIZE 16
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
struct PlayerData
{
DWORD dwID; // Client ID
DWORD NetID; // NetID for owning player (0 if none)
DWORD dwVersion; // Version of the owning player
BOOL bAllow; // If FALSE, then we should drop this player
// Links for the various lists
PlayerData* pNext; // Free/active PlayerData list (double link)
PlayerData* pPrevious;
PlayerData* pNextInCell; // Cell list (single link)
PlayerData* pNextInIDHashBucket; // ID hash bucket (single link)
FLOAT fLastDisplayTime;
FLOAT fLastCITime;
BOOL bActive;
float fPosX; // Floating point position
float fPosY;
WORD wCellX; // Coordinates of the cell this player is in
WORD wCellY; // or (0xffff,0xffff) for off-map
ANGLE aCameraYaw;
DWORD dwNumNearbyPlayers; // Number of nearby players
DWORD pad[4];
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
struct ServerCell
{
PlayerData* pFirstPlayerData;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
class CMazeServer : public INetServer
{
public:
CMazeServer();
// From INetServer
void OnAddConnection( DWORD dwID );
HRESULT OnPacket( DWORD from, void* pData, DWORD dwSize );
void OnRemoveConnection( DWORD dwID );
void OnSessionLost( DWORD dwReason );
// Hook up to the network service
void SetOutboundServer( IOutboundServer* poutnet ) { m_pNet = poutnet; };
// Initialisation - need to hook up a maze object
HRESULT Init( BOOL bLocalLoopback, const CMaze* pmaze );
void Shutdown();
// Chance of server sending packets via reliable transport
void SetServerReliableRate( DWORD percent ) { m_dwServerReliableRate = percent; };
DWORD GetServerReliableRate() const { return m_dwServerReliableRate; };
// Timeout of server's packets
void SetServerTimeout( DWORD timeout ) { m_dwServerTimeout = timeout; };
DWORD GetServerTimeout() const { return m_dwServerTimeout; };
// Change of client sending packets via reliable transport. Setting this causes the server
// to propagate this setting to all currently connected clients
void SetClientReliableRate( DWORD percent );
DWORD GetClientReliableRate() const { return DWORD(m_ClientNetConfig.ubReliableRate); };
// Change client update rate. Setting this causes the server to propagate this setting to all
// currently connected clients
void SetClientUpdateRate( DWORD rate );
DWORD GetClientUpdateRate() const { return DWORD(m_ClientNetConfig.wUpdateRate); };
// Change client timeout. Setting this causes the server to propagate this setting to all
// currently connected clients
void SetClientTimeout( DWORD timeout );
DWORD GetClientTimeout() const { return DWORD(m_ClientNetConfig.wTimeout); };
// Change Client position pack size. Setting this causes the server to propagate this setting to all
// currently connected clients
void SetClientPackSize( DWORD size );
DWORD GetClientPackSize() const { return DWORD(m_ClientNetConfig.wClientPackSizeArray[m_ClientNetConfig.ubClientPackIndex]); };
// Change Server position pack size.
void SetServerPackSize( DWORD size );
DWORD GetServerPackSize() const { return DWORD(m_ClientNetConfig.wServerPackSizeArray[m_ClientNetConfig.ubServerPackIndex]); };
// How long the user wants to hold the Server's Dplay Threads
void SetServerThreadWait( DWORD threadwait ) { m_dwServerThreadWait = threadwait; };
DWORD GetServerThreadWait() const { return m_dwServerThreadWait; };
// How long the user wants to hold the Server's Dplay Threads
void SetClientThreadWait( DWORD threadwait );
DWORD GetClientThreadWait() const { return DWORD(m_ClientNetConfig.dwThreadWait); };
// Various commands
void DisplayConnectionInfo( DWORD dwID );
void DisplayNextConnectionInfo();
void PrintStats();
void SetLogLevel( DWORD dwLogLevel ) { m_dwLogLevel = dwLogLevel; }
DWORD GetLogLevel() { return m_dwLogLevel; }
DWORD GetNumPlayers() { return m_dwPlayerCount; }
protected:
BOOL m_bLocalLoopback;
IOutboundServer* m_pNet;
const CMaze* m_pMaze;
DWORD m_dwLogLevel;
DWORD m_dwWidth;
DWORD m_dwHeight;
CCriticalSection m_AddRemoveLock;
// A fixed sized grid of locks which we lay over the maze to control access to it
// We demand that the maze dimensions are a power-of-2 times the dimensions of this
// grid, and pre-store that power to allow fast translation
CLockArray<LOCK_GRID_SIZE,LOCK_GRID_SIZE> m_LockGrid;
DWORD m_dwMazeXShift;
DWORD m_dwMazeYShift;
void LockCell( DWORD x , DWORD y );
void UnlockCell( DWORD x , DWORD y );
void LockRange( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 );
void UnlockRange( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 );
void LockCellPair( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 );
void UnlockCellPair( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 );
CCriticalSection m_OffMapLock;
// The PlayerData lists
PlayerData m_PlayerDatas[MAX_PLAYER_OBJECTS];
PlayerData* m_pFirstActivePlayerData;
PlayerData* m_pFirstFreePlayerData;
DWORD m_dwActivePlayerDataCount;
DWORD m_dwPlayerDataUniqueValue;
CCriticalSection m_PlayerDataListLock;
// The player object locks
enum { NUM_PLAYER_OBJECT_LOCKS = 16 };
CCriticalSection m_PlayerDataLocks[NUM_PLAYER_OBJECT_LOCKS];
void LockPlayerData( PlayerData* pPlayerData ) { m_PlayerDataLocks[((pPlayerData-m_PlayerDatas) & (NUM_PLAYER_OBJECT_LOCKS-1))].Enter(); };
void UnlockPlayerData( PlayerData* pPlayerData ) { m_PlayerDataLocks[((pPlayerData-m_PlayerDatas) & (NUM_PLAYER_OBJECT_LOCKS-1))].Leave(); };
PlayerData* CreatePlayerData();
void DestroyPlayerData( PlayerData* pPlayerData );
// The cell array and the "off-map" cell.
ServerCell m_OffMapCell;
ServerCell m_Cells[SERVER_MAX_WIDTH][SERVER_MAX_HEIGHT];
// Remove playerdata from its cell
void RemovePlayerDataFromCell( PlayerData* pPlayerData );
// Unsafe versions of add/remove. Must have playerdata and cell locked when you call this
void UnsafeRemovePlayerDataFromCell( PlayerData* pPlayerData );
void UnsafeAddPlayerDataToCell( PlayerData* pPlayerData );
ServerCell* GetCell( PlayerData* pPlayerData )
{
if ( pPlayerData->wCellX == 0xffff )
return &m_OffMapCell;
else
return &m_Cells[pPlayerData->wCellY][pPlayerData->wCellX];
};
void HandleClientPosPacket( DWORD dwFrom, ClientPosPacket* pPacket );
void HandleClientVersionPacket( DWORD dwFrom, ClientVersionPacket* pClientVersionPack );
void HandleUnknownPacket( DWORD dwFrom, ClientPacket* pClientPack, DWORD size );
BOOL IsValidPackSize( DWORD wSize );
BOOL IsClientVersionSupported( DWORD dwClientVersion );
DWORD m_dwPlayerCount;
CCriticalSection m_csThreadCountLock;
WORD m_wActiveThreadCount;
WORD m_wMaxThreadCount;
FLOAT m_fAvgThreadCount;
FLOAT m_fAvgThreadTime;
FLOAT m_fMaxThreadTime;
DWORD m_dwPeakPlayerCount;
// Hashing DPIDs to PlayerData pointers
void SetPlayerDataForID( DWORD dwID, PlayerData* pPlayerData );
PlayerData* GetPlayerDataForID( DWORD dwID );
void RemovePlayerDataID( PlayerData* pPlayerData );
DWORD IDHash( DWORD dwID );
enum { NUM_ID_HASH_BUCKETS = 1024 };
enum { NUM_ID_HASH_BUCKET_LOCKS = 16 };
PlayerData* m_pstIDHashBucket[NUM_ID_HASH_BUCKETS];
CCriticalSection m_IDHashBucketLocks[NUM_ID_HASH_BUCKET_LOCKS];
// Random number generator
CRandom m_Rand;
// Send packet wrapper
HRESULT SendPacket( DWORD dwTo, void* pData, DWORD dwSize, BOOL bReliable, DWORD dwTimeout );
void SendConfigPacketToAll( ServerConfigPacket* pPacket );
// Network configuration parameters
DWORD m_dwServerReliableRate;
DWORD m_dwServerTimeout;
DWORD m_dwServerThreadWait;
ClientNetConfig m_ClientNetConfig;
CCriticalSection m_ClientNetConfigLock;
};
//-----------------------------------------------------------------------------
// Name:
// Desc: This function is called by the server to output informational text.
// In the client it should probably just be a dummy function, in the server
// it should probably just spew out to the console
//-----------------------------------------------------------------------------
enum ServerBufferType { SLINE_PROMPT, SLINE_INPUT, SLINE_LOG, SLINE_CMD };
void ConsolePrintf( ServerBufferType enumLineType, const TCHAR* fmt , ... );
#endif

View File

@@ -0,0 +1,86 @@
//----------------------------------------------------------------------------
// File: netabstract.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _NETABSTRACT_H
#define _NETABSTRACT_H
#define SERVER_ID 1
enum { DISCONNNECT_REASON_UNKNOWN = 0, DISCONNNECT_REASON_CLIENT_OUT_OF_DATE };
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
interface INetServer
{
public:
virtual ~INetServer() {} ;
virtual HRESULT OnPacket( DWORD dwFrom, void* dwData, DWORD dwSize ) = 0;
virtual void OnAddConnection( DWORD dwID ) = 0;
virtual void OnRemoveConnection( DWORD dwID ) = 0;
virtual void OnSessionLost( DWORD dwReason ) = 0;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
interface INetClient
{
public:
virtual ~INetClient() {} ;
virtual HRESULT OnPacket( DWORD dwFrom, void* dwData, DWORD dwSize ) = 0;
virtual void OnSessionLost( DWORD dwReason ) = 0;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
interface IOutboundServer
{
public:
virtual HRESULT SendPacket( DWORD dwTo, void* dwData, DWORD dwSize,
BOOL dwGuaranteed, DWORD dwTimeout ) = 0;
virtual HRESULT GetConnectionInfo( DWORD dwID, TCHAR* strConnectionInfo ) = 0;
virtual HRESULT RejectClient( DWORD dwID, HRESULT hrReason ) = 0;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
interface IOutboundClient
{
public:
virtual HRESULT SendPacket( void* dwData, DWORD dwSize,
BOOL dwGuaranteed, DWORD dwTimeout ) = 0;
virtual DWORD GetThroughputBPS() = 0;
virtual DWORD GetRoundTripLatencyMS() = 0;
virtual BOOL IsSessionLost() = 0;
virtual DWORD GetSessionLostReason() = 0;
virtual HRESULT GetConnectionInfo( TCHAR* strConnectionInfo ) = 0;
};
#endif

View File

@@ -0,0 +1,188 @@
//----------------------------------------------------------------------------
// File: packets.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _PACKETS_H
#define _PACKETS_H
#include "Trig.h"
#pragma pack(push)
#pragma pack(1)
#define PACK_ARRAY_SIZE 10 //Set the number of elements in our pack array to 10.
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
enum
{
PACKETTYPE_SERVER_CONFIG, // first packet sent
PACKETTYPE_CLIENT_VERSION, // client responds to PACKETTYPE_SERVER_CONFIG w/ this
PACKETTYPE_SERVER_ACKVERSION, // server then responds to PACKETTYPE_CLIENT_VERSION w/ this and game begins
PACKETTYPE_CLIENT_POS, // sent to server as client moves
PACKETTYPE_SERVER_ACKPOS // sent to client as server acks the PACKETTYPE_CLIENT_POS packets
};
//-----------------------------------------------------------------------------
// Name:
// Desc: Base class for packets sent from client to server
//-----------------------------------------------------------------------------
struct ClientPacket
{
ClientPacket();
ClientPacket( WORD type ) :
wType(type) {};
WORD wType;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
struct ClientPosPacket : public ClientPacket
{
ClientPosPacket();
ClientPosPacket( float x , float y, ANGLE cameraYaw )
: ClientPacket( PACKETTYPE_CLIENT_POS ) , fX(x) , fY(y), aCameraYaw(cameraYaw) {};
float fX,fY;
ANGLE aCameraYaw;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
struct ClientVersionPacket : public ClientPacket
{
ClientVersionPacket();
ClientVersionPacket( DWORD version )
: ClientPacket( PACKETTYPE_CLIENT_VERSION ) , dwVersion(version) {};
DWORD dwVersion;
};
//-----------------------------------------------------------------------------
// Name:
// Desc: Structure containing client net configuration data.
// The server sends this to clients
//-----------------------------------------------------------------------------
struct ClientNetConfig
{
DWORD dwMazeWidth;
DWORD dwMazeHeight;
DWORD dwThreadWait;
WORD wUpdateRate;
WORD wTimeout;
WORD wClientPackSizeArray[PACK_ARRAY_SIZE]; //Array of 10 custom sizes passed in by user.
WORD wServerPackSizeArray[PACK_ARRAY_SIZE]; //Array of 10 custom server sizes.
BYTE ubClientPackIndex;
BYTE ubServerPackIndex;
BYTE ubReliableRate; // Percentage of packets to be transmitted reliably
};
//-----------------------------------------------------------------------------
// Name:
// Desc: Base class for packets sent from server to client
//-----------------------------------------------------------------------------
struct ServerPacket
{
ServerPacket();
ServerPacket( WORD type ) : wType(type) {};
WORD wType;
};
//-----------------------------------------------------------------------------
// Name:
// Desc: Configuration data send from server to client
//-----------------------------------------------------------------------------
struct ServerAckVersionPacket : public ServerPacket
{
ServerAckVersionPacket();
ServerAckVersionPacket( BOOL accepted, DWORD clientID ) :
ServerPacket(PACKETTYPE_SERVER_ACKVERSION), bAccepted(accepted), dwClientID(clientID) {};
DWORD dwClientID;
BOOL bAccepted;
};
//-----------------------------------------------------------------------------
// Name:
// Desc: Configuration data send from server to client
//-----------------------------------------------------------------------------
struct ServerConfigPacket : public ServerPacket
{
ServerConfigPacket();
ServerConfigPacket( const ClientNetConfig& config ) :
ServerPacket(PACKETTYPE_SERVER_CONFIG) , Config(config) {};
ClientNetConfig Config;
};
//-----------------------------------------------------------------------------
// Name:
// Desc: Chunk of client data for server send to a client
//-----------------------------------------------------------------------------
struct PlayerStatePacket
{
DWORD dwID;
float fX;
float fY;
ANGLE aCameraYaw;
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
struct ServerAckPacket : public ServerPacket
{
ServerAckPacket();
ServerAckPacket( DWORD playercount )
: ServerPacket(PACKETTYPE_SERVER_ACKPOS),
wPlayerCount(WORD(playercount)) {};
WORD wPlayerCount; // Count of total players on server
WORD wPlayerStatePacketCount; // Count of following PlayerStatePacket structures
};
#pragma pack(pop)
#endif

View File

@@ -0,0 +1,66 @@
//----------------------------------------------------------------------------
// File: random.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _RANDOM_H
#define _RANDOM_H
//-----------------------------------------------------------------------------
// Name:
// Desc: Random number generator class - a simple linear congruential generator.
// We use this instead of the CRT function because we want to be certain that
// we are using the exact same generator on both server and client side and so
// (a) don't want to be at the mercy of CRT version changes, and (b) may want
// multiple independent generators which we can rely on the sequencing of.
//-----------------------------------------------------------------------------
class CRandom
{
public:
// Constructor. The random formula is X(n+1) = (a*X(n) + b) mod m
// The default values for a,b,m give a maximal period generator that does not
// overflow with 32-bit interger arithmetic.
CRandom( DWORD seed = 31415, DWORD a = 8121,
DWORD b = 28411, DWORD m = 134456 ) :
m_dwSeed(seed),m_dwA(a),m_dwB(b),m_dwM(m) {};
// Grab a random DWORD between 0 and m
DWORD Get()
{
m_dwSeed = ((m_dwA*m_dwSeed)+m_dwB) % m_dwM;
return m_dwSeed;
};
// Grab a random DWORD in the range [0,n-1]
DWORD Get( DWORD n )
{
return (Get() % n);
};
// Grab a random float in the range [0,1]
float GetFloat()
{
return Get() / float(m_dwM-1);
};
// Reset the seed
void Reset( DWORD seed = 31415 )
{
m_dwSeed = seed;
};
protected:
DWORD m_dwA, m_dwB, m_dwM;
DWORD m_dwSeed;
};
#endif

View File

@@ -0,0 +1,54 @@
//----------------------------------------------------------------------------
// File: simplestack.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _SIMPLE_STACK_H
#define _SIMPLE_STACK_H
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
template< class Item , DWORD size > class SimpleStack
{
public:
SimpleStack()
{
m_dwCount = 0;
};
void Push( const Item& item )
{
m_Stack[m_dwCount++] = item;
};
Item Pop()
{
return m_Stack[--m_dwCount];
};
DWORD GetCount() const
{
return m_dwCount;
};
void Empty()
{
m_dwCount = 0;
};
private:
Item m_Stack[size];
DWORD m_dwCount;
};
#endif

View File

@@ -0,0 +1,27 @@
//----------------------------------------------------------------------------
// File: stressmazeguid.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _STRESSMAZE_GUID_H
#define _STRESSMAZE_GUID_H
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
// {C8078137-D0BD-444a-9CF3-4CFB2A0B759E}
static const GUID StressMazeAppGUID =
{ 0xc8078137, 0xd0bd, 0x444a, { 0x9c, 0xf3, 0x4c, 0xfb, 0x2a, 0xb, 0x75, 0x9e } };
#endif

View File

@@ -0,0 +1,204 @@
//----------------------------------------------------------------------------
// File: syncobjects.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _SYNC_OBJECTS_H
#define _SYNC_OBJECTS_H
#include <assert.h>
//-----------------------------------------------------------------------------
// Name:
// Desc: Simple wrapper for critical section
//-----------------------------------------------------------------------------
class CCriticalSection
{
public:
CCriticalSection( DWORD spincount = 2000 )
{
InitializeCriticalSection( &m_CritSec );
};
~CCriticalSection()
{
DeleteCriticalSection( &m_CritSec );
};
void Enter()
{
EnterCriticalSection( &m_CritSec );
};
void Leave()
{
LeaveCriticalSection( &m_CritSec );
};
private:
CRITICAL_SECTION m_CritSec;
};
//-----------------------------------------------------------------------------
// Name:
// Desc: Array of critical section objects. The methods allow locking single
// elements, rectangular regions, or a combination. Using these methods
// ensures the cells are locked/unlocked in a consistent order
// which prevents deadlocks.
//-----------------------------------------------------------------------------
template< DWORD width , DWORD height > class CLockArray
{
public:
#define CS_RESOLUTION 4
// Lock/Unlock a single cell
void LockCell( DWORD x , DWORD y )
{
x /= CS_RESOLUTION;
y /= CS_RESOLUTION;
assert( x<width && y<height );
m_Grid[y][x].Enter();
};
void UnlockCell( DWORD x , DWORD y )
{
x /= CS_RESOLUTION;
y /= CS_RESOLUTION;
assert( x<width && y<height );
m_Grid[y][x].Leave();
};
// Lock/Unlock a rectangular range of cells
void LockRange( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 )
{
x1 /= CS_RESOLUTION;
y1 /= CS_RESOLUTION;
x2 /= CS_RESOLUTION;
y2 /= CS_RESOLUTION;
if ( x1 > x2 ) { DWORD t = x1; x1 = x2; x2 = t; }; // x1 == min
if ( y1 > y2 ) { DWORD t = y1; y1 = y2; y2 = t; }; // y1 == min
assert( x1 <= x2 && y1 <= y2 );
assert( x1<width && y1<height );
assert( x2<width && y2<height );
// Lock from xmin,ymin to xmax,ymax (from xmin,y to xmax,y first)
for ( INT y = y1 ; y <= (INT) y2 ; y++ )
for ( INT x = x1 ; x <= (INT) x2 ; x++ )
m_Grid[y][x].Enter();
};
void UnlockRange( DWORD x1 , DWORD y1 , DWORD x2 , DWORD y2 )
{
x1 /= CS_RESOLUTION;
y1 /= CS_RESOLUTION;
x2 /= CS_RESOLUTION;
y2 /= CS_RESOLUTION;
if ( x1 > x2 ) { DWORD t = x1; x1 = x2; x2 = t; }; // x1 == min
if ( y1 > y2 ) { DWORD t = y1; y1 = y2; y2 = t; }; // y1 == min
assert( x1 <= x2 && y1 <= y2 );
assert( x1<width && y1<height );
assert( x2<width && y2<height );
// Unlock from xmax,ymax to xmin,ymin
for ( INT y = y2 ; y >= (INT) y1 ; y-- )
for ( INT x = x2 ; x >= (INT) x1 ; x-- )
m_Grid[y][x].Leave();
};
void LockCellPair( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
{
x1 /= CS_RESOLUTION;
y1 /= CS_RESOLUTION;
x2 /= CS_RESOLUTION;
y2 /= CS_RESOLUTION;
assert( x1<width && y1<height );
assert( x2<width && y2<height );
if( x1 == x2 && y1 == y2 )
{
LockCell( x1, y1 );
return;
}
if( (y1<y2) || ((y1==y2)&&(x1<=x2)) )
{
assert( ((y1<y2)) || // y1 < y2 case
((y1==y2)&&(x1<=x2)) ); // y1 == y2 case
// Lock from xmin,ymin to xmax,ymax (from xmin,y to xmax,y first)
LockCell(x1,y1);
LockCell(x2,y2);
}
else
{
assert( ((y1>=y2)) || // y1 < y2 case
((y1==y2)&&(x1>x2)) ); // y1 == y2 case
// Lock from xmin,ymin to xmax,ymax (from xmin,y to xmax,y first)
LockCell(x2,y2);
LockCell(x1,y1);
}
}
void UnlockCellPair( DWORD x1, DWORD y1, DWORD x2, DWORD y2 )
{
x1 /= CS_RESOLUTION;
y1 /= CS_RESOLUTION;
x2 /= CS_RESOLUTION;
y2 /= CS_RESOLUTION;
assert( x1<width && y1<height );
assert( x2<width && y2<height );
if( x1 == x2 && y1 == y2 )
{
UnlockCell( x1, y1 );
return;
}
if( (y1<y2) || ((y1==y2)&&(x1<=x2)) )
{
assert( ((y1<y2)) || // y1 < y2 case
((y1==y2)&&(x1<=x2)) ); // y1 == y2 case
// Unlock from xmax,ymax to xmin,ymin (from xmax,y to xmin,y first)
UnlockCell(x2,y2);
UnlockCell(x1,y1);
}
else
{
assert( ((y1>=y2)) || // y1 < y2 case
((y1==y2)&&(x1>x2)) ); // y1 == y2 case
// Unlock from xmax,ymax to xmin,ymin (from xmax,y to xmin,y first)
UnlockCell(x1,y1);
UnlockCell(x2,y2);
}
}
private:
CCriticalSection m_Grid[height][width];
};
#endif

View File

@@ -0,0 +1,33 @@
//----------------------------------------------------------------------------
// File:
//
// Desc:
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _CLIENT_ID_H
#define _CLIENT_ID_H
//-----------------------------------------------------------------------------
// Name:
// Desc: Client IDs are 32-bit values that refer to a particular Client. They are
// broken up into two bitfields, one of which can be used into an index
// of a list of Client 'slots', the other bitfield is a "uniqueness" value
// that is incremented each time a new Client is created. Hence, although
// the same slot may be reused by different Clients are different times,
// it's possible to distinguish between the two by comparing uniqueness
// values (you can just compare the whole 32-bit id).
//-----------------------------------------------------------------------------
typedef DWORD CLClientID;
#define CLIENT_SLOT_BITS 13
#define MAX_CLIENTS (1<<CLIENT_SLOT_BITS)
#define CLIENT_SLOT_MASK (MAX_CLIENTS-1)
#endif

View File

@@ -0,0 +1,30 @@
//----------------------------------------------------------------------------
// File: trig.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _TRIG_H
#define _TRIG_H
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
typedef DWORD ANGLE;
const float TRIG_ANGLE_SCALE = (3.1415926536f*2.0f) / 65536.0f;
#define TRIG_ANGLE_MASK 0xffff
inline float Sin( ANGLE angle ) { return float(sin(float(angle&0xffff)*TRIG_ANGLE_SCALE)); };
inline float Cos( ANGLE angle ) { return float(cos(float(angle&0xffff)*TRIG_ANGLE_SCALE)); };
inline float AngleToFloat( ANGLE angle ) { return float(angle&0xffff) * TRIG_ANGLE_SCALE; };
#endif

View File

@@ -0,0 +1,845 @@
//----------------------------------------------------------------------------
// File: dplay8client.cpp
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#include <windows.h>
#include <d3dx.h>
#include <process.h>
#include <tchar.h>
#include <assert.h>
#include <dxerr8.h>
#include <stdio.h>
#include <dplay8.h>
#include <dpaddr.h>
#include "DummyConnector.h"
#include "StressMazeGUID.h"
#include "DXUtil.h"
#include "MazeApp.h"
#include "DPlay8Client.h"
#define DPMAZESERVER_PORT 2309
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CDPlay8Client::CDPlay8Client()
{
m_dwNumSessions = 0;
m_wActiveThreadCount = 0;
m_wMaxThreadCount = 0;
m_fAvgThreadCount = 0;
m_fAvgThreadTime = 0;
m_fMaxThreadTime = 0;
m_pClient = NULL;
m_pDPlay = NULL;
m_dpnhEnum = NULL;
m_bSessionLost = TRUE;
m_dwSessionLostReason = DISCONNNECT_REASON_UNKNOWN;
for( DWORD dwIndex = 0; dwIndex < MAX_SESSIONS; dwIndex++ )
{
m_pHostAddresses[dwIndex] = NULL;
m_pDeviceAddresses[dwIndex] = NULL;
}
InitializeCriticalSection( &m_csLock );
InitializeCriticalSection( &m_csThreadCountLock );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CDPlay8Client::~CDPlay8Client()
{
DeleteCriticalSection( &m_csLock );
DeleteCriticalSection( &m_csThreadCountLock );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CDPlay8Client::Init(MazeConfig* pMazeConfig)
{
HRESULT hr;
m_MazeConfig = pMazeConfig;
Shutdown();
if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Client, NULL,
CLSCTX_ALL, IID_IDirectPlay8Client,
(LPVOID*) &m_pDPlay ) ) )
return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
if( FAILED( hr = m_pDPlay->Initialize( this, StaticReceiveHandler, 0 ) ) )
return DXTRACE_ERR( TEXT("Initialize"), hr );
// If between Range, attempt to set SPThreads.
if((m_MazeConfig->dwSPThreads >= MIN_SP_THREADS) && (m_MazeConfig->dwSPThreads <= MAX_SP_THREADS))
{
SetNumSPThreads(m_MazeConfig->dwSPThreads);
}
// If less than MAX, set our SP Buffer.
if(m_MazeConfig->dwSPBufferSize <= MAX_SP_BUFFER)
{
SetSPBuffer(m_MazeConfig->dwSPBufferSize);
}
m_fLastUpdateConnectInfoTime = DXUtil_Timer( TIMER_GETAPPTIME );
m_bSessionLost = TRUE;
m_dwNumSessions = 0;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD CDPlay8Client::GetNumSPThreads()
{
HRESULT hr = DPN_OK;
DPN_SP_CAPS dnSPCaps;
DWORD dwFlags = NULL;
DWORD dwNumSPThreads = NULL;
DXTRACE_MSG( TEXT("Attempting to Get SP Thread Count.") );
dnSPCaps.dwSize = sizeof(DPN_SP_CAPS);
hr = m_pDPlay->GetSPCaps(&CLSID_DP8SP_TCPIP, &dnSPCaps, dwFlags);
if(hr != DPN_OK)
{
DXTRACE_ERR( TEXT("GetSPCaps error"), hr );
dwNumSPThreads = 0;
}
else
{
dwNumSPThreads = dnSPCaps.dwNumThreads;
}
return dwNumSPThreads;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CDPlay8Client::SetNumSPThreads(DWORD dwNumSPThreads)
{
HRESULT hr = DPN_OK;
DPN_SP_CAPS dnSPCaps;
DWORD dwFlags = NULL;
dnSPCaps.dwSize = sizeof(DPN_SP_CAPS);
hr = m_pDPlay->GetSPCaps(&CLSID_DP8SP_TCPIP, &dnSPCaps, dwFlags);
if(hr == DPN_OK)
{
if((dwNumSPThreads >= MIN_SP_THREADS) && (dwNumSPThreads <= MAX_SP_THREADS))
{
dnSPCaps.dwNumThreads = dwNumSPThreads;
// Attempt to set the number of SP Threads.
hr = m_pDPlay->SetSPCaps(&CLSID_DP8SP_TCPIP, &dnSPCaps, dwFlags);
if(hr != DPN_OK)
{
DXTRACE_ERR( TEXT("SetSPCaps error."), hr );
}
}
}
else
{
DXTRACE_ERR( TEXT("GetSPCaps error."), hr );
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD CDPlay8Client::GetSPBuffer()
{
HRESULT hr = DPN_OK;
DPN_SP_CAPS dnSPCaps;
DWORD dwFlags = NULL;
DWORD dwSPBufferSize = NULL;
DXTRACE_MSG( TEXT("Attempting to Get SP Buffer Size.") );
dnSPCaps.dwSize = sizeof(DPN_SP_CAPS);
hr = m_pDPlay->GetSPCaps(&CLSID_DP8SP_TCPIP, &dnSPCaps, dwFlags);
if(hr != DPN_OK)
{
DXTRACE_ERR( TEXT("GetSPCaps error"), hr );
dwSPBufferSize = 0xffffffff;
}
else
{
dwSPBufferSize = dnSPCaps.dwSystemBufferSize;
}
return dwSPBufferSize;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CDPlay8Client::SetSPBuffer(DWORD dwSPBufferSize)
{
HRESULT hr = DPN_OK;
DPN_SP_CAPS dnSPCaps;
DWORD dwFlags = NULL;
dnSPCaps.dwSize = sizeof(DPN_SP_CAPS);
hr = m_pDPlay->GetSPCaps(&CLSID_DP8SP_TCPIP, &dnSPCaps, dwFlags);
if(hr == DPN_OK)
{
if(dwSPBufferSize <= MAX_SP_BUFFER)
dnSPCaps.dwSystemBufferSize = dwSPBufferSize;
// Attempt to set the number of SP Threads.
hr = m_pDPlay->SetSPCaps(&CLSID_DP8SP_TCPIP, &dnSPCaps, dwFlags);
if(hr != DPN_OK)
{
DXTRACE_ERR( TEXT("SetSPCaps error."), hr );
}
}
else // !GetSPCase != DPN_OK
{
DXTRACE_ERR( TEXT("GetSPCaps error."), hr );
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
void CDPlay8Client::Shutdown()
{
for( DWORD dwIndex = 0; dwIndex < MAX_SESSIONS; dwIndex++ )
{
SAFE_RELEASE( m_pHostAddresses[dwIndex] );
SAFE_RELEASE( m_pDeviceAddresses[dwIndex] );
}
if( m_dpnhEnum != NULL )
{
// Cancel the enumeration if its in progress, and ignore any errors
m_pDPlay->CancelAsyncOperation( m_dpnhEnum, 0 );
m_dpnhEnum = NULL;
}
if( m_pDPlay != NULL )
m_pDPlay->Close(0);
SAFE_RELEASE( m_pDPlay );
m_bSessionLost = TRUE;
m_dwNumSessions = 0;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CDPlay8Client::StartSessionEnum( const TCHAR* ipaddress )
{
if( NULL == m_pDPlay )
return E_FAIL;
DPN_APPLICATION_DESC dpnAppDesc;
IDirectPlay8Address* pDP8AddressHost = NULL;
IDirectPlay8Address* pDP8AddressLocal = NULL;
WCHAR* wszHostName = NULL;
HRESULT hr;
DWORD dwPort;
m_dwNumSessions = 0;
if( m_dpnhEnum != NULL )
{
// If an enumeration is already running, cancel
// it and start a new one. Ignore any errors from CancelAsyncOperation
m_pDPlay->CancelAsyncOperation( m_dpnhEnum, 0 );
m_dpnhEnum = NULL;
}
// Create the local device address object
if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL,
CLSCTX_ALL, IID_IDirectPlay8Address,
(LPVOID*) &pDP8AddressLocal ) ) )
{
DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
goto LCleanup;
}
// Set IP service provider
if( FAILED( hr = pDP8AddressLocal->SetSP( &CLSID_DP8SP_TCPIP ) ) )
{
DXTRACE_ERR( TEXT("SetSP"), hr );
goto LCleanup;
}
// Create the remote host address object
if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL,
CLSCTX_ALL, IID_IDirectPlay8Address,
(LPVOID*) &pDP8AddressHost ) ) )
{
DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
goto LCleanup;
}
// Set IP service provider
if( FAILED( hr = pDP8AddressHost->SetSP( &CLSID_DP8SP_TCPIP ) ) )
{
DXTRACE_ERR( TEXT("SetSP"), hr );
goto LCleanup;
}
// Maze uses a fixed port, so add it to the host address
dwPort = DPMAZESERVER_PORT;
hr = pDP8AddressHost->AddComponent( DPNA_KEY_PORT,
&dwPort, sizeof(dwPort),
DPNA_DATATYPE_DWORD );
if( FAILED(hr) )
{
DXTRACE_ERR( TEXT("AddComponent"), hr );
goto LCleanup;
}
// Set the remote host name (if provided)
if( ipaddress != NULL && ipaddress[0] != 0 )
{
wszHostName = new WCHAR[_tcslen(ipaddress)+1];
DXUtil_ConvertGenericStringToWide( wszHostName, ipaddress );
hr = pDP8AddressHost->AddComponent( DPNA_KEY_HOSTNAME, wszHostName,
(wcslen(wszHostName)+1)*sizeof(WCHAR),
DPNA_DATATYPE_STRING );
if( FAILED(hr) )
{
DXTRACE_ERR( TEXT("AddComponent"), hr );
goto LCleanup;
}
}
ZeroMemory( &dpnAppDesc, sizeof( DPN_APPLICATION_DESC ) );
dpnAppDesc.dwSize = sizeof( DPN_APPLICATION_DESC );
dpnAppDesc.guidApplication = StressMazeAppGUID;
// Enumerate all StressMazeApp hosts running on IP service providers
hr = m_pDPlay->EnumHosts( &dpnAppDesc, pDP8AddressHost,
pDP8AddressLocal, NULL,
0, INFINITE, 0, INFINITE, NULL,
&m_dpnhEnum, 0 );
if( hr != DPNERR_PENDING && FAILED(hr) )
{
DXTRACE_ERR_NOMSGBOX( TEXT("EnumHosts"), hr );
goto LCleanup;
}
LCleanup:
SAFE_RELEASE( pDP8AddressHost);
SAFE_RELEASE( pDP8AddressLocal );
SAFE_DELETE( wszHostName );
return hr;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CDPlay8Client::StopSessionEnum()
{
if( NULL == m_pDPlay )
return E_FAIL;
// If an enumeration is already running, cancel it and ignore
// any errors from CancelAsyncOperation
if( m_dpnhEnum != NULL )
{
m_pDPlay->CancelAsyncOperation( m_dpnhEnum, 0 );
m_dpnhEnum = NULL;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
BOOL CDPlay8Client::EnumSessionCallback( const DPN_APPLICATION_DESC *pdesc,
IDirectPlay8Address* pDP8AddressHost,
IDirectPlay8Address* pDP8AddressDevice )
{
if( NULL == m_pDPlay )
return DPN_OK;
EnterCriticalSection( &m_csLock );
if( m_dwNumSessions < MAX_SESSIONS )
{
// Search for existing record for this session, if
// there is one, break this loop so we just update
// the current entry.
for( DWORD dwIndex = 0; dwIndex < m_dwNumSessions; dwIndex++ )
{
if( m_Sessions[dwIndex].guidInstance == pdesc->guidInstance )
break;
}
memcpy( &m_Sessions[dwIndex], pdesc, sizeof( DPN_APPLICATION_DESC ) );
// Copy pDP8AddressHost to m_pHostAddresses[dwIndex]
SAFE_RELEASE( m_pHostAddresses[dwIndex] );
pDP8AddressHost->QueryInterface( IID_IDirectPlay8Address,
(LPVOID*) &m_pHostAddresses[dwIndex] );
// Copy pDP8AddressDevice to m_pDeviceAddresses[dwIndex]
SAFE_RELEASE( m_pDeviceAddresses[dwIndex] );
pDP8AddressDevice->QueryInterface( IID_IDirectPlay8Address,
(LPVOID*) &m_pDeviceAddresses[dwIndex] );
if( pdesc->pwszSessionName != NULL )
{
DXUtil_ConvertWideStringToGeneric( m_szSessionNames[dwIndex],
pdesc->pwszSessionName );
}
else
{
_tcscpy( m_szSessionNames[dwIndex], TEXT("Untitled") );
}
if( m_dwNumSessions == dwIndex )
m_dwNumSessions++;
}
LeaveCriticalSection( &m_csLock );
return DPN_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CDPlay8Client::JoinSession( DWORD num )
{
HRESULT hr;
IDirectPlay8Address* pHostAddress = NULL;
IDirectPlay8Address* pDeviceAddress = NULL;
if( m_pDPlay == NULL )
return E_FAIL;
DXTRACE( TEXT("MazeClient: Trying to connect to server\n") );
DPN_APPLICATION_DESC dpnAppDesc;
ZeroMemory( &dpnAppDesc, sizeof( DPN_APPLICATION_DESC ) );
dpnAppDesc.dwSize = sizeof( DPN_APPLICATION_DESC );
dpnAppDesc.guidApplication = StressMazeAppGUID;
dpnAppDesc.guidInstance = m_Sessions[num].guidInstance;
EnterCriticalSection( &m_csLock );
// Copy the host and device address pointers, and addref them.
// If this is not done, then there is a rare chance that
// EnumSessionCallback() may be called during the Connect() call
// and destory the address before DirectPlay gets a chance to copy them.
pHostAddress = m_pHostAddresses[num];
pHostAddress->AddRef();
pDeviceAddress = m_pDeviceAddresses[num];
pDeviceAddress->AddRef();
LeaveCriticalSection( &m_csLock );
// Connect to the remote host
// The enumeration is automatically canceled after Connect is called
if( FAILED( hr = m_pDPlay->Connect( &dpnAppDesc, // Application description
pHostAddress, // Session host address
pDeviceAddress, // Address of device used to connect to the host
NULL, NULL, // Security descriptions & credientials (MBZ in DPlay8)
NULL, 0, // User data & its size
NULL, // Asynchronous connection context (returned with DPNMSG_CONNECT_COMPLETE in async handshaking)
NULL, // Asynchronous connection handle (used to cancel connection process)
DPNOP_SYNC ) ) ) // Connect synchronously
{
if( hr == DPNERR_NORESPONSE || hr == DPNERR_ABORTED )
goto LCleanup; // These are possible if the server exits while joining
if( hr == DPNERR_INVALIDINSTANCE )
goto LCleanup; // This is possible if the original server exits and another server comes online while we are connecting
DXTRACE_ERR_NOMSGBOX( TEXT("Connect"), hr );
goto LCleanup;
}
m_bSessionLost = FALSE;
DXTRACE( TEXT("MazeClient: Connected to server. Enum automatically canceled\n") );
UpdateConnectionInfo();
m_fLastUpdateConnectInfoTime = DXUtil_Timer( TIMER_GETAPPTIME );
LCleanup:
SAFE_RELEASE( pHostAddress );
SAFE_RELEASE( pDeviceAddress );
return hr;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CDPlay8Client::SendPacket( void* pData, DWORD dwSize,
BOOL bGuaranteed, DWORD dwTimeout )
{
if( NULL == m_pDPlay )
return S_OK;
DPNHANDLE hAsync;
DWORD dwFlags;
DPNHANDLE* phAsync;
if( bGuaranteed )
{
// If we are guaranteed then we must specify
// DPNSEND_NOCOMPLETE and pass in non-null for the
// pvAsyncContext
dwFlags = DPNSEND_GUARANTEED;
}
else
{
// If we aren't guaranteed then we can
// specify DPNSEND_NOCOMPLETE. And when
// DPNSEND_NOCOMPLETE is on pvAsyncContext
// must be NULL.
dwFlags = DPNSEND_NOCOMPLETE;
}
// Must pass in a value for the Asyn handle. Will be thrown when proc completes.
phAsync = &hAsync;
DPN_BUFFER_DESC dpnBufferDesc;
dpnBufferDesc.dwBufferSize = dwSize;
dpnBufferDesc.pBufferData = (PBYTE) pData;
// Update the throughput counter
m_dwThroughputBytes += dwSize;
// DirectPlay will tell via the message handler
// if there are any severe errors, so ignore any errors
m_pDPlay->Send( &dpnBufferDesc, 1, dwTimeout,
NULL, phAsync, dwFlags );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CDPlay8Client::StaticReceiveHandler( void *pvContext, DWORD dwMessageType,
void *pvMessage )
{
CDPlay8Client* pThisObject = (CDPlay8Client*) pvContext;
return pThisObject->ReceiveHandler( pvContext, dwMessageType, pvMessage );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CDPlay8Client::ReceiveHandler( void *pvContext, DWORD dwMessageType,
void *pvMessage )
{
// Increment our Active Thread Counter.
EnterCriticalSection( &m_csThreadCountLock );
//Get the time when we entered the Message Handler
FLOAT fStartTime = DXUtil_Timer( TIMER_GETAPPTIME );
m_wActiveThreadCount++;
if(m_wActiveThreadCount > m_wMaxThreadCount)
m_wMaxThreadCount = m_wActiveThreadCount;
// Calculate an average.
FLOAT fdiff = m_wActiveThreadCount - m_fAvgThreadCount;
m_fAvgThreadCount += fdiff/8;
LeaveCriticalSection( &m_csThreadCountLock );
switch( dwMessageType )
{
case DPN_MSGID_RECEIVE:
{
PDPNMSG_RECEIVE pRecvData = (PDPNMSG_RECEIVE) pvMessage;
// Update the throughput counter
m_dwThroughputBytes += pRecvData->dwReceiveDataSize;
if( m_pClient != NULL )
{
m_pClient->OnPacket( pRecvData->dpnidSender,
pRecvData->pReceiveData,
pRecvData->dwReceiveDataSize );
}
break;
}
case DPN_MSGID_TERMINATE_SESSION:
{
m_dwSessionLostReason = DISCONNNECT_REASON_UNKNOWN;
PDPNMSG_TERMINATE_SESSION pTermMsg = (PDPNMSG_TERMINATE_SESSION) pvMessage;
// The MazeServer passes a DWORD in pvTerminateData if
// it disconnected us, otherwise it will be null.
if( pTermMsg->pvTerminateData != NULL )
{
DWORD* pdw = (DWORD*) pTermMsg->pvTerminateData;
m_dwSessionLostReason = *pdw;
}
if( m_pClient != NULL )
m_pClient->OnSessionLost( m_dwSessionLostReason );
// Now that the session is lost we need to restart DirectPlay by calling
// Close() and Init() on m_pDPlay, however this can not be
// done in the DirectPlay message callback, so the main thread will
// do this when IsSessionLost() returns TRUE
m_bSessionLost = TRUE;
break;
}
case DPN_MSGID_ENUM_HOSTS_RESPONSE:
{
PDPNMSG_ENUM_HOSTS_RESPONSE pEnumResponse = (PDPNMSG_ENUM_HOSTS_RESPONSE) pvMessage;
EnumSessionCallback( pEnumResponse->pApplicationDescription,
pEnumResponse->pAddressSender,
pEnumResponse->pAddressDevice );
break;
}
}
EnterCriticalSection( &m_csThreadCountLock );
m_wActiveThreadCount-- ;
// Calculate an average.
FLOAT fDiffTime = ( DXUtil_Timer( TIMER_GETAPPTIME ) - fStartTime ) - m_fAvgThreadTime;
m_fAvgThreadTime += fDiffTime/8;
//Get the Max time in the thread.
if ( fDiffTime > m_fMaxThreadTime )
{
m_fMaxThreadTime = fDiffTime;
}
LeaveCriticalSection( &m_csThreadCountLock );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CDPlay8Client::UpdateConnectionInfo()
{
if( NULL == m_pDPlay )
return E_FAIL;
// Update the DPN_CONNECTION_INFO every 1/2 second...
float fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
if( fCurTime - m_fLastUpdateConnectInfoTime > 0.5f )
{
// Call GetConnectionInfo to get DirectPlay stats about connection
ZeroMemory( &m_dpnConnectionInfo, sizeof(DPN_CONNECTION_INFO) );
m_dpnConnectionInfo.dwSize = sizeof(DPN_CONNECTION_INFO);
m_pDPlay->GetConnectionInfo( &m_dpnConnectionInfo, 0 );
// Call GetSendQueueInfo to get DirectPlay stats about messages
m_pDPlay->GetSendQueueInfo( &m_dwHighPriMessages, &m_dwHighPriBytes,
DPNGETSENDQUEUEINFO_PRIORITY_HIGH );
m_pDPlay->GetSendQueueInfo( &m_dwNormalPriMessages, &m_dwNormalPriBytes,
DPNGETSENDQUEUEINFO_PRIORITY_NORMAL );
m_pDPlay->GetSendQueueInfo( &m_dwLowPriMessages, &m_dwLowPriBytes,
DPNGETSENDQUEUEINFO_PRIORITY_LOW );
m_fLastUpdateConnectInfoTime = fCurTime;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD CDPlay8Client::GetThroughputBPS()
{
static float s_fLastThroughputBPSTime = DXUtil_Timer( TIMER_GETAPPTIME );
float fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
if( fCurTime - s_fLastThroughputBPSTime > 1.0f )
{
m_fThroughputBPS = (float) m_dwThroughputBytes / (fCurTime - s_fLastThroughputBPSTime);
s_fLastThroughputBPSTime = fCurTime;
m_dwThroughputBytes = 0;
}
return (DWORD) m_fThroughputBPS;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
DWORD CDPlay8Client::GetRoundTripLatencyMS()
{
UpdateConnectionInfo();
return m_dpnConnectionInfo.dwRoundTripLatencyMS;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CDPlay8Client::GetConnectionInfo( TCHAR* strConnectionInfo )
{
UpdateConnectionInfo();
_stprintf( strConnectionInfo,
TEXT(" Thread Count: Current=%d Avg= %.2f Max=%d\n") \
TEXT(" Thread Time: Avg= %.4f Max=%.4f(s)\n") \
\
TEXT(" Round Trip Latency MS=%dms\n") \
TEXT(" Throughput BPS: Current=%d Peak=%d\n") \
\
TEXT(" Messages Received=%d\n") \
\
TEXT(" Sent: GB=%d GP=%d NGB=%d NGP=%d\n") \
TEXT(" Received: GB=%d GP=%d NGB=%d NGP=%d\n") \
\
TEXT(" Messages Transmitted: HP=%d NP=%d LP=%d\n") \
TEXT(" Messages Timed Out: HP=%d NP=%d LP=%d\n") \
\
TEXT(" Retried: GB=%d GP=%d\n") \
TEXT(" Dropped: NGB=%d NGP=%d\n") \
\
TEXT(" Send Queue Messages: HP=%d NP=%d LP=%d\n") \
TEXT(" Send Queue Bytes: HP=%d NP=%d LP=%d\n"), \
\
\
\
m_wActiveThreadCount, m_fAvgThreadCount, m_wMaxThreadCount,
m_fAvgThreadTime, m_fMaxThreadTime,
m_dpnConnectionInfo.dwRoundTripLatencyMS,
m_dpnConnectionInfo.dwThroughputBPS,
m_dpnConnectionInfo.dwPeakThroughputBPS,
m_dpnConnectionInfo.dwMessagesReceived,
m_dpnConnectionInfo.dwBytesSentGuaranteed,
m_dpnConnectionInfo.dwPacketsSentGuaranteed,
m_dpnConnectionInfo.dwBytesSentNonGuaranteed,
m_dpnConnectionInfo.dwPacketsSentNonGuaranteed,
m_dpnConnectionInfo.dwBytesReceivedGuaranteed,
m_dpnConnectionInfo.dwPacketsReceivedGuaranteed,
m_dpnConnectionInfo.dwBytesReceivedNonGuaranteed,
m_dpnConnectionInfo.dwPacketsReceivedNonGuaranteed,
m_dpnConnectionInfo.dwMessagesTransmittedHighPriority,
m_dpnConnectionInfo.dwMessagesTransmittedNormalPriority,
m_dpnConnectionInfo.dwMessagesTransmittedLowPriority,
m_dpnConnectionInfo.dwMessagesTimedOutHighPriority,
m_dpnConnectionInfo.dwMessagesTimedOutNormalPriority,
m_dpnConnectionInfo.dwMessagesTimedOutLowPriority,
m_dpnConnectionInfo.dwBytesRetried,
m_dpnConnectionInfo.dwPacketsRetried,
m_dpnConnectionInfo.dwBytesDropped,
m_dpnConnectionInfo.dwPacketsDropped,
m_dwHighPriMessages, m_dwNormalPriMessages, m_dwLowPriMessages,
m_dwHighPriBytes, m_dwNormalPriBytes, m_dwLowPriBytes
);
return S_OK;
}

View File

@@ -0,0 +1,102 @@
//----------------------------------------------------------------------------
// File: dplay8client.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _DPLAY8_CLIENT_H
#define _DPLAY8_CLIENT_H
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
#include "NetAbstract.h"
struct MazeConfig;
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
class CDPlay8Client : public IOutboundClient
{
public:
CDPlay8Client();
~CDPlay8Client();
HRESULT Init(MazeConfig* pMazeConfig);
void Shutdown();
HRESULT StartSessionEnum( const TCHAR* ipaddress );
HRESULT StopSessionEnum();
DWORD GetNumSessions() const { return m_dwNumSessions; };
const TCHAR* GetSessionName( DWORD num ) const { return m_szSessionNames[num]; };
DWORD GetSessionMaxPlayers( DWORD num ) const { return m_Sessions[num].dwMaxPlayers; };
DWORD GetSessionCurrentPlayers( DWORD num ) const { return m_Sessions[num].dwCurrentPlayers; };
GUID GetSessionGUID( DWORD num ) const { return m_Sessions[num].guidInstance; };
HRESULT JoinSession( DWORD num );
void SetClient( INetClient* pclient ) { m_pClient = pclient; };
IOutboundClient* GetOutboundClient() const { return ((IOutboundClient*)this); };
DWORD GetNumSPThreads();
void SetNumSPThreads(DWORD dwNumSPThreads);
DWORD GetSPBuffer();
void SetSPBuffer(DWORD dwSPBufferSize);
// IOutboundClient
virtual HRESULT SendPacket( void* pData, DWORD dwSize, BOOL bGuaranteed, DWORD dwTimeout );
virtual DWORD GetThroughputBPS();
virtual DWORD GetRoundTripLatencyMS();
virtual BOOL IsSessionLost() { return m_bSessionLost; };
virtual DWORD GetSessionLostReason() { return m_dwSessionLostReason; };
virtual HRESULT GetConnectionInfo( TCHAR* strConnectionInfo );
protected:
IDirectPlay8Client* m_pDPlay;
BOOL m_bConnected;
INetClient* m_pClient;
BOOL m_bSessionLost;
DWORD m_dwSessionLostReason;
FLOAT m_fThroughputBPS;
DWORD m_dwThroughputBytes;
CRITICAL_SECTION m_csThreadCountLock;
WORD m_wActiveThreadCount;
WORD m_wMaxThreadCount;
FLOAT m_fAvgThreadCount;
FLOAT m_fAvgThreadTime;
FLOAT m_fMaxThreadTime;
// Configuration info.
MazeConfig* m_MazeConfig;
// Connection info
FLOAT m_fLastUpdateConnectInfoTime;
DPN_CONNECTION_INFO m_dpnConnectionInfo;
DWORD m_dwHighPriMessages, m_dwHighPriBytes;
DWORD m_dwNormalPriMessages, m_dwNormalPriBytes;
DWORD m_dwLowPriMessages, m_dwLowPriBytes;
HRESULT UpdateConnectionInfo();
BOOL EnumSessionCallback( const DPN_APPLICATION_DESC *pdesc, IDirectPlay8Address* pDP8AddressHost, IDirectPlay8Address* pDP8AddressDevice );
static HRESULT WINAPI StaticReceiveHandler( void *pvContext, DWORD dwMessageType, void *pvMessage );
HRESULT WINAPI ReceiveHandler( void *pvContext, DWORD dwMessageType, void *pvMessage );
enum {MAX_SESSIONS=256};
enum {MAX_SESSION_NAME=64};
DWORD m_dwNumSessions;
DPN_APPLICATION_DESC m_Sessions[MAX_SESSIONS];
IDirectPlay8Address* m_pHostAddresses[MAX_SESSIONS];
IDirectPlay8Address* m_pDeviceAddresses[MAX_SESSIONS];
TCHAR m_szSessionNames[MAX_SESSIONS][MAX_SESSION_NAME];
FLOAT m_fSessionLastSeenTime[MAX_SESSIONS];
CRITICAL_SECTION m_csLock;
DPNHANDLE m_dpnhEnum;
};
#endif

View File

@@ -0,0 +1,35 @@
//----------------------------------------------------------------------------
// File: imazegraphics.h
//
// Desc: see main.cpp
//
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef _IMAZEGRAPHICS_
#define _IMAZEGRAPHICS_
class CMazeApp;
class CDPlay8Client;
class CMazeClient;
enum EnumLineType { LINE_PROMPT, LINE_INPUT, LINE_LOG, LINE_CMD };
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
interface IMazeGraphics
{
public:
virtual VOID Init( CMazeApp* pMazeApp, CDPlay8Client* pDP8Client, CMazeClient* pMazeClient ) = 0;
virtual HRESULT Create( HINSTANCE hInstance ) = 0;
virtual INT Run() = 0;
virtual void HandleOutputMsg( EnumLineType enumLineType, TCHAR* strLine ) = 0;
virtual BOOL IsPreview() = 0;
virtual void Shutdown() = 0;
};
#endif