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:
@@ -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
|
||||
@@ -0,0 +1,12 @@
|
||||
//----------------------------------------------------------------------------
|
||||
// File:
|
||||
//
|
||||
// Desc:
|
||||
//
|
||||
// Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _IDPCLIENT_H
|
||||
#define _IDPCLIENT_H
|
||||
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user