// HTNetServer.cpp: implementation of the HTNetServer class.
//
//////////////////////////////////////////////////////////////////////

#define STRICT
#include "global.h"
#include <dplay8.h>
#include <dpaddr.h>
#include <dplobby8.h>
#include <dxerr8.h>
#include "dxutil.h"
#include "simpleclientserver.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

#define PLAYER_LOCK()				EnterCriticalSection( &m_csPlayerContext );
#define PLAYER_UNLOCK()				LeaveCriticalSection( &m_csPlayerContext );
#define PLAYER_ADDREF( pPlayerInfo ) { if (pPlayerInfo) pPlayerInfo->lRefCount++; }
#define PLAYER_RELEASE( pPlayerInfo ) { if (pPlayerInfo) { pPlayerInfo->lRefCount--; if (pPlayerInfo->lRefCount <= 0) SAFE_DELETE( pPlayerInfo ); } pPlayerInfo = NULL; }

#define HTNETSERVER_DEFAULT_PORT 0x6328
#define HTNETSERVER_DEFAULT_NAME TEXT("HoverTank")

class HTNetServer* HTNetServer::s_pHTNetServer = NULL;

DWORD WINAPI ServerThreadFunc( LPVOID lpParam );

HTNetServer::HTNetServer()
{
	m_pDPServer			= NULL;
	m_bInitialized		= false;
	m_bServerRunning	= false;
	m_nActivePlayers	= 0;

	// Set defaults
	m_dwPort				= HTNETSERVER_DEFAULT_PORT;
	strcpy(m_sServerName,HTNETSERVER_DEFAULT_NAME);

	// For message forwarding
	s_pHTNetServer = this;

	InitializeCriticalSection( &m_csPlayerContext );

	m_ServerThread = NULL;
}

HTNetServer::~HTNetServer()
{
	if (m_bServerRunning)
		StopServer();
}

HRESULT WINAPI HTNetServer::DirectPlayerMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer )
{
    // Try not to stay in this message handler for too long, otherwise
    // there will be a backlog of data.  The best solution is to 
    // queue data as it comes in, and then handle it on other threads.
    
    // This function is called by the DirectPlay message handler pool of 
    // threads, so be careful of thread synchronization problems with shared memory

    switch( dwMessageId )
    {
        case DPN_MSGID_CREATE_PLAYER:
        {
			   HRESULT hr;
            PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
            pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER)pMsgBuffer;

				hr = AddPlayer( pCreatePlayerMsg );
				if (hr != S_OK)	return hr;

            break;
        }

        case DPN_MSGID_DESTROY_PLAYER:
        {
            PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
            pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;

				RemovePlayer(pDestroyPlayerMsg);

            break;
        }

        case DPN_MSGID_TERMINATE_SESSION:
        {
            PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
            pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
            break;
        }

        case DPN_MSGID_RECEIVE:
        {
            PDPNMSG_RECEIVE pReceiveMsg;
            pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
            HTN_PLAYER_INFO* pPlayerInfo = (HTN_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;

            HTMSG_GENERIC* pMsg = (HTMSG_GENERIC*) pReceiveMsg->pReceiveData;

				switch (pMsg->dwType)
				{
					case HT_MSGID_UPDATE_INPUT:
						RetrieveNetworkInput( (HTMSG_UPDATE_INPUT*)pMsg );
 						break;
					case HT_MSGID_COMMAND_TO_SERVER:
						HandleClientCommand( (HTMSG_COMMAND_TO_SERVER*)pMsg );
						break;
					case HT_MSGID_CHAT:
						// Simply relay the message to everyone
						RelayChatMessage( (HTMSG_CHAT_FULL*)pMsg );
						break;
					default:
						printf("Unexpected packet id from client: %ld\n",(long)(pMsg->dwType));
						break;
				}

            break;
        }
    }

    return S_OK;
}

bool HTNetServer::Initialize()
{
	m_bInitialized = true;
	return m_bInitialized;
}

bool HTNetServer::StartServer()
{
	// Make sure it's not already running
	if (m_bServerRunning)	return m_bServerRunning;

	HRESULT hr;
	PDIRECTPLAY8ADDRESS pDP8AddrLocal = NULL;

	// Create the IDirectPlay8Server object (COM)
	if ( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Server, NULL,
													CLSCTX_INPROC_SERVER,
													IID_IDirectPlay8Server,
													(LPVOID*) &m_pDPServer ) ) )
	{
		DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
		return m_bServerRunning;
	}

	// Intialize the IDP8Server object
	if ( FAILED( hr = m_pDPServer->Initialize(NULL, StaticDirectPlayerMessageForwarder, 0 ) ) )
	{
		DXTRACE_ERR( TEXT("Initialize"), hr );
		return m_bServerRunning;
	}

	hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL, CLSCTX_ALL, IID_IDirectPlay8Address, (LPVOID*) & pDP8AddrLocal );
	if ( FAILED( hr ))
	{
		DXTRACE_ERR( TEXT("CoCreateInstance 2"), hr );
		goto LCleanup;
	}

	hr = pDP8AddrLocal->SetSP( &CLSID_DP8SP_TCPIP );
	if ( FAILED( hr ))
	{
		DXTRACE_ERR( TEXT("SetSP"), hr );
		goto LCleanup;
	}

	// Add the port to pDP8AddrLocal if it's non-zero.
	// If it is zero, then DP will pick a port automatically.
	if ( m_dwPort != 0 )
	{
		if (FAILED( hr = pDP8AddrLocal->AddComponent( DPNA_KEY_PORT, &m_dwPort, sizeof(m_dwPort), DPNA_DATATYPE_DWORD )))
		{
			DXTRACE_ERR( TEXT("AddComponent"), hr );
			goto LCleanup;
		}
	}

	DPN_APPLICATION_DESC dpnAppDesc;
	ZeroMemory( &dpnAppDesc, sizeof( DPN_APPLICATION_DESC ) );
	dpnAppDesc.dwSize = sizeof( DPN_APPLICATION_DESC );
	dpnAppDesc.dwFlags = DPNSESSION_CLIENT_SERVER;
	dpnAppDesc.guidApplication = g_guidApp;
	dpnAppDesc.dwMaxPlayers = MAX_PLAYERS+1;

	WCHAR wstrSessionName[MAX_PATH];
	DXUtil_ConvertGenericStringToWide( wstrSessionName, m_sServerName, MAX_PATH	);
	dpnAppDesc.pwszSessionName = wstrSessionName;

	hr = m_pDPServer->Host( &dpnAppDesc, &pDP8AddrLocal, 1, NULL, NULL, NULL, 0 );
	if (FAILED(hr))
	{
		DXTRACE_ERR( TEXT("Host"), hr );
		goto LCleanup;
	}

	DWORD threadid;
	// Create a thread that starts handling the directplay messages
	m_ServerThread = CreateThread(
						NULL,			// No inheriting this handle - default security
						0,				// Default stack size
						ServerThreadFunc,	// Thread function
						this,			// Argument for function
						0,				// Run immediately
						&threadid);
	if (m_ServerThread)
	{
		//SetThreadPriority(m_ServerThread,THREAD_PRIORITY_BELOW_NORMAL);
		// Wait for the thread to start running --
		// if it worked at all, then setting m_ThreadRunning should be
		// the very first thing it does
		while (!m_ThreadRunning)
		{}
		printf("Thread started!\n");
	}

	m_bServerRunning = true;

LCleanup:
	SAFE_RELEASE( pDP8AddrLocal );

	return m_bServerRunning;

}

void HTNetServer::StopServer()
{
	if ( m_pDPServer )
	{
		if (m_ThreadRunning)
		{
			DWORD waitret;
			m_QuitThread = true;
			waitret = WaitForSingleObject(m_ServerThread, 1000);	// Give it 1000 ms to quit
			if (m_ThreadRunning || waitret == WAIT_TIMEOUT)
				printf("Server object error: Could not get thread to close.  How sad.\n");
		}
		if (m_ServerThread)
		{
			CloseHandle( m_ServerThread );
			m_ServerThread = NULL;
		}

		m_pDPServer->Close(0);
		SAFE_RELEASE( m_pDPServer );
	}
	m_bServerRunning = false;
}

void HTNetServer::update(float dtime)
{
	int i;
	//for (i=0; i < m_world.nActivePlayers; i++)
	for (i=0; i < MAX_PLAYERS; i++)
		if (m_ValidPlayerIdx[i])
			m_world.tank[i].HandleInput(dtime,m_ConvertedInput[i]);
	m_world.update(dtime);
}


DWORD sysTimeNow_SERVER;
DWORD sysTimeOld_SERVER;
LARGE_INTEGER lasttime_server;
LARGE_INTEGER timenow_server;
LARGE_INTEGER timerfreq_server;

#ifdef HT_SERVER
GameInformation game;
#endif

#include <conio.h>

// Update the system every X ms
#define TIME_STEP_UPDATE 50

DWORD HTNetServer::ServerThread()
{
	float dtime;
	long sleeptime;
	int i;

	m_QuitThread = false;
	m_ThreadRunning = true;

	// Initialize the high-speed system timer
	QueryPerformanceFrequency(&timerfreq_server);
	QueryPerformanceCounter(&lasttime_server);
	//sysTimeOld = timeGetTime();

	m_currentTime = timenow_server.QuadPart / (double)timerfreq_server.QuadPart;
	m_world.currentTime = m_currentTime;

	dtime = 0;

	for (i=0; i < MAX_PLAYERS; i++)
	{
		m_NextStatusUpdateTime[i] = 0;
		m_StatusUpdateDelay[i] = DefaultStatusUpdateDelay;
		m_ValidPlayerIdx[i] = false;
	}

	printf("SERVER> Loading world data...\n");
	m_world.LoadData();
	m_world.map.GenTrees(timeGetTime(),200);
	m_world.Reset();
	m_world.settings.sound = false;
	m_world.ThisWorldIsTheServer = true;
	printf("SERVER> World data loaded.\n");

	while (!m_QuitThread)
	{
		do {
			//sysTimeNow = timeGetTime();
			QueryPerformanceCounter(&timenow_server);
			dtime = (timenow_server.QuadPart - lasttime_server.QuadPart) / (float)timerfreq_server.QuadPart;
			//dtime = (sysTimeNow-sysTimeOld)/1000.0;
			sleeptime = TIME_STEP_UPDATE * 0.9 - dtime*1000.0;
			if (sleeptime > 0) Sleep( sleeptime );
		} while (dtime*1000.0 < TIME_STEP_UPDATE);
		lasttime_server = timenow_server;
		//sysTimeOld = sysTimeNow;
		m_currentTime = timenow_server.QuadPart / (double)timerfreq_server.QuadPart;
		m_world.currentTime = m_currentTime;

		UpdateInput();

		update(dtime);

		for (i=0; i < MAX_PLAYERS; i++)
			if (m_ValidPlayerIdx[i])
				if (m_currentTime >= m_NextStatusUpdateTime[i])
					SendMovementInformationOfAll(i);

		if (_kbhit())	m_QuitThread |= (getch()=='q');
	}

	m_ThreadRunning = false;
	return 0;
}

HRESULT HTNetServer::AddPlayer( PDPNMSG_CREATE_PLAYER pCreatePlayerMsg )
{
	HRESULT hr;

   // Get the peer info and extract its name
   DWORD dwSize = 0;
   DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
   hr = m_pDPServer->GetClientInfo( pCreatePlayerMsg->dpnidPlayer,
                                    pdpPlayerInfo, &dwSize, 0 );

   if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL )
   {
       if( hr == DPNERR_INVALIDPLAYER )
       {
           // Ignore this message if this is for the host
           return S_OK;
       }

       return DXTRACE_ERR( TEXT("GetClientInfo"), hr );
   }

	pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
   ZeroMemory( pdpPlayerInfo, dwSize );
   pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
   hr = m_pDPServer->GetClientInfo( pCreatePlayerMsg->dpnidPlayer, 
                              pdpPlayerInfo, &dwSize, 0 );
   if( FAILED(hr) )
       return DXTRACE_ERR( TEXT("GetClientInfo"), hr );

	TCHAR newPlayerName[MAX_PLAYER_NAME+1];
	DXUtil_ConvertWideStringToGeneric( newPlayerName, pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );

	if (pdpPlayerInfo->dwDataSize != sizeof(HTMSG_PLAYER_CONFIG))
		printf("Error: data size = %d, expected %d\n",pdpPlayerInfo->dwDataSize,sizeof(HTMSG_PLAYER_CONFIG));

	HTMSG_PLAYER_CONFIG* playerConfig = (HTMSG_PLAYER_CONFIG*)pdpPlayerInfo->pvData;

	printf("Adding player [%s] (model %d) from %d ",newPlayerName,playerConfig->nextRespawnModelNum,m_nActivePlayers);
   

   // Create a new and fill in a APP_PLAYER_INFO
   HTN_PLAYER_INFO* pPlayerInfo = new HTN_PLAYER_INFO;
   ZeroMemory( pPlayerInfo, sizeof(HTN_PLAYER_INFO) );
   pPlayerInfo->lRefCount   = 1;
   pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
	pPlayerInfo->nextModelNum = playerConfig->nextRespawnModelNum;

   // This stores a extra TCHAR copy of the player name for 
   // easier access.  This will be redundent copy since DPlay 
   // also keeps a copy of the player name in GetClientInfo()
   DXUtil_ConvertWideStringToGeneric( pPlayerInfo->playerName, 
                                      pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );

   SAFE_DELETE_ARRAY( pdpPlayerInfo );

   // Tell DirectPlay to store this pPlayerInfo 
   // pointer in the pvPlayerContext.
   pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;

   // Tell this new player about the world state
   SendWorldStateToNewPlayer( pCreatePlayerMsg->dpnidPlayer );
   // Send all connected players a message telling about this new player
   SendCreatePlayerMsg( pPlayerInfo, DPNID_ALL_PLAYERS_GROUP );

	//-------------------------------------------
	//EnterCriticalSection( &m_csPlayerData );

	// Update the input info and the world info:
	int newPlayerIndexNumber = -1;
	for (int i=0; i < MAX_PLAYERS; i++)
	{
		if (!m_ValidPlayerIdx[i])
		{
			newPlayerIndexNumber = i;
			break;
		}
	}
	if (newPlayerIndexNumber == -1)
	{
		printf("Critical errror:  Could not find free player slot when adding player\n");
		return -1;
	}
	m_pids[newPlayerIndexNumber] = pPlayerInfo->dpnidPlayer;
	pPlayerInfo->playerIdx = newPlayerIndexNumber;
	ZeroMemory( &m_NetInput[newPlayerIndexNumber] , sizeof(CompactInputStatus_type) );
	ZeroMemory( &m_ConvertedInput[newPlayerIndexNumber] , sizeof(InputStatus_type) );
	m_ValidPlayerIdx[newPlayerIndexNumber] = true;
	m_world.tank[newPlayerIndexNumber].Reset(&m_world);
	m_world.tank[newPlayerIndexNumber].Spawn();
	m_world.tank[newPlayerIndexNumber].active = true;

   // Update the number of active players, and 
   // post a message to the dialog thread to update the 
   // UI.  This keeps the DirectPlay message handler 
   // from blocking
   InterlockedIncrement( &m_nActivePlayers );

	//LeaveCriticalSection( &m_csPlayerData );
	//-------------------------------------------

	printf("to %d players\n",m_nActivePlayers);
	
	return S_OK;
}

void HTNetServer::RemovePlayer( PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg )
{
	HTN_PLAYER_INFO* pPlayerInfo = (HTN_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;

	// Ignore this message if this is the host player
	if( pPlayerInfo == NULL )
		return;

	printf("Removing player:  Had %d, ",m_nActivePlayers);

	// Send all connected players a message telling about this destroyed player
	SendDestroyPlayerMsgToAll( pPlayerInfo );

	PLAYER_LOCK();                  // enter player context CS

	int oldPlayerIndexNumber = pPlayerInfo->playerIdx;
	m_pids[oldPlayerIndexNumber] = 0;
	m_world.tank[oldPlayerIndexNumber].active = false;
	m_ValidPlayerIdx[oldPlayerIndexNumber] = false;

	PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed

	PLAYER_UNLOCK();                // leave player context CS

	// Update the number of active players, and 
	// post a message to the dialog thread to update the 
	// UI.  This keeps the DirectPlay message handler 
	// from blocking
	InterlockedDecrement( &m_nActivePlayers );

	printf("now %d players left\n",m_nActivePlayers);
}

void HTNetServer::UpdateInput()
{
	int i;

	for (i=0; i < m_nActivePlayers; i++)
	{
		m_ConvertedInput[i].brake = m_NetInput[i].brake==1;
		m_ConvertedInput[i].left = m_NetInput[i].left==1;
		m_ConvertedInput[i].right = m_NetInput[i].right==1;
		m_ConvertedInput[i].thrust = m_NetInput[i].thrust==1;
		m_ConvertedInput[i].shield = m_NetInput[i].shield==1;
		m_ConvertedInput[i].fire = m_NetInput[i].fire==1;

		m_ConvertedInput[i].turnAmount = 0;

		m_world.tank[i].model.dir = m_NetInput[i].dir / 10.0;

		if (m_NetInput[i].engine)
		{
			if (m_world.tank[i].hoverStatus == HOVER_OFF)
				m_world.tank[i].StartHover();
		}
		else
		{
			if (m_world.tank[i].hoverStatus == HOVER_GOING)
				m_world.tank[i].StopHover();
		}
	}
}

void HTNetServer::SendCreatePlayerMsg( HTN_PLAYER_INFO* pPlayerAbout, DPNID dpnidTarget )
{
    HTMSG_CREATE_PLAYER msgCreatePlayer;
    msgCreatePlayer.dwType = HT_MSGID_CREATE_PLAYER;
    msgCreatePlayer.pid    = pPlayerAbout->dpnidPlayer;
	 msgCreatePlayer.modelnum = pPlayerAbout->nextModelNum;
    strcpy( msgCreatePlayer.playerName, pPlayerAbout->playerName );

    DPN_BUFFER_DESC bufferDesc;
    bufferDesc.dwBufferSize = sizeof(HTMSG_CREATE_PLAYER);
    bufferDesc.pBufferData  = (BYTE*) &msgCreatePlayer;

    // DirectPlay will tell via the message handler 
    // if there are any severe errors, so ignore any errors 
    DPNHANDLE hAsync;
    m_pDPServer->SendTo( dpnidTarget, &bufferDesc, 1,
                         0, NULL, &hAsync, DPNSEND_NOLOOPBACK );

}
void HTNetServer::SendWorldStateToNewPlayer( DPNID dpnidPlayer )
{
    HRESULT hr;
    DWORD dwNumPlayers = 0;
    DPNID* aPlayers = NULL;

	 // For sending messages:
    DPN_BUFFER_DESC bufferDesc;
    DPNHANDLE hAsync;

	 //----------------------------------------------------------
    // Tell this player the dpnid of itself
    HTMSG_SET_ID msgSetID;
    msgSetID.dwType		= HT_MSGID_SET_ID;
    msgSetID.pid			= dpnidPlayer;

    bufferDesc.dwBufferSize = sizeof(HTMSG_SET_ID);
    bufferDesc.pBufferData  = (BYTE*) &msgSetID;

    // DirectPlay will tell via the message handler 
    // if there are any severe errors, so ignore any errors 
    m_pDPServer->SendTo( dpnidPlayer, &bufferDesc, 1,
                         0, NULL, &hAsync, DPNSEND_NOLOOPBACK );


	 //----------------------------------------------------------
    // Tell this player about the game world
    HTMSG_SETUP_GAME msgSetupGame;
	 msgSetupGame.dwType		= HT_MSGID_SETUP_GAME;
	 msgSetupGame.mapID		= m_world.map.GetMapID();
	 msgSetupGame.numTrees  = m_world.map.GetNumTrees();
	 msgSetupGame.treeSeed  = m_world.map.GetTreeSeed();

	printf("\nServer> map data: ID%d, seed:%ud, ntrees:%d\n",msgSetupGame.mapID,msgSetupGame.treeSeed,msgSetupGame.numTrees);

    bufferDesc.dwBufferSize = sizeof(HTMSG_SETUP_GAME);
    bufferDesc.pBufferData  = (BYTE*) &msgSetupGame;

    // DirectPlay will tell via the message handler 
    // if there are any severe errors, so ignore any errors 
    m_pDPServer->SendTo( dpnidPlayer, &bufferDesc, 1,
                         0, NULL, &hAsync, DPNSEND_NOLOOPBACK );
	 
    // Enumerate all the connected players
    while( TRUE )
    {
        hr = m_pDPServer->EnumPlayersAndGroups( aPlayers, &dwNumPlayers, DPNENUM_PLAYERS );
        if( SUCCEEDED(hr) )
            break;

        if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL )
		  {
            DXTRACE_ERR( TEXT("EnumPlayersAndGroups"), hr );
				return;
		  }

        SAFE_DELETE_ARRAY( aPlayers );
        aPlayers = new DPNID[ dwNumPlayers ];
    }

    // For each player, send a "create player" message to the new player
    for( DWORD i = 0; i<dwNumPlayers; i++ )
    {
        HTN_PLAYER_INFO* pPlayerInfo = NULL;

        // Don't send a create msg to the new player about itself.  This will 
        // be already done when we sent one to DPNID_ALL_PLAYERS_GROUP
        if( aPlayers[i] == dpnidPlayer )
            continue;  

        hr = m_pDPServer->GetPlayerContext( aPlayers[i], (LPVOID*) &pPlayerInfo, 0 );

        // Ignore this player if we can't get the context
        if( pPlayerInfo == NULL || FAILED(hr) )
            continue; 

        SendCreatePlayerMsg( pPlayerInfo, dpnidPlayer );
    }

    SAFE_DELETE_ARRAY( aPlayers );

    return;
}

void HTNetServer::SendDestroyPlayerMsgToAll( HTN_PLAYER_INFO* pPlayerInfo )
{
    HTMSG_DESTROY_PLAYER msgDestroyPlayer;
    msgDestroyPlayer.dwType	= HT_MSGID_DESTROY_PLAYER;
    msgDestroyPlayer.pid		= pPlayerInfo->dpnidPlayer;

    DPN_BUFFER_DESC bufferDesc;
    bufferDesc.dwBufferSize = sizeof(HTMSG_CREATE_PLAYER);
    bufferDesc.pBufferData  = (BYTE*) &msgDestroyPlayer;

    // DirectPlay will tell via the message handler 
    // if there are any severe errors, so ignore any errors 
    DPNHANDLE hAsync;
    m_pDPServer->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
                         0, NULL, &hAsync, DPNSEND_NOLOOPBACK );

	return;
}

void HTNetServer::SendMovementInformationOfAll( int targetPlayerIdx )
{
	int i;
	for (i=0; i < MAX_PLAYERS; i++)
	{
		if (m_ValidPlayerIdx[i])
			SendPlayerStatusUpdateMsg(targetPlayerIdx,i);
	}
}

void HTNetServer::SendPlayerStatusUpdateMsg(int playerTargetIdx, int playerInfoIdx)
{
	//m_NetInput[playerInfoIdx].engine = m_world.tank[playerInfoIdx].hoverStatus != HOVER_OFF;

	HTMSG_UPDATE_PLAYER msgUpdatePlayer;
	msgUpdatePlayer.dwType	= HT_MSGID_UPDATE_PLAYER;
	msgUpdatePlayer.pid		= m_pids[playerInfoIdx];
	msgUpdatePlayer.input	= m_NetInput[playerInfoIdx];
	msgUpdatePlayer.pos		= m_world.tank[playerInfoIdx].model.pos;
	msgUpdatePlayer.vel		= m_world.tank[playerInfoIdx].model.vel;
	msgUpdatePlayer.armor	= m_world.tank[playerInfoIdx].armor;
	msgUpdatePlayer.state	= m_world.tank[playerInfoIdx].state;

	msgUpdatePlayer.kills   = m_world.tank[playerInfoIdx].kills;
	msgUpdatePlayer.deaths  = m_world.tank[playerInfoIdx].deaths;

	msgUpdatePlayer.input.engine = m_world.tank[playerInfoIdx].hoverStatus != HOVER_OFF;

	DPN_BUFFER_DESC bufferDesc;
	bufferDesc.dwBufferSize	= sizeof( HTMSG_UPDATE_PLAYER );
	bufferDesc.pBufferData	= (BYTE*) &msgUpdatePlayer;

	DPNHANDLE hAsync;
	m_pDPServer->SendTo( m_pids[playerTargetIdx], &bufferDesc, 1,
								DefaultStatusUpdateDelay/2.0,
								NULL, &hAsync, DPNSEND_NOLOOPBACK );

	m_NextStatusUpdateTime[playerTargetIdx] = m_currentTime + m_StatusUpdateDelay[playerTargetIdx];
}

void HTNetServer::RetrieveNetworkInput( HTMSG_UPDATE_INPUT* pMsgUpdateInput )
{
	int i;
	for (i=0; i < MAX_PLAYERS; i++)
	{
		if (m_pids[i] == pMsgUpdateInput->pid)
		{
			if (!m_ValidPlayerIdx[i])
			{
				printf("Server error: Received InputUpdate packet from invalid player index!!\n");
				return;
			}
			m_NetInput[i] = pMsgUpdateInput->input;
			return;
		}
	}
	printf("Server error: Received InputUpdate packet from unknown dpnid!\n");
}

DWORD WINAPI ServerThreadFunc( LPVOID lpParam )
{
	HTNetServer* theServer = (HTNetServer*)lpParam;
	return theServer->ServerThread();
}

GUID HTNetServer::GetInstanceGuid()
{
	HRESULT hr;
	DPN_APPLICATION_DESC appDesc;
	DPN_APPLICATION_DESC* pDesc;
	BYTE buffer[1000];
	ZeroMemory( &appDesc, sizeof( DPN_APPLICATION_DESC ) );
	ZeroMemory( buffer, 1000 );
	DWORD bufferSize = sizeof(DPN_APPLICATION_DESC);
	GUID zero = { 0,0,0,0 };
	appDesc.guidInstance = zero;
	if (m_pDPServer)
	{
		static bool z=true;

		hr = m_pDPServer->GetApplicationDesc( NULL, &bufferSize, 0 );
		if (FAILED(hr) && z)
		{
			printf("\r(1) Failed: error code is: ");
			switch (hr)
			{
				case DPNERR_BUFFERTOOSMALL: printf("DPNERR_BUFFERTOOSMALL\n"); break;
				case DPNERR_INVALIDFLAGS: printf("DPNERR_INVALIDFLAGS\n"); break;
				case DPNERR_INVALIDPARAM: printf("DPNERR_INVALIDPARAM\n"); break;
				case DPNERR_NOCONNECTION: printf("DPNERR_NOCONNECTION\n"); break;
			}
		}
		if (z)	printf("\r(1) Got necessary buffer size: %d\n",bufferSize);

		appDesc.dwSize = bufferSize;
		hr = m_pDPServer->GetApplicationDesc( &appDesc, &bufferSize, 0 );
		
		if (FAILED(hr) && z)
		{
			printf("\r(2) Failed: error code is: ");
			switch (hr)
			{
				case DPNERR_BUFFERTOOSMALL: printf("DPNERR_BUFFERTOOSMALL\n"); break;
				case DPNERR_INVALIDFLAGS: printf("DPNERR_INVALIDFLAGS\n"); break;
				case DPNERR_INVALIDPARAM: printf("DPNERR_INVALIDPARAM\n"); break;
				case DPNERR_NOCONNECTION: printf("DPNERR_NOCONNECTION\n"); break;
			}
		}
		if (z)	printf("\r(2) Got necessary buffer size: %d\n",bufferSize);

		//appDesc.dwSize = bufferSize;
		pDesc = (DPN_APPLICATION_DESC*)buffer;
		pDesc->dwSize = sizeof(appDesc);
		hr = m_pDPServer->GetApplicationDesc( pDesc, &bufferSize, 0 );
		
		if (FAILED(hr) && z)
		{
			printf("\r(3) Failed: error code is: ");
			switch (hr)
			{
				case DPNERR_BUFFERTOOSMALL: printf("DPNERR_BUFFERTOOSMALL\n"); break;
				case DPNERR_INVALIDFLAGS: printf("DPNERR_INVALIDFLAGS\n"); break;
				case DPNERR_INVALIDPARAM: printf("DPNERR_INVALIDPARAM\n"); break;
				case DPNERR_NOCONNECTION: printf("DPNERR_NOCONNECTION\n"); break;
			}
		}
		if (z)	printf("\r(3) Got necessary buffer size: %d\n",bufferSize);

		if (z)
			printf("\r SERVER: (%d) %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X *\n",
					(int)bufferSize,
					(int)appDesc.guidInstance.Data1,
					(int)appDesc.guidInstance.Data2,
					(int)appDesc.guidInstance.Data3,
					(int)appDesc.guidInstance.Data4[0],
					(int)appDesc.guidInstance.Data4[1],
					(int)appDesc.guidInstance.Data4[2],
					(int)appDesc.guidInstance.Data4[3],
					(int)appDesc.guidInstance.Data4[4],
					(int)appDesc.guidInstance.Data4[5],
					(int)appDesc.guidInstance.Data4[6],
					(int)appDesc.guidInstance.Data4[7]  );
		z=false;
	}
	return appDesc.guidInstance;
}

void HTNetServer::HandleClientCommand(HTMSG_COMMAND_TO_SERVER* cmd)
{
	switch (cmd->commandID)
	{
		case HT_CMDID_SUICIDE:
			{
				for (int i=0; i < MAX_PLAYERS; i++)
					if (m_ValidPlayerIdx[i] && m_pids[i] == cmd->pid)
					{
						m_world.tank[i].SetState(TS_EXPLODING);

						HTMSG_NOTIFY_KILL msgKill;
						msgKill.dwType = HT_MSGID_NOTIFY_SUICIDE;
						msgKill.pid1   = cmd->pid;

						DPN_BUFFER_DESC bufferDesc;
						bufferDesc.dwBufferSize	= sizeof( HTMSG_NOTIFY_KILL );
						bufferDesc.pBufferData	= (BYTE*) &msgKill;

						DPNHANDLE hAsync;
						m_pDPServer->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
													0,  // Insure message gets through
													NULL, &hAsync, DPNSEND_NOLOOPBACK );
						printf("sent suicide message to all");
					}
				break;
			}
		default:
			printf("SERVER: Unknown client command received: %d\n",cmd->commandID);
	}
}

void HTNetServer::RelayChatMessage(HTMSG_CHAT_FULL *pMsg)
{
	DPN_BUFFER_DESC bufferDesc;
	bufferDesc.dwBufferSize	= sizeof( HTMSG_CHAT_HEADER ) + pMsg->messageLength + 1;
	bufferDesc.pBufferData	= (BYTE*) pMsg;

	DPNHANDLE hAsync;
	m_pDPServer->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
								0,  // Insure message gets through
								NULL, &hAsync, DPNSEND_NOLOOPBACK );
}

void HTNetServer::NotifyOfKill(TankObject *killer, TankObject *killee)
{
	int killerIdx=-1, killeeIdx=-1;
	int i;

	for (i=0; i < MAX_PLAYERS; i++)
	{
		if (&m_world.tank[i] == killer)
			killerIdx = i;
		if (&m_world.tank[i] == killee)
			killeeIdx = i;
	}
	// Couldn't find players, ignore message
	if (killerIdx < 0 || killeeIdx < 0)
		return;

	HTMSG_NOTIFY_KILL msgKill;
	msgKill.dwType = HT_MSGID_NOTIFY_KILL;
	msgKill.pid1 = m_pids[killerIdx];
	msgKill.pid2 = m_pids[killeeIdx];
	
	DPN_BUFFER_DESC bufferDesc;
	bufferDesc.dwBufferSize	= sizeof( HTMSG_NOTIFY_KILL );
	bufferDesc.pBufferData	= (BYTE*) &msgKill;

	DPNHANDLE hAsync;
	m_pDPServer->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
								0,  // Insure message gets through
								NULL, &hAsync, DPNSEND_NOLOOPBACK );
}
