#include "pch.hpp"
//
//============================================================
//
// Contents:  Network Init/Cleanup
//
// Owner   :  AndyG
// Created :  Aug 9 1999
//
// All Rights reserved worldwide (c) Microsoft
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL
//============================================================
//
#include "Net_Header.hpp"
#include "Windows.hpp"
#include "Time.hpp"
#include "ErrorHandler.hpp"
#include "GUNGameList.h"


#include "mreplay.h"

#define USE_DPKEEPALIVE
#define USE_CLIENTSERVER

extern HGOSHEAP DefaultHeap;

char GameNameBuffer[128];
char PasswordBuffer[128];

extern DWORD UpdatedExe;					// Has the exe been updated since last run?

int NGStatsHash;

#ifdef OUTBOUND_WINDOW
OutboundWindow * OutboundWindow::m_pHead = NULL;
const int OutboundWindow::m_WindowSize = 8;
const double OutboundWindow::m_ResendTime = 1.0; // seconds
extern bool g_bDontQueueMessages;

extern bool IsNumberAhead(int value, int position, int number_ahead);


bool gos_OutboundWindowSync()
{
	return OutboundWindow::Synchronize();
}

//========================================================================
//========================================================================
OutboundWindow::OutboundWindow(DPID dpid) :
	m_NextPacketNumberToSend(0),
	m_LastPacketWeReceived(0),
	m_LastPacketTheyReceived(0),
	m_TimeLastPacketSent(0.0),
	m_dpid(-2),
	m_PacketQueue(NULL),
	m_pNext(NULL)
{
	OutboundWindow *pOW = Find(dpid);
	if (pOW)
	{
		pOW->Remove();
	}

	m_dpid = dpid;
}



//========================================================================
//========================================================================
bool _stdcall OutboundWindow::Synchronize(void)
{
	//
	// go through the entire list
	// and see if there's any that need new messages sent
	//
	OutboundWindow *p = m_pHead;
	double time=gos_GetElapsedTime(1);
	while (p)
	{
		if ((time - p->m_TimeLastPacketSent) > m_ResendTime)
		{
			static char buffer;

			p->m_TimeLastPacketSent = time;
			//$TODO
			// add a packet send
			NetPacket Message;
			Message.FromID	= 0;
			Message.ToID	= p->m_dpid;
			Message.Flags	= 0;
			Message.Length	= 1;
			Message.Type	= gosNet_Heartbeat;
			Message.pData	= &buffer;


			// hey, the window might be full, but who cares?
			g_bDontQueueMessages = true;
			gos_NetSendMessage( &Message );
			g_bDontQueueMessages = false;
		}

		//
		// see if we're cool to send packets
		//

		while (IsNumberAhead(p->m_NextPacketNumberToSend, p->m_LastPacketTheyReceived, p->m_WindowSize))
		{
			//
			// are there guaranteed messages to send?
			//
			if (p->m_PacketQueue)
			{
				PACKETQUEUE *pQ = p->m_PacketQueue;

				g_bDontQueueMessages=true;
				gos_NetSendMessage( pQ->pPacket );
				g_bDontQueueMessages=false;
				if (pQ->pPacket->pData)
					gos_Free(pQ->pPacket->pData);
				gos_Free(pQ->pPacket);
				p->m_PacketQueue = pQ->pNext;
				gos_Free(pQ);
			}
			else
				break;
		}

		p=p->m_pNext;
	}

	return true;
}

//========================================================================
//========================================================================
bool _stdcall OutboundWindow::Add(void)
{
	m_pNext = m_pHead;
	m_pHead = this;
	return true;	
}

//========================================================================
//========================================================================
OutboundWindow* _stdcall OutboundWindow::Find(DPID dpid)
{
	OutboundWindow *p = m_pHead;
	while (p)
	{
		if (p->m_dpid == dpid)
			break;
		p=p->m_pNext;
	}

	return p;
}


//========================================================================
//========================================================================
bool FlushIncomingMessageQueue(void)
{
//
// Destroy any pending messages
//
	Messages* pMess=pMessages;
	while( pMess )
	{
		Messages* pTemp=pMess;
		pMess=pMess->pNext;
		gos_Free( pTemp );
	}
	pMessages=0;
//
// Is there a 'last message'?
//
	if( pLastMessage )
	{
		gos_Free( pLastMessage );
	}
	pLastMessage=0;


	if (HandleNetworkThread)
	{

		EnterCriticalSection( &NetworkCriticalSection );

		while( pPackets )
		{

	//
	// 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 );
	}

	return true;

}


//========================================================================
//========================================================================
bool _stdcall OutboundWindow::CleanAll(void)
{
	OutboundWindow *p=m_pHead;

	while (p)
	{
		m_pHead = p;
		p=p->m_pNext;
		delete m_pHead;
		m_pHead = p;
	}

	//
	// now, also read messages from GameOS until there's nothing left
	//
	FlushIncomingMessageQueue();

	return true;
}

//========================================================================
//========================================================================
bool _stdcall OutboundWindow::Remove(void)
{
	OutboundWindow **p=&m_pHead;
	while (*p)
	{
		if (*p == this)
		{
			*p = m_pNext;
			delete this;
			return true;
		}

		p = &((*p)->m_pNext);
	}

	return false;
}
#endif //OUTBOUND_WINDOW

#define PROTOCOL_DEFAULT	0
#define PROTOCOL_TCPIP		1
#define PROTOCOL_IPX		2

int InitializeProtocol(int iProtocol=PROTOCOL_DEFAULT);

#ifdef MLYONS
#define FIX_WINSOCK_PROXY
#endif

#ifdef FIX_WINSOCK_PROXY
static bool FixWinsockProxy(void);
#endif


//
// Called from the main loop to get all network messages
//
void _stdcall UpdateNetwork()
{
//
// Do the 'recieve loop' - process all messages
//
	if( InNetworkGame )
	{
		ReceivePackets();
	}
//
// If connection exists, but a game is not created or joined, enumerate games/players every 2 seconds
//
	//if( Connected && !InNetworkGame )
	//{
	//	GamesUpdateList();
	//}
//
// Process any server browser services registered
//
#ifdef OUTBOUND_WINDOW
	OutboundWindow::Synchronize();
#endif
	ServerBrowser::SynchronizeAll();
	ServerAdvertiser::SynchronizeAll();


//
// Deal with debugging information
//
	UpdateNetworkDebugInfo();
}




//
// Called once at the start of GameOS
//
void _stdcall InitNetworking()
{


#ifdef FIX_WINSOCK_PROXY
//
// fix proxy settings
//
	FixWinsockProxy();
#endif

//
// Clear variables
//
	LastEnumTime=0.0;
	WaitingForEnum=0;
	pDebugPacketLog=0;
	pPacketLog=0;
	InNetworkGame=0;
	IAmTheServer=0;
	MyDirectPlayID=0;
	CurrentPlayers=0;
	pLastMessage=0;
	pMessages=0;
	HandleNetworkThread=0;
	pEmptyList=0;
	pPackets=0;
	pLastPacket=0;


// after so we can check the status of HandleNetworkThread
#ifdef OUTBOUND_WINDOW
	OutboundWindow::CleanAll();
#endif


//
// Allocate a 1K buffer for receiving messages
//
	MessageBuffer=(BYTE*)malloc( 1536 );		// DO NOT USE gos_Malloc!
	MessageBufferSize=1536;
//
//
//
	if( Environment.NetworkGame )
	{
//
// Check the GUID passed in the environment
//
		#ifdef LAB_ONLY
		if( Environment.NetworkGUID[0]==0 &&
			Environment.NetworkGUID[1]==0 &&
			Environment.NetworkGUID[2]==0 &&
			Environment.NetworkGUID[3]==0 &&
			Environment.NetworkGUID[4]==0 &&
			Environment.NetworkGUID[5]==0 &&
			Environment.NetworkGUID[6]==0 &&
			Environment.NetworkGUID[7]==0 )
			STOP(( "No network GUID specified in the Environment!" ));
		#endif
//
// Create a DirectPlayLobby3 object
//
		wCoCreateInstance( CLSID_DirectPlayLobby, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayLobby3A, (LPVOID*)&dplobby3 );
//
// Create main DirectPlay object
//
		wCoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay4A, (LPVOID*)&dplay4 );
//
// Create a receive thread where all packets will be received, and other variables
//
		InitializeCriticalSection( &NetworkCriticalSection );
		NetworkEvent=CreateEvent( NULL, false, false, NULL );
		KillNetworkEvent=CreateEvent( NULL, false, false, NULL );
		HandleNetworkThread=(HANDLE)_beginthreadex( 0, 0, NetworkThread, 0, 0, &NetworkThreadID );
		SetThreadPriority( HandleNetworkThread, THREAD_PRIORITY_HIGHEST );
//
// Create a enumeration thread where all games/players will be enumerated, and other variables
//
		InitializeCriticalSection( &EnumCriticalSection );
		EnumEvent=CreateEvent( NULL, false, false, NULL );
		KillEnumEvent=CreateEvent( NULL, false, false, NULL );
		HandleEnumThread=(HANDLE)_beginthreadex( 0, 0, EnumThread, 0, 0, &EnumThreadID );
//
// Check protocols available on machine
//
		CheckProtocols();
//
// Register game as a lobby aware application (onyl when first time app run)
// In RELEASE this MUST be done by the setup program (HKLM)
//
		#ifdef LAB_ONLY
		if( UpdatedExe )
		{
			DPAPPLICATIONDESC Description;
			memset( &Description, 0, sizeof(Description) );
			Description.dwSize=sizeof(Description);
			Description.lpszApplicationNameA=ApplicationName;
			memcpy( &Description.guidApplication, Environment.NetworkGUID, sizeof(Description.guidApplication) );
			char drive[_MAX_PATH ];
			char dir[_MAX_DIR ];
			char fname[_MAX_FNAME ];
			char ext[_MAX_EXT ];
			char BufferFileName[MAX_PATH];
			char BufferPath[MAX_PATH];
			_splitpath( _pgmptr, drive, dir, fname, ext );
			strcpy( BufferFileName, fname );
			strcat( BufferFileName, ext );
			Description.lpszFilenameA=BufferFileName;
			Description.lpszCommandLineA="-lobby";
			strcpy( BufferPath, drive );
			strcat( BufferPath, dir );
			if( BufferPath[strlen(BufferPath)-1]=='\\' )
				BufferPath[strlen(BufferPath)-1]=0;
			Description.lpszPathA=BufferPath;
			char CurrentPath[MAX_PATH];
			gos_GetCurrentPath( CurrentPath, MAX_PATH-1 );
			Description.lpszCurrentDirectoryA=CurrentPath;
			Description.lpszDescriptionA=ApplicationName;
			AllowFail=1;
			wRegisterApplication( dplobby3, 0, &Description );
			AllowFail=0;
		}
		#endif
	}
}



//
// Called once during Exit GameOS
//
void DestroyNetworking()
{	
#ifdef OUTBOUND_WINDOW
	OutboundWindow::CleanAll();
#endif

    if ( ServerBrowser::GetGameList() )
    {
        ServerBrowser::GetGameList()->ShutdownPing();
    }
	ServerBrowser::ReleaseAll();
	ServerAdvertiser::ReleaseAll();

    if ( ServerBrowser::GetGameList() )
    {
        ServerBrowser::DestroyGameList();
    }

    GUNDestroyNetworking();

//
// Shut down game if any
//
	gos_ShutdownNetwork();	
//
// Close down packet receiving thread
//
	if( HandleNetworkThread )
	{
        SetEvent( KillNetworkEvent );
        WaitForSingleObject( HandleNetworkThread, INFINITE );
		CloseHandle( HandleNetworkThread );
		DeleteCriticalSection( &NetworkCriticalSection );
		CloseHandle( NetworkEvent );
		CloseHandle( KillNetworkEvent );
		HandleNetworkThread=0;
	}
//
// Close down enumeration thread
//
	if( HandleEnumThread )
	{
        SetEvent( KillEnumEvent );
        WaitForSingleObject( HandleEnumThread, INFINITE );
		CloseHandle( HandleEnumThread );
		DeleteCriticalSection( &EnumCriticalSection );
		CloseHandle( EnumEvent );
		CloseHandle( KillEnumEvent );
		HandleEnumThread=0;
	}
//
// Destroy any game/player enumeration data
//
	while( EnumNames )
	{
		ListOfGames* pThis=EnumNames;
		if( pThis->pPlayers )
		{
			ListOfNames* pTemp=pThis->pPlayers;
			while( pTemp )
			{
				ListOfNames* pThis=pTemp;			// Remove list of player names
				pTemp=pTemp->pNext;
				free( pThis );						// Cannot use gos_Free!
			}
		}
		EnumNames=EnumNames->pNext;
		free( pThis );								// Remove gane name and information
	}
//
// Remove any packet buffers
//
	while( pEmptyList )
	{
		PacketBuffer* pThis=pEmptyList->pNext;
		free( pEmptyList );								// DO NOT use gos_Free
		pEmptyList=pThis;
	}
	while( pPackets )
	{
		PacketBuffer* pThis=pPackets->pNext;
		free( pPackets );								// DO NOT use gos_Free
		pPackets=pThis;
	}
	pLastPacket=0;
//
// Destory any protocol data
//
	ListOfNames* pTemp=SerialPortNames;
	while( pTemp )
	{
		ListOfNames* pThis=pTemp;
		pTemp=pTemp->pNext;
		gos_Free( pThis );
	}
	SerialPortNames=0;
	pTemp=ModemNames;
	while( pTemp )
	{
		ListOfNames* pThis=pTemp;
		pTemp=pTemp->pNext;
		gos_Free( pThis );
	}
	ModemNames=0;
//
// Destory any logging data
//
	PacketLogging* pTempLog=pPacketLog;
	while( pTempLog )
	{
		PacketLogging* pThisLog=pTempLog;
		pTempLog=pTempLog->pNext;
		gos_Free( pThisLog );
	}
	pPacketLog=0;
	pDebugPacketLog=0;
//
// Destroy global objects
//
	if( dplobby3 )
	{
		wRelease( dplobby3 );
		dplobby3=0;
	}
	if( dplay4 )
	{
		wRelease( dplay4 );
		dplay4=0;
	}
//
// Destroy any message buffer
//
	if( MessageBuffer )
	{
		free( MessageBuffer );				// DO NOT USE gos_Free!
		MessageBuffer=0;
		MessageBufferSize=0;
	}

//
// Disable all protocols
//
	hasTCP=0;
	hasIPX=0;
	hasModem=0;
	hasSerial=0;
	hasZoneMatch=0;
}












//
// Visible to App - resets networking API's
//
void __stdcall gos_InitializeNetworking()
{
#ifdef OUTBOUND_WINDOW
	OutboundWindow::CleanAll();
#endif

//
// If in a network game, shut it down
//
	gos_ShutdownNetwork();
	ServerBrowser::InitializeAll();
	ServerAdvertiser::InitializeAll();

//
// Init rest of networking code
//
	MyDirectPlayID=0;
	CurrentPlayers=0;
	IAmTheServer=0;
	InNetworkGame=0;
	Connected=NP_NOT;
	GameNames=0;
	EnumNames=0;

}








//
// Visible to App - closes down networking API's
//
void __stdcall gos_ShutdownNetwork()
{
#ifdef OUTBOUND_WINDOW
	OutboundWindow::CleanAll();
#endif

//
// Destroy all global network objects
//
	if( MyDirectPlayID )
	{
		// it may have already been destroyed
		AllowFail=1;
		wDestroyPlayer( dplay4, MyDirectPlayID );
		AllowFail=0;
		MyDirectPlayID=0;
	}
	if( MyIPAddress )
	{
		gos_Free( MyIPAddress );
		MyIPAddress=0;
	}
	if( InNetworkGame )
	{
		WaitTillQueueEmpty();
        if (g_nMR != 2) {
		    wClose( dplay4 );
        }
		InNetworkGame=0;
	}

	ServerBrowser::DisconnectAll();
    //if ( GamesShutdownApp )
    //{
    //    GamesShutdownApp();
    //}

	if( dplay4 )
	{
		wRelease( dplay4 );
		dplay4=0;
		wCoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay4A, (LPVOID*)&dplay4 );
	}
//
// Set no connection
//
	Connected=NP_NOT;
	InNetworkGame=0;
//
// Destroy any game/player enumeration data
//
	while( GameNames )
	{
		ListOfGames* pThis=GameNames;
		if( pThis->pPlayers )
		{
			ListOfNames* pTemp=pThis->pPlayers;
			while( pTemp )
			{
				ListOfNames* pThis=pTemp;			// Remove list of player names
				pTemp=pTemp->pNext;
				free( pThis );						// Cannot use gos_Free!
			}
		}
		GameNames=GameNames->pNext;
		free( pThis );								// Remove gane name and information
	}

//
// Destroy current session players
//
	while( CurrentPlayers )
	{
		ListOfNames* pTemp=CurrentPlayers;
		CurrentPlayers=CurrentPlayers->pNext;
		free( pTemp );
	}
//
// Destroy any pending messages
//
	Messages* pMess=pMessages;
	while( pMess )
	{
		Messages* pTemp=pMess;
		pMess=pMess->pNext;
		gos_Free( pTemp );
	}
	pMessages=0;
//
// Is there a 'last message'?
//
	if( pLastMessage )
	{
		gos_Free( pLastMessage );
	}
	pLastMessage=0;
}








//
//
// Returns information about the networking status
//
//
void* __stdcall gos_NetInformation( gosNetInfo Info, DWORD Parameter, char* Name )
{
    // jcem - mission replay
    if (g_pfn_NETINFORMATION) {
        return (*g_pfn_NETINFORMATION)(Info, Parameter, Name);
    }
    // jcem - mission replay

	static char Buffer[256];
	void* result=0;

	switch( Info )
	{
//
// Returns TRUE if in a networked game
//
		case gos_Networking:
		{
			result=(void*)(InNetworkGame ? 1 : 0);
			
			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(1,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,1,Log_NetInfo);
			}
			return result;
		}
//
// Returns TRUE if you created the game (FALSE=Joined)
//
		case gos_GameSecure:
		{
			result=(void*)(CurrentSession.dwFlags&DPSESSION_SECURESERVER);
			
			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(1,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,1,Log_NetInfo);
			}
			return result;
		}
//
// Returns NULL or pointer to password string
//
		case gos_GamePassword:
		{
			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
				{
					DWORD Len=ReadBitStream(16,Log_DataBlock);
					ReadLogData( (BYTE*)&PasswordBuffer, Len );
				}
			}
			else				// Log file recording
			{
				if( !InDebugger )
				{
					DWORD Len=strlen(PasswordBuffer)+1;
					WriteLogData( (BYTE*)&PasswordBuffer, Len);
				}
			}

			if( PasswordBuffer[0] )
				result=PasswordBuffer;

			return result;
		}
//
// Returns TRUE if the host created the game as a secure game
//
		case gos_AmITheServer:
		{
			result=(void*)IAmTheServer;

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(1,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,1,Log_NetInfo);
			}
			
			return result;
		}
//
// Returns the number of player in the current game
//
		case gos_NumberOfPlayers:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				DWORD NumPlayers=0;
				ListOfNames* pThis=CurrentPlayers;
				while( pThis )
				{
					NumPlayers++;
					pThis=pThis->pNext;
				}
				result=(void*)NumPlayers;			
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(8,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,8,Log_NetInfo);
			}

			return result;
		}


	
//
// Returns a char* for the ip address
//
		case gos_MyIPAddress:
		{
			result=(void*)"";
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				result=(void*)MyIPAddress;	
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
				{
					DWORD Len=ReadBitStream(16,Log_DataBlock);
					ReadLogData( (BYTE*)&Buffer, Len );

					// handle the NULL case
					if (Len == 1)
					{
						result=NULL;
					}
					else
					{
						result=Buffer;
					}
				}
			}
			else				// Log file recording
			{
				if( !InDebugger )
				{
					// if it is NULL just write one byte and we'll fix it on the way out.
					if (MyIPAddress)
					{
						DWORD Len=strlen((char*)result)+1;
						WriteLogData( (BYTE*)result, Len);
					}
					else
					{
						DWORD Len=1;
						BYTE data = 0;
						WriteLogData(&data, Len);
					}
				}
			}

			return result;
		}

//
// Returns a DWORD for this machines player NUMBER
//
		case gos_MyID:
		{
			result=(void*)MyDirectPlayID;
			
			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}
			
			return result;
		}
//
// Returns a DWORD for the servers player NUMBER
//
		case gos_ServerID:
		{
			if( Environment.NetworkGame && Connected )
				result=(void*)DPID_SERVERPLAYER;

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns a DWORD for ALL players (broadcast)
//
		case gos_AllID:
		{
			if( Environment.NetworkGame && Connected )
				result=(void*)DPID_ALLPLAYERS;

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns a player ID for the player NUMBER passed in the parameter
//
		case gos_PlayerID:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				DWORD NumPlayers=0;
				ListOfNames* pThis=CurrentPlayers;
				while( pThis )
				{
					if( NumPlayers==Parameter )
					{
						result=(void*)pThis->Data;
						break;
					}
					NumPlayers++;
					pThis=pThis->pNext;
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns a NUMBER for the player ID passed in the parameter (or 0xffffffff if not found)
//
		case gos_PlayerNumber:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				result=(void*)0xffffffff;
				DWORD NumPlayers=0;
				ListOfNames* pThis=CurrentPlayers;
				while( pThis )
				{
					if( pThis->Data==Parameter )
					{
						result=(void*)NumPlayers;
						break;
					}
					NumPlayers++;
					pThis=pThis->pNext;
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns a char* for the player NUMBER passed in the parameter
//
		case gos_PlayerName:
		{
			result=(void*)"";
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				DWORD NumPlayers=0;
				ListOfNames* pThis=CurrentPlayers;
				while( pThis )
				{
					if( NumPlayers==Parameter )
					{
						result=(void*)pThis->Name;
					}
					NumPlayers++;
					pThis=pThis->pNext;
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
				{
					DWORD Len=ReadBitStream(16,Log_DataBlock);
					ReadLogData( (BYTE*)&Buffer, Len );
					result=Buffer;
				}
			}
			else				// Log file recording
			{
				if( !InDebugger )
				{
					DWORD Len=strlen((char*)result)+1;
					WriteLogData( (BYTE*)result, Len);
				}
			}

			return result;
		}
//
// Returns if the game is currently locked
//
		case gos_LockedStatus:
		{
			result=(void*)(CurrentSession.dwFlags&DPSESSION_JOINDISABLED ? 1 : 0);

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(1,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,1,Log_NetInfo);
			}

			return result;
		}
//
// Returns number of packets sent in the past frame to the player NUMBER passed
//
		case gos_PacketsSentLastFrame:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				PacketLogging* pThis=pDebugPacketLog;
				DWORD PlayerID=(DWORD)gos_NetInformation(gos_PlayerID);
				DWORD Counter=0;
				while( pThis )
				{
					if( CurrentFrameNumber-1==pThis->FrameNumber && PlayerID==pThis->ToID )
						Counter++;
					pThis=pThis->pNext;
				}
				result=(void*)Counter;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns number of packets recieved in the past Frame from the player NUMBER passed
//
		case gos_PacketsFromLastFrame:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				PacketLogging* pThis=pDebugPacketLog;
				DWORD PlayerID=(DWORD)gos_NetInformation(gos_PlayerID);
				DWORD Counter=0;
				while( pThis )
				{
					if( CurrentFrameNumber-1==pThis->FrameNumber && PlayerID==pThis->FromID )
						Counter++;
					pThis=pThis->pNext;
				}
				result=(void*)Counter;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns number of bytes sent in the past Frame to the player NUMBER passed
//
		case gos_BytesSentLastFrame:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				PacketLogging* pThis=pDebugPacketLog;
				DWORD PlayerID=(DWORD)gos_NetInformation(gos_PlayerID);
				DWORD Counter=0;
				while( pThis )
				{
					if( CurrentFrameNumber-1==pThis->FrameNumber && PlayerID==pThis->ToID )
						Counter+=pThis->Size;
					pThis=pThis->pNext;
				}
				result=(void*)Counter;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns number of bytes recieved in the past Frame from the player NUMBER passed
//
		case gos_BytesFromLastFrame:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				PacketLogging* pThis=pDebugPacketLog;
				DWORD PlayerID=(DWORD)gos_NetInformation(gos_PlayerID);
				DWORD Counter=0;
				while( pThis )
				{
					if( CurrentFrameNumber-1==pThis->FrameNumber && PlayerID==pThis->FromID )
						Counter+=pThis->Size;
					pThis=pThis->pNext;
				}
				result=(void*)Counter;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns total number of bytes sent in the past Frame to the player NUMBER passed (including packet overhead)
//
		case gos_BandwithSentLastFrame:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				PacketLogging* pThis=pDebugPacketLog;
				DWORD PlayerID=(DWORD)gos_NetInformation(gos_PlayerID);
				DWORD Counter=0;
				while( pThis )
				{
					if( CurrentFrameNumber-1==pThis->FrameNumber && PlayerID==pThis->ToID )
						Counter+=pThis->Size+PACKET_OVERHEAD;
					pThis=pThis->pNext;
				}
				result=(void*)Counter;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns total number of bytes recieved in the past Frame from the player NUMBER passed (including packet overhead)
//
		case gos_BandwithFromLastFrame:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				PacketLogging* pThis=pDebugPacketLog;
				DWORD PlayerID=(DWORD)gos_NetInformation(gos_PlayerID);
				DWORD Counter=0;
				while( pThis )
				{
					if( CurrentFrameNumber-1==pThis->FrameNumber && PlayerID==pThis->FromID )
						Counter+=pThis->Size+PACKET_OVERHEAD;
					pThis=pThis->pNext;
				}
				result=(void*)Counter;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(32,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,32,Log_NetInfo);
			}

			return result;
		}
//
// Returns number of packets lost from the player NUMBER passed (based on packet numbers and retries)
//
		case gos_PacketsLostLastFrame:
		{
			return (void*)0;
		}
//
// Returns the average ping time for any guaranteed packets sent in the last frame to the player NUMBER
//
		case gos_LatencyLastFrame:
		{
			return (void*)0;
		}
//
// Returns the number of packets still in the send queue
//
		case gos_GetMessageSendQueue:
		{
			if( InNetworkGame && !(1==LogMode && !InDebugger) )
			{
				DWORD Messages;
				AllowFail=1;
				HRESULT hr=wGetMessageQueue( dplay4, 0, Parameter, DPMESSAGEQUEUE_SEND, &Messages, NULL );
				AllowFail=0;
				if( SUCCEEDED(hr) )
					result=(void*)Messages;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(8,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,8,Log_NetInfo);
			}

			return result;
		}
//
// Returns the number of Games found on network (or 1 if in a network game)
//
		case gos_NumberOfGames:
		{
			if( InNetworkGame )
			{
				result=(void*)1;
			}
			else
			{
				DWORD NumberGames=0;
				ListOfGames* pThis=GameNames;
				while( pThis )
				{
					NumberGames++;
					pThis=pThis->pNext;
				}
				result=(void*)NumberGames;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(8,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,8,Log_NetInfo);
			}

			return result;
		}
//
// Returns the name of the Game with the parameter passed   (If in a game, returns this games name)
//
		case gos_NameOfGame:
		{
			result=(void*)"";
			
			if( InNetworkGame )
			{
				if( 0==Parameter )
					result=(void*)CurrentSession.lpszSessionNameA;
			}
			else
			{
				DWORD NumberGames=0;
				ListOfGames* pThis=GameNames;
				while( pThis )
				{
					if( NumberGames==Parameter )
					{
						result=(void*)pThis->Name;
						break;
					}
					NumberGames++;
					pThis=pThis->pNext;
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
				{
					DWORD Len=ReadBitStream(16,Log_DataBlock);
					ReadLogData( (BYTE*)&Buffer, Len );
					result=Buffer;
				}
			}
			else				// Log file recording
			{
				if( !InDebugger )
				{
					DWORD Len=strlen((char*)result)+1;
					WriteLogData( (BYTE*)result, Len);
				}
			}

			return result;
		}
//
// Returns true if the game name passed is passworded, Name=Game Name
//
		case gos_IsGamePassworded:
		{
			if( InNetworkGame )
			{
				result=(void*)((CurrentSession.dwFlags&DPSESSION_PASSWORDREQUIRED) ? 1 : 0);
			}
			else
			{
				if( Name )
				{
					ListOfGames* pThis=GameNames;
					while( pThis )
					{
						if( 0==stricmp(pThis->Name, Name) )
						{
							result=(void*)((pThis->SessionDescriptor.dwFlags&DPSESSION_PASSWORDREQUIRED) ? 1 : 0);
							break;
						}
						pThis=pThis->pNext;
					}
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(1,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,1,Log_NetInfo);
			}

			return result;
		}
//
// Returns true if the game name passed is secure, Name=Game Name
//
		case gos_IsGameSecure:
		{
			if( InNetworkGame )
			{
				result=(void*)((CurrentSession.dwFlags&DPSESSION_SECURESERVER) ? 1 : 0);
			}
			else
			{
				if( Name )
				{
					ListOfGames* pThis=GameNames;
					while( pThis )
					{
						if( 0==stricmp(pThis->Name, Name) )
						{
							result=(void*)((pThis->SessionDescriptor.dwFlags&DPSESSION_SECURESERVER) ? 1 : 0);
							break;
						}
						pThis=pThis->pNext;
					}
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(1,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,1,Log_NetInfo);
			}

			return result;
		}
//
// Returns true if the game name passed is locked so no more players can enter, Name=Game Name
//
		case gos_IsGameLocked:
		{
			if( InNetworkGame )
			{
				result=(void*)((CurrentSession.dwFlags&DPSESSION_JOINDISABLED) ? 1 : 0);
			}
			else
			{
				if( Name )
				{
					ListOfGames* pThis=GameNames;
					while( pThis )
					{
						if( 0==stricmp(pThis->Name, Name) )
						{
							result=(void*)((pThis->SessionDescriptor.dwFlags&DPSESSION_JOINDISABLED) ? 1 : 0);
							break;
						}
						pThis=pThis->pNext;
					}
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(1,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,1,Log_NetInfo);
			}

			return result;
		}
//
// Returns maximum number of players in a game, Name=Game Name
//
		case gos_MaxPlayersInGame:
		{
			if( InNetworkGame )
			{
				result=(void*)CurrentSession.dwMaxPlayers;
			}
			else
			{
				if( Name )
				{
					ListOfGames* pThis=GameNames;
					while( pThis )
					{
						if( 0==stricmp(pThis->Name, Name) )
						{
							result=(void*)pThis->SessionDescriptor.dwMaxPlayers;
							break;
						}
						pThis=pThis->pNext;
					}
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(8,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,8,Log_NetInfo);
			}

			return result;
		}
//
// Returns a pointer to 16 bytes of information from the game, Name=Game Name  (Games set this in Environment.NetGameInfo)
//
		case gos_InformationAboutGame:
		{
			if( InNetworkGame )
			{
				result=(void*)&CurrentSession.dwUser1;
			}
			else
			{
				if( Name )
				{
					ListOfGames* pThis=GameNames;
					while( pThis )
					{
						if( 0==stricmp(pThis->Name, Name) )
						{
							result=(void*)&pThis->SessionDescriptor.dwUser1;
							break;
						}
						pThis=pThis->pNext;
					}
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
				{
					ReadBitStream(16,Log_DataBlock);				// Ignore size (always 16)
					ReadLogData( (BYTE*)&Buffer, 16 );
					result=Buffer;
				}
			}
			else				// Log file recording
			{
				if( !InDebugger )
				{
					WriteLogData( (BYTE*)result, 16);
				}
			}

			return result;
		}
//
// Returns number of players in a game, Name=Game Name   (If in a game, returns player in this game)
//
		case gos_NumberOfPlayersInGame:
		{
			if( InNetworkGame )
			{
				result=gos_NetInformation(gos_NumberOfPlayers);
			}
			else
			{
				if( Name )
				{
					ListOfGames* pThis=GameNames;
					while( pThis )
					{
						if( 0==stricmp(pThis->Name, Name) )
						{
							DWORD Players=0;
							ListOfNames* GamePlayers=pThis->pPlayers;
							while( GamePlayers )
							{
								Players++;
								GamePlayers=GamePlayers->pNext;
							}
							result=(void*)Players;
						}
						pThis=pThis->pNext;
					}
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(8,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,8,Log_NetInfo);
			}

			return result;
		}
//
// Return players name - Parameter=Player #, Name=Game Name.   (If in a game, returns players name in this game)
//
		case gos_NameOfPlayersInGame:
		{
			if( InNetworkGame )
			{
				result=gos_NetInformation(gos_PlayerName,Parameter);
			}
			else
			{
				if( Name )
				{
					ListOfGames* pThis=GameNames;
					while( pThis )
					{
						if( 0==stricmp(pThis->Name, Name) )
						{
							DWORD Players=0;
							ListOfNames* GamePlayers=pThis->pPlayers;
							result=(void*)Players;
							while( GamePlayers )
							{
								if( Players==Parameter )
								{
									result=(void*)GamePlayers->Name;
									break;
								}
								Players++;
								GamePlayers=GamePlayers->pNext;
							}
							break;
						}
						pThis=pThis->pNext;
					}
				}
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
				{
					DWORD Len=ReadBitStream(16,Log_DataBlock);
					ReadLogData( (BYTE*)&Buffer, Len );
					result=Buffer;
				}
			}
			else				// Log file recording
			{
				if( !InDebugger )
				{
					DWORD Len=strlen((char*)result)+1;
					WriteLogData( (BYTE*)result, Len);
				}
			}

			return result;
		}
//
// Returns the number of modems installed
//
		case gos_NumberOfModems:
		{
			ListOfNames* pThis=ModemNames;
			DWORD ModemNumber=0;
			while( pThis )
			{
				pThis=pThis->pNext;
				ModemNumber++;
			}
			result=(void*)ModemNumber;

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(8,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,8,Log_NetInfo);
			}

			return result;
		}
//
// Returns the name of the modem number passed
//
		case gos_NameOfModem:
		{
			result=(void*)"";
			ListOfNames* pThis=ModemNames;
			DWORD ModemNumber=0;
			while( pThis )
			{
				if( Parameter==ModemNumber )
				{
					result=(void*)pThis->Name;
					break;
				}
				pThis=pThis->pNext;
				ModemNumber++;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
				{
					DWORD Len=ReadBitStream(16,Log_DataBlock);
					ReadLogData( (BYTE*)&Buffer, Len );
					result=Buffer;
				}
			}
			else				// Log file recording
			{
				if( !InDebugger )
				{
					DWORD Len=strlen((char*)result)+1;
					WriteLogData( (BYTE*)result, Len);
				}
			}

			return result;
		}
//
// Returns the number of serial ports installed
//
		case gos_NumberOfSerialPorts:
		{
			ListOfNames* pThis=SerialPortNames;
			DWORD PortNumber=0;
			while( pThis )
			{
				pThis=pThis->pNext;
				PortNumber++;
			}
			result=(void*)PortNumber;

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
					result=(void*)ReadBitStream(8,Log_NetInfo);
			}
			else				// Log file recording
			{
				if( !InDebugger )
					WriteBitStream((DWORD)result,8,Log_NetInfo);
			}

			return result;
		}
//
// Returns the name of the serial port number passed
//
		case gos_NameOfSerialPort:
		{
			result=(void*)"";
			ListOfNames* pThis=SerialPortNames;
			DWORD PortNumber=0;
			while( pThis )
			{
				if( Parameter==PortNumber )
				{
					result=(void*)pThis->Name;
					break;
				}
				pThis=pThis->pNext;
				PortNumber++;
			}

			if( 1==LogMode )	// Log file playing back?
			{
				if( !InDebugger )
				{
					DWORD Len=ReadBitStream(16,Log_DataBlock);
					ReadLogData( (BYTE*)&Buffer, Len );
					result=Buffer;
				}
			}
			else				// Log file recording
			{
				if( !InDebugger )
				{
					DWORD Len=strlen((char*)result)+1;
					WriteLogData( (BYTE*)result, Len);
				}
			}

			return result;
		}
/* To be added once UI queries required decided
//
// Returns the status of the ZoneTech matchmaking connect attempt for game server
// 0 is success, 1 not yet tried, 2 pending, 3 failed, 4 lost connection
//
		case gos_GameRegistrationStatus:
		{
			DWORD status = 1;
			
			IZoneAsync::STATUS actual =IZoneAsync::STATUS_FAILED;
			
			if (g_pZMAsync)
			{
				g_pZMAsync->GetStatus(&actual);
				switch (actual)
				{
				case IZoneAsync::STATUS_COMPLETED:
					status =0;	
					break;
				case IZoneAsync::STATUS_STARTING:
					status = 2;
					break;
				default:
					status = 3;
					break;
				}
			}
			
			return (void*) status;
			
		}
//
// Returns the status of the ZoneTech matchmaking connect attempt for game joiner
// 0 is success, 1 not yet tried, 2 pending, 3 failed, 4 lost connection
//
		case gos_GameBrowseStatus:
		{
			DWORD status = 1;
			
			IZoneAsync::STATUS actual =IZoneAsync::STATUS_FAILED;
			
			if (g_pZMAsync)
			{
				g_pZMAsync->GetStatus(&actual);
				switch (actual)
				{
				case IZoneAsync::STATUS_COMPLETED:
					status =0;	
					break;
				case IZoneAsync::STATUS_STARTING:
					status = 2;
					break;
				default:
					status = 3;
					break;
				}
			}
			
			return (void*) status;
			
		}
//
//
//
*/
#ifdef OUTBOUND_WINDOW
		case gos_GetNumOutboudWindows:
			OutboundWindow * pOW;
			pOW = OutboundWindow::m_pHead;
			int i;
			i=0;
			while (pOW)
			{
				i++;
				pOW = pOW->m_pNext;
			}
			return (void *)i;
			break;

		case gos_GetOutboundWindowSize:
			pOW = OutboundWindow::m_pHead;
			while (pOW)
			{
				if (Parameter==0)
					break;
				Parameter--;
				pOW = pOW->m_pNext;
			}
			if (Parameter != 0)
				return (void *)-1;
			return (void *)(pOW->m_NextPacketNumberToSend - pOW->m_LastPacketTheyReceived);
			break;

		case gos_GetOutboundWindowSizeDpid:
			pOW=OutboundWindow::Find(Parameter);
			if (pOW)
			{
				return (void *)(pOW->m_NextPacketNumberToSend - pOW->m_LastPacketTheyReceived);
			}
			
			return (void *)-10000;
		
			break;

		case gos_GetOB_NxtPkt2Send:
			pOW = OutboundWindow::m_pHead;
			while (pOW)
			{
				if (Parameter==0)
					break;
				Parameter--;
				pOW = pOW->m_pNext;
			}
			if (Parameter != 0)
				return (void *)-1;
			return (void *)(pOW->m_NextPacketNumberToSend);
			break;

		case gos_GetOB_LstPktRcvd:
			pOW = OutboundWindow::m_pHead;
			while (pOW)
			{
				if (Parameter==0)
					break;
				Parameter--;
				pOW = pOW->m_pNext;
			}
			if (Parameter != 0)
				return (void *)-1;
			return (void *)(pOW->m_LastPacketWeReceived);
			break;

		case gos_GetOB_LstPktTheyRcvd:
			pOW = OutboundWindow::m_pHead;
			while (pOW)
			{
				if (Parameter==0)
					break;
				Parameter--;
				pOW = pOW->m_pNext;
			}
			if (Parameter != 0)
				return (void *)-1;
			return (void *)(pOW->m_LastPacketTheyReceived);
			break;

#else
		case gos_GetNumOutboudWindows:
		case gos_GetOutboundWindowSize:
		case gos_GetOB_NxtPkt2Send:
		case gos_GetOB_LstPktRcvd:
		case gos_GetOB_LstPktTheyRcvd:
			return 0;
			break;
#endif

		default:
			STOP(( "Unknown info %d",Info ));
	}
	return 0;
}







//
// If this computer is the server these commands can be used
//
void __stdcall gos_NetServerCommands( gos_NetCommands Command, DWORD Data1 )
{
    // jcem - mission replay
    if (g_pfn_NETSERVERCOMMANDS) {
        (*g_pfn_NETSERVERCOMMANDS)(Command, Data1);
        return;
    }
    // jcem - mission replay

	switch( Command )
	{
//
// Remove the player with PlayerID == Data1 from the game
//
		case gos_Command_KillPlayer:
		{
			if( 1==LogMode )	// Log file playing back?
				return;
			
			gosASSERT( InNetworkGame && IAmTheServer && Data1!=MyDirectPlayID );			// Not allowed to delete yourself like this

			AllowFail=1;
			wDestroyPlayer( dplay4, Data1 );				// Allow any ID - so players already dead / dieing don't cause an error
			AllowFail=0;
			return;
		}
//
// Datat1=1 to lock the game, 0=to unlock the game - no more players can join when a game is locked
//
		case gos_Command_LockGame:
		{
			if( 1==LogMode )	// Log file playing back?
				return;

			gosASSERT( InNetworkGame && IAmTheServer );

			DWORD				dwSize;
			LPDPSESSIONDESC2	pdpsd;

			AllowFail=1;
			wGetSessionDesc( dplay4, NULL, &dwSize);
			pdpsd = (LPDPSESSIONDESC2)gos_Malloc(dwSize);
			wGetSessionDesc( dplay4, (void *)pdpsd, &dwSize);
			AllowFail=0;
			
			pdpsd->dwFlags&=~(DPSESSION_JOINDISABLED|DPSESSION_NEWPLAYERSDISABLED);
			
			if( Data1 )
				pdpsd->dwFlags|=DPSESSION_JOINDISABLED|DPSESSION_NEWPLAYERSDISABLED;

			AllowFail=1;
			wSetSessionDesc( dplay4, pdpsd, 0 );
			AllowFail=0;

			gos_Free(pdpsd);
			break;
		}
//
// Updates the 'game data' from Environment.NetGameInfo - so players enumerating games can see the changes
//
		case gos_Command_UpdateGameData:
		{
			if( 1==LogMode )	// Log file playing back?
				return;

			gosASSERT( InNetworkGame && IAmTheServer );
			
			memcpy( &CurrentSession.dwUser1, Environment.NetGameInfo, 16 );

			AllowFail=1;
			wSetSessionDesc( dplay4, &CurrentSession, 0 );
			AllowFail=0;
			break;
		}
//
// Updates the number of maximum players allowed in a game from Environment.NetworkMaxPlayers
//
		case gos_Commend_UpdateMaxPlayers:
		{
			if( 1==LogMode )	// Log file playing back?
				return;

			gosASSERT( InNetworkGame && IAmTheServer );
			
			CurrentSession.dwMaxPlayers=Environment.NetworkMaxPlayers;

			AllowFail=1;
			wSetSessionDesc( dplay4, &CurrentSession, 0 );
			AllowFail=0;
			break;
		}

		default:
			STOP(( "Bad command" ));
	}
}



extern LPDIRECTPLAY4A __stdcall GetIPXDPObject(void);
int InitializeProtocol(int iProtocol)
{
	DPCOMPOUNDADDRESSELEMENT comAdd[3];
	DWORD Size;
	void* p_address;
	DWORD result;

	if (iProtocol == PROTOCOL_DEFAULT)
	{
		iProtocol = ServerBrowser::IPX ? PROTOCOL_IPX : PROTOCOL_TCPIP;
	}

	////if (iProtocol == PROTOCOL_IPX)
	////{
	////	wRelease(dplay4);
	////	dplay4 = GetIPXDPObject();
	////	dplay4->AddRef();
	////
	////	return 0;
	////}

	int port = GetDirectPlayPort();

	comAdd[0].guidDataType	= DPAID_ServiceProvider;
	comAdd[0].dwDataSize	= sizeof(GUID);
	if (iProtocol == PROTOCOL_IPX)
	{
		comAdd[0].lpData	= (void*)&DPSPGUID_IPX;
		port				= 0;
	}
	else
		comAdd[0].lpData	= (void*)&DPSPGUID_TCPIP;
	comAdd[1].guidDataType	= DPAID_INet;								// Create a compund address
	comAdd[1].dwDataSize	= 1;
	comAdd[1].lpData		= "";
	comAdd[2].guidDataType	= DPAID_INetPort;
	comAdd[2].dwDataSize	= sizeof(port);
	comAdd[2].lpData		= (void *)&port;

	int num_elements;
	if (port)
		num_elements = 3;
	else
		num_elements = 2;

	AllowFail=1;
	Size=0;
	wCreateCompoundAddress( dplobby3, comAdd, num_elements, NULL, &Size );
	p_address = (void*)gos_Malloc(Size,DefaultHeap);
	wCreateCompoundAddress( dplobby3, comAdd, num_elements, p_address, &Size );
	result = wInitializeConnection( dplay4, p_address, 0 );		// Try to initialize the connection
	AllowFail=0;
	if (FAILED(result))
	{
		gos_Free(p_address);

		//
		// maybe we should try IPX?
		//
		if (iProtocol != PROTOCOL_IPX)
		{
			return InitializeProtocol(PROTOCOL_IPX);
		}
	}
	//
	// force the directplay protocol for non-tcp/ip protocols
	if (iProtocol != PROTOCOL_TCPIP)
	{
		Environment.DirectPlayProtocol = 1;
		CurrentSession.dwFlags |= DPSESSION_DIRECTPLAYPROTOCOL;
	}
	gos_Free(p_address);
	GameOSFPU()
	AllowFail=0;

	if( SUCCEEDED(result) )
		return 0;

	return 1;
}



//
// 0=OK
// 1=Bad game name
// 2=Bad player name
// 3=Failed
// 4=Waiting for answer
// 5=Bad/Reserved character used
//
int InternalCreateGame( char* GameName, char* PlayerName, int MaxPlayers, char* Password, bool Secure, DWORD dwFlags )
{
#ifdef OUTBOUND_WINDOW
	OutboundWindow::CleanAll();
#endif

//
// Make sure the game name is not invalid
//
	if( 0==GameName)
		return 1;
//
// Skip any leading spaces (ignore names that are all spaces)
//
	while( *GameName==32 )
		GameName++;
	if( Password )
	{
		while( *Password==32 )
			Password++;
	}
//
// Make sure name is not blank
//
	if( *GameName==0 )
		return 1;
//
// Make sure player name is not invalid
//
	if( 0==PlayerName)
		return 2;
//
// Skip any leading spaces (ignore names that are all spaces)
//
	while( *PlayerName==32 )
		PlayerName++;
//
// Make sure name is not blank
//
	if( *PlayerName==0 )
		return 2;
//
// Make copies of names
//
	strcpy( GameNameBuffer, GameName );
	if( Password )
		strcpy( PasswordBuffer, Password );
	else
		PasswordBuffer[0]=0;
//
// Fill in description of session
//
	memset( &CurrentSession, 0, sizeof(CurrentSession) );
	CurrentSession.dwSize=sizeof(CurrentSession);
#ifdef USE_DPKEEPALIVE
	CurrentSession.dwFlags=DPSESSION_KEEPALIVE | (Password ? DPSESSION_PASSWORDREQUIRED : 0) | (Secure ? DPSESSION_SECURESERVER  : (Environment.NetServerMigration ? DPSESSION_MIGRATEHOST : 0)) | (Connected==NP_TCP ? 0 : DPSESSION_DIRECTPLAYPROTOCOL) | (Connected==NP_ZONEMATCH ? 0 : DPSESSION_DIRECTPLAYPROTOCOL) ;
#else
	CurrentSession.dwFlags=                      (Password ? DPSESSION_PASSWORDREQUIRED : 0) | (Secure ? DPSESSION_SECURESERVER  : (Environment.NetServerMigration ? DPSESSION_MIGRATEHOST : 0)) | (Connected==NP_TCP ? 0 : DPSESSION_DIRECTPLAYPROTOCOL) | (Connected==NP_ZONEMATCH ? 0 : DPSESSION_DIRECTPLAYPROTOCOL) ;
#endif

	if (Environment.DirectPlayProtocol)
		CurrentSession.dwFlags |= DPSESSION_DIRECTPLAYPROTOCOL;
	else
		CurrentSession.dwFlags &= ~DPSESSION_DIRECTPLAYPROTOCOL;

#ifdef USE_CLIENTSERVER
	CurrentSession.dwFlags |= DPSESSION_CLIENTSERVER;
	CurrentSession.dwFlags &= ~DPSESSION_MIGRATEHOST;
#endif

	memcpy( &CurrentSession.guidApplication, &Environment.NetworkGUID, sizeof(GUID) );
	CurrentSession.dwMaxPlayers=(Connected==NP_SERIAL || Connected==NP_MODEM) ? 2 : MaxPlayers;
	CurrentSession.dwCurrentPlayers=0;
	memcpy( &CurrentSession.dwUser1, Environment.NetGameInfo, 16 );
	CurrentSession.lpszSessionNameA=GameNameBuffer;
	CurrentSession.lpszPasswordA=PasswordBuffer;
	CurrentSession.dwUser1 = dwFlags;

	if (dwFlags & NETFLAGS_DEDICATED_FLAG)
		CurrentSession.dwMaxPlayers++;

//
// Make sure no player enumeration is still occuring
//
	EnterCriticalSection( &EnumCriticalSection );
//
// Create the session
//
	AllowFail=1;
	HRESULT hr;
	if( Secure )
	{
		if (InitializeProtocol())
			hr=-1;
		else
			hr=wSecureOpen( dplay4, &CurrentSession, DPOPEN_CREATE | DPOPEN_RETURNSTATUS, 0 ,0 );
	}
	else
	{
		if (InitializeProtocol())
			hr=-1;
		else
			hr=wOpen( dplay4, &CurrentSession, DPOPEN_CREATE | DPOPEN_RETURNSTATUS );
	}
	AllowFail=0;
//
// Now enum thread can start again
//
	LeaveCriticalSection( &EnumCriticalSection );
//
// Any errors, just return failed
//
	SPEW(( GROUP_DIRECTPLAY, "Open Result- (0x%x - %s)", hr, ErrorNumberToMessage(hr) ));
	if( DPERR_CONNECTING==hr )
		return 4;
	if( FAILED(hr) )
		return 3;
//
// Get the current session description,  not all session information is filled in during open
//
	DWORD Size=0;;
	DPSESSIONDESC2* pDesc=0;
	AllowFail=1;
	wGetSessionDesc( dplay4, pDesc, &Size );	
	AllowFail=0;
	if( Size==0 )
		return 3;
	pDesc=(DPSESSIONDESC2*)gos_Malloc(Size,DefaultHeap);
	wGetSessionDesc( dplay4, pDesc, &Size );	
	memcpy( &CurrentSession.guidInstance, &pDesc->guidInstance, sizeof(GUID) );
	gos_Free( pDesc );
//
// Create the server player
//
	DPID ServerID;
	DPNAME ServerName;

	memset( &ServerName, 0, sizeof(ServerName) );
	ServerName.dwSize=sizeof(ServerName);
	ServerName.lpszShortNameA=PlayerName;

	wCreatePlayer( dplay4, &ServerID, &ServerName, NetworkEvent, 0, 0, DPPLAYER_SERVERPLAYER );
//
// Get TCPIP address if possible
//
	if( true )
	{
		MyIPAddress=0;
		DWORD TCPSize=0;
		AllowFail=1;
		wGetPlayerAddress( dplay4, ServerID, NULL, &TCPSize );
		if (TCPSize)
		{
			AllowFail=0;
			void* address = gos_Malloc(TCPSize,DefaultHeap);
			AllowFail=1;
			HRESULT hr=wGetPlayerAddress( dplay4, ServerID, address, &TCPSize );
			AllowFail=0;
			if( DD_OK==hr )
			{
				AllowFail=1;
				wEnumAddress( dplobby3, TCPIPCallback, address, TCPSize, 0 );
				AllowFail=0;
			}
			gos_Free( address );
		}
	}
//
// Stop further enumeration of games and players because now we are a server
//
	MyDirectPlayID=ServerID;
	InNetworkGame=1;
	IAmTheServer=1;
	GetCurrentPlayers();

	//
	// once we create a game, we no longer need to be browsing...
	//
	ServerBrowser::ReleaseAll();

//
// Do any additional work required by games enumeration driver
//
	if (!ServerAdvertiser::CreateGameAll(GameName, PlayerName, MaxPlayers, Password, CurrentSession.guidInstance, dwFlags))
	{
		SPEW(("mlyons", "Could not advertise on at least one service"));
	}
    // jcem - mission replay
    if (g_pfn_ONGAMEOPEN) {
        (*g_pfn_ONGAMEOPEN)(GameNameBuffer, MyDirectPlayID, PlayerName, PasswordBuffer);
    }
    // jcem - mission replay

	return 0;
}

void gos_ReConnectGame(DWORD dwFlags)
{
	ListOfNames* pThis=CurrentPlayers;

	ServerAdvertiser::CreateGameAll(GameNameBuffer, pThis->Name, CurrentSession.dwMaxPlayers, PasswordBuffer, CurrentSession.guidInstance, dwFlags);
}


int __stdcall gos_CreateGame( char* GameName, char* PlayerName, int MaxPlayers, char* Password, bool Secure ,char * UserPassword, DWORD dwFlags)
{
	int result=0;
	
	if( 1==LogMode )	// Log file playing back?
	{
		if( !InDebugger )
		{
			return (int)ReadBitStream(8,Log_NetInfo);
		}
	}
	else				// Log file recording
	{
		if (UserPassword)
			NGStatsSetPlayerId(PlayerName, UserPassword);
		else
			NGStatsPlayerId=GUID_NULL;

		result=InternalCreateGame( GameName, PlayerName, MaxPlayers, Password, Secure, dwFlags );

		if( !InDebugger )
		{
			WriteBitStream((DWORD)result,8,Log_NetInfo);
		}
	}

	return result;
}








//
// 0=OK
// 1=Bad game name (or waiting for game on modem/serial)
// 2=Bad player name
// 3=Failed to connect
// 4=No dial tone
// 5=Invalid password
// 6=Too many players in the game
//
int InternalJoinGame( char* GameName, char* PlayerName, char* Password )
{
#ifdef OUTBOUND_WINDOW
	OutboundWindow::CleanAll();
#endif

//
// On a serial / modem game, must wait for the game name through enumeration
//
	if( Connected==NP_SERIAL || Connected==NP_MODEM )
	{
		if( (DWORD)gos_NetInformation( gos_NumberOfGames )==0 )
		{
			if( EnumResult==DPERR_CONNECTING || EnumResult==DP_OK || EnumResult==0 )
				return 1;
			else
			{
				EnumResult=0;
				if( EnumResult==DPERR_NOCONNECTION )
				{
					return 4;				// No dial tone
				}
				return 3;					// Failed to connect
			}
		}
//
// Wait until stopped any enumeration in the background
//
		EnterCriticalSection( &EnumCriticalSection );
		LeaveCriticalSection( &EnumCriticalSection );
		if( 0==GameName )
			GameName=(char*)gos_Malloc( 128, DefaultHeap );
		strcpy( GameName, (char*)gos_NetInformation( gos_NameOfGame, 0 ));
	}

    // mdm - this function, found in Games_GUN.cpp, pushes the game list
    // stored in GUNGameList to the gamename and playername lists that
    // gameos uses from the old days.
    PushGameList();

//
// Make sure the game name is not invalid
//
	if( 0==GameName)
		return 1;
//
// Skip any leading spaces (ignore names that are all spaces)
//
	while( *GameName==32 )
		GameName++;
	if( Password )
	{
		while( *Password==32 )
			Password++;
	}
//
// Make sure name is not blank
//
	if( *GameName==0 )
		return 1;
//
// Make sure player name is not invalid
//
	if( 0==PlayerName)
		return 2;
//
// Skip any leading spaces (ignore names that are all spaces)
//
	while( *PlayerName==32 )
		PlayerName++;
//
// Make sure name is not blank
//
	if( *PlayerName==0 )
		return 2;
//
// Find the game name is the currently found games
//
	ListOfGames* pThis=GameNames;
	while( pThis )
	{
		if( 0==stricmp( pThis->Name, GameName ) )
			break;
		pThis=pThis->pNext;
	}
//
// Could not find game
//
	if( 0==pThis )
		return 1;


//
// Check for duplicate player name
//
	ListOfNames* pNames=pThis->pPlayers;
	while( pNames )
	{
		if( 0==stricmp( pNames->Name, PlayerName ) )
			return 2;
		pNames=pNames->pNext;
	}
//
// Make copies of names
//
	strcpy( GameNameBuffer, GameName );
	if( Password )
		strcpy( PasswordBuffer, Password );
	else
		PasswordBuffer[0]=0;

//
// Fill in description of session
//
	memset( &CurrentSession, 0, sizeof(CurrentSession) );
	CurrentSession.dwSize=sizeof(CurrentSession);
	CurrentSession.guidInstance=pThis->SessionDescriptor.guidInstance;
	CurrentSession.lpszSessionNameA=GameNameBuffer;
	CurrentSession.lpszPasswordA=PasswordBuffer;
	memcpy( &CurrentSession.dwUser1, &pThis->SessionDescriptor.dwUser1, 16 );
//
// Make sure no player enumeration is still occuring
//
	EnterCriticalSection( &EnumCriticalSection );
//
// Create the session
//

	//InitializeProtocol(pThis->bIPX ? PROTOCOL_IPX : PROTOCOL_TCPIP);
	ServerBrowser::JoinGame(GameName, (void **)&dplay4);

    ////if ( true || !pThis->bIPX )
    ////{
    ////    // force enumeration to complete synchronously, so we are sure
    ////    // we have enumerated the IP address of the game server before
    ////    // we try to open it in DPLAY with wOpen() below.
	////    HRESULT result = QuickEnum( false );
	////    if( FAILED(result) )
    ////    {
    ////        LeaveCriticalSection( &EnumCriticalSection );
    ////        return 3; // failed to connect.
    ////    }
    ////}

	AllowFail=1;
	HRESULT hr=wOpen( dplay4, &CurrentSession, DPOPEN_JOIN | DPOPEN_RETURNSTATUS );
	AllowFail=0;
//
// Now enum thread can start again
//
	LeaveCriticalSection( &EnumCriticalSection );
//
// Any errors, just return failed
//
	if( hr==DPERR_CANTCREATEPLAYER )
		return 6;
	if (hr==DPERR_NONEWPLAYERS)
		return 6;
	if( hr==DPERR_INVALIDPASSWORD )
		return 5;
	if( FAILED(hr) )
		return 3;
//
// Get the current session description
//
	DWORD Size=0;;
	DPSESSIONDESC2* pDesc=0;
	AllowFail=1;
	wGetSessionDesc( dplay4, pDesc, &Size );	
	AllowFail=0;
	if( Size==0 )			// Cannot get decription - just return error.
		return 3;
	pDesc=(DPSESSIONDESC2*)gos_Malloc(Size,DefaultHeap);
	wGetSessionDesc( dplay4, pDesc, &Size );	
	CurrentSession.dwFlags=pDesc->dwFlags;
	CurrentSession.dwMaxPlayers=pDesc->dwMaxPlayers;
	CurrentSession.dwUser1=pDesc->dwUser1;
	CurrentSession.dwUser2=pDesc->dwUser2;
	CurrentSession.dwUser3=pDesc->dwUser3;
	CurrentSession.dwUser4=pDesc->dwUser4;
	memcpy( &CurrentSession.guidApplication, &pDesc->guidApplication, sizeof(GUID) );
	gos_Free( pDesc );
//
// Create a player
//
	DPID PlayerID;
	DPNAME DPPlayerName;

#ifdef OUTBOUND_WINDOW
	//
	// create the outbound window for the server
	//
	OutboundWindow *pOW = new OutboundWindow(1);
	if (pOW)
	{
		pOW->Add();
	}
#endif

	memset( &DPPlayerName, 0, sizeof(DPPlayerName) );
	DPPlayerName.dwSize=sizeof(DPPlayerName);
	DPPlayerName.lpszShortNameA=PlayerName;

	AllowFail=1;
	hr=wCreatePlayer( dplay4, &PlayerID, &DPPlayerName, NetworkEvent, 0, 0, 0 );
	AllowFail=0;

	if (FAILED(hr))
	{
		wClose(dplay4);
		return 7;
	}
//
// Get TCPIP address if possible
//
	if( true )
	{
		MyIPAddress=0;
		DWORD TCPSize=0;
		AllowFail=1;
		wGetPlayerAddress( dplay4, PlayerID, NULL, &TCPSize );
		if (TCPSize)
		{
			AllowFail=0;
			void* address = gos_Malloc(TCPSize,DefaultHeap);								// Enumerate all modems
			AllowFail=1;
			HRESULT hr=wGetPlayerAddress( dplay4, PlayerID, address, &TCPSize );
			AllowFail=0;
			if( DD_OK==hr )
			{
				AllowFail=1;
				wEnumAddress( dplobby3, TCPIPCallback, address, TCPSize, 0 );
				AllowFail=0;
			}
			gos_Free( address );
		}
	}
//
// Stop further enumeration of games and players
//
	MyDirectPlayID=PlayerID;
	InNetworkGame=1;
	IAmTheServer=0;
	GetCurrentPlayers();

	ServerBrowser::ReleaseAll();

//
// invoke virtual function for connection-specific post-processing
//
	return 0;

    //return GamesJoinGame( GameName, PlayerName, Password );
}


int __stdcall gos_GameIsExist( const char* pcszGameName)
{
    PushGameList();

	ListOfGames* pThis=GameNames;

	while( pThis )
	{
		if( 0==stricmp( pThis->Name, pcszGameName) )
			return TRUE;
		pThis=pThis->pNext;
	}
    if (!ServerBrowser::StillBusy()) {
        ServerBrowser::RefreshList();
    }

    return FALSE;
}


//
//
//
int __stdcall gos_JoinGame( char* GameName, char* PlayerName, char* Password ,char * UserPassword)
{
	int result=0;

	if( 1==LogMode )	// Log file playing back?
	{
		if( !InDebugger )
		{
			return (int)ReadBitStream(8,Log_NetInfo);
		}
	}
	else				// Log file recording
	{
		if (UserPassword)
			NGStatsSetPlayerId(PlayerName, UserPassword);
		else
			NGStatsPlayerId=GUID_NULL;

#ifdef _DEBUG
		if (PlayerName == NULL)
			PlayerName = "MikeClientPlayerTest";
#endif

		result=InternalJoinGame( GameName, PlayerName, Password );

		if( !InDebugger )
		{
			WriteBitStream((DWORD)result,8,Log_NetInfo);
		}
	}
	return result;
}







//
// Destory and recreate networking
//
void __stdcall gos_Disconnect()
{
//
// Destroy game list functionality
//
	ServerBrowser::DisconnectAll();
    //if ( GamesDisconnectNet )
    //{
    //    GamesDisconnectNet();
    //}
//
//
//
	if( MyDirectPlayID )
	{
		wDestroyPlayer( dplay4, MyDirectPlayID );
		MyDirectPlayID=0;
	}
	if( MyIPAddress )
	{
		gos_Free( MyIPAddress );
		MyIPAddress=0;
	}
	if( InNetworkGame )
	{
		WaitTillQueueEmpty();
		wClose( dplay4 );
		InNetworkGame=0;
	}
//
// Kill dplay object
//
	if( dplay4 )
	{
		wRelease( dplay4 );
		dplay4=0;
	}
//
// Create a clean DPLAY object
//
	wCoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay4A, (LPVOID*)&dplay4 );
	gos_ShutdownNetwork();
	gos_InitializeNetworking();
}


//========================================================================
//========================================================================
BOOL FAR PASCAL TryToJoinASpecificGameEnumCallback(LPCDPSESSIONDESC2 pdpsd, LPDWORD pdwTimeOut, DWORD dwFlags, LPVOID szGameName)
{
	if (dwFlags & DPESC_TIMEDOUT)
	{
		return FALSE;
	}

	if (szGameName && ((char *)szGameName)[0])
	{
		// if we are looking for a specific game name, and it doesn't match this one, return
		if (strcmp((char *)szGameName, pdpsd->lpszSessionNameA))
			return TRUE;
	}

	memcpy(&CurrentSession, pdpsd, sizeof(CurrentSession));
	return FALSE;
}

//========================================================================
//========================================================================
bool TryToJoinASpecificGame(char *szIPAddress, char *szGameName)
{
	DPCOMPOUNDADDRESSELEMENT comAdd[3];
	DWORD Size;
	void* p_address;
	DWORD result;

	int port = GetDirectPlayPort();

	comAdd[0].guidDataType	= DPAID_ServiceProvider;
	comAdd[0].dwDataSize	= sizeof(GUID);
	comAdd[0].lpData	= (void*)&DPSPGUID_TCPIP;
	comAdd[1].guidDataType	= DPAID_INet;								// Create a compund address
	comAdd[1].dwDataSize	= 1+strlen(szIPAddress);
	comAdd[1].lpData		= szIPAddress;
	comAdd[2].guidDataType	= DPAID_INetPort;
	comAdd[2].dwDataSize	= sizeof(port);
	comAdd[2].lpData		= (void *)&port;

	int num_elements;
	if (port)
		num_elements = 3;
	else
		num_elements = 2;

	AllowFail=1;
	Size=0;
	wCreateCompoundAddress( dplobby3, comAdd, num_elements, NULL, &Size );
	p_address = (void*)gos_Malloc(Size,DefaultHeap);
	wCreateCompoundAddress( dplobby3, comAdd, num_elements, p_address, &Size );
	result = wInitializeConnection( dplay4, p_address, 0 );		// Try to initialize the connection
	AllowFail=0;
	if (FAILED(result))
	{
		gos_Free(p_address);
		return false;
	}

	gos_Free(p_address);

	//
	// now, enumerate games and see if you can find the game we want
	//
	DPSESSIONDESC2 dpsd;

	ZeroMemory(&dpsd, sizeof(dpsd));
	dpsd.dwSize	= sizeof(dpsd);
	memcpy(&dpsd.guidApplication, &CurrentSession.guidApplication, sizeof(GUID));

	AllowFail=1;
	wEnumSessions(dplay4, &dpsd, 5000, TryToJoinASpecificGameEnumCallback, szGameName, DPENUMSESSIONS_ALL | DPENUMSESSIONS_AVAILABLE | DPENUMSESSIONS_RETURNSTATUS);
	AllowFail=0;

	//
	// check to see if we found an instance
	//
	if (memcmp(&CurrentSession.guidInstance, &GUID_NULL, sizeof(GUID)) == 0)
	{
		return false;
	}

	AllowFail=1;
	ZeroMemory(&dpsd, sizeof(dpsd));
	dpsd.dwSize	= sizeof(dpsd);
	memcpy(&dpsd.guidApplication, &CurrentSession.guidApplication, sizeof(GUID));
	memcpy(&dpsd.guidInstance, &CurrentSession.guidInstance, sizeof(GUID));

	HRESULT hr=wOpen( dplay4, &dpsd, DPOPEN_JOIN | DPOPEN_RETURNSTATUS );
	AllowFail=0;

	if (FAILED(hr))
	{
		AllowFail=1;
		wClose( dplay4 );
		AllowFail=0;
		return false;
	}

	return true;
}

//
// Check if started by a lobby
//
bool InternalNetCheckLobby()
{
	gosASSERT( status==Running );					// Must not be called during Game init.
	IDirectPlay4* newdplay4;

	if( 0==Environment.NetworkGame )
		return 0;									// Not in a networking game

	DWORD dwSize=0;
	HRESULT hr=wGetConnectionSettings( dplobby3, 0, NULL, &dwSize );
	if( DPERR_NOTLOBBIED==hr )
	{
		//
		// see if we are trying to join a specific game on the command line
		//
		char *p;
		p = strstr(CommandLine, "/join:");
		if (!p)
			p = strstr(CommandLine, "-join:");

		if (p)
		{
			char szIPAddress[256];
			char szGameName[256];

			strncpy(szIPAddress, &p[sizeof("/join")], sizeof(szIPAddress));

			//
			// now, advance to the first invalid character, and see if it's a comma
			//
			for (p=szIPAddress ; *p && (isalnum(*p) || *p=='_' || *p=='.') ; p++);
			if (*p == ',')
			{
				*p=0;
				strncpy(szGameName, &p[1], sizeof(szGameName));

				p = szGameName;
				while (*p && (isalnum(*p) || *p=='_'))
					p++;
				*p=0;
			}
			else
			{
				*p=0;
				szGameName[0]=0;
			}

			//
			// now, try to find the game
			//
			if (TryToJoinASpecificGame(szIPAddress, szGameName))
			{
				DWORD Size=0;;
				DPSESSIONDESC2* pDesc=0;
				AllowFail=1;
				wGetSessionDesc( dplay4, pDesc, &Size );	
				AllowFail=0;
				if( Size==0 )			// Cannot get decription - just return error.
					return false;
				pDesc=(DPSESSIONDESC2*)gos_Malloc(Size,DefaultHeap);
				wGetSessionDesc( dplay4, pDesc, &Size );	
				CurrentSession.dwFlags=pDesc->dwFlags;
				CurrentSession.dwMaxPlayers=pDesc->dwMaxPlayers;
				CurrentSession.dwUser1=pDesc->dwUser1;
				CurrentSession.dwUser2=pDesc->dwUser2;
				CurrentSession.dwUser3=pDesc->dwUser3;
				CurrentSession.dwUser4=pDesc->dwUser4;
				memcpy( &CurrentSession.guidApplication, &pDesc->guidApplication, sizeof(GUID) );
				gos_Free( pDesc );

				//
				// Create a player
				//
					DPID PlayerID;
					DPNAME DPPlayerName;

				#ifdef OUTBOUND_WINDOW
					//
					// create the outbound window for the server
					//
					OutboundWindow *pOW = new OutboundWindow(1);
					if (pOW)
					{
						pOW->Add();
					}
				#endif

					memset( &DPPlayerName, 0, sizeof(DPPlayerName) );
					DPPlayerName.dwSize=sizeof(DPPlayerName);
					DPPlayerName.lpszShortNameA=Environment.defaultPlayerName;

					AllowFail=1;
					hr=wCreatePlayer( dplay4, &PlayerID, &DPPlayerName, NetworkEvent, 0, 0, 0 );
					AllowFail=0;

					if (FAILED(hr))
					{
						wClose(dplay4);
						return false;
					}
				//
				// Get TCPIP address if possible
				//
					if( true )
					{
						MyIPAddress=0;
						DWORD TCPSize=0;
						AllowFail=1;
						wGetPlayerAddress( dplay4, PlayerID, NULL, &TCPSize );
						if (TCPSize)
						{
							AllowFail=0;
							void* address = gos_Malloc(TCPSize,DefaultHeap);								// Enumerate all modems
							AllowFail=1;
							HRESULT hr=wGetPlayerAddress( dplay4, PlayerID, address, &TCPSize );
							AllowFail=0;
							if( DD_OK==hr )
							{
								AllowFail=1;
								wEnumAddress( dplobby3, TCPIPCallback, address, TCPSize, 0 );
								AllowFail=0;
							}
							gos_Free( address );
						}
					}
				//
				// Stop further enumeration of games and players
				//
					MyDirectPlayID=PlayerID;
					InNetworkGame=1;
					IAmTheServer=0;
					GetCurrentPlayers();


			}
			else
			{
				return 0;
			}

		}
		return 0;
	}

//
// Get connection settings
//
	DPLCONNECTION* pConnection=(DPLCONNECTION*)gos_Malloc( dwSize, Heap_Network );
	hr=wGetConnectionSettings( dplobby3, 0, pConnection, &dwSize );
	if( FAILED(hr) )
		return 0;
#ifdef MLYONS
#ifdef _DEBUG
	if (MessageBox(hWindow, "You were lobby launched. Would you like to debug?", "Debug lobby launch", MB_YESNO) == IDYES)
		__asm int 3;
#endif
#endif

//
// We only support TCPIP lobby launch
//
	if( !IsEqualGUID( pConnection->guidSP, DPSPGUID_TCPIP) )
		return 0;
	Connected=NP_TCP;
	InitLanGames();
//
// Setup valid session flags
//
#ifdef USE_DPKEEPALIVE
	pConnection->lpSessionDesc->dwFlags=DPSESSION_KEEPALIVE | (Environment.NetServerMigration ? DPSESSION_MIGRATEHOST : 0);
#else
	pConnection->lpSessionDesc->dwFlags=                      (Environment.NetServerMigration ? DPSESSION_MIGRATEHOST : 0);
#endif

	if (Environment.DirectPlayProtocol)
		pConnection->lpSessionDesc->dwFlags |= DPSESSION_DIRECTPLAYPROTOCOL;
	else
		pConnection->lpSessionDesc->dwFlags &= ~DPSESSION_DIRECTPLAYPROTOCOL;

#ifdef USE_CLIENTSERVER
	pConnection->lpSessionDesc->dwFlags |= DPSESSION_CLIENTSERVER;
	pConnection->lpSessionDesc->dwFlags &= ~DPSESSION_MIGRATEHOST;
#endif

	if (Environment.defaultPlayerName)
		pConnection->lpPlayerName->lpszShortNameA = Environment.defaultPlayerName;


	wSetConnectionSettings( dplobby3, 0, 0, pConnection );
//
// Created the game?
//
	if( pConnection->dwFlags==DPLCONNECTION_CREATESESSION )
	{
//
// Wait till connected
//
		hr=wConnectEx( dplobby3, 0, IID_IDirectPlay4A, (void**)&newdplay4, NULL );
		if( FAILED(hr) )
			return 0;
//
// Get the current session description, not all session information is filled in during open
//
		DWORD Size=0;;
		DPSESSIONDESC2* pDesc=0;
		AllowFail=1;
		wGetSessionDesc( newdplay4, pDesc, &Size );	
		AllowFail=0;
		if( Size==0 )
			return 0;
		pDesc=(DPSESSIONDESC2*)gos_Malloc(Size,DefaultHeap);
		wGetSessionDesc( newdplay4, pDesc, &Size );	
//
// Copy to 'global' session description
//
		memcpy( &CurrentSession, pDesc, sizeof(CurrentSession) );
		GameNameBuffer[0]=0;
		PasswordBuffer[0]=0;
		if( pDesc->lpszSessionNameA )
			strcpy( GameNameBuffer, pDesc->lpszSessionNameA );
		if( (pDesc->dwFlags&DPSESSION_PASSWORDREQUIRED) && pDesc->lpszPasswordA )
			strcpy( PasswordBuffer, pDesc->lpszPasswordA );
		CurrentSession.lpszSessionNameA=GameNameBuffer;
		CurrentSession.lpszPasswordA=PasswordBuffer;
		gos_Free( pDesc );
//
// Create the server player
//
		DPID ServerID;
		DPNAME ServerName;
		memset( &ServerName, 0, sizeof(ServerName) );
		ServerName.dwSize=sizeof(ServerName);
		ServerName.lpszShortNameA=pConnection->lpPlayerName->lpszShortNameA;
		wCreatePlayer( newdplay4, &ServerID, &ServerName, NetworkEvent, 0, 0, DPPLAYER_SERVERPLAYER );
//
// Get TCPIP address if possible
//
		MyIPAddress=0;
		DWORD TCPSize=0;
		AllowFail=1;
		wGetPlayerAddress( newdplay4, ServerID, NULL, &TCPSize );
		AllowFail=0;
		void* address = gos_Malloc(TCPSize,DefaultHeap);
		AllowFail=1;
		HRESULT hr=wGetPlayerAddress( newdplay4, ServerID, address, &TCPSize );
		AllowFail=0;
		if( DD_OK==hr )
		{
			AllowFail=1;
			wEnumAddress( dplobby3, TCPIPCallback, address, TCPSize, 0 );
			AllowFail=0;
		}
		gos_Free( address );
//
// Make new dplay object the current one
//
		if( dplay4 )
		{
			wRelease( dplay4 );
		}
		dplay4=newdplay4;
//
// Update variables
//
		MyDirectPlayID=ServerID;
		InNetworkGame=1;
		IAmTheServer=1;
		GetCurrentPlayers();
//
// Return success
//
		return 1;
	}
	else
//
// Join a game
//
	{
//
// Wait 10 seconds or until connected
//
		double starttime=gos_GetHiResTime();
		hr=-1;
		newdplay4=0;
		while( FAILED(hr) && gos_GetHiResTime()-starttime < 10.0f )
		{
			hr=wConnectEx( dplobby3, DPCONNECT_RETURNSTATUS, IID_IDirectPlay4A, (void**)&newdplay4, NULL );
		}
		if( FAILED(hr) )
			return 0;
//
// Get the current session description, not all session information is filled in during open
//
		DWORD Size=0;;
		DPSESSIONDESC2* pDesc=0;
		AllowFail=1;
		wGetSessionDesc( newdplay4, pDesc, &Size );	
		AllowFail=0;
		if( Size==0 )
			return 0;
		pDesc=(DPSESSIONDESC2*)gos_Malloc(Size,DefaultHeap);
		wGetSessionDesc( newdplay4, pDesc, &Size );	
//
// Copy to 'global' session description
//
		memcpy( &CurrentSession, pDesc, sizeof(CurrentSession) );
		GameNameBuffer[0]=0;
		PasswordBuffer[0]=0;
		if( pDesc->lpszSessionNameA )
			strcpy( GameNameBuffer, pDesc->lpszSessionNameA );
		if( (pDesc->dwFlags&DPSESSION_PASSWORDREQUIRED) && pDesc->lpszPasswordA )
			strcpy( PasswordBuffer, pDesc->lpszPasswordA );
		CurrentSession.lpszSessionNameA=GameNameBuffer;
		CurrentSession.lpszPasswordA=PasswordBuffer;
		gos_Free( pDesc );
//
// Create the player
//
		DPID PlayerID;
		DPNAME PlayerName;
		memset( &PlayerName, 0, sizeof(PlayerName) );
		PlayerName.dwSize=sizeof(PlayerName);
		PlayerName.lpszShortNameA=pConnection->lpPlayerName->lpszShortNameA;
		wCreatePlayer( newdplay4, &PlayerID, &PlayerName, NetworkEvent, 0, 0, 0 );
//
// Get TCPIP address if possible
//
		MyIPAddress=0;
		DWORD TCPSize=0;
		AllowFail=1;
		wGetPlayerAddress( newdplay4, PlayerID, NULL, &TCPSize );
		AllowFail=0;
		void* address = gos_Malloc(TCPSize,DefaultHeap);
		AllowFail=1;
		HRESULT hr=wGetPlayerAddress( newdplay4, PlayerID, address, &TCPSize );
		AllowFail=0;
		if( DD_OK==hr )
		{
			AllowFail=1;
			wEnumAddress( dplobby3, TCPIPCallback, address, TCPSize, 0 );
			AllowFail=0;
		}
		gos_Free( address );
//
// Make new dplay object the current one
//
		if( dplay4 )
		{
			wRelease( dplay4 );
		}
		dplay4=newdplay4;
//
// Update variables
//
		MyDirectPlayID=PlayerID;
		InNetworkGame=1;
		IAmTheServer=0;
		GetCurrentPlayers();

#ifdef OUTBOUND_WINDOW
	//
	// create the outbound window for the server
	//
	OutboundWindow *pOW = new OutboundWindow(1);
	if (pOW)
	{
		pOW->Add();
	}
#endif


//
// Return success
//
		return 1;
	}
}








bool __stdcall gos_NetCheckLobby()
{
	bool result=0;

	if( 1==LogMode )	// Log file playing back?
	{
		if( !InDebugger )
		{
			return (bool)(ReadBitStream(1,Log_NetInfo)!=0);
		}
	}
	else				// Log file recording
	{
		result=InternalNetCheckLobby();

		if( !InDebugger )
		{
			WriteBitStream((DWORD)(result ? 1 : 0),1,Log_NetInfo);
		}
	}

	return result;
}






GUID __stdcall gos_GenerateUniqueGUID()
{

	GUID new_id;

	::CoCreateGuid(&new_id);

	if (new_id == GUID_NULL)
	{
		STOP(("Error Creating unique player GUID"));
		return GUID_NULL;
	}

	return new_id;
}





#ifdef FIX_WINSOCK_PROXY
//========================================================================
// FixWinsockProxy
//
// As per an email sent by Aaron Ogus, Dev Lead on DirectPlay, the WSP
// client causes trouble with our games due to a configuartion that ITG
// updates every six hours. To temporarily disable it, you must
// put a "Disable=1" setting specific to your app in the INI file, and
// replace the 255.255.255.254 reference in some WSP data file with
// 255.255.255.255. This code does exactly this, and marks the files
// as read only, which prevents ITG from changing the settings until the
// next reboot.
//========================================================================
bool FixWinsockProxy(void)
{
	char szWSPLocation[300];

	//
	// get the location of the WSP client
	//
	GetPrivateProfileString("Microsoft Proxy Service", "Configuration Location", "", szWSPLocation, sizeof(szWSPLocation), "system.ini");
	if (szWSPLocation[0])
	{
		//
		// WSP client is installed, muck with the settings
		//
		if (szWSPLocation[strlen(szWSPLocation)-1] == '\\')
			szWSPLocation[strlen(szWSPLocation)-1] = 0;

		//
		// settings file
		//
		if (true)
		{
			char szWSPSettingsFile[300];
			strcpy(szWSPSettingsFile, szWSPLocation);
			strcat(szWSPSettingsFile, "\\mspclnt.ini");

			//
			// make sure it's not read-only
			//
			gos_FileSetReadWrite(szWSPSettingsFile);

			char szFullFileName[MAX_PATH];
			GetModuleFileName(NULL, szFullFileName, sizeof(szFullFileName));
			char *p;
			for (p=szFullFileName ; *p ; p++);
			for (; p>=szFullFileName && *p!='\\' ; p--);
			p++;

			WritePrivateProfileString(p, "Disable", "1", szWSPSettingsFile);

			//
			// make it read only
			//
			//gos_FileSetReadOnly(szWSPSettingsFile);

		}

		//
		//
		if (true)
		{
			char szWSPInfoFile[300];
			strcpy(szWSPInfoFile, szWSPLocation);
			strcat(szWSPInfoFile, "\\msplat.txt");

			//
			// make sure it's not read-only
			//
			gos_FileSetReadWrite(szWSPInfoFile);

			//
			// edit the file
			//
			DWORD dwSize = gos_FileSize(szWSPInfoFile);
			if (dwSize != (DWORD)-1)
			{
				char *pFile = (char *)gos_Malloc(dwSize+1,DefaultHeap);

				if (pFile)
				{
					HANDLE hFile;

					hFile = CreateFile(szWSPInfoFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
					if (hFile && hFile != INVALID_HANDLE_VALUE)
					{
						ReadFile(hFile, pFile, dwSize, &dwSize, NULL);
						pFile[dwSize]=0;

						char *p;

						p = strstr(pFile, "255.255.255.254");

						while (p)
						{
							// change the 4 to 5
							*strchr(p, '4') = '5';

							p = strstr(p, "255.255.255.254");
						}

						SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
						WriteFile(hFile, pFile, dwSize, &dwSize, NULL);
						CloseHandle(hFile);
					}

					gos_Free(pFile);
				}
			}

			//
			// make it read only
			//
			//gos_FileSetReadOnly(szWSPInfoFile);


		}
	}

	return true;

}
#endif