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 / MazeApp.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  30.2 KB  |  902 lines

  1. //----------------------------------------------------------------------------
  2. // File: mazeapp.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 <tchar.h>
  14. #include <math.h>
  15. #include <process.h>
  16. #include <dxerr8.h>
  17. #include <dplay8.h>
  18. #include "DXUtil.h"
  19. #include "SyncObjects.h"
  20. #include "DummyConnector.h"
  21. #include "DPlay8Client.h"
  22. #include "MazeClient.h"
  23. #include "IMazeGraphics.h"
  24. #include "MazeApp.h"
  25.  
  26.  
  27. static CMazeApp* s_pMazeApp = NULL;
  28.  
  29.  
  30. //-----------------------------------------------------------------------------
  31. // Name:
  32. // Desc:
  33. //-----------------------------------------------------------------------------
  34. CMazeApp::CMazeApp()
  35. {
  36.     s_pMazeApp                  = this;
  37.     m_hOutputMsgThread          = NULL;
  38.     m_bQuitThread               = NULL;
  39.     m_hOutputMsgEvent           = NULL;
  40.     m_dwNextOutputMsg           = 0;
  41.     m_bLocalLoopback            = TRUE;
  42.     m_bAllowConnect             = FALSE;
  43.     m_bConnectNow               = TRUE;
  44.     m_bSaveSettings             = TRUE;
  45.     m_lQueueSize                = 0;
  46.     m_dwNextFreeOutputMsg       = 0;
  47.     m_hLogFile                  = NULL;
  48.  
  49.     m_bLocalLoopbackInitDone    = FALSE;
  50.     m_bInitDone                 = FALSE;
  51.  
  52.     // Create an event object to flag pending output messages
  53.     m_hOutputMsgEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  54.  
  55.     //Initialize our Config structure to 0.
  56.     ZeroMemory(&m_Config, sizeof(m_Config));
  57.  
  58.     //Must init buffer size to -1, since 0 is valid.
  59.     m_Config.dwSPBufferSize = 0xffffffff;
  60.  
  61.  
  62. }
  63.  
  64.  
  65.  
  66.  
  67. //-----------------------------------------------------------------------------
  68. // Name:
  69. // Desc:
  70. //-----------------------------------------------------------------------------
  71. CMazeApp::~CMazeApp()
  72. {
  73.     CloseHandle( m_hOutputMsgEvent );
  74. }
  75.  
  76.  
  77.  
  78.  
  79. //-----------------------------------------------------------------------------
  80. // Name:
  81. // Desc:
  82. //-----------------------------------------------------------------------------
  83. HRESULT CMazeApp::Create( IMazeGraphics* pMazeGraphics )
  84. {
  85.     m_pMazeGraphics = pMazeGraphics;
  86.     if( m_pMazeGraphics == NULL )
  87.         return E_FAIL;
  88.  
  89.     m_pMazeGraphics->Init( this, &m_DP8Client, &m_MazeClient );
  90.  
  91.     return S_OK;
  92. }
  93.  
  94.  
  95.  
  96.  
  97. //-----------------------------------------------------------------------------
  98. // Name:
  99. // Desc:
  100. //-----------------------------------------------------------------------------
  101. int CMazeApp::Run( HINSTANCE hInst )
  102. {
  103.     if( NULL == m_pMazeGraphics )
  104.         return 0;
  105.  
  106.     // Start the timer and init the random seed
  107.     DXUtil_Timer( TIMER_START );
  108.     DWORD dwSRand = (DWORD) (DXUtil_Timer( TIMER_GETABSOLUTETIME ) * UINT_MAX * (DWORD)GetCurrentThreadId() );
  109.     srand( dwSRand );
  110.  
  111.     // Tell OS's that have power management to not 
  112.     // sleep, since this app will be using the 
  113.     // network connection and need very little user input
  114.     SuspendPowerManagement();
  115.  
  116.     // Initialize COM
  117.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  118.  
  119.     // Extract configuration settings from the registry
  120.     ReadConfig();
  121.  
  122.     if( m_Config.bFileLogging )
  123.         CreateTempLogFile();
  124.  
  125.     if( SUCCEEDED( m_pMazeGraphics->Create( hInst ) ) )
  126.     {
  127.         ConsolePrintf( LINE_LOG, TEXT("DirectPlayMaze client started.") );
  128.  
  129.         // Spin up a thread to record/display the output
  130.         UINT dwOutputMsgThreadID;
  131.         m_hOutputMsgThread = (HANDLE)_beginthreadex( NULL, 0, StaticOutputMsgThread, 
  132.                                                   NULL, 0, &dwOutputMsgThreadID );
  133.  
  134.         // Initialize maze client object - basically just build the maze
  135.         m_MazeClient.Init( this, m_pMazeGraphics );
  136.  
  137.         m_pMazeGraphics->Run();
  138.  
  139.         // Wait for threads to shutdown
  140.         DXUtil_Trace( TEXT("Quiting\n") );
  141.         m_bQuitThread = TRUE;
  142.         SetEvent( m_hOutputMsgEvent );
  143.         WaitForSingleObject( m_hOutputMsgThread, INFINITE );
  144.         CloseHandle( m_hOutputMsgThread );
  145.  
  146.         // Write configuration settings to registry
  147.         WriteConfig();
  148.  
  149.         m_pMazeGraphics->Shutdown();
  150.     }
  151.  
  152.     // Clean up
  153.     DXUtil_Trace( TEXT("Shutting down client\n") );
  154.     m_MazeClient.Shutdown();
  155.     DXUtil_Trace( TEXT("Shutting down dp8\n") );
  156.     m_DP8Client.Shutdown();
  157.     CoUninitialize();
  158.  
  159.     return 0;
  160. }
  161.     
  162.  
  163.  
  164.  
  165. //-----------------------------------------------------------------------------
  166. // Name: 
  167. // Desc: 
  168. //-----------------------------------------------------------------------------
  169. void CMazeApp::SuspendPowerManagement()
  170. {
  171.     TCHAR szPath[MAX_PATH];
  172.     HINSTANCE hInstKernel32 = NULL;
  173.     typedef EXECUTION_STATE (WINAPI* LPSETTHREADEXECUTIONSTATE)( EXECUTION_STATE esFlags );
  174.     LPSETTHREADEXECUTIONSTATE pSetThreadExecutionState = NULL;
  175.  
  176.     GetSystemDirectory(szPath, MAX_PATH);
  177.  
  178.     // SetThreadExecutionState() isn't availible on some old OS's, 
  179.     // so do a LoadLibrary to get to it.
  180.     lstrcat(szPath, TEXT("\\kernel32.dll"));
  181.     hInstKernel32 = LoadLibrary(szPath);
  182.  
  183.     if (hInstKernel32 != NULL)
  184.     {
  185.         pSetThreadExecutionState = (LPSETTHREADEXECUTIONSTATE)GetProcAddress(hInstKernel32, "SetThreadExecutionState");
  186.         if( pSetThreadExecutionState != NULL )
  187.         {
  188.             // Tell OS's that have power management to not 
  189.             // sleep, since this app will be using the 
  190.             // network connection and need very little user input
  191.             pSetThreadExecutionState( ES_SYSTEM_REQUIRED | ES_CONTINUOUS );
  192.         }
  193.  
  194.         FreeLibrary(hInstKernel32);
  195.     }
  196. }
  197.  
  198.  
  199.  
  200.  
  201. //-----------------------------------------------------------------------------
  202. // Name: 
  203. // Desc: 
  204. //-----------------------------------------------------------------------------
  205. HRESULT CMazeApp::FrameMove( FLOAT fElapsedTime )
  206. {
  207.     HRESULT hr;
  208.     FLOAT fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
  209.  
  210.     static FLOAT s_fLastConnect     = INT_MIN;
  211.     static FLOAT s_fStartEnumTime   = INT_MIN;
  212.     static FLOAT s_fStopEnumTime    = INT_MIN;
  213.     static FLOAT s_fEnumStarted     = FALSE;
  214.  
  215.     if( m_DP8Client.IsSessionLost() ) 
  216.     {
  217.  
  218.         if( FALSE == m_bLocalLoopbackInitDone )
  219.         {
  220.             if( m_bInitDone )
  221.             {
  222.                 ConsolePrintf( LINE_LOG, TEXT("Disconnected from server") );
  223.  
  224.                 if( m_DP8Client.GetSessionLostReason() == DISCONNNECT_REASON_CLIENT_OUT_OF_DATE )
  225.                 {
  226.                     ConsolePrintf( LINE_LOG, TEXT("Disconnected because MazeClient is out of date.") );
  227.                     ConsolePrintf( LINE_LOG, TEXT("Please get updated version") );
  228.                     ConsolePrintf( LINE_LOG, TEXT("from http://msdn.microsoft.com/directx/") );
  229.                     m_bOutOfDateClient = TRUE;
  230.                 }
  231.  
  232.                 // Disconnected, so retry in 10 seconds
  233.                 s_fStopEnumTime = fCurTime - m_Config.dwNetworkRetryDelay * 60.0f + 10.0f;     
  234.             }
  235.             else
  236.             {
  237.                 // If just starting up, then retry immediately
  238.                 m_bInitDone = TRUE;
  239.             }
  240.  
  241.             m_MazeClient.LockWorld();
  242.             m_MazeClient.Reset();
  243.  
  244.             // Now that the session is lost we need to 
  245.             // restart DirectPlay by calling Close() 
  246.             // and Init() on m_pDPlay
  247.             m_DP8Client.Shutdown();
  248.  
  249.             //Pass in the our structure in order to get out configuration data.
  250.             m_DP8Client.Init(GetConfig());
  251.  
  252.             if( m_bAllowLoopback )
  253.                 InitServerForLoopback();
  254.  
  255.             m_MazeClient.UnlockWorld();
  256.             m_bLocalLoopbackInitDone = TRUE;
  257.         }
  258.  
  259.         if( ( !s_fEnumStarted && fCurTime - s_fStopEnumTime > m_Config.dwNetworkRetryDelay * 60.0f || m_bConnectNow )
  260.             && m_bAllowConnect && !m_bOutOfDateClient )
  261.         {
  262.             m_bConnectNow = FALSE;
  263.  
  264.             if( SUCCEEDED( hr = StartSessionEnum() ) )
  265.             {
  266.                 // DirectPlay host enumeration started
  267.                 ConsolePrintf( LINE_LOG, TEXT("Starting DirectPlay host enumeration") );
  268.                 s_fStartEnumTime = fCurTime;
  269.                 s_fEnumStarted = TRUE;
  270.             }
  271.             else
  272.             {
  273.                 //If we are in the stress connect, try the next connection. 
  274.                 //When we get back to 1, then we have gone through the list, so let's wait.
  275.                 if(m_Config.dwStressConnect > 1) 
  276.                 {
  277.                     //We are going through the list of machines to attempt connection. So lets try the next right away.
  278.                     ConsolePrintf( LINE_LOG, TEXT("Host not found. Trying local subnet.") );
  279.                     m_bConnectNow = TRUE;  //Try the enum right away with the next item in the list.
  280.                 }
  281.                 else
  282.                 {
  283.                     ConsolePrintf( LINE_LOG, TEXT("DirectPlay host enumeration failed to start.") );
  284.                     ConsolePrintf( LINE_LOG, TEXT("Will try again in %d minutes."), m_Config.dwNetworkRetryDelay );
  285.                 }
  286.                 
  287.                 // DirectPlay host enumeration failed to start
  288.                 // Will try again in m_Config.dwNetworkRetryDelay minutes
  289.                 s_fStopEnumTime = fCurTime;
  290.                 s_fEnumStarted = FALSE;
  291.             }
  292.         }
  293.  
  294.         if( s_fEnumStarted && fCurTime - s_fStartEnumTime > 5.0f * 60.0f )
  295.         {
  296.             //If we are in the stress connect, try the next connection. 
  297.             //When we get back to 1, then we have gone through the list, so let's start over.
  298.             if(m_Config.dwStressConnect > 1) 
  299.             {
  300.                 //We are going through the list of machines to attempt connection. So lets try the next right away.
  301.                 ConsolePrintf( LINE_LOG, TEXT("Host not found. Trying local subnet.") );
  302.                 m_bConnectNow = TRUE;  //Try the enum right away with the next item in the list.
  303.             }
  304.             else
  305.             {
  306.                 ConsolePrintf( LINE_LOG, TEXT("No host found. Stopping DirectPlay host enumeration") );
  307.                 ConsolePrintf( LINE_LOG, TEXT("Will try again in %d minutes."), m_Config.dwNetworkRetryDelay );
  308.             }
  309.             // Stop enumeration
  310.             m_DP8Client.StopSessionEnum();
  311.             s_fStopEnumTime = fCurTime;
  312.             s_fEnumStarted = FALSE;
  313.         }
  314.  
  315.         if( s_fEnumStarted && fCurTime - s_fLastConnect > 0.5f )
  316.         {
  317.             if( TRUE == TryToConnect() )
  318.             {
  319.                 // Connect successful 
  320.                 ConsolePrintf( LINE_LOG, TEXT("Connected to server.  Host enumeration stopped.") );
  321.                 m_bLocalLoopback    = FALSE;
  322.                 s_fEnumStarted      = FALSE;
  323.                 m_bLocalLoopbackInitDone = FALSE;
  324.                 
  325.             }
  326.  
  327.             s_fLastConnect = fCurTime;
  328.         }
  329.  
  330.         m_bDisconnectNow = FALSE;
  331.     }
  332.     else
  333.     {
  334.         m_bLocalLoopbackInitDone = FALSE;
  335.  
  336.         if( m_Config.dwLogLevel > 1 )
  337.         {
  338.             // Display position every so often
  339.             static float fLastPosUpdate = fCurTime;
  340.             if( fCurTime - fLastPosUpdate > 10.0f )
  341.             {
  342.                 D3DXVECTOR3 vPos = m_MazeClient.GetCameraPos(); 
  343.                 DWORD dwNumPlayers, dwNumNearbyPlayers;
  344.                 m_MazeClient.GetPlayerStats( &dwNumPlayers, &dwNumNearbyPlayers );
  345.                 ConsolePrintf( LINE_LOG, TEXT("Position: (%5.1f,%5.1f), Players: %d, Nearby Players: %d"), 
  346.                               vPos.x, vPos.z, dwNumPlayers, dwNumNearbyPlayers );
  347.                 fLastPosUpdate = fCurTime;
  348.             }
  349.         }
  350.  
  351.         // Display connection info every so often
  352.         static float fLastLogUpdate = fCurTime;
  353.         if( m_Config.dwAutoPrintStats > 0 && 
  354.             fCurTime - fLastLogUpdate > m_Config.dwAutoPrintStats * 60.0f )
  355.         {
  356.             D3DXVECTOR3 vPos = m_MazeClient.GetCameraPos(); 
  357.             DWORD dwNumPlayers, dwNumNearbyPlayers;
  358.             m_MazeClient.GetPlayerStats( &dwNumPlayers, &dwNumNearbyPlayers );
  359.             ConsolePrintf( LINE_LOG, TEXT("Position: (%5.1f,%5.1f), Players: %d, Nearby Players: %d"), 
  360.                           vPos.x, vPos.z, dwNumPlayers, dwNumNearbyPlayers );
  361.  
  362.             TCHAR strInfo[5000];
  363.             TCHAR* strEndOfLine;
  364.             TCHAR* strStartOfLine;
  365.  
  366.             // Query the IOutboudNet for info about the connection to this user
  367.             m_DP8Client.GetConnectionInfo( strInfo );
  368.  
  369.             ConsolePrintf( LINE_LOG, TEXT("Displaying connection info for 0x%0.8x"), m_MazeClient.GetLocalClientID() );
  370.             ConsolePrintf( LINE_LOG, TEXT("(Key: G=Guaranteed NG=Non-Guaranteed B=Bytes P=Packets)") );
  371.  
  372.             // Display each line seperately
  373.             strStartOfLine = strInfo;
  374.             while( TRUE )
  375.             {
  376.                 strEndOfLine = _tcschr( strStartOfLine, '\n' );
  377.                 if( strEndOfLine == NULL )
  378.                     break;
  379.  
  380.                 *strEndOfLine = 0;
  381.                 ConsolePrintf( LINE_LOG, strStartOfLine );
  382.                 strStartOfLine = strEndOfLine + 1;
  383.             }
  384.  
  385.             fLastLogUpdate = fCurTime;
  386.         }
  387.  
  388.         // If we are testing connect/disconnect, break after so many iterations.
  389.         if( m_Config.bAutoDisconnnect )  
  390.         {
  391.             // Disconnect between 5-25seconds 
  392.             static float fDisconnectCountdown = 10.0f;
  393.  
  394.             fDisconnectCountdown -= fElapsedTime;
  395.             if( fDisconnectCountdown < 0.0f )  
  396.             {
  397.                 fDisconnectCountdown = (float)(rand() % 20000 + 5000 ) / 1000.0f;
  398.                 ConsolePrintf( LINE_LOG, TEXT("Intentional disconnect.  Connecting again for %0.0f seconds..."), fDisconnectCountdown );
  399.                 m_DP8Client.Shutdown();
  400.                 m_MazeClient.Shutdown();
  401.             }
  402.         }
  403.  
  404.         if( m_bDisconnectNow )
  405.         {
  406.             m_bDisconnectNow = FALSE;
  407.             ConsolePrintf( LINE_LOG, TEXT("Intentional disconnect.") );
  408.             m_DP8Client.Shutdown();
  409.             m_MazeClient.Shutdown();
  410.  
  411.             if( m_bAllowLoopback )
  412.                 InitServerForLoopback();
  413.         }
  414.     }
  415.  
  416.     // Update state of client
  417.     m_MazeClient.Update( fElapsedTime );
  418.  
  419.     return S_OK;
  420. }
  421.  
  422.  
  423.  
  424.  
  425. //-----------------------------------------------------------------------------
  426. // Name: 
  427. // Desc: 
  428. //-----------------------------------------------------------------------------
  429. HRESULT CMazeApp::StartSessionEnum()
  430. {
  431.     HRESULT hr;
  432.  
  433.     // If we're not the preview, then enum sessions
  434.     if( m_pMazeGraphics->IsPreview() )
  435.         return S_OK;
  436.  
  437.     // Start enumerating available sessions at specified IP address.
  438.  
  439.     if( m_Config.bConnectToMicrosoftSite )
  440.     {
  441.         ConsolePrintf( LINE_LOG, TEXT("Connecting to DirectPlayMaze.rte.microsoft.com") );
  442.         hr = m_DP8Client.StartSessionEnum( MICROSOFT_SERVER );
  443.     }
  444.     else if( m_Config.bConnectToLocalServer )
  445.     {
  446.         ConsolePrintf( LINE_LOG, TEXT("Connecting to local server (searches the local subnet)") );
  447.         hr = m_DP8Client.StartSessionEnum( TEXT("") );
  448.     }
  449.     else if( m_Config.bConnectToRemoteServer )
  450.     {
  451.         ConsolePrintf( LINE_LOG, TEXT("Connecting to remote server at '%s'"), m_Config.szIPAddress );
  452.         hr = m_DP8Client.StartSessionEnum( m_Config.szIPAddress );
  453.     }
  454.     // If users wants Stress Connect, go through the sequence they have requested.
  455.     else if( m_Config.dwStressConnect )
  456.     {
  457.         if( m_Config.dwStressConnect == 1 )
  458.         {
  459.             // Point to local subnet for next enum.
  460.             m_Config.dwStressConnect = 2;
  461.  
  462.             ConsolePrintf( LINE_LOG, TEXT("Connecting to remote server at '%s'"), m_Config.szIPAddress );
  463.             hr = m_DP8Client.StartSessionEnum( m_Config.szIPAddress );
  464.             
  465.         }
  466.         else
  467.         {
  468.             // Point back at remote server for next enum.
  469.             m_Config.dwStressConnect = 1;
  470.  
  471.             // Must equal 2 or something higher. Try the local subnet.
  472.             ConsolePrintf( LINE_LOG, TEXT("Connecting to local server (searches the local subnet)") );
  473.             hr = m_DP8Client.StartSessionEnum( TEXT("") );
  474.             
  475.         }
  476.     }
  477.  
  478.     return hr;
  479. }
  480.  
  481.  
  482.  
  483.  
  484. //-----------------------------------------------------------------------------
  485. // Name: 
  486. // Desc: 
  487. //-----------------------------------------------------------------------------
  488. BOOL CMazeApp::TryToConnect()
  489. {
  490.     if( m_DP8Client.GetNumSessions() > 0 )
  491.     {
  492.         m_MazeClient.Reset();
  493.         m_MazeClient.SetOutboundClient( m_DP8Client.GetOutboundClient() );
  494.         m_DP8Client.SetClient( &m_MazeClient );
  495.  
  496.         // Loop through the available sessions and attempt to connect
  497.         for( DWORD i = 0; i < m_DP8Client.GetNumSessions(); i++ )
  498.         {
  499.             if( SUCCEEDED(m_DP8Client.JoinSession( i ) ) )
  500.             {
  501.                 return TRUE;
  502.             }
  503.         }
  504.     }
  505.  
  506.     return FALSE;
  507. }
  508.  
  509.  
  510.  
  511.  
  512. //-----------------------------------------------------------------------------
  513. // Name: 
  514. // Desc: 
  515. //-----------------------------------------------------------------------------
  516. HRESULT CMazeApp::InitServerForLoopback()
  517. {
  518.     HRESULT hr;
  519.  
  520.     m_bLocalLoopback = TRUE;
  521.  
  522.     #define LOOPBACK_MAZE_WIDTH  16
  523.     #define LOOPBACK_MAZE_HEIGHT 16
  524.  
  525.     // Initalize maze and server objects for loopback mode
  526.     if( FAILED( hr = m_MazeClient.m_Maze.Init( LOOPBACK_MAZE_WIDTH, 
  527.                                                LOOPBACK_MAZE_HEIGHT, 
  528.                                                DEFAULT_SEED ) ) )
  529.     {
  530.         return DXTRACE_ERR( TEXT("m_Maze.Init"), hr );
  531.     }
  532.  
  533.     // Initialize maze server object - hook up to the maze object in the client
  534.     m_MazeServer.Init( m_bLocalLoopback, &m_MazeClient.m_Maze );
  535.  
  536.     m_DummyClientConnection.SetTarget( &m_MazeServer );
  537.     m_MazeClient.SetOutboundClient( &m_DummyClientConnection );
  538.     m_DummyServerConnection.SetTarget( &m_MazeClient );
  539.     m_MazeServer.SetOutboundServer( &m_DummyServerConnection );
  540.  
  541.     m_DummyClientConnection.Connect( 2 );
  542.     m_MazeClient.EngageAutopilot( TRUE );
  543.  
  544.     return S_OK;
  545. }
  546.  
  547.  
  548.  
  549.  
  550. //-----------------------------------------------------------------------------
  551. // Name: 
  552. // Desc: 
  553. //-----------------------------------------------------------------------------
  554. UINT WINAPI CMazeApp::StaticOutputMsgThread( LPVOID pParam )
  555. {
  556.     return s_pMazeApp->OutputMsgThread( pParam );
  557. }
  558.  
  559.  
  560.  
  561.  
  562. //-----------------------------------------------------------------------------
  563. // Name: 
  564. // Desc: 
  565. //-----------------------------------------------------------------------------
  566. UINT WINAPI CMazeApp::OutputMsgThread( LPVOID pParam )
  567. {
  568. #define MAX_LOG_LINES 100
  569.  
  570.     TCHAR szLogBuffer[MAX_PATH];
  571.     DWORD dwNumProcessed = 0;
  572.  
  573.     while( 1 )
  574.     {
  575.         // Wait for output to be added to the queue or the quit flag to be set
  576.         WaitForSingleObject( m_hOutputMsgEvent, INFINITE );
  577.         if( m_bQuitThread )
  578.             break;
  579.  
  580.         // Update the time stamp
  581.         UpdateTimeStamp();
  582.  
  583.         // Lock output queue
  584.         m_OutputMsgQueueLock.Enter();
  585.  
  586.         dwNumProcessed = 0;
  587.  
  588.         // While we have there are messages to print and we
  589.         // have display'ed less than 5 messages 
  590.         while ( m_lQueueSize > 0 && dwNumProcessed < 5 )
  591.         {
  592.             switch( m_EnumLineType[m_dwNextOutputMsg] )
  593.             {
  594.                 case LINE_LOG:
  595.                 {
  596.                     // Add m_szOutputMsgBuffer[m_dwNextOutputMsg] to szLogBuffer array,
  597.                     // and redisplay the array on the top half of the screen
  598.                     _stprintf( szLogBuffer, TEXT("%s %s"), 
  599.                                m_strTimeStamp, m_szOutputMsgBuffer[m_dwNextOutputMsg] );
  600.  
  601. #ifdef _DEBUG
  602.                     OutputDebugString( szLogBuffer );
  603.                     OutputDebugString( TEXT("\n") );
  604. #endif
  605.                     if( m_hLogFile )
  606.                     {
  607.                         DWORD dwWritten;
  608.                         WriteFile( m_hLogFile, szLogBuffer, 
  609.                                    lstrlen( szLogBuffer ), &dwWritten, NULL );
  610.                         TCHAR strEOL = TEXT('\r');
  611.                         WriteFile( m_hLogFile, &strEOL, 
  612.                                    sizeof(TCHAR), &dwWritten, NULL );
  613.                         strEOL = TEXT('\n');
  614.                         WriteFile( m_hLogFile, &strEOL, 
  615.                                    sizeof(TCHAR), &dwWritten, NULL );
  616.  
  617.                         static float s_fLastFlushTime = DXUtil_Timer( TIMER_GETAPPTIME );
  618.                         float fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
  619.                         if( fCurTime - s_fLastFlushTime > 0.2f )
  620.                         {
  621.                             FlushFileBuffers( m_hLogFile );
  622.                             s_fLastFlushTime = fCurTime;
  623.                         }
  624.                     }
  625.                     break;
  626.                 }
  627.  
  628.                 default:
  629.                     _tcscpy( szLogBuffer, m_szOutputMsgBuffer[m_dwNextOutputMsg] );
  630.                     break;
  631.             }
  632.  
  633.             m_pMazeGraphics->HandleOutputMsg( m_EnumLineType[m_dwNextOutputMsg], szLogBuffer );
  634.  
  635.             m_dwNextOutputMsg++;
  636.             if( m_dwNextOutputMsg == MAX_OUTPUT_QUEUE )
  637.                 m_dwNextOutputMsg = 0;
  638.  
  639.             m_lQueueSize--;
  640.             dwNumProcessed++;
  641.         }
  642.  
  643.         // Unlock output queue
  644.         m_OutputMsgQueueLock.Leave();
  645.  
  646.         if( m_hLogFile )
  647.             FlushFileBuffers( m_hLogFile );
  648.  
  649.         // Yield time to other threads
  650.         Sleep( 10 );
  651.  
  652.         // If there are still messages left, then signal the event
  653.         if( m_lQueueSize > 0 )
  654.             SetEvent( m_hOutputMsgEvent );
  655.     }
  656.  
  657.     if( m_hLogFile )
  658.     {
  659.         CloseHandle( m_hLogFile );
  660.         m_hLogFile = NULL;
  661.     }
  662.  
  663.     return 0;
  664. }
  665.  
  666.  
  667.  
  668.  
  669. //-----------------------------------------------------------------------------
  670. // Name: 
  671. // Desc: 
  672. //-----------------------------------------------------------------------------
  673. void CMazeApp::ConsolePrintf( EnumLineType enumLineType, const TCHAR* fmt, ... )
  674. {
  675.     // Format the message into a buffer
  676.     TCHAR buffer[512];
  677.     _vstprintf( buffer, fmt, (CHAR*) ((&fmt)+1) );
  678.  
  679.     // Lock the output queue
  680.     m_OutputMsgQueueLock.Enter();
  681.  
  682.     // Find free spot
  683.     if( m_lQueueSize != MAX_OUTPUT_QUEUE )
  684.     {
  685.         // Format message into the buffer
  686.         _vstprintf( m_szOutputMsgBuffer[m_dwNextFreeOutputMsg], fmt, (CHAR*)((&fmt)+1) );
  687.         m_EnumLineType[m_dwNextFreeOutputMsg] = enumLineType;
  688.  
  689.         // Increment output pointer and wrap around
  690.         m_dwNextFreeOutputMsg++;
  691.         if( m_dwNextFreeOutputMsg == MAX_OUTPUT_QUEUE )
  692.             m_dwNextFreeOutputMsg = 0;
  693.  
  694.         // Increment message count
  695.         m_lQueueSize++;
  696.     }
  697.  
  698.     // Unlock output queue
  699.     m_OutputMsgQueueLock.Leave();
  700.  
  701.     // Signal event so the output thread empties the queue
  702.     SetEvent( m_hOutputMsgEvent );
  703. }
  704.  
  705.  
  706.  
  707.  
  708. //-----------------------------------------------------------------------------
  709. // Name: 
  710. // Desc: 
  711. //-----------------------------------------------------------------------------
  712. void CMazeApp::UpdateTimeStamp()
  713. {
  714.     static float s_fTimeStampUpdateCountdown = -10.0f;
  715.     float fCurTime = DXUtil_Timer( TIMER_GETAPPTIME );
  716.  
  717.     if( fCurTime - s_fTimeStampUpdateCountdown > 1.0f )
  718.     {
  719.         SYSTEMTIME sysTime;
  720.         GetLocalTime( &sysTime );
  721.         _stprintf( m_strTimeStamp, TEXT("[%02d-%02d-%02d %02d:%02d:%02d]"),
  722.                    sysTime.wMonth, sysTime.wDay, sysTime.wYear % 100, 
  723.                    sysTime.wHour, sysTime.wMinute, sysTime.wSecond );
  724.  
  725.         // Compute how many milliseconds until the next second change
  726.         s_fTimeStampUpdateCountdown = fCurTime;
  727.     }
  728. }
  729.     
  730.  
  731.  
  732.  
  733. //-----------------------------------------------------------------------------
  734. // Name: 
  735. // Desc: 
  736. //-----------------------------------------------------------------------------
  737. void CMazeApp::CreateTempLogFile()
  738. {
  739.     BOOL bSuccess;
  740.     TCHAR strTempFileName[MAX_PATH];
  741.     TCHAR strTime[MAX_PATH];
  742.     DWORD dwCount;
  743.     
  744.     GetTempPath( MAX_PATH, m_strLogDir );
  745.     lstrcat( m_strLogDir, TEXT("DirectPlayMaze\\") );
  746.  
  747.     // Create the directory if it doesn't exist
  748.     if( GetFileAttributes( m_strLogDir ) == -1 )
  749.     {
  750.         bSuccess = CreateDirectory( m_strLogDir, NULL );
  751.         if( !bSuccess )
  752.         {
  753.             ConsolePrintf( LINE_LOG, TEXT("Could not create create temp directory '%s'"), m_strLogDir );
  754.             goto LFail;
  755.         }
  756.     }
  757.  
  758.     ConsolePrintf( LINE_LOG, TEXT("Log Directory: '%s'"), m_strLogDir );
  759.  
  760.     SYSTEMTIME sysTime;
  761.     GetLocalTime( &sysTime );
  762.     _stprintf( strTime, TEXT("client-%04d-%02d-%02d-"),
  763.                sysTime.wYear, sysTime.wMonth, sysTime.wDay );
  764.  
  765.     dwCount = 0;
  766.  
  767.     while(TRUE)
  768.     {
  769.         wsprintf( m_strLogFile, TEXT("%s%05d.log"), strTime, dwCount );
  770.         lstrcpy( strTempFileName, m_strLogDir );
  771.         lstrcat( strTempFileName, m_strLogFile );
  772.         DWORD dwResult = GetFileAttributes( strTempFileName );
  773.         if( dwResult == -1 )
  774.             break;
  775.  
  776.         dwCount++;
  777.     }
  778.  
  779.     if( m_hLogFile )
  780.     {
  781.         CloseHandle( m_hLogFile );
  782.         m_hLogFile = NULL;
  783.     }
  784.  
  785.     m_hLogFile = CreateFile( strTempFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, 
  786.                              CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
  787.     if( m_hLogFile == INVALID_HANDLE_VALUE )
  788.     {
  789.         ConsolePrintf( LINE_LOG, TEXT("Could not create create temp file '%s'"), strTempFileName );
  790.         goto LFail;
  791.     }
  792.  
  793.     ConsolePrintf( LINE_LOG, TEXT("Logging to temp file: '%s'"), m_strLogFile );
  794.     return;
  795.  
  796. LFail:
  797.     ConsolePrintf( LINE_LOG, TEXT("File logging disabled") );
  798.     m_Config.bFileLogging = FALSE;
  799. }
  800.  
  801.  
  802.  
  803.  
  804. //-----------------------------------------------------------------------------
  805. // Name: 
  806. // Desc: 
  807. //-----------------------------------------------------------------------------
  808. void CMazeApp::CloseTempLogFile()
  809. {
  810.     CloseHandle( m_hLogFile );
  811.     m_hLogFile = NULL;
  812. }
  813.  
  814.  
  815.  
  816.  
  817. //-----------------------------------------------------------------------------
  818. // Name: 
  819. // Desc: 
  820. //-----------------------------------------------------------------------------
  821. void CMazeApp::ReadConfig()
  822. {
  823.     HKEY hKey = NULL;
  824.     RegOpenKeyEx( HKEY_CURRENT_USER, MAZE_REGKEYNAME, 0, KEY_READ, &hKey );
  825.  
  826.     DXUtil_ReadBoolRegKey( hKey, TEXT("ConnectToMicrosoftSite"), &m_Config.bConnectToMicrosoftSite, TRUE );
  827.     DXUtil_ReadBoolRegKey( hKey, TEXT("ConnectToLocalServer"), &m_Config.bConnectToLocalServer, FALSE );
  828.     DXUtil_ReadBoolRegKey( hKey, TEXT("ConnectToRemoteServer"), &m_Config.bConnectToRemoteServer, FALSE );
  829.     DXUtil_ReadIntRegKey(  hKey, TEXT("NetworkRetryDelay"), &m_Config.dwNetworkRetryDelay, 30 );
  830.     DXUtil_ReadBoolRegKey( hKey, TEXT("FileLogging"), &m_Config.bFileLogging, TRUE );
  831.     DXUtil_ReadStringRegKey( hKey, TEXT("IPAddress"), m_Config.szIPAddress, sizeof(m_Config.szIPAddress), TEXT("\0") );
  832.  
  833.     DXUtil_ReadBoolRegKey( hKey, TEXT("ShowFramerate"), &m_Config.bShowFramerate, TRUE );
  834.     DXUtil_ReadBoolRegKey( hKey, TEXT("ShowIndicators"), &m_Config.bShowIndicators, TRUE );
  835.     DXUtil_ReadBoolRegKey( hKey, TEXT("DrawMiniMap"), &m_Config.bDrawMiniMap, TRUE );
  836.     DXUtil_ReadBoolRegKey( hKey, TEXT("FullScreen"), &m_Config.bFullScreen, TRUE );
  837.     DXUtil_ReadBoolRegKey( hKey, TEXT("Reflections"), &m_Config.bReflections, FALSE );
  838.  
  839.     DXUtil_ReadBoolRegKey( hKey, TEXT("AutoDisconnnect"), &m_Config.bAutoDisconnnect, FALSE );
  840.     DXUtil_ReadBoolRegKey( hKey, TEXT("AutoConnnect"), &m_Config.bAutoConnnect, FALSE );
  841.     DXUtil_ReadIntRegKey( hKey, TEXT("LogLevel"), &m_Config.dwLogLevel, 2 );
  842.     DXUtil_ReadIntRegKey( hKey, TEXT("AutoPrintStats"), &m_Config.dwAutoPrintStats, 10 );
  843.  
  844.     RegCloseKey( hKey );
  845. }
  846.  
  847.  
  848.  
  849.  
  850. //-----------------------------------------------------------------------------
  851. // Name: 
  852. // Desc: 
  853. //-----------------------------------------------------------------------------
  854. void CMazeApp::WriteConfig()
  855. {
  856.     HKEY    hKey;
  857.     DWORD   dwDisposition;
  858.     
  859.     if( !m_bSaveSettings )
  860.         return;
  861.  
  862.     RegCreateKeyEx( HKEY_CURRENT_USER, MAZE_REGKEYNAME, 0, NULL, 
  863.                     REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, 
  864.                     &hKey, &dwDisposition );
  865.  
  866.     DXUtil_WriteBoolRegKey( hKey, TEXT("ConnectToMicrosoftSite"), m_Config.bConnectToMicrosoftSite );
  867.     DXUtil_WriteBoolRegKey( hKey, TEXT("ConnectToLocalServer"), m_Config.bConnectToLocalServer );
  868.     DXUtil_WriteBoolRegKey( hKey, TEXT("ConnectToRemoteServer"), m_Config.bConnectToRemoteServer );
  869.     DXUtil_WriteIntRegKey(  hKey, TEXT("NetworkRetryDelay"), m_Config.dwNetworkRetryDelay );
  870.     DXUtil_WriteBoolRegKey( hKey, TEXT("FileLogging"), m_Config.bFileLogging );
  871.     DXUtil_WriteStringRegKey( hKey, TEXT("IPAddress"), m_Config.szIPAddress );
  872.  
  873.     DXUtil_WriteBoolRegKey( hKey, TEXT("ShowFramerate"), m_Config.bShowFramerate );
  874.     DXUtil_WriteBoolRegKey( hKey, TEXT("ShowIndicators"), m_Config.bShowIndicators );
  875.     DXUtil_WriteBoolRegKey( hKey, TEXT("DrawMiniMap"), m_Config.bDrawMiniMap );
  876.     DXUtil_WriteBoolRegKey( hKey, TEXT("FullScreen"), m_Config.bFullScreen );
  877.     DXUtil_WriteBoolRegKey( hKey, TEXT("Reflections"), m_Config.bReflections );
  878.  
  879.     DXUtil_WriteBoolRegKey( hKey, TEXT("AutoDisconnnect"), m_Config.bAutoDisconnnect );
  880.     DXUtil_WriteBoolRegKey( hKey, TEXT("AutoConnnect"), m_Config.bAutoConnnect );
  881.     DXUtil_WriteIntRegKey( hKey, TEXT("LogLevel"), m_Config.dwLogLevel );
  882.     DXUtil_WriteIntRegKey( hKey, TEXT("AutoPrintStats"), m_Config.dwAutoPrintStats );
  883.  
  884.     RegCloseKey( hKey );
  885. }
  886.  
  887.  
  888.  
  889.  
  890. //-----------------------------------------------------------------------------
  891. // Name: 
  892. // Desc: 
  893. //-----------------------------------------------------------------------------
  894. void ConsolePrintf( ServerBufferType enumLineType, const TCHAR* fmt , ... )
  895. {
  896.     // Format the message into a buffer
  897.     TCHAR buffer[512];
  898.     _vstprintf( buffer, fmt, (CHAR*) ((&fmt)+1) );
  899.  
  900.     s_pMazeApp->ConsolePrintf( LINE_LOG, buffer );
  901. }
  902.