#include "pch.hpp"
//
//============================================================
//
// Contents:  Network Threads
//
// Owner   :  AndyG
// Created :  Oct 9 1999
//
// All Rights reserved worldwide (c) Microsoft 1999
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               
//============================================================
//
#include "Net_Header.hpp"
#include "Time.hpp"





//
//
// Enumeration - this thread enumerates games and players
//
//
unsigned int __stdcall EnumThread(void*)
{
	HANDLE	EventHandles[2];
	EventHandles[0]=KillEnumEvent;
	EventHandles[1]=EnumEvent;

    while( WaitForMultipleObjects(2, EventHandles, FALSE, INFINITE) == WAIT_OBJECT_0+1 )		// Kill event always checked first
	{
		EnterCriticalSection( &EnumCriticalSection );
//
// Enum games available
//
			DPSESSIONDESC2 sessionDesc;
			memset( &sessionDesc, 0, sizeof(sessionDesc) );
			sessionDesc.dwSize=sizeof(sessionDesc);
			memcpy( &sessionDesc.guidApplication, &Environment.NetworkGUID, sizeof(GUID) );
			if( Connected==NP_IPX )
				EnumResult=dplay4->EnumSessions( &sessionDesc, 2500, EnumSessionsCallback, 0, DPENUMSESSIONS_RETURNSTATUS | DPENUMSESSIONS_ASYNC | DPENUMSESSIONS_ALL | DPENUMSESSIONS_PASSWORDREQUIRED );
			else
				EnumResult=dplay4->EnumSessions( &sessionDesc, 0, EnumSessionsCallback, 0, DPENUMSESSIONS_RETURNSTATUS | DPENUMSESSIONS_ASYNC | DPENUMSESSIONS_ALL | DPENUMSESSIONS_PASSWORDREQUIRED );
//
// Now enumerate the players in each session
//
			if( SUCCEEDED(EnumResult) )
			{
				ListOfGames* pThis=EnumNames;
				while( pThis )
				{
//
// Allowed to fail - this just means no players.
//
					dplay4->EnumPlayers( &pThis->SessionDescriptor.guidInstance, EnumPlayersCallback, &pThis->pPlayers, DPENUMPLAYERS_ALL | DPENUMPLAYERS_SESSION );
					pThis=pThis->pNext;
				}
			}
//
// Signal we are done
//
		LastEnumTime=gos_GetHiResTime();
		LeaveCriticalSection( &EnumCriticalSection );
		WaitingForEnum=0;
	}
//
// Exit and clean up thread
//
	_endthreadex(0);
	return 0;
}









//
//
// Recieve thread - this thread recieves all the network packets and gives them an accurate time stamp
//
// All processing occurs in the ReceivePackets() routine
//
//
unsigned int __stdcall NetworkThread(void*)
{
	HANDLE	EventHandles[2];
	bool Encrypted;
	EventHandles[0]=KillNetworkEvent;
	EventHandles[1]=NetworkEvent;

    while( WaitForMultipleObjects(2, EventHandles, FALSE, INFINITE) == WAIT_OBJECT_0+1 )		// Kill event always checked first
	{
		DPID FromID;
		DPID ToID;
		DWORD Size=MessageBufferSize;
		HRESULT hr;
//
// Get the next message
//
		do
		{
			Encrypted=0;
			hr=dplay4->Receive( &FromID, &ToID, DPRECEIVE_ALL, MessageBuffer, &Size );
//
// Must have an adjustable size buffer (to prevent overflow crashes by hackers)
//
			if( hr==DPERR_BUFFERTOOSMALL )
			{
				if( MessageBuffer )
					free( MessageBuffer );					// DO NOT USE gos_Free!
				MessageBuffer=(BYTE*)malloc( Size );		// DO NOT USE gos_Malloc!
				MessageBufferSize=Size;
				hr=dplay4->Receive( &FromID, &ToID, DPRECEIVE_ALL, MessageBuffer, &Size );
			}
//
// Exit if no more messages, or error if there was an error.
//
			if( SUCCEEDED(hr) )
			{
//
// Check for ping packets that are handled immediately
//
				if( gosNet_Ping==MessageBuffer[0] )
				{
					DWORD* TimeStamp=(DWORD*)&MessageBuffer[1];
					DWORD Now=(DWORD)((float)(gos_GetHiResTime()*1000.0f));		// Current time in milliseconds

					if( 0==TimeStamp[3] )						// 3rd slot filled? Pass onto game
					{
						if( 0==TimeStamp[1] )
							TimeStamp[1]=Now;
						else if( 0==TimeStamp[2] )				// Otherwise fill highest slot and send back
							TimeStamp[2]=Now;
						else
							TimeStamp[3]=Now;

						dplay4->SendEx( MyDirectPlayID, FromID, DPSEND_NOSENDCOMPLETEMSG | DPSEND_ASYNC, MessageBuffer, 17, 0, 0, 0, 0 );
						continue;
					}
				}
//
// Check for DirectPlay packets that contain pointers to names
//
				if( DPID_SYSMSG==FromID )
				{
					DPMSG_GENERIC* pMessage=(DPMSG_GENERIC*)MessageBuffer;
					switch( pMessage->dwType )
					{
						case DPSYS_CREATEPLAYERORGROUP:
						{
							DPMSG_CREATEPLAYERORGROUP* pMess=(DPMSG_CREATEPLAYERORGROUP*)pMessage;
							if( pMess->dwPlayerType==DPPLAYERTYPE_PLAYER )
							{
								pMess->dpnName.lpszShortNameA-=(int)pMess;			// Turn pointer into offset from start of message
								break;
							}
						}
						case DPSYS_DESTROYPLAYERORGROUP:
						{
							DPMSG_DESTROYPLAYERORGROUP* pMess=(DPMSG_DESTROYPLAYERORGROUP*)pMessage;
							if( pMess->dwPlayerType==DPPLAYERTYPE_PLAYER )
							{
								pMess->dpnName.lpszShortNameA-=(int)pMess;			// Turn pointer into offset from start of message
								break;
							}
						}
						case DPSYS_SECUREMESSAGE:
						{
							DPMSG_SECUREMESSAGE* pMess=(DPMSG_SECUREMESSAGE*)pMessage;
							FromID=pMess->dpIdFrom;
							Size=pMess->dwDataSize;
							memcpy( pMessage, pMess->lpData, Size );				// Make any secure messages into regular messages
							Encrypted=1;
							break;
						}
					}
				}
//
// Stop main thread from effecting packet list
//
				EnterCriticalSection( &NetworkCriticalSection );
//
// Find an empty buffer for the message
//
				PacketBuffer* pLast=0;
				PacketBuffer* pThis=pEmptyList;
				while( pThis )
				{
					if( pThis->Size>=Size )			// Find one larger than the message
						break;
					pLast=pThis;
					pThis=pThis->pNext;
				}
//
// Remove from empty list
//
				if( 0==pThis )						// If one does not exist, malloc a new buffer
				{
					pThis=(PacketBuffer*)malloc(sizeof(PacketBuffer)+Size);		// DO NOT use gos_Malloc!
					pThis->Size=Size;
				}
				else
				{
					if( pLast )
					{
						pLast->pNext=pThis->pNext;
					}
					else
					{
						pEmptyList=pThis->pNext;
					}
				}
//
// Copy network packet into buffer
//
				pThis->Encrypted=Encrypted;
				pThis->pNext=0;
				pThis->PacketSize=Size;
				pThis->FromID=FromID;
				pThis->ToID=ToID;
				pThis->TimeStamp=gos_GetHiResTime();
				memcpy( &pThis->Data, MessageBuffer, Size );

				if( pLastPacket )
				{
					pLastPacket->pNext=pThis;
				}
				else
				{
					pPackets=pThis;
				}
				pLastPacket=pThis;
//
// Allow main thread access again
//
				LeaveCriticalSection( &NetworkCriticalSection );
			}
		} while( SUCCEEDED(hr) );
	}
//
// Exit and clean up thread
//
	_endthreadex(0);
	return 0;
}



