home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectPlay / Maze / MazeCommon / MazeClient.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  30.9 KB  |  946 lines

  1. //----------------------------------------------------------------------------
  2. // File: mazecient.cpp
  3. //
  4. // Desc: see main.cpp
  5. //
  6. // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #define STRICT
  9. #define D3D_OVERLOADS
  10. #include <windows.h>
  11. #include <d3dx.h>
  12. #include <stdio.h>
  13. #include <math.h>
  14. #include <dplay8.h>
  15. #include <dpaddr.h>
  16. #include <dxerr8.h>
  17. #include "DXUtil.h"
  18. #include "SyncObjects.h"
  19. #include "IMazeGraphics.h"
  20. #include "DummyConnector.h"
  21. #include "MazeApp.h"
  22. #include "MazeClient.h"
  23. #include "Packets.h"
  24. #include "DXUtil.h"
  25.  
  26.  
  27.  
  28.  
  29. //-----------------------------------------------------------------------------
  30. // Name: 
  31. // Desc: 
  32. //-----------------------------------------------------------------------------
  33. #define NORTH_ANGLE 0x8000
  34. #define EAST_ANGLE  0xc000
  35. #define SOUTH_ANGLE 0x0000
  36. #define WEST_ANGLE  0x4000
  37.  
  38.  
  39.  
  40.  
  41. //-----------------------------------------------------------------------------
  42. // Name: 
  43. // Desc: 
  44. //-----------------------------------------------------------------------------
  45. CMazeClient::CMazeClient()
  46. {
  47.     ZeroMemory( m_pctCells, sizeof(m_pctCells) );
  48.     m_hReady            = CreateEvent( NULL, TRUE, FALSE, NULL );
  49.     m_hGotFirstConfig   = CreateEvent( NULL, TRUE, FALSE, NULL );
  50.     m_pNet              = NULL;
  51.     m_bAutopilot        = FALSE;
  52.     m_bEngageAutopilot  = TRUE;
  53.     m_dwNumNearbyPlayers = 0;
  54.  
  55.     m_NetConfig.ubReliableRate = 0;
  56.     m_NetConfig.wUpdateRate    = 150;
  57.     m_NetConfig.wTimeout       = 150;
  58.     m_NetConfig.dwMazeWidth    = 0;
  59.     m_NetConfig.dwMazeHeight   = 0;
  60.  
  61.     m_NetConfig.dwThreadWait = 0;
  62.  
  63.     m_NetConfig.ubClientPackIndex = 0;
  64.     m_NetConfig.ubServerPackIndex = 0;
  65.     for(WORD x = 0; x < PACK_ARRAY_SIZE; x++)
  66.     {
  67.         m_NetConfig.wClientPackSizeArray[x]  = 0;
  68.         m_NetConfig.wServerPackSizeArray[x]  = 0;
  69.     }
  70.  
  71. }
  72.  
  73.  
  74.  
  75.  
  76. //-----------------------------------------------------------------------------
  77. // Name: 
  78. // Desc: 
  79. //-----------------------------------------------------------------------------
  80. CMazeClient::~CMazeClient()
  81. {
  82.     CloseHandle( m_hGotFirstConfig );
  83.     CloseHandle( m_hReady );
  84. }
  85.  
  86.  
  87.  
  88.  
  89. //-----------------------------------------------------------------------------
  90. // Name: 
  91. // Desc: 
  92. //-----------------------------------------------------------------------------
  93. HRESULT CMazeClient::Init( CMazeApp* pMazeApp, IMazeGraphics* pMazeGraphics )
  94. {
  95.     m_pMazeApp              = pMazeApp;
  96.     m_pMazeGraphics         = pMazeGraphics;
  97.     m_aCameraYaw            = 0;
  98.     m_fLastOutboundTime     = DXUtil_Timer( TIMER_GETAPPTIME );
  99.     m_bHaveInputFocus       = TRUE;
  100.  
  101.     Reset();
  102.  
  103.     return S_OK;
  104. }
  105.  
  106.  
  107.  
  108.  
  109. //-----------------------------------------------------------------------------
  110. // Name: 
  111. // Desc: 
  112. //-----------------------------------------------------------------------------
  113. HRESULT CMazeClient::Reset()
  114. {
  115.     m_dwNumPlayers = 0;
  116.     m_bAutopilot   = FALSE;
  117.  
  118.     SetFirstConfig( FALSE );
  119.     SetMazeReady( FALSE ); 
  120.  
  121.     ZeroMemory( m_pctCells, sizeof(m_pctCells) );
  122.  
  123.     PlayerObject* pPlayerObject = m_PlayerObjects;
  124.     for( DWORD i = 0; i < MAX_PLAYER_OBJECTS; i++, pPlayerObject++ )
  125.     {
  126.         pPlayerObject->dwID = 0;
  127.         pPlayerObject->wCellX = pPlayerObject->wCellY = 0xffff;
  128.         pPlayerObject->pNext = NULL;
  129.     }
  130.  
  131.     return S_OK;
  132. }
  133.  
  134.  
  135.  
  136.  
  137.  
  138. //-----------------------------------------------------------------------------
  139. // Name: 
  140. // Desc: 
  141. //-----------------------------------------------------------------------------
  142. void CMazeClient::Shutdown()
  143. {
  144.     // Destroy the maze
  145.  
  146.     Reset();
  147.     m_Maze.Empty();
  148. }
  149.  
  150.  
  151.  
  152.  
  153. //-----------------------------------------------------------------------------
  154. // Name: 
  155. // Desc: 
  156. //-----------------------------------------------------------------------------
  157. void CMazeClient::Update( FLOAT fElapsed )
  158. {
  159.     // Don't do anyPlayerObject until we get a server config packet
  160.     if( !IsMazeReady() ) 
  161.         return;
  162.  
  163.     if( m_bAutopilot )
  164.         DoAutopilot( fElapsed );
  165.     else
  166.         DoManualPilot( fElapsed );
  167.  
  168.     // See if it's time to send a packet to the server with our updated coordinates
  169.     FLOAT fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
  170.     if( (fCurTime - m_fLastOutboundTime)*1000.0f > m_NetConfig.wUpdateRate )
  171.     {
  172.  
  173.         ClientPosPacket packet( m_vCameraPos.x, m_vCameraPos.z, m_aCameraYaw );
  174.  
  175.         // Pack the buffer with dummy data for testing.
  176.         if(m_NetConfig.wClientPackSizeArray[m_NetConfig.ubClientPackIndex] > 0)
  177.         {
  178.             WORD    wBufferSize = sizeof(packet) + m_NetConfig.wClientPackSizeArray[m_NetConfig.ubClientPackIndex];
  179.             VOID*   pTempBuffer = 0;
  180.  
  181.             pTempBuffer = malloc(wBufferSize);
  182.             if( NULL == pTempBuffer )
  183.             {
  184.                 //Out of memory, just bail
  185.                 DXTRACE_ERR_NOMSGBOX( TEXT("System out of Memory!"), E_OUTOFMEMORY );
  186.                 return;
  187.             }
  188.  
  189.             
  190.             FillMemory(pTempBuffer, wBufferSize, 'Z');
  191.             memcpy(pTempBuffer, &packet, sizeof(packet));
  192.  
  193.             SendPacket( (ClientPacket*)pTempBuffer, wBufferSize, FALSE, m_NetConfig.wTimeout );
  194.         
  195.             free(pTempBuffer);
  196.         }   
  197.         else
  198.         {
  199.             SendPacket( &packet, sizeof(packet), FALSE, m_NetConfig.wTimeout );
  200.         }
  201.  
  202.         m_fLastOutboundTime = fCurTime;
  203.  
  204.     }
  205. }
  206.  
  207.  
  208.  
  209.  
  210. //-----------------------------------------------------------------------------
  211. // Name: 
  212. // Desc: 
  213. //-----------------------------------------------------------------------------
  214. void CMazeClient::DoManualPilot( FLOAT fElapsed )
  215. {
  216.     // Check if we have the input focus
  217.     if( !m_bHaveInputFocus )
  218.         return;
  219.  
  220.     // Do rotations
  221.     if( GetAsyncKeyState( VK_LEFT ) & 0x8000 )
  222.         m_aCameraYaw += (DWORD) ((fElapsed*1000.0f) * 40.0f);
  223.     if( GetAsyncKeyState( VK_RIGHT ) & 0x8000 )
  224.         m_aCameraYaw -= (DWORD) ((fElapsed*1000.0f) * 40.0f);
  225.  
  226.     float e = fElapsed*1000.0f;
  227.  
  228.     // Compute new position based key input
  229.     D3DXVECTOR3 pos = m_vCameraPos;
  230.     if( GetAsyncKeyState( VK_UP ) & 0x8000 )
  231.     {
  232.         pos.x -= Sin(m_aCameraYaw) * 0.002f * e;
  233.         pos.z += Cos(m_aCameraYaw) * 0.002f * e;
  234.     }
  235.         
  236.     if( GetAsyncKeyState( VK_DOWN ) & 0x8000 )
  237.     {
  238.         pos.x += Sin(m_aCameraYaw) * 0.002f * e;
  239.         pos.z -= Cos(m_aCameraYaw) * 0.002f * e;
  240.     }
  241.  
  242.     // Ensure that we have stayed within the maze boundaries
  243.     if( pos.x < 0 ) pos.x = 0.1f;
  244.     if( pos.x >= m_Maze.GetWidth() ) pos.x = m_Maze.GetWidth() - 0.1f;
  245.     if( pos.z < 0 ) pos.z = 0.1f;
  246.     if( pos.z >= m_Maze.GetHeight() ) pos.z = m_Maze.GetHeight() - 0.1f;
  247.             
  248.     m_vCameraPos = pos;    
  249. }
  250.  
  251.  
  252.  
  253.  
  254. //-----------------------------------------------------------------------------
  255. // Name: 
  256. // Desc: 
  257. //-----------------------------------------------------------------------------
  258. void CMazeClient::DoAutopilot( FLOAT fElapsed )
  259. {
  260.     // While there is still time to use up...
  261.     while( fElapsed )
  262.     {
  263.         // See if we need to turn
  264.         if( m_aAutopilotTargetAngle != m_aCameraYaw )
  265.         {
  266.             SHORT diff = SHORT((m_aAutopilotTargetAngle - m_aCameraYaw)&TRIG_ANGLE_MASK);
  267.             FLOAT fNeeded = abs(diff)/40.0f;
  268.             if( fNeeded/1000.0f <= fElapsed )
  269.             {
  270.                 m_aCameraYaw = m_aAutopilotTargetAngle;
  271.                 fElapsed -= fNeeded/1000.0f;
  272.             }
  273.             else
  274.             {
  275.                 if( diff < 0 )
  276.                     m_aCameraYaw -= (DWORD) ((fElapsed*1000.0f) * 40.0f);
  277.                 else
  278.                     m_aCameraYaw += (DWORD) ((fElapsed*1000.0f) * 40.0f);
  279.                 fElapsed = 0;
  280.             }
  281.         }
  282.         else
  283.         {
  284.             // Ensure vAutopilotTarget is inside the maze boundry
  285.             if( m_vAutopilotTarget.x < 0                  || 
  286.                 m_vAutopilotTarget.x >= m_Maze.GetWidth() ||
  287.                 m_vAutopilotTarget.z < 0                  ||
  288.                 m_vAutopilotTarget.z >= m_Maze.GetHeight() )
  289.             {
  290.                 ZeroMemory( m_AutopilotVisited, sizeof(m_AutopilotVisited) );
  291.                 m_AutopilotStack.Empty();
  292.                 PickAutopilotTarget();
  293.                 return;
  294.             }
  295.             
  296.             // Facing right way, so now compute distance to target
  297.             D3DXVECTOR3 diff = m_vAutopilotTarget - m_vCameraPos;
  298.  
  299.             float fRange = float(sqrt((diff.x*diff.x)+(diff.z*diff.z)));
  300.  
  301.             // Are we there yet?
  302.             if( fRange > 0 )
  303.             {
  304.                 // No, so compute how long we'd need
  305.                 FLOAT fNeeded = fRange / 0.002f;
  306.  
  307.                 //Ensure we never leave the boundary of the Maze.
  308.                 D3DXVECTOR3 pos = m_vCameraPos;
  309.                 
  310.                 // Do we have enough time this frame?
  311.                 if( fNeeded/1000.0f <= fElapsed )
  312.                 {
  313.  
  314.                     // Yes, so just snap us there
  315.                     pos.x = m_vAutopilotTarget.x;
  316.                     pos.z = m_vAutopilotTarget.z;
  317.                     
  318.                     fElapsed -= fNeeded/1000.0f;
  319.                 }
  320.                 else
  321.                 {
  322.                     // No, so move us as far as we can
  323.                     pos.x -= Sin(m_aCameraYaw) * 0.002f * fElapsed*1000.0f;
  324.                     pos.z += Cos(m_aCameraYaw) * 0.002f * fElapsed*1000.0f;
  325.                     
  326.                     fElapsed = 0;
  327.                 }
  328.  
  329.                 // Ensure that we have stayed within the maze boundaries
  330.                 if( pos.x < 0 ) pos.x = 0.1f;
  331.                 if( pos.x >= m_Maze.GetWidth() ) pos.x = m_Maze.GetWidth() - 0.1f;
  332.                 if( pos.z < 0 ) pos.z = 0.1f;
  333.                 if( pos.z >= m_Maze.GetHeight() ) pos.z = m_Maze.GetHeight() - 0.1f;
  334.             
  335.                 // Assign our new values back to our globals.
  336.                 m_vCameraPos = pos;
  337.  
  338.             }
  339.             else
  340.             {
  341.                 // Reached target, so pick another
  342.                 PickAutopilotTarget();
  343.             }
  344.         }
  345.     }
  346. }
  347.  
  348.  
  349.  
  350.  
  351. //-----------------------------------------------------------------------------
  352. // Name: 
  353. // Desc: 
  354. //-----------------------------------------------------------------------------
  355. void CMazeClient::EngageAutopilot( BOOL bEngage )
  356. {
  357.     m_bEngageAutopilot = bEngage;
  358.  
  359.     if( !IsMazeReady() ) 
  360.         return;
  361.  
  362.     BOOL bPrevious = m_bAutopilot;
  363.     m_bAutopilot = bEngage;
  364.  
  365.     // If we weren't on autopilot before and are are autopilot now then need to init autopilot
  366.     if( m_bAutopilot && !bPrevious )
  367.     {
  368.         // First of all, snap us to the centre of the current cell
  369.         int cellx = int(m_vCameraPos.x);
  370.         int cellz = int(m_vCameraPos.z);
  371.         m_vCameraPos.x = cellx + 0.5f;
  372.         m_vCameraPos.z = cellz + 0.5f;
  373.  
  374.         // Ensure we're within the maze boundaries
  375.         if( cellx < 0 ) m_vCameraPos.x = 0.5f;
  376.         if( cellx >= int(m_Maze.GetWidth()) ) m_vCameraPos.x = m_Maze.GetWidth() - 0.5f;
  377.         if( cellz < 0 ) m_vCameraPos.z = 0.5f;
  378.         if( cellz >= int(m_Maze.GetHeight()) ) m_vCameraPos.z = m_Maze.GetHeight() - 0.5f;
  379.  
  380.         // Clear the visited array and stack
  381.         ZeroMemory( m_AutopilotVisited, sizeof(m_AutopilotVisited) );
  382.         m_AutopilotStack.Empty();
  383.  
  384.         // Pick the next target cell
  385.         PickAutopilotTarget();
  386.     }
  387. }
  388.  
  389.  
  390.  
  391.  
  392. //-----------------------------------------------------------------------------
  393. // Name: 
  394. // Desc: 
  395. //-----------------------------------------------------------------------------
  396. void CMazeClient::PickAutopilotTarget()
  397. {
  398.     // Get current cell and mark as visited
  399.     DWORD currentx = DWORD(m_vCameraPos.x);
  400.     DWORD currentz = DWORD(m_vCameraPos.z);
  401.     m_AutopilotVisited[currentz][currentx] = 1;
  402.  
  403.     // Figure out which directions are allowed. We're allowed to go in any direction
  404.     // where there isn't a wall in the way and that takes us to a cell we've visited before.
  405.     BYTE cell = m_Maze.GetCell(currentx,currentz);
  406.     ANGLE alloweddirs[5];
  407.     DWORD dwAllowed = 0;
  408.  
  409.     if( !(cell & MAZE_WALL_NORTH) && !m_AutopilotVisited[currentz-1][currentx] )
  410.         alloweddirs[dwAllowed++] = NORTH_ANGLE;
  411.     if( !(cell & MAZE_WALL_WEST) && !m_AutopilotVisited[currentz][currentx-1] )
  412.         alloweddirs[dwAllowed++] = WEST_ANGLE;
  413.     if( !(cell & MAZE_WALL_EAST) && !m_AutopilotVisited[currentz][currentx+1] )
  414.         alloweddirs[dwAllowed++] = EAST_ANGLE;
  415.     if( !(cell & MAZE_WALL_SOUTH) && !m_AutopilotVisited[currentz+1][currentx] )
  416.         alloweddirs[dwAllowed++] = SOUTH_ANGLE;
  417. /*
  418.     printf( "Walls: ") );
  419.     if( (cell & MAZE_WALL_NORTH) )
  420.         printf( "N ") );
  421.     if( (cell & MAZE_WALL_WEST) )
  422.         printf( "W ") );
  423.     if( (cell & MAZE_WALL_EAST) )
  424.         printf( "E ") );
  425.     if( (cell & MAZE_WALL_SOUTH) )
  426.         printf( "S ") );
  427.     printf( "\n") );
  428. */
  429.  
  430.     // Is there anywhere to go?
  431.     if( dwAllowed == 0 )
  432.     {
  433.         // Nope. Can we backtrack?
  434.         if( m_AutopilotStack.GetCount() > 0 )
  435.         {
  436.             // Yes, so pop cell off the stack
  437.             AutopilotCell   cell(m_AutopilotStack.Pop());
  438.             m_vAutopilotTarget.x = float(cell.x) + 0.5f;
  439.             m_vAutopilotTarget.z = float(cell.y) + 0.5f;
  440.  
  441.             if( cell.x < currentx )
  442.                 m_aAutopilotTargetAngle = WEST_ANGLE;
  443.             else if( cell.x > currentx )
  444.                 m_aAutopilotTargetAngle = EAST_ANGLE;
  445.             else if( cell.y > currentz )
  446.                 m_aAutopilotTargetAngle = SOUTH_ANGLE;
  447.             else
  448.                 m_aAutopilotTargetAngle = NORTH_ANGLE;
  449.         }
  450.         else
  451.         {
  452.             // No, so we have explored entire maze and must start again
  453.             ZeroMemory( m_AutopilotVisited, sizeof(m_AutopilotVisited) );
  454.             m_AutopilotStack.Empty();
  455.             PickAutopilotTarget();
  456.         }
  457.     }
  458.     else
  459.     {
  460.         // See if we can continue in current direction
  461.         BOOL bPossible = FALSE;
  462.         for( DWORD i = 0; i < dwAllowed; i++ )
  463.         {
  464.             if( alloweddirs[i] == m_aCameraYaw )
  465.             {
  466.                 bPossible = TRUE;
  467.                 break;
  468.             }
  469.         }
  470.  
  471.         // If it's allowed to go forward, then have 1 in 2 chance of doing that anyway, otherwise pick randomly from
  472.         // available alternatives
  473.         if( bPossible && (rand() & 0x1000) )
  474.             m_aAutopilotTargetAngle = m_aCameraYaw;
  475.         else
  476.             m_aAutopilotTargetAngle = alloweddirs[ (rand() % (dwAllowed<<3) ) >>3 ];
  477.  
  478.         m_vAutopilotTarget.z = float(currentz) + 0.5f;
  479.         m_vAutopilotTarget.x = float(currentx) + 0.5f;
  480.  
  481.         switch( m_aAutopilotTargetAngle )
  482.         {
  483.             case SOUTH_ANGLE:
  484.                 m_vAutopilotTarget.z += 1.0f;
  485.                 break;
  486.  
  487.             case WEST_ANGLE:
  488.                 m_vAutopilotTarget.x -= 1.0f;
  489.                 break;
  490.  
  491.             case EAST_ANGLE:
  492.                 m_vAutopilotTarget.x += 1.0f;
  493.                 break;
  494.  
  495.             case NORTH_ANGLE:
  496.                 m_vAutopilotTarget.z -= 1.0f;
  497.                 break;
  498.         }
  499.  
  500.         // Push current cell onto stack
  501.         m_AutopilotStack.Push( AutopilotCell(BYTE(currentx),BYTE(currentz)) );
  502.     }
  503. }
  504.  
  505.  
  506.  
  507.  
  508. //-----------------------------------------------------------------------------
  509. // Name: 
  510. // Desc: 
  511. //-----------------------------------------------------------------------------
  512. HRESULT CMazeClient::OnPacket( DWORD dwFrom, void* dwData, DWORD dwSize )
  513. {
  514.     HRESULT hr  = DPN_OK;
  515.     
  516.     BOOL    fFoundSize = FALSE;
  517.     DWORD   dwReqSize = 0;
  518.     DWORD   dwSRand = 0;
  519.     
  520.     ServerPacket* pPacket = (ServerPacket*)dwData;
  521.     switch( pPacket->wType )
  522.     {
  523.         case PACKETTYPE_SERVER_CONFIG:
  524.         {
  525.             if( dwSize != sizeof(ServerConfigPacket) )
  526.             {
  527.                 m_pMazeApp->SetDisconnectNow( TRUE );
  528.                 m_pMazeApp->SetOutOfDateClient( TRUE );
  529.                 m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Disconnected because MazeClient is out of date.") );
  530.                 m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Please get updated version") );
  531.                 m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("from http://msdn.microsoft.com/directx/") );
  532.                 break;
  533.             }
  534.  
  535.             m_NetConfig = ((ServerConfigPacket*)pPacket)->Config;
  536.  
  537.             m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Got MazeServer config settings") );
  538.             m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Maze Size=(%d,%d) ReliableRate=%d%%"), 
  539.                                        m_NetConfig.dwMazeWidth, m_NetConfig.dwMazeHeight, 
  540.                                        DWORD(m_NetConfig.ubReliableRate) );
  541.             m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("UpdateRate=%dms Timeout=%d"), 
  542.                                        m_NetConfig.wUpdateRate, m_NetConfig.wTimeout );
  543.             m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("ThreadWait=%dms"), 
  544.                                        m_NetConfig.dwThreadWait );
  545.             m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("ClientPackSize=%d ServerPackSize=%d (bytes)"), 
  546.                                        m_NetConfig.wClientPackSizeArray[m_NetConfig.ubClientPackIndex],
  547.                                        m_NetConfig.wServerPackSizeArray[m_NetConfig.ubServerPackIndex]);
  548.             
  549.             // See if we have gotten our fist config. If not, send version info.
  550.             if(! GotFirstConfig()) //If first time in maze.
  551.             {
  552.                 // The client expects the server to send a ServerConfigPacket packet first, 
  553.                 // then the client sends a ClientVersionPacket, and then the server sends a 
  554.                 // ServerAckVersionPacket packet and the game begins
  555.                 ClientVersionPacket packet( MAZE_CLIENT_VERSION );
  556.                 SendPacket( &packet, sizeof(packet), TRUE, 0 );
  557.  
  558.                 SetFirstConfig( TRUE );
  559.             }
  560.  
  561.             break;
  562.         }
  563.  
  564.         case PACKETTYPE_SERVER_ACKVERSION:
  565.         {
  566.             if( dwSize != sizeof(ServerAckVersionPacket) )
  567.             {
  568.                 m_pMazeApp->SetDisconnectNow( TRUE );
  569.                 m_pMazeApp->SetOutOfDateClient( TRUE );
  570.                 m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Disconnected because MazeClient is out of date.") );
  571.                 m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Please get updated version") );
  572.                 m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("from http://msdn.microsoft.com/directx/") );
  573.                 break;
  574.             }
  575.  
  576.             ServerAckVersionPacket* pAckVersionPacket = (ServerAckVersionPacket*)pPacket;
  577.  
  578.             // Record the dpnid that the server uses for to talk to us. 
  579.             // This is just done so that we can record this number in the 
  580.             // logs to help match server side logs with client side logs.
  581.             m_dwLocalClientID = pAckVersionPacket->dwClientID;
  582.  
  583.             m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Server assigned ID: 0x%0.8x"), m_dwLocalClientID );
  584.             m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Server accepted client version") );
  585.  
  586.  
  587.             hr = m_Maze.Init( m_NetConfig.dwMazeWidth, 
  588.                               m_NetConfig.dwMazeHeight, DEFAULT_SEED );
  589.             if( FAILED(hr) )
  590.                 DXTRACE_ERR( TEXT("Init"), hr );
  591.  
  592.             //Seed the random number generator.
  593.             dwSRand = (DWORD) (DXUtil_Timer( TIMER_GETABSOLUTETIME ) * (DWORD)GetCurrentThreadId() );
  594.             srand( dwSRand );
  595.  
  596.             // Set random start location
  597.             m_vCameraPos = D3DXVECTOR3( rand() % m_Maze.GetWidth() + 0.5f, 0.5, 
  598.                                         rand() % m_Maze.GetHeight() + 0.5f );
  599.  
  600.             SetMazeReady( TRUE );
  601.             EngageAutopilot( TRUE );
  602.             break;
  603.         }
  604.  
  605.         case PACKETTYPE_SERVER_ACKPOS:
  606.             
  607.             //Make sure we at least have a ServerAckPacket.
  608.             if( dwSize < sizeof(ServerAckPacket) )
  609.             {
  610.                 fFoundSize = FALSE;
  611.             }
  612.             else 
  613.             {   
  614.                 //Size of our required packet. Does not include custom pack data.
  615.                 dwReqSize = (sizeof(ServerAckPacket) + 
  616.                              (sizeof(PlayerStatePacket) *
  617.                              ((ServerAckPacket*)pPacket)->wPlayerStatePacketCount));
  618.                 
  619.                 //Check to see if we have a valid packet size.
  620.                 if (dwSize < dwReqSize)
  621.                     fFoundSize = FALSE;
  622.                 else if ( !IsValidPackSize( dwSize - dwReqSize ))
  623.                     fFoundSize = FALSE;
  624.                 else
  625.                     fFoundSize = TRUE;
  626.             }
  627.             
  628.             //If we did not find a correct packet size. Exit.
  629.             if( !fFoundSize )
  630.             {
  631.                 m_pMazeApp->SetDisconnectNow( TRUE );
  632.                 m_pMazeApp->SetOutOfDateClient( TRUE );
  633.                 m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Disconnected because MazeClient is out of date.") );
  634.                 m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Please get updated version") );
  635.                 m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("from http://msdn.microsoft.com/directx/") );
  636.                 break;
  637.             }
  638.  
  639.             //Found correct packet size. Update clients.
  640.             SetPlayerStats( ((ServerAckPacket*)pPacket)->wPlayerCount,
  641.                             ((ServerAckPacket*)pPacket)->wPlayerStatePacketCount );
  642.  
  643.             if( ((ServerAckPacket*)pPacket)->wPlayerStatePacketCount )
  644.                 HandlePlayerObjectsInAckPacket( (ServerAckPacket*)pPacket );
  645.             break;
  646.  
  647.         default:
  648.             m_pMazeApp->ConsolePrintf( LINE_LOG, TEXT("Received unknown %d byte packet from server"), dwSize );
  649.             break;
  650.     };
  651.  
  652.     
  653.     //If the server has given us a custom wait time, Let's sleep for that amount of time.
  654.     if( m_NetConfig.dwThreadWait > 0 )
  655.     {
  656.         Sleep( m_NetConfig.dwThreadWait );
  657.     }
  658.           
  659.         
  660.     return S_OK;
  661. }
  662.  
  663.  
  664.  
  665.  
  666. //-----------------------------------------------------------------------------
  667. // Name: 
  668. // Desc: 
  669. //-----------------------------------------------------------------------------
  670. BOOL CMazeClient::IsValidPackSize( DWORD dwSize )
  671. {
  672.     BOOL fFoundSize = FALSE;
  673.     BYTE ubPackLocation = m_NetConfig.ubServerPackIndex;
  674.  
  675.     // Look for valid Client pack size in PackArray.
  676.     // If found, return TRUE, otherwise FALSE.
  677.     if(dwSize != m_NetConfig.wServerPackSizeArray[ubPackLocation])
  678.     {
  679.         for( --ubPackLocation; ubPackLocation != m_NetConfig.ubServerPackIndex; ubPackLocation--)
  680.         {
  681.             if(dwSize == m_NetConfig.wServerPackSizeArray[ubPackLocation])
  682.             {
  683.                 fFoundSize = TRUE;
  684.                 break;
  685.             }
  686.             
  687.             if(ubPackLocation >= PACK_ARRAY_SIZE)  ubPackLocation = PACK_ARRAY_SIZE;  //Wrap the array.
  688.         }
  689.     }
  690.     else
  691.     {
  692.         fFoundSize = TRUE;
  693.     }
  694.     
  695.     return fFoundSize;
  696.  
  697. }
  698.  
  699. //-----------------------------------------------------------------------------
  700. // Name: 
  701. // Desc: 
  702. //-----------------------------------------------------------------------------
  703. void CMazeClient::OnSessionLost( DWORD dwReason )
  704. {
  705. }
  706.  
  707.  
  708.  
  709. //-----------------------------------------------------------------------------
  710. // Name: 
  711. // Desc: 
  712. //-----------------------------------------------------------------------------
  713. void CMazeClient::SendPacket( ClientPacket* pPacket, DWORD dwSize, 
  714.                               BOOL bGuaranteed, DWORD dwTimeout )
  715. {
  716.     if( m_NetConfig.ubReliableRate > m_NetRandom.Get( 100 ) )
  717.         bGuaranteed = TRUE;
  718.  
  719.     m_pNet->SendPacket( pPacket, dwSize, bGuaranteed, dwTimeout );
  720. };
  721.  
  722.  
  723.  
  724.  
  725. //-----------------------------------------------------------------------------
  726. // Name: 
  727. // Desc: 
  728. //-----------------------------------------------------------------------------
  729. DWORD CMazeClient::GetRoundTripLatencyMS()
  730. {
  731.     return m_pNet->GetRoundTripLatencyMS();
  732. }
  733.  
  734.  
  735.  
  736.  
  737. //-----------------------------------------------------------------------------
  738. // Name: 
  739. // Desc: 
  740. //-----------------------------------------------------------------------------
  741. DWORD CMazeClient::GetThroughputBPS()
  742. {
  743.     return m_pNet->GetThroughputBPS();
  744. }
  745.  
  746.  
  747.  
  748. //-----------------------------------------------------------------------------
  749. // Name: 
  750. // Desc: 
  751. //-----------------------------------------------------------------------------
  752. void CMazeClient::HandlePlayerObjectsInAckPacket( ServerAckPacket* pPacket )
  753. {
  754.     PlayerStatePacket* pClientInfo = (PlayerStatePacket*)(pPacket+1);
  755.  
  756.     if( !IsMazeReady() ) 
  757.         return;
  758.  
  759.     // Lock the world database
  760.     LockWorld();
  761.  
  762.     // Loop though the PlayerObject chunks
  763.     for( DWORD count = pPacket->wPlayerStatePacketCount; count; count--, pClientInfo++ )
  764.     {
  765.         // Get the PlayerObject we think this is
  766.         PlayerObject* pPlayerObject = &m_PlayerObjects[pClientInfo->dwID & PLAYER_OBJECT_SLOT_MASK];
  767.  
  768.         // Does the ID match the one we have?
  769.         if( pPlayerObject->dwID != pClientInfo->dwID )
  770.         {
  771.             // No, so the PlayerObject we have needs to be deleted (server reused the same slot
  772.             // number, so old PlayerObject must be toast)
  773.             RemovePlayerObjectFromCells( pPlayerObject );
  774.  
  775.             // Set the ID to the new ID
  776.             pPlayerObject->dwID = pClientInfo->dwID;
  777.             pPlayerObject->wCellX = WORD(pClientInfo->fX);
  778.             pPlayerObject->wCellY = WORD(pClientInfo->fY);
  779.  
  780.             // Insert into the appropriate cell list
  781.             AddPlayerObjectToCells( pPlayerObject );
  782.         }
  783.         else
  784.         {
  785.             // Yes, compute the new cell coordinates
  786.             DWORD newcellx = DWORD(pClientInfo->fX);
  787.             DWORD newcelly = DWORD(pClientInfo->fY);
  788.  
  789.             // Are they the same as the ones we already have?
  790.             if( newcellx != pPlayerObject->wCellX || newcelly != pPlayerObject->wCellY )
  791.             {
  792.                 // No, so need to remove from old cell and add to new one
  793.                 RemovePlayerObjectFromCells( pPlayerObject );
  794.                 pPlayerObject->wCellX = WORD(newcellx);
  795.                 pPlayerObject->wCellY = WORD(newcelly);
  796.                 AddPlayerObjectToCells( pPlayerObject );
  797.             }
  798.         }
  799.  
  800.         // Update timestamp and position
  801.         pPlayerObject->vPos.x     = pClientInfo->fX;
  802.         pPlayerObject->vPos.y     = 0.5f;
  803.         pPlayerObject->vPos.z     = pClientInfo->fY;
  804.         pPlayerObject->aCameraYaw = pClientInfo->aCameraYaw;
  805.         pPlayerObject->fLastValidTime = DXUtil_Timer( TIMER_GETAPPTIME );
  806.     }
  807.  
  808.     // Unlock world database
  809.     UnlockWorld();
  810. }
  811.  
  812.  
  813.  
  814.  
  815. //-----------------------------------------------------------------------------
  816. // Name: 
  817. // Desc: 
  818. //-----------------------------------------------------------------------------
  819. void CMazeClient::AddPlayerObjectToCells( PlayerObject* pPlayerObject )
  820. {
  821.     if( pPlayerObject->wCellX == 0xffff )
  822.         return;
  823.  
  824.     if( FALSE == IsPlayerObjectInCell( pPlayerObject->wCellX, pPlayerObject->wCellY, pPlayerObject ) )
  825.     {
  826.         PlayerObject** ppCell = &m_pctCells[pPlayerObject->wCellY][pPlayerObject->wCellX];
  827.         pPlayerObject->pNext = *ppCell;
  828.         *ppCell = pPlayerObject;
  829.     }
  830. }
  831.  
  832.  
  833.  
  834.  
  835. //-----------------------------------------------------------------------------
  836. // Name: 
  837. // Desc: 
  838. //-----------------------------------------------------------------------------
  839. void CMazeClient::RemovePlayerObjectFromCells( PlayerObject* pPlayerObject )
  840. {
  841.     if( pPlayerObject->wCellX == 0xffff )
  842.         return;
  843.  
  844.     PlayerObject** ppCell   = &m_pctCells[pPlayerObject->wCellY][pPlayerObject->wCellX];
  845.     PlayerObject* pCur      = *ppCell;
  846.     PlayerObject* pPrev     = NULL;
  847.     while( pCur )
  848.     {
  849.         if( pCur == pPlayerObject )
  850.         {
  851.             pCur = pPlayerObject->pNext;
  852.  
  853.             // Found pPlayerObject, so remove pPlayerObject from the m_pctCells linked list
  854.             if( pPrev )
  855.                 pPrev->pNext = pPlayerObject->pNext;
  856.             else
  857.                 *ppCell = pPlayerObject->pNext;
  858.             pPlayerObject->pNext = NULL;
  859.  
  860.             // Update pPlayerObject so that it is marked as removed
  861.             pPlayerObject->wCellX = pPlayerObject->wCellY = 0xffff;
  862.  
  863.             // Continue searching, and remove any other instances of 
  864.             // pPlayerObject from list (there shouldn't be, however)
  865.         }
  866.         else
  867.         {
  868.             pPrev = pCur;
  869.             pCur  = pCur->pNext;
  870.         }
  871.     }
  872. }
  873.  
  874.  
  875.  
  876.  
  877. //-----------------------------------------------------------------------------
  878. // Name: 
  879. // Desc: 
  880. //-----------------------------------------------------------------------------
  881. PlayerObject* CMazeClient::GetFirstPlayerObjectInCell( DWORD x, DWORD z )
  882. {
  883.     if( !IsMazeReady() ) 
  884.         return NULL;
  885.  
  886.     FLOAT fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
  887.  
  888.     // Remove any PlayerObjects which are out of date (since they're probably not really in this
  889.     // cell any more, but the server has just stopped telling us about them)
  890.     PlayerObject** ppCell           = &m_pctCells[z][x];
  891.     PlayerObject* pCur              = m_pctCells[z][x];
  892.     PlayerObject* pPrev             = NULL;
  893.     PlayerObject* pPlayerObject     = NULL;
  894.     while( pCur )
  895.     {
  896.         // Too old?
  897.         if( (fCurTime - pCur->fLastValidTime) > 5.0f )
  898.         {
  899.             pPlayerObject = pCur;
  900.             pCur = pCur->pNext;
  901.  
  902.             // pPlayerObject is too old, so remove pPlayerObject from the m_pctCells linked list
  903.             if( pPrev )
  904.                 pPrev->pNext = pPlayerObject->pNext;
  905.             else
  906.                 *ppCell = pPlayerObject->pNext;
  907.             pPlayerObject->pNext = NULL;
  908.  
  909.             // Update pPlayerObject so that it is marked as removed
  910.             pPlayerObject->wCellX = pPlayerObject->wCellY = 0xffff;
  911.  
  912.             // Continue searching, and remove any other old instances from list 
  913.         }
  914.         else
  915.         {
  916.             pPrev = pCur;
  917.             pCur  = pCur->pNext;
  918.         }
  919.     }
  920.  
  921.     // Now return first remaining PlayerObject in the cell (if any)
  922.     return m_pctCells[z][x];
  923. }
  924.  
  925.  
  926.  
  927.  
  928. //-----------------------------------------------------------------------------
  929. // Name: 
  930. // Desc: 
  931. //-----------------------------------------------------------------------------
  932. BOOL CMazeClient::IsPlayerObjectInCell( DWORD wCellX, DWORD wCellY, PlayerObject* pPlayerObject )
  933. {
  934.     PlayerObject* pPlayerObjectTmp = m_pctCells[wCellY][wCellX];
  935.     while( pPlayerObjectTmp )
  936.     {
  937.         if( pPlayerObjectTmp == pPlayerObject )
  938.             return TRUE;
  939.  
  940.         pPlayerObjectTmp = pPlayerObjectTmp->pNext;
  941.     }
  942.  
  943.     return FALSE;
  944. }
  945.  
  946.