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


#include "mreplay.h"

char* GetNetworkMessage( NetPacket* pMessage );


#ifdef OUTBOUND_WINDOW
bool g_bDontQueueMessages=false;

#endif

#define MAX_WINDOW_ID 50000
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool IsNumberAhead(int value, int position, int number_ahead)
{
	int result = value - position;
	if (result >= 0 && result <= number_ahead)
		return true;

	result = value + MAX_WINDOW_ID - position;
	if (result >= 0 && result <= number_ahead)
		return true;

	return false;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool IsNumberBehind(int value, int position, int number_behind)
{

	int result = position - value; 
	if (result > 0 && result <= number_behind)
		return true;

	result = position + MAX_WINDOW_ID - value;
	if (result > 0 && result <= number_behind)
		return true;

	return false;
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool IsNumberWithin(int value, int positoin, int number_within)
{
	if (IsNumberAhead(value, positoin, number_within)||IsNumberBehind(value, positoin, number_within))
	{
		return true;
	}
	return  false;
};


//
// Analyze and maintain the packet logging
//
void _stdcall UpdateNetworkDebugInfo()
{
//
// Grab the current hires time
//
	NetworkDebugTime=gos_GetHiResTime();
//
// Skip over 1st 35 packets (fills the debugger packet log screen)
//
	PacketLogging* pThis=pPacketLog;
	DWORD Count=0;
	while( pThis )
	{
		Count++;
		if( Count==35 )
			break;
		pThis=pThis->pNext;
	}
//
// Keep packets from this frame, the last frame and less than 1 second ago
//
	while( pThis )
	{
		if( (pThis->FrameNumber!=CurrentFrameNumber-1 && pThis->FrameNumber!=CurrentFrameNumber) && ((pThis->TimeStamp+1.0f) < NetworkDebugTime) )
			break;
		pThis=pThis->pNext;
	}
//
// Delete packets older than a second
//
	PacketLogging* pEndOfList=pThis;
	if( pThis )
		pThis=pThis->pNext;
	while( pThis )
	{
		PacketLogging* pTemp=pThis;
		pThis=pThis->pNext;
		gos_Free( pTemp );
	}
	if( pEndOfList )
		pEndOfList->pNext=0;
//
// Save pointer to current 1st packet (so all info this frame is the same)
//
	pDebugPacketLog=pPacketLog;
}









//
//
// Receive and process network packets
//
//
void _stdcall ReceivePackets()
{
    if (g_nMR == 2) // jcem mission replay
        return;
//
// Stop receive thread from altering list
//
	EnterCriticalSection( &NetworkCriticalSection );
//
// Go through list of message received
//
	while( pPackets )
	{
		DWORD FromID;
		DWORD ToID;
		DWORD Size;
		double TimeStamp;
//
// Copy data from network packet
//
		FromID=pPackets->FromID;
		ToID=pPackets->ToID;
		Size=pPackets->PacketSize;
		TimeStamp=pPackets->TimeStamp;
//
// System messages (From DPLAY)
//
		if( DPID_SYSMSG==FromID )
		{
			DPMSG_GENERIC* pMessage=(DPMSG_GENERIC*)pPackets->Data;
			switch( pMessage->dwType )
			{
//
// This message occurs when a new player or group is created
//
				case DPSYS_CREATEPLAYERORGROUP:
				{
					DPMSG_CREATEPLAYERORGROUP* pMess=(DPMSG_CREATEPLAYERORGROUP*)pMessage;

					if( pMess->dwPlayerType==DPPLAYERTYPE_PLAYER )
					{						
//
// Add the player to the internal list of active players
//
						AddPlayerToGame( &CurrentPlayers, (int)pMess+pMess->dpnName.lpszShortNameA, pMess->dpId );		// Note pointer turned into an offset!

//
// Add the player to the external list of active players
//
						ServerAdvertiser::AddPlayerAll(pMess->dpId, (int)pMess+pMess->dpnName.lpszShortNameA);
						//GamesAddPlayer(pMess->dpId,(int)pMess+pMess->dpnName.lpszShortNameA);
#ifdef OUTBOUND_WINDOW
						OutboundWindow *pOW = new OutboundWindow(pMess->dpId);
						if (pOW)
							pOW->Add();
#endif

//
// Tell application this player has joined
//
						Messages* pThis=(Messages*)gos_Malloc( sizeof(Messages), Heap_Network );
						pThis->pNext=0;
						pThis->Data.FromID=pMess->dpId;
						pThis->Data.Flags=0;
						pThis->Data.Length=0;
						pThis->Data.pData=0;
						pThis->Data.ToID=0;
						pThis->Data.Type=gosNet_PlayerAdded;

						AddGOSMessage( pThis );
//
// Record logging information
//
						#ifdef LAB_ONLY
						PacketLogging* pLogging=(PacketLogging*)gos_Malloc( sizeof(PacketLogging), Heap_Network );
						pLogging->TimeStamp=TimeStamp;
						pLogging->FrameNumber=CurrentFrameNumber;
						pLogging->FromID=0;
						pLogging->ToID=0;
						pLogging->Size=0;
						strcpy( pLogging->Type, "Add Player" );
						memcpy( pLogging->Player, GetName10( pMess->dpId ), 11 );
						pLogging->pNext=pPacketLog;
						pPacketLog=pLogging;
						#endif
					}
					else
					{
						SPEW(( GROUP_NETWORK, "DPMSG_CREATEPLAYERORGROUP - Got group?" ));
					}
					break;
				}
//
// This message occurs when a player or group is destoryed
//
				case DPSYS_DESTROYPLAYERORGROUP:
				{
					DPMSG_DESTROYPLAYERORGROUP* pMess=(DPMSG_DESTROYPLAYERORGROUP*)pMessage;
					if( pMess->dwPlayerType==DPPLAYERTYPE_PLAYER )
					{						
//
// Remove the player from the external list of active players
//

						ServerAdvertiser::RemovePlayerAll(pMess->dpId,(int)pMess+pMess->dpnName.lpszShortNameA, false);
						//GamesDestroyPlayer(pMess->dpId,(int)pMess+pMess->dpnName.lpszShortNameA);

#ifdef OUTBOUND_WINDOW
						OutboundWindow *pOW = OutboundWindow::Find(pMess->dpId);
						if (pOW)
							pOW->Remove();
#endif

//
// Tell application this player no longer exists (If MY ID, send a game ended message)
//
						Messages* pThis=(Messages*)gos_Malloc( sizeof(Messages), Heap_Network );
						pThis->pNext=0;

						if( pMess->dpId==MyDirectPlayID )
						{
							pThis->Data.FromID=0;
							pThis->Data.Flags=0;
							pThis->Data.Length=0;
							pThis->Data.pData=0;
							pThis->Data.ToID=0;
							pThis->Data.Type=gosNet_GameEnded;
							MyDirectPlayID=0;
						}
						else
						{
							pThis->Data.FromID=pMess->dpId;
							pThis->Data.Flags=0;
							pThis->Data.Length=0;
							pThis->Data.pData=0;
							pThis->Data.ToID=0;
							pThis->Data.Type=gosNet_PlayerDeleted;
						}
						AddGOSMessage( pThis );
//
// Record logging information
//
						#ifdef LAB_ONLY
						PacketLogging* pLogging=(PacketLogging*)gos_Malloc( sizeof(PacketLogging), Heap_Network );
						pLogging->TimeStamp=TimeStamp;
						pLogging->FrameNumber=CurrentFrameNumber;
						pLogging->FromID=0;
						pLogging->ToID=0;
						pLogging->Size=0;
						strcpy( pLogging->Type, "Del Player" );
						memcpy( pLogging->Player, GetName10( pMess->dpId ), 11 );
						pLogging->pNext=pPacketLog;
						pPacketLog=pLogging;
						#endif
					}
					else
					{
						SPEW(( GROUP_NETWORK, "DPMSG_DESTROYPLAYERORGROUP - Got group?" ));
					}
					break;
				}
//
// These messages are currently unused
//
				case DPSYS_ADDGROUPTOGROUP:
				{
					DPMSG_ADDGROUPTOGROUP* pMess=(DPMSG_ADDGROUPTOGROUP*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_ADDGROUPTOGROUP" ));
					break;
				}
				case DPSYS_ADDPLAYERTOGROUP:
				{
					DPMSG_ADDPLAYERTOGROUP* pMess=(DPMSG_ADDPLAYERTOGROUP*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_ADDPLAYERTOGROUP" ));
					break;
				}
				case DPSYS_CHAT:
				{
					DPMSG_CHAT* pMess=(DPMSG_CHAT*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_CHAT" ));
					break;
				}
				case DPSYS_DELETEGROUPFROMGROUP:
				{
					DPMSG_DELETEGROUPFROMGROUP* pMess=(DPMSG_DELETEGROUPFROMGROUP*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_DELETEGROUPFROMGROUP" ));
					break;
				}
				case DPSYS_DELETEPLAYERFROMGROUP:
				{
					DPMSG_DELETEPLAYERFROMGROUP* pMess=(DPMSG_DELETEPLAYERFROMGROUP*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_DELETEPLAYERFROMGROUP" ));
					break;
				}
				case DPSYS_HOST:
				{
					DPMSG_HOST* pMess=(DPMSG_HOST*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_HOST" ));
					IAmTheServer=1;
					break;
				}
				case DPSYS_SECUREMESSAGE:
				{
					DPMSG_SECUREMESSAGE* pMess=(DPMSG_SECUREMESSAGE*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_SECUREMESSAGE" ));
					break;
				}
				case DPSYS_SENDCOMPLETE:
				{
					DPMSG_SENDCOMPLETE* pMess=(DPMSG_SENDCOMPLETE*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_SENDCOMPLETE" ));
					break;
				}
				case DPSYS_SESSIONLOST:
				{
					DPMSG_SESSIONLOST* pMess=(DPMSG_SESSIONLOST*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_SESSIONLOST" ));
//
// Tell application this game no longer exists
//
					Messages* pThis=(Messages*)gos_Malloc( sizeof(Messages), Heap_Network );
					pThis->pNext=0;
					pThis->Data.FromID=0;
					pThis->Data.Flags=0;
					pThis->Data.Length=0;
					pThis->Data.pData=0;
					pThis->Data.ToID=0;
					pThis->Data.Type=gosNet_GameEnded;

					AddGOSMessage( pThis );
//
// Record logging information
//
					#ifdef LAB_ONLY
					PacketLogging* pLogging=(PacketLogging*)gos_Malloc( sizeof(PacketLogging), Heap_Network );
					pLogging->TimeStamp=TimeStamp;
					pLogging->FrameNumber=CurrentFrameNumber;
					pLogging->FromID=0;
					pLogging->ToID=0;
					pLogging->Size=0;
					strcpy( pLogging->Type, "Game Over " );
					memcpy( pLogging->Player, "Server    ", 11 );
					pLogging->pNext=pPacketLog;
					pPacketLog=pLogging;
					#endif
					break;
				}
				case DPSYS_SETGROUPOWNER:
				{
					DPMSG_SETGROUPOWNER* pMess=(DPMSG_SETGROUPOWNER*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_SETGROUPOWNER" ));
					break;
				}
				case DPSYS_SETPLAYERORGROUPDATA:
				{
					DPMSG_SETPLAYERORGROUPDATA* pMess=(DPMSG_SETPLAYERORGROUPDATA*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_SETPLAYERORGROUPDATA" ));
					break;
				}
				case DPSYS_SETPLAYERORGROUPNAME:
				{
					DPMSG_SETPLAYERORGROUPNAME* pMess=(DPMSG_SETPLAYERORGROUPNAME*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_SETPLAYERORGROUPNAME" ));
					break;
				}
//
// This occurs when the server updates the game data (so don't spew!)
//
				case DPSYS_SETSESSIONDESC:
				{
					DPMSG_SETSESSIONDESC* pMess=(DPMSG_SETSESSIONDESC*)pMessage;
					pMess=pMess;
					break;
				}
				case DPSYS_STARTSESSION:
				{
					DPMSG_STARTSESSION* pMess=(DPMSG_STARTSESSION*)pMessage;
					pMess=pMess;
					SPEW(( GROUP_NETWORK,"DPMSG_STARTSESSION" ));
					break;
				}
				default:
				{
					STOP(( "Unknown system message 0x%x",pMessage->dwType ));
					break;
				}
			}
		}
		else
		{
//
// User messages - copy to a GameOS Message structure, remove GameOS overhead from size
//
			Size-=sizeof(PacketHeader);
//
// Ignore message that are too large
//
			if( Size>1024 )
				continue;
//
// Copy message to GameOS memory
//
			Messages* pThis=(Messages*)gos_Malloc( sizeof(Messages)+Size, Heap_Network );

			pThis->pNext=0;
			pThis->Data.FromID=FromID;
			pThis->Data.ToID=ToID;
			pThis->Data.Flags=(WORD)(pPackets->Encrypted ? gosNetFlag_encrypt : 0);
			pThis->Data.Type=pPackets->Data[0];
			pThis->Data.TimeStamp=TimeStamp;
			
			if( 0==Size )
			{
				pThis->Data.Length=0;
				pThis->Data.pData=0;
			}
			else
			{
				BYTE* ptr=((BYTE*)pThis)+sizeof(Messages);
				memcpy( ptr, &(pPackets->Data[0]) + sizeof(PacketHeader), Size );
				pThis->Data.Length=(WORD)Size;
				pThis->Data.pData=ptr;
			}

#ifdef OUTBOUND_WINDOW
//
// Update the OutboundWindow
//
			OutboundWindow *pOW = OutboundWindow::Find(FromID);
			//gosASSERT(pPackets->Data[0] != gosNet_Ping);
			if (pOW && (pPackets->Data[0] != gosNet_Ping))
			{


			

				//
				// update the last packet we received
				//
				WORD LastPacketWeReceived = ((PacketHeader *)pPackets->Data)->ThisPacketNumber;
			
				//if (pOW->m_LastPacketWeReceived < LastPacketWeReceived)
				//	pOW->m_LastPacketWeReceived = LastPacketWeReceived;
				// only go up, 6000 is about 5 minutes worth of packets at 20pps
				if (!IsNumberBehind(LastPacketWeReceived, pOW->m_LastPacketWeReceived, 6000))
				{
					pOW->m_LastPacketWeReceived = LastPacketWeReceived;	
				}


				WORD LastPacketTheyReceived = ((PacketHeader *)pPackets->Data)->LastPacketReceived;
			
				//if (pOW->m_LastPacketTheyReceived < LastPacketTheyReceived)
				//	pOW->m_LastPacketTheyReceived = LastPacketTheyReceived;
				//else
				//{
				//	// make sure we're not WAY WAY off...
				//	if (pOW->m_LastPacketTheyReceived - LastPacketTheyReceived > 40)
				//		pOW->m_LastPacketTheyReceived = LastPacketTheyReceived;
				//}

				if (!IsNumberBehind(LastPacketTheyReceived, pOW->m_LastPacketTheyReceived, 6000))
				{
					pOW->m_LastPacketTheyReceived = LastPacketTheyReceived;
				}



				//SPEWALWAYS(("jerryeds", "REC - window : %d we:%d them:%d", pOW->m_NextPacketNumberToSend - pOW->m_LastPacketTheyReceived, pOW->m_LastPacketWeReceived, pOW->m_LastPacketTheyReceived));
			}
#endif




//
// Add message to list visible to application
//
			AddGOSMessage( pThis );
//
// Update debug information
//
			#ifdef LAB_ONLY
			{
//
// Update 'time since last got a packet' count
//
				ListOfNames* pTemp=CurrentPlayers;
				while( pTemp )
				{
					if( pTemp->Data==FromID )
					{
						pTemp->LastTime=gos_GetElapsedTime();
						pTemp->PacketsReceived++;				
						break;
					}
					pTemp=pTemp->pNext;
				}
//
// Add packet to log
//
				PacketLogging* pLog=(PacketLogging*)gos_Malloc( sizeof(PacketLogging), Heap_Network );
				pLog->TimeStamp=pThis->Data.TimeStamp;
				pLog->FrameNumber=CurrentFrameNumber;
				pLog->FromID=FromID;
				pLog->ToID=ToID;
				pLog->Size=Size;
				char Buffer[32];
				if( pPackets->Encrypted )
					sprintf( Buffer, "Decrypt %02X",pThis->Data.Type );
				else
					sprintf( Buffer, "Receive %02X",pThis->Data.Type );
				strcpy( pLog->Type, Buffer );
				memcpy( pLog->Player, GetName10( FromID ), 11 );
				memcpy( pLog->Data, pThis->Data.pData, (pThis->Data.Length<8) ? pThis->Data.Length : 8 );
				pLog->pNext=pPacketLog;
				pPacketLog=pLog;
			}
			#endif
		}
//
// Get pointer to next message
//
		PacketBuffer* pNext=pPackets->pNext;
//
// Put current buffer on 'empty list'
//
		pPackets->pNext=pEmptyList;
		pEmptyList=pPackets;
//
// Move onto next message
//
		pPackets=pNext;
	}
//
// Mark list of received packets empty, let network thread run again
//
	pLastPacket=0;
	LeaveCriticalSection( &NetworkCriticalSection );
}









//
// Adds a message to the list of messages visible to the application
//
void _stdcall AddGOSMessage( Messages* pMessage )
{
	if( pMessages )
	{
		Messages* pLast=pMessages;
		while( pLast->pNext )
		{
			pLast=pLast->pNext;
		};
		pLast->pNext=pMessage;
	}
	else
	{
		pMessages=pMessage;
	}
}



//
// App visible, reads message available
//
NetPacket* __stdcall gos_NetGetMessage()
{
    // jcem - mission replay
    if (g_pfn_NETGETMESSAGE) {
        return (*g_pfn_NETGETMESSAGE)();
    }
    // jcem - mission replay

	NetPacket* result=0;
	
	if( InNetworkGame && 1!=LogMode )
	{
		if( pLastMessage )
		{
			if (pLastMessage->Data.Type == gosNet_PlayerDeleted)
			{
				int OldLogMode=LogMode;
				bool OldInDebugger=InDebugger;		// So data does not effect log
				InDebugger=1;
				LogMode=2;
				// Remove the player from the list of active players
				//
				RemovePlayerFromGame( &CurrentPlayers, 
					(char*)gos_NetInformation(gos_PlayerName, (DWORD)gos_NetInformation(gos_PlayerNumber, pLastMessage->Data.FromID)), 
					pLastMessage->Data.FromID );
		
				LogMode=OldLogMode;
				InDebugger=OldInDebugger;		// So data does not effect log
			}

			gos_Free( pLastMessage );
			pLastMessage=0;
		}
		Messages* pThis=pMessages;
		if( pMessages )
		{
			pLastMessage=pMessages;
			pMessages=pMessages->pNext;
			SPEW(( GROUP_NETWORK, "gos_NetGetMessage( %s)",GetNetworkMessage((NetPacket*)&pThis->Data) ));
			result=&pThis->Data;
		}
	}

	if( 1==LogMode )	// Log file playing back?
	{
		static char Buffer[2048];
		DWORD Len=ReadBitStream(16,Log_DataBlock);
		if( Len )
		{
			ReadLogData( (BYTE*)&Buffer, Len );
			result=(NetPacket*)&Buffer;
			if( result->pData )
			{
				result->pData=result+1;
			}
		}
		else
		{
			result=0;
		}
	}
	else				// Log file recording
	{
		if( result==0 )
		{
			WriteBitStream((DWORD)result,16,Log_DataBlock);
		}
		else
		{
			DWORD Len=sizeof(NetPacket)+result->Length;
			WriteLogData( (BYTE*)result, Len);
		}
	}

	return result;
}






typedef struct tagPING
{
	char data[16];
} PING;


//
//
// Send a network message
//
//
bool __stdcall gos_NetSendMessage( NetPacket* Message )
{
    // jcem - mission replay
    if (g_pfn_NETSENDMESSAGE) {
        return (*g_pfn_NETSENDMESSAGE)(Message);
    }
    // jcem - mission replay

	if( 1==LogMode )	// Log file playing back?
	{
		#ifdef LAB_ONLY
		GetName10(Message->ToID);
		#endif
		return true;
	}

	gosASSERT( Message->Length<=1024 );
	gosASSERT( !((Message->Flags&gosNetFlag_encrypt) && 0==(CurrentSession.dwFlags&DPSESSION_SECURESERVER)) );

	char Buffer[1536];
	char *pBuffer;

	((PacketHeader *)Buffer)->Type=Message->Type;
	pBuffer = &Buffer[sizeof(PacketHeader)];

//
//
//
	if( gosNet_Ping==Message->Type )
	{
		gosASSERT( Message->Length==0 );					// Send ping packet with 0 length
		memset( pBuffer, 0, sizeof(PING) );

		DWORD* TimeStamp=(DWORD*)pBuffer;
		*TimeStamp=(DWORD)((float)(gos_GetHiResTime()*1000.0f));		// Add time stamp from this computer
//
// Send using DirectPlay as soon as possible
//
		AllowFail=1;
		HRESULT hr=wSendEx( dplay4, MyDirectPlayID, Message->ToID, DPSEND_NOSENDCOMPLETEMSG | DPSEND_ASYNC, &Buffer, sizeof(PacketHeader)+sizeof(PING), 0, 0, 0, 0 );
		AllowFail=0;
//
// If the error was 'unimplemented' - try without ASCYN (the provider does not support it) - IPX for example
//
		if( hr==0x80004001 )
		{
			AllowFail=1;
			wSend( dplay4, MyDirectPlayID, Message->ToID, 0 , &Buffer, sizeof(PacketHeader)+sizeof(PING) );
			AllowFail=0;
		}
//
// Restore current GameOS FPU control word setup
//
		GameOSFPU();
	}
	else
	{
#ifdef OUTBOUND_WINDOW
		//
		// see if we really can send the message
		//
		OutboundWindow *pOW=OutboundWindow::Find(Message->ToID);
		//jcem -? gosASSERT(pOW);
		if (pOW && !g_bDontQueueMessages)
		{
		

			//
			// if it's a guaranteed message and there's messages in the queue OR if the window
			// is full
			//
			if (
				(pOW->m_PacketQueue) && (Message->Flags&gosNetFlag_guaranteed) ||
				(!IsNumberAhead(pOW->m_NextPacketNumberToSend, pOW->m_LastPacketTheyReceived, pOW->m_WindowSize))
			   )
			{
				//
				// can't send!!
				//
				if (Message->Flags&gosNetFlag_guaranteed)
				{
					// queue it up
					PACKETQUEUE *pQ = pOW->m_PacketQueue;
					if (pQ)
					{
						while (pQ->pNext)
						{
							pQ=pQ->pNext;
						}

						pQ->pNext = (PACKETQUEUE *)gos_Malloc(sizeof(PACKETQUEUE), Heap_Network);
						pQ=pQ->pNext;
					}
					else
					{
						pOW->m_PacketQueue = (PACKETQUEUE *)gos_Malloc(sizeof(PACKETQUEUE), Heap_Network);
						pQ = pOW->m_PacketQueue;
					}

					pQ->pNext = NULL;
					pQ->pPacket = (NetPacket *)gos_Malloc(sizeof(NetPacket), Heap_Network);
					memcpy(pQ->pPacket, Message, sizeof(NetPacket));

					if (pQ->pPacket->Length)
					{
						pQ->pPacket->pData = gos_Malloc(pQ->pPacket->Length, Heap_Network);
						memcpy(pQ->pPacket->pData, Message->pData, pQ->pPacket->Length);
					}
					else
					{
						pQ->pPacket->pData = NULL;
					}
				}
				SPEW(("mlyons", "The Outbound Message Window has been exceeded"));
				//SPEW(("jerryeds", "The Outbound Message Window has been exceeded"));

				return false;
			}
			else
			{

				//
				// can send
				//
				((PacketHeader *)Buffer)->ThisPacketNumber = pOW->m_NextPacketNumberToSend;
				((PacketHeader *)Buffer)->LastPacketReceived = pOW->m_LastPacketWeReceived;

				//SPEWALWAYS(("jerryeds", "SEND - window : %d : next:%d we:%d them:%d",pOW->m_NextPacketNumberToSend - pOW->m_LastPacketTheyReceived, pOW->m_NextPacketNumberToSend, pOW->m_LastPacketWeReceived, pOW->m_LastPacketTheyReceived));


				pOW->m_TimeLastPacketSent = gos_GetElapsedTime(1);
				pOW->m_NextPacketNumberToSend++;

				if (pOW->m_NextPacketNumberToSend >= MAX_WINDOW_ID)
				{
					pOW->m_NextPacketNumberToSend = 0;
				}
			}
		}
		else if (pOW)
		{
		

			//
			// can send
			//
			((PacketHeader *)Buffer)->ThisPacketNumber = pOW->m_NextPacketNumberToSend;
			((PacketHeader *)Buffer)->LastPacketReceived = pOW->m_LastPacketWeReceived;

			//SPEWALWAYS(("jerryeds", "SENDQUE - window : %d next:%d we:%d them:%d",pOW->m_NextPacketNumberToSend - pOW->m_LastPacketTheyReceived, pOW->m_NextPacketNumberToSend, pOW->m_LastPacketWeReceived, pOW->m_LastPacketTheyReceived));


			pOW->m_TimeLastPacketSent = gos_GetElapsedTime(1);
			pOW->m_NextPacketNumberToSend++;
			if (pOW->m_NextPacketNumberToSend >= MAX_WINDOW_ID)
			{
				pOW->m_NextPacketNumberToSend = 0;
			}

		}
#endif

		DWORD MessageSize=Message->Length;
//
// Move message into temp buffer
//
		memcpy( pBuffer, Message->pData, MessageSize );
//
// Send using DirectPlay as soon as possible
//
		AllowFail=1;
		DWORD timeout;
		if ((Message->Flags&gosNetFlag_encrypt) || (Message->Flags&gosNetFlag_guaranteed))
		{
			timeout=0;
		}
		else
		{
			timeout=1000;
		};

		HRESULT hr=wSendEx( dplay4, MyDirectPlayID, Message->ToID, DPSEND_NOSENDCOMPLETEMSG | DPSEND_ASYNC | ((Message->Flags&gosNetFlag_encrypt) ? DPSEND_ENCRYPTED|DPSEND_GUARANTEED : 0) | ((Message->Flags&gosNetFlag_guaranteed) ? DPSEND_GUARANTEED : 0), &Buffer, MessageSize + sizeof(PacketHeader), 0, timeout, 0, 0 );
		AllowFail=0;
//
// If the error was 'unimplemented' - try without ASYNC (the provider does not support it) - IPX for example
//
		if( hr==0x80004001 )
		{
			AllowFail=1;
			wSend( dplay4, MyDirectPlayID, Message->ToID, 0, &Buffer, MessageSize+sizeof(PacketHeader) );
			AllowFail=0;
		}
//
// Restore current GameOS FPU control word setup
//
		GameOSFPU();
	}
//
// Add the packet information to the start of the list
//
	#ifdef LAB_ONLY
	PacketLogging* pThis=(PacketLogging*)gos_Malloc( sizeof(PacketLogging), Heap_Network );
	pThis->TimeStamp=gos_GetHiResTime();
	pThis->FrameNumber=CurrentFrameNumber;
	pThis->FromID=MyDirectPlayID;
	pThis->ToID=Message->ToID;
	pThis->Size=Message->Length;

	char Buffer1[32];
	sprintf( Buffer1, "Send %02X   ",Message->Type );
	strcpy( pThis->Type, Buffer1 );
	memcpy( pThis->Player, GetName10( Message->ToID ), 11 );
	memcpy( pThis->Data, Message->pData, (pThis->Size<8) ? pThis->Size : 8 );
	pThis->pNext=pPacketLog;
	pPacketLog=pThis;
	#endif

	return true;
}



