#include "pch.hpp"
#include "GUNGameList.h"


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


//========================================================================
// macros
//========================================================================
#define RELEASE(p)					if (p) { (p)->Release(); (p)=NULL; }
#define MAX_ENUM_PLAYERS_THREADS	1
#define NUM_ELEMENTS(arr)			(sizeof(arr)/sizeof((arr)[0]))

#define MAX_DESCRIPTION				30
#define ENUMERATE_PERIOD			30.0	// time between enumerating players
#define ENUM_SESSIONS_TIMEOUT		2500
#define UPDATE_COMPLETE_TIME		5.0		// time we let dplay scan for games before saying we are done...

#define USER_FLAGS2_MAP_MASK		0xFF000000
#define USER_FLAGS2_GAME_MASK		0x00FF0000
#define USER_FLAGS2_BOT_MASK		0x0000F000
#define FixSZString(a)				((a)[sizeof(a)-1]) = 0

//
// if x is a binary integer, then x-1 will be equal to x where the bottom bit
// of x is zero and the lesser 0's are all 1. Thus, [x & (x-1)] will effectively
// be x with the bottom bit off. Then, if you XOR it with the original x, you'll
// get a value equal to only the bottom bit that was on.
//
#define GetBottomBit(n)				(n ^ (n & (n-1)))

//========================================================================
// MLYONS Debug helper functions
//========================================================================
#ifdef _DEBUG
static void __stdcall SetThreadName(DWORD dwThreadID, char *szThreadName);
#else
#define SetThreadName(a,b)
#endif

class EnumPlayersRequest;
class EnumPlayersQueue;
class LANServerBrowser;

//========================================================================
//========================================================================
class LANServerBrowser : public ServerBrowser
{
	friend int __stdcall InitTCPIPServerBrowser(char *szIPAddress);
	friend bool __stdcall InitIPXServerBrowser(void);

	friend class EnumPlayersQueue;
protected:
	// general use functions derrived from ServerBrowser
	virtual	bool __stdcall Initialize(void);
	virtual	bool __stdcall Disconnect(void);
	virtual int __stdcall GetStatus(void);
	virtual bool __stdcall Synchronize(void);
	virtual const char *__stdcall GetDescription(void);
	virtual bool __stdcall Release(void);
    virtual bool __stdcall PrepareJoinGame(const char * szGameName, void **ppDPlay);

	virtual bool __stdcall PrepareRefresh(void);
	virtual bool __stdcall Refresh(char *gameID);
	virtual bool __stdcall RefreshServerInfo(char *gameID);
	virtual bool __stdcall StartUpdate(void);
	virtual bool __stdcall CancelActivity(void);
	virtual bool __stdcall GetNetStatus(void);



protected:
	//
	// member variables
	//
	double					m_StartUpdateTime;
	LPDIRECTPLAY4A			m_lpDP;
	char					m_szAddress[30];
	char					m_szDescription[MAX_DESCRIPTION];
	EnumPlayersQueue *		m_EnumPlayersQueue;
	HANDLE					m_hMutex;
	bool					m_bInitialized;
	void *					m_AddressType;
	const char *			m_pszProtocol;

	char					m_szFullSessionName[3000];
	char					m_szPlayerName[16][64];
	char					m_szClanName[16][64];
	char					m_szMapName[64];
	int						m_iNumPlayers;
	char					m_szGameName[64];
	char					m_szVersion[64];

	void ParseSessionName(void);

	//
	// constructor
	//
	LANServerBrowser() : m_lpDP(NULL), m_StartUpdateTime(0.0), m_EnumPlayersQueue(NULL), m_bInitialized(false), m_iNumPlayers(0)
	{
	}

	//
	// static callbacks that do nothing but call the local callback
	//
	static BOOL PASCAL EmptyEnumSessionsCallback( LPCDPSESSIONDESC2 lpSessionDesc, LPDWORD lpdwTimeOut, DWORD dwFlags, void* lpContext );
	static BOOL FAR PASCAL StaticEnumSessionsCallback(LPCDPSESSIONDESC2 lpThisSD, LPDWORD lpdwTimeOut, DWORD dwFlags, LPVOID lpContext);

	//
	// local versions of the callbacks
	//
	BOOL FAR PASCAL EnumSessionsCallback(LPCDPSESSIONDESC2 lpThisSD, LPDWORD lpdwTimeOut, DWORD dwFlags);

};




//========================================================================
// EnumPlayerRequest
//
// EnumPlayerRequest is a single item in a linked list that represents
// a request to enumerate players
//========================================================================
class EnumPlayersRequest
{
	friend class EnumPlayersQueue;

private:
	void * operator new(size_t size) { return malloc(size); }
	void operator delete( void* ptr ) { free(ptr); }
	void operator delete[] ( void* ptr ) { free(ptr); }
	BOOL __stdcall EnumPlayerCallback(DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags);

	//
	// data for each request
	//
	Browse::GameList *			m_pGameList;
	char						m_szGameID[256];
	IDirectPlay4A *				m_pDP;
	GUID						m_guidSessionInstance;
	LANServerBrowser *			m_Browser;
	EnumPlayersRequest *		m_pNext;
	bool						m_bActive;
};


//========================================================================
//========================================================================
BOOL FAR PASCAL EnumPlayersRequest::EnumPlayerCallback(DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags)
{
	if ((dwPlayerType == DPPLAYERTYPE_PLAYER) && (lpName->lpszShortNameA != NULL))
	{
		m_pGameList->Lock();
		Browse::PlayerList *pPlayerList = m_pGameList->GetPlayerList( m_szGameID );
		if (pPlayerList)
		{
			pPlayerList->CreateEntry(lpName->lpszShortNameA);
			pPlayerList->SetField(lpName->lpszShortNameA, PLAYERLIST_KEY_PLAYER_NAME, lpName->lpszShortNameA);
			pPlayerList->SetField(lpName->lpszShortNameA, PLAYERLIST_KEY_UPDATE_TIME, "new");
		}

		m_pGameList->Unlock();
	}

	return TRUE;
}



//========================================================================
// EnumPlayerQueue
//
// This is a class that is part of every LANServerBrowser object. What
// this class consists of is:
//
//   1. A set of threads, handling requests to enumerate players
//   2. A list of requests to do just that.
//========================================================================
class EnumPlayersQueue
{
public:
	bool __stdcall Initialize(LANServerBrowser *);
	bool __stdcall Terminate(void);
	bool __stdcall AddRequest(Browse::GameList *pGameList, const char *gameID, IDirectPlay4A *pDP, const GUID *pSessionInstance);

	EnumPlayersQueue() : m_bInitialized(false), m_pHead(NULL)
	{;}

	~EnumPlayersQueue()
	{
		gosASSERT(m_bInitialized == false);
	}

protected: // data
	EnumPlayersRequest *		m_pHead;
	CRITICAL_SECTION			m_cs;
	HANDLE						m_hThread[MAX_ENUM_PLAYERS_THREADS];
	int							m_iNumThreads;
	bool						m_bInitialized;
	bool						m_bKill;
	HANDLE						m_hWakeUpEvent;
	LANServerBrowser *			m_Parent;

protected: // functions
	void ThreadProc(void);


protected: // static functions
	static DWORD WINAPI StaticThreadProc(LPVOID lpParameter);
	static BOOL FAR PASCAL StaticEnumPlayersCallback(DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext);
};


//========================================================================
//========================================================================
BOOL FAR PASCAL EnumPlayersQueue::StaticEnumPlayersCallback(DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext)
{
	EnumPlayersRequest *pRequest = (EnumPlayersRequest *)lpContext;

	return pRequest->EnumPlayerCallback(dpId, dwPlayerType, lpName, dwFlags);
}

//========================================================================
//========================================================================
DWORD WINAPI EnumPlayersQueue::StaticThreadProc(LPVOID lpParameter)
{
	((EnumPlayersQueue *)lpParameter)->ThreadProc();
	return 0;
}

//========================================================================
//========================================================================
void EnumPlayersQueue::ThreadProc(void)
{
	SetThreadName(GetCurrentThreadId(), "Enum");
	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);

	//
	// wait until something wakes us up
	//
	while (true)
	{
		WaitForSingleObject(m_hWakeUpEvent, INFINITE);

		if (m_bKill)
			break;

		EnumPlayersRequest * pThis = NULL;

		EnterCriticalSection(&m_cs);
		while (m_pHead)
		{
			pThis = m_pHead;
			m_pHead = pThis->m_pNext;
			pThis->m_pNext = NULL;

			//
			// is this request still active?
			//
			if (!pThis->m_bActive)
			{
				RELEASE(pThis->m_pDP);
				delete pThis;
			}
			else
			{
				//
				// pThis is set to what we want to process
				//
				break;
			}
			
		}
		LeaveCriticalSection(&m_cs);

		//
		// see if we have a request to process
		//
		if (pThis)
		{
			//
			// check to see if any of the players should be removed
			//
			if (true)
			{
				Browse::IndexedTableView * pIndexView = NULL;

				pThis->m_pGameList->Lock();
				Browse::PlayerList *pPlayerList = pThis->m_pGameList->GetPlayerList( pThis->m_szGameID );

				if (pPlayerList)
				{
					pIndexView = pPlayerList->GetIndexedView();

					for (int i=0 ; i<pIndexView->GetCount() ; i++)
					{
						char * playerID;

						if ( pIndexView->GetIndex( i, &playerID ) == false )
						{
							PAUSE(( "Unable to find playerID for index %d", i ));
							continue;
						}
						const char * playerUpdate = pPlayerList->GetDataField(playerID, PLAYERLIST_KEY_UPDATE_TIME );

						if (!strcmp(playerUpdate, "old"))
						{
							// delete this player!
							pPlayerList->Remove(playerID);
							i--;
						}
						else
						{
							pPlayerList->SetField(playerID, PLAYERLIST_KEY_UPDATE_TIME, "old");
						}
					}
				}

				pThis->m_pGameList->Unlock();
			}

			if (WaitForSingleObject(m_Parent->m_hMutex, INFINITE) == WAIT_OBJECT_0)
			{
                HRESULT hr = pThis->m_pDP->EnumPlayers(&pThis->m_guidSessionInstance, StaticEnumPlayersCallback, pThis, DPENUMPLAYERS_ALL | DPENUMPLAYERS_SESSION);

				if (FAILED(hr))
				{
					// error!
        			pThis->m_pGameList->Lock();
	                pThis->m_pGameList->SetField( pThis->m_szGameID, GAMELIST_KEY_SERVER_INFO_STATUS, "2" );
					pThis->m_pGameList->Unlock();
					__asm nop;
			}
                else
                {
                    // at this point we've enumerated all the players successfully
					pThis->m_pGameList->Lock();
                    pThis->m_pGameList->SetField( pThis->m_szGameID, GAMELIST_KEY_SERVER_INFO_STATUS, "1" );
    				pThis->m_pGameList->Unlock();
                }
				ReleaseMutex(m_Parent->m_hMutex);
			}


			RELEASE(pThis->m_pDP);
			delete pThis;
		}
	}

	return;
}

//========================================================================
//========================================================================
bool __stdcall EnumPlayersQueue::Initialize(LANServerBrowser *pBrowser)
{
	SetThreadName(GetCurrentThreadId(), "Main");
	Terminate();

	m_Parent = pBrowser;

	InitializeCriticalSection(&m_cs);
	m_bInitialized = true;

	m_hWakeUpEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (!m_hWakeUpEvent)
	{
		Terminate();
		return false;
	}

	m_bKill = false;
	for (m_iNumThreads=0 ; m_iNumThreads<NUM_ELEMENTS(m_hThread) ; m_iNumThreads++)
	{
		DWORD dwThreadId;
		m_hThread[m_iNumThreads] = CreateThread(NULL, 0, StaticThreadProc, this, 0, &dwThreadId);

		if (m_hThread == NULL)
		{
			Terminate();
			return false;
		}
	}

	return true;
}

//========================================================================
//========================================================================
bool __stdcall EnumPlayersQueue::Terminate(void)
{
	//
	// wake up and kill all the threads
	//
	if (m_bInitialized)
	{
		//SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
		//SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

		EnterCriticalSection(&m_cs);

		m_bKill = true;
		while (m_iNumThreads)
		{
			int iThreadKilled;
			SetEvent(m_hWakeUpEvent);
			iThreadKilled = WaitForMultipleObjects(m_iNumThreads, m_hThread, FALSE, INFINITE) - WAIT_OBJECT_0;

			CloseHandle(m_hThread[iThreadKilled]);

			//
			// now, copy the last still alive thread into this thread's handle
			//
			m_hThread[iThreadKilled] = m_hThread[m_iNumThreads - 1];
			m_hThread[m_iNumThreads - 1] = NULL;

			m_iNumThreads--;
		}

		if (m_hWakeUpEvent)
		{
			CloseHandle(m_hWakeUpEvent);
			m_hWakeUpEvent = NULL;
		}

		gosASSERT(m_hThread[0] == NULL);
		gosASSERT(m_hWakeUpEvent == NULL);

		m_bInitialized = false;

		//
		// free up all memory used by the linked list
		//
		while (m_pHead)
		{
			EnumPlayersRequest *p = m_pHead->m_pNext;
			delete m_pHead;
			m_pHead = p;
		}

		LeaveCriticalSection(&m_cs);
		DeleteCriticalSection(&m_cs);
	}

	return true;
}

//========================================================================
//========================================================================
bool __stdcall EnumPlayersQueue::AddRequest(Browse::GameList *pGameList, const char *gameID, IDirectPlay4A *pDP, const GUID *pSessionInstance)
{
	gosASSERT(m_bInitialized);
	if (!m_bInitialized)
		return false;


	EnterCriticalSection(&m_cs);

	EnumPlayersRequest * pNext = m_pHead;

	//
	// first, check to see if there's already an EnumPlayers request in the
	// list for the same session we're asking to add
	//
	EnumPlayersRequest *p = m_pHead;

	while (p)
	{
		if (p->m_guidSessionInstance == *pSessionInstance)
			break;

		p=p->m_pNext;
	}

	if (!p)
	{
		//
		// there was no request in the list for the same instance,
		// so add one
		//
		m_pHead = new EnumPlayersRequest;
		m_pHead->m_pNext = pNext;
		m_pHead->m_pGameList			= pGameList;
		m_pHead->m_pDP					= pDP;
		m_pHead->m_bActive				= true;
		m_pHead->m_pDP->AddRef();
		strncpy(m_pHead->m_szGameID, gameID, sizeof(m_pHead->m_szGameID));

		m_pHead->m_guidSessionInstance	= *pSessionInstance;
	}

	LeaveCriticalSection(&m_cs);

	SetEvent(m_hWakeUpEvent);
	return true;
}






//========================================================================
//========================================================================
bool __stdcall LANServerBrowser::Release(void)
{
	//
	// make sure that the EnumPlayer thread is done
	//
	Disconnect();
	Unregister();
	delete m_EnumPlayersQueue;
	delete this;
	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerBrowser::Initialize(void)
{
	IDirectPlayLobby3 *			lpDPL = NULL;
	void *						lpConnection = NULL;
	DWORD						dwSize;
	HRESULT						hr;

	m_szFullSessionName[0]	= 0;
	m_szMapName[0]			= 0;
	m_szGameName[0]			= 0;

	if (!m_bInitialized)
	{
		m_hMutex = CreateMutex(NULL, FALSE, NULL);
		m_bInitialized = true;
	}

	if (!m_EnumPlayersQueue)
	{
		m_EnumPlayersQueue = new EnumPlayersQueue;
		if (!m_EnumPlayersQueue)
			return false;

		if (!m_EnumPlayersQueue->Initialize(this))
		{
			delete m_EnumPlayersQueue;
			return false;
		}
	}


	//
	// make sure we're not initialized
	//
	RELEASE(m_lpDP);

	//
	// create the DirectPlay object
	//
	if (FAILED(hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_ALL, IID_IDirectPlay4A, (VOID**)&m_lpDP)))
		goto fail;

	//
	// create a DirectPlayLobby object for creation of an address
	//
	if (FAILED(hr = CoCreateInstance( CLSID_DirectPlayLobby, NULL, CLSCTX_ALL, IID_IDirectPlayLobby3, (VOID**)&lpDPL)))
		goto fail;

	//
	// create a TCP/IP address to enumerate
	//
	DPCOMPOUNDADDRESSELEMENT comAdd[3];

	int port;
	port = GetDirectPlayPort();

	comAdd[0].guidDataType	= DPAID_ServiceProvider;
	comAdd[0].dwDataSize	= sizeof(GUID);
	comAdd[0].lpData		= m_AddressType;
	comAdd[1].guidDataType	= DPAID_INet;								// Create a compund address
	comAdd[1].dwDataSize	= strlen(m_szAddress) + 1;
	comAdd[1].lpData		= m_szAddress;
	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;


	//
	// create the address
	//
	dwSize = 0;
	lpDPL->CreateCompoundAddress(comAdd, num_elements, NULL, &dwSize );
	lpConnection = gos_Malloc(dwSize);
	if (FAILED(hr = lpDPL->CreateCompoundAddress(comAdd, num_elements, lpConnection, &dwSize )))
		goto fail;

	//
	// initialize the connection
	//
	if (FAILED(hr = m_lpDP->InitializeConnection(lpConnection, 0)))
		goto fail;

	gos_Free(lpConnection);
	RELEASE(lpDPL);
	return true;

fail:
	if (lpConnection)
		gos_Free(lpConnection);
	RELEASE(lpDPL);
	Disconnect();
	return false;
}

//========================================================================
//========================================================================
BOOL FAR PASCAL LANServerBrowser::StaticEnumSessionsCallback(LPCDPSESSIONDESC2 lpThisSD, LPDWORD lpdwTimeOut, DWORD dwFlags, LPVOID lpContext)
{
	return ((LANServerBrowser *)lpContext)->EnumSessionsCallback(lpThisSD, lpdwTimeOut, dwFlags);
}

//========================================================================
//========================================================================
void LANServerBrowser::ParseSessionName(void)
{
	char *p;
	m_iNumPlayers = 0;

	p = m_szFullSessionName;
	m_szGameName[0]=0;
	m_szMapName[0]=0;
	m_szVersion[0]=0;

	while (*p)
	{
		while (*p && *p == '\\')
		{
			char *dst;
			p++;
			int i;

			switch (*p)
			{
			case 'g': // game name
				p+=2;
				dst = m_szGameName;

				while (*p && *p!='\\')
					*dst++ = *p++;
				*dst=0;
				break;

			case 'm': // map name
				p+=2;
				dst = m_szMapName;

				while (*p && *p!='\\')
					*dst++ = *p++;
				*dst=0;
				break;

			case 'p': // player name
				i=(p[1]-'0')*10 + (p[2]-'0');
				p+=4;
				if ((i+1)>m_iNumPlayers)
					m_iNumPlayers = (i+1);
				dst = m_szPlayerName[i];
				
				while (*p && *p!='\\')
					*dst++ = *p++;
				*dst=0;
				break;

			case 'c': // clan name
				i=(p[1]-'0')*10 + (p[2]-'0');
				p+=4;
				if ((i+1)>m_iNumPlayers)
					m_iNumPlayers = (i+1);
				dst = m_szClanName[i];

				while (*p && *p!='\\')
					*dst++ = *p++;
				*dst=0;
				break;
			case 'v': // version name
				p+=2;
				dst = m_szVersion;

				while (*p && *p!='\\')
					*dst++ = *p++;
				*dst=0;
				break;
			}
		}

		p++;
	}


}

//========================================================================
//========================================================================
BOOL FAR PASCAL LANServerBrowser::EnumSessionsCallback(LPCDPSESSIONDESC2 lpThisSD, LPDWORD lpdwTimeOut, DWORD dwFlags)
{
	if (dwFlags & DPESC_TIMEDOUT)
		return FALSE;

	Browse::GameList * pGameList = GetGameList();

	//unsigned char			m_szFullSessionName[3000];
	//unsigned char			m_szPlayerName[16][64];
	//unsigned char			m_szClanName[16][64];
	//unsigned char			m_szMapName[64];
	//int						m_iNumPlayers;
	//unsigned char			m_szGameName[64];

	strncpy(m_szFullSessionName, lpThisSD->lpszSessionNameA, sizeof(m_szFullSessionName));
	ParseSessionName();
	if (m_szGameName[0]==0)
		return true;

	char szGameIdentifier[256];
	char szNumPlayers[256];
	char szMaxPlayers[256];
	char szGUID[256];
	char *pszPrefix;

	pszPrefix = m_szDescription;

	sprintf(szGUID, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
		lpThisSD->guidInstance.Data1,
		lpThisSD->guidInstance.Data2,
		lpThisSD->guidInstance.Data3,
		lpThisSD->guidInstance.Data4[0],
		lpThisSD->guidInstance.Data4[1],
		lpThisSD->guidInstance.Data4[2],
		lpThisSD->guidInstance.Data4[3],
		lpThisSD->guidInstance.Data4[4],
		lpThisSD->guidInstance.Data4[5],
		lpThisSD->guidInstance.Data4[6],
		lpThisSD->guidInstance.Data4[7]);

	strncpy( szGameIdentifier, pszPrefix, sizeof(szGameIdentifier));
	strncat( szGameIdentifier, ": ", sizeof(szGameIdentifier));
	strncat( szGameIdentifier, m_szGameName, sizeof(szGameIdentifier));
	strncat( szGameIdentifier, szGUID, sizeof(szGameIdentifier));

#ifdef PREPEND_PREFIX_TO_GAME_NAMES
	char szGameName[256];
	strncpy( szGameName, pszPrefix, sizeof(szGameName));
	strncat( szGameName, ": ", sizeof(szGameName));
	strncat( szGameName, m_szGameName, sizeof(szGameName));
#endif

	//
	// figure out how many bots we've got
	//
	int iNumBots = (USER_FLAGS2_BOT_MASK & lpThisSD->dwUser2) / GetBottomBit(USER_FLAGS2_BOT_MASK);

	sprintf(szNumPlayers, "%d", lpThisSD->dwCurrentPlayers+iNumBots + ((lpThisSD->dwUser1 & NETFLAGS_DEDICATED_FLAG) ? -1 : 0));
	sprintf(szMaxPlayers, "%d", lpThisSD->dwMaxPlayers+iNumBots + ((lpThisSD->dwUser1 & NETFLAGS_DEDICATED_FLAG) ? -1 : 0));

    // mdm - need to lock the list to avoid race conditions since this enumeration comes on a separate
    pGameList->Lock();

	int index;
	char szFlags[25];
	char szPort[25];
	char szNumBots[25];

    if ( pGameList->Exists(szGameIdentifier) == false )
    {
    	if (pGameList->CreateEntry(szGameIdentifier) == false)
        {
            STOP(( "Unable to create game %s.\n", szGameIdentifier ));
            goto CLEANUP;
        }
    }

	sprintf(szFlags, "%u", lpThisSD->dwUser1);
	sprintf(szPort, "%u", GetDirectPlayPort());
	sprintf(szNumBots, "%u", iNumBots);

	//
	// the game didn't exist yet, so make it now
	//
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MOD_NAME,			"");
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PRODUCT_NAME,		Environment.applicationName);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PRODUCT_VERSION,		m_szVersion);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_ADDRESS,		m_szAddress);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_PORT,			szPort);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_NAME,			m_szGameName);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_NUM_PLAYERS,			szNumPlayers);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_NUM_BOTS,			szNumBots);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MAX_PLAYERS,			szMaxPlayers);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MAP_TYPE,			"");
	if (lpThisSD->dwFlags & DPSESSION_PASSWORDREQUIRED)
		pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PASSWORD_PROTECTED,	"Yes");
	else
		pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PASSWORD_PROTECTED,	"No");
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_INSTANCE,			szGUID);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_BROWSER_HANDLE,		GetBrowserHandle());
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_FLAGS,				szFlags);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PROTOCOL,			m_pszProtocol);
#ifdef PREPEND_PREFIX_TO_GAME_NAMES
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_NAME,			szGameName);
#endif

	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MAP_TYPE,			m_szMapName);

	index = (USER_FLAGS2_GAME_MASK & lpThisSD->dwUser2) / GetBottomBit(USER_FLAGS2_GAME_MASK);
	char szGameType[256];
	Environment.GetSpecialGameData(0, index, sizeof(szGameType), szGameType);
	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_GAME_TYPE,			szGameType);

	pGameList->SetField(szGameIdentifier, GAMELIST_KEY_UPDATE_TIME,		"new");
    pGameList->SetField(szGameIdentifier, GAMELIST_KEY_REFRESH_PENDING, GAMELIST_VALUE_REFRESH_PENDING_FALSE);

	Browse::PlayerList *pPlayerList;
	pPlayerList = pGameList->GetPlayerList( szGameIdentifier );
	if (pPlayerList)
	{
		pPlayerList->RemoveAll();

		for (int i=0 ; i<m_iNumPlayers ; i++)
		{
			char id[3];
			sprintf(id, "%02d", i);

			pPlayerList->CreateEntry(id);
			pPlayerList->SetField(id, PLAYERLIST_KEY_PLAYER_NAME, m_szPlayerName[i]);
			pPlayerList->SetField(id, PLAYERLIST_KEY_CLAN_NAME, m_szClanName[i]);
			pPlayerList->SetField(id, PLAYERLIST_KEY_UPDATE_TIME, "new");
		}
	}

CLEANUP:
    pGameList->Unlock();

	return TRUE;
}

//========================================================================
//========================================================================
bool __stdcall LANServerBrowser::Disconnect(void)
{
	if (m_bInitialized)
	{
		CloseHandle(m_hMutex);
		m_hMutex=NULL;
		m_bInitialized = false;
	}

	if (m_EnumPlayersQueue)
		m_EnumPlayersQueue->Terminate();

	RELEASE(m_lpDP);
	if (m_lpDP)
	{
		m_lpDP->Release();
		m_lpDP = NULL;
	}
	return true;
}

//========================================================================
//========================================================================
const char *__stdcall LANServerBrowser::GetDescription(void)
{
	return m_szDescription;
}

//========================================================================
//========================================================================
int __stdcall LANServerBrowser::GetStatus(void)
{
	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerBrowser::Synchronize(void)
{
	if (!m_lpDP)
		return false;

	HRESULT hr;
	DPSESSIONDESC2 dpsd;
	dpsd.dwSize				= sizeof(dpsd);
	dpsd.dwFlags			= 0;
	dpsd.guidInstance		= GUID_NULL;
	dpsd.guidApplication	= *(GUID *)&Environment.NetworkGUID;
	dpsd.dwMaxPlayers		= 0;
	dpsd.dwCurrentPlayers	= 0;
	dpsd.lpszSessionNameA	= 0;
	dpsd.lpszPasswordA		= 0;
	dpsd.dwReserved1		= 0;
	dpsd.dwReserved2		= 0;
	dpsd.dwUser1			= 0;
	dpsd.dwUser2			= 0;
	dpsd.dwUser3			= 0;
	dpsd.dwUser4			= 0;

	//
	// mark other sessions as "old", just to be changed to "new" when enumerated
	//
	if (true)
	{
		GetGameList()->Lock();

		Browse::IndexedTableView * indexView = GetGameList()->GetIndexedView();
		gosASSERT(indexView);
		int count;

		for ( count = 0; count < indexView->GetCount(); count++ )
		{
			char * gameID;
			if ( indexView->GetIndex(count, &gameID) == false )
			{
				PAUSE(( "Unable to get gamelist id at index %d", count ));
				continue;
			}

			//
			// we're only concerned with games we added
			//
            const char * handle = GetGameList()->GetDataField(gameID, GAMELIST_KEY_BROWSER_HANDLE);

            if (!handle || strcmp(handle, GetBrowserHandle()))
                continue;

			GetGameList()->SetField(gameID, GAMELIST_KEY_UPDATE_TIME, "old");
			SPEW(("mlyons", "Setting game to old"));
		}

		GetGameList()->Unlock();
	}


	//
	// now, enumerate if we can - note .01 is so if startupdatetime is zero we still won't execute enumeration
	//
	if ( m_StartUpdateTime <= 0.01 )
	{
		// mdm - early abort -- shouldn't be enumsessioning at this time.
		return true;
	}

	if (WaitForSingleObject(m_hMutex, 10) == WAIT_OBJECT_0)
	{
		if (FAILED(hr = m_lpDP->EnumSessions(&dpsd, ENUM_SESSIONS_TIMEOUT, StaticEnumSessionsCallback, this, DPENUMSESSIONS_ALL | DPENUMSESSIONS_ASYNC | DPENUMSESSIONS_PASSWORDREQUIRED)))
		{
			// no more updates
			m_StartUpdateTime = -UPDATE_COMPLETE_TIME; // so getnetstatus will say we are no longer updating
			return false;
		}
		ReleaseMutex(m_hMutex);

		//
		// remove old sessions
		//
		if (true)
		{
			GetGameList()->Lock();

			Browse::IndexedTableView * indexView = GetGameList()->GetIndexedView();
			gosASSERT(indexView);
			int count;

			for ( count = 0; count < indexView->GetCount(); count++ )
			{
				char * gameID;
				if ( indexView->GetIndex(count, &gameID) == false )
				{
					PAUSE(( "Unable to get gamelist id at index %d", count ));
					continue;
				}

				//
				// we're only concerned with games we added
				//
				if (strcmp(GetGameList()->GetDataField(gameID, GAMELIST_KEY_BROWSER_HANDLE), GetBrowserHandle()))
					continue;

				if (!strcmp(GetGameList()->GetDataField(gameID, GAMELIST_KEY_UPDATE_TIME), "old"))
				{
					//
					// it's an old game---delete it
					//
					SPEW(("mlyons", "Game deleted 'cause it's old"));
					GetGameList()->Remove(gameID);
					count--;
				}
			}

			GetGameList()->Unlock();
		}



	}
	else
	{
		// we were trying to enum a game that's been deleted
	}

	return true;
}

//========================================================================
//========================================================================
BOOL PASCAL LANServerBrowser::EmptyEnumSessionsCallback( LPCDPSESSIONDESC2 lpSessionDesc, LPDWORD lpdwTimeOut, DWORD dwFlags, void* lpContext )
{
	return FALSE;
}

//========================================================================
// PrepareJoinGame
//
// This is called just before open is called on a game. In here, we must
// set the DirectPlay object to be the correct protocol and to correctly
// enumerate the game on the LAN
//
//========================================================================
bool __stdcall LANServerBrowser::PrepareJoinGame(const char * szGameName, void **ppDPlay)
{
	IDirectPlay4A *				lpDP = NULL;
	IDirectPlayLobby3 *			lpDPL = NULL;
	HRESULT						hr;
	void* p_address = NULL;

	//
	// get the IP address of the game in question
	//
	const char *szGameIdentifier;
    szGameIdentifier = GetGameList()->GetIDFromField( GAMELIST_KEY_SERVER_NAME, szGameName );
	const char *szPort = GetGameList()->GetDataField(szGameIdentifier, GAMELIST_KEY_SERVER_PORT);
	int port;
	if (szPort)
		port = atoi(szPort);
	else
		port = 0;

	//
	// create the DirectPlay object
	//
	if (FAILED(hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_ALL, IID_IDirectPlay4A, (VOID**)&lpDP)))
		goto fail;

	//
	// release the original GameOS DirectPlay object
	//
	if (*(IDirectPlay4A **)ppDPlay)
	{
		(*(IDirectPlay4A **)ppDPlay)->Release();
		*(IDirectPlay4A **)ppDPlay = NULL;
	}

	//
	// copy the DirectPlay object back to the GameOS DirectPlay object
	//
	*(IDirectPlay4A **)ppDPlay = lpDP;

	//
	// create a lobby object
	//

	//
	// create a DirectPlayLobby object for creation of an address
	//
	if (FAILED(hr = CoCreateInstance( CLSID_DirectPlayLobby, NULL, CLSCTX_ALL, IID_IDirectPlayLobby3, (VOID**)&lpDPL)))
		goto fail;

	//
	// create the address needed
	//
	DPCOMPOUNDADDRESSELEMENT comAdd[3];
	DWORD Size;

	comAdd[0].guidDataType	= DPAID_ServiceProvider;
	comAdd[0].dwDataSize	= sizeof(GUID);
	comAdd[0].lpData		= m_AddressType;
	comAdd[1].guidDataType	= DPAID_INet;								// Create a compund address
	comAdd[1].dwDataSize	= strlen(m_szAddress) + 1;
	comAdd[1].lpData		= m_szAddress;
	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;

	//
	// create the address
	//
	lpDPL->CreateCompoundAddress(comAdd, num_elements, NULL, &Size );
	p_address = (void*)gos_Malloc(Size);
	if (FAILED(hr = lpDPL->CreateCompoundAddress(comAdd, num_elements, p_address, &Size )))
		goto fail;

	//
	// initialize the connection
	//
	if (FAILED(hr = lpDP->InitializeConnection(p_address, 0)))
		goto fail;

	//
	// enumerate
	//
	if (true)
	{
		DPSESSIONDESC2	dpsd;
		memset( &dpsd, 0, sizeof(dpsd) );

		dpsd.dwSize		= sizeof(dpsd);
		memcpy( &dpsd.guidApplication, &Environment.NetworkGUID, sizeof(GUID) );
		DWORD dwFlags = DPENUMSESSIONS_RETURNSTATUS | DPENUMSESSIONS_ALL | DPENUMSESSIONS_PASSWORDREQUIRED;

		if (FAILED(hr = lpDP->EnumSessions(&dpsd, 0, EmptyEnumSessionsCallback, 0, dwFlags )))
			goto fail;
	}

    if ( p_address )
    {
        gos_Free(p_address);
        p_address = NULL;
    }

	return true;

fail:
	RELEASE(lpDPL);
    if ( p_address )
    {
        gos_Free(p_address);
        p_address = NULL;
    }
	return false;
}




//========================================================================
//========================================================================
bool __stdcall LANServerBrowser::PrepareRefresh(void)
{
	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerBrowser::Refresh(char *gameID)
{
    // this is just like update for LAN games
	m_StartUpdateTime = gos_GetHiResTime();
	return true;
}

//========================================================================
// This function assumes the game list has been locked by the caller
//
//========================================================================
bool __stdcall LANServerBrowser::RefreshServerInfo(char *gameID)
{
    Browse::GameList * gameList = ServerBrowser::GetGameList();

    if ( gameList == NULL )
    {
        return false;
    }

	gameList->Lock();
	gameList->SetField( gameID, GAMELIST_KEY_SERVER_INFO_STATUS, "1" );
	gameList->Unlock();

	return true;

    // find out the protocol type for the game
    const char * pProtocol = gameList->GetDataField( gameID, GAMELIST_KEY_PROTOCOL );

    if ( pProtocol == NULL )
    {
        // no such game or no protocol identified for it.
        return false;
    }

    // see if the protocl matches our type
    if ( !strcmp( pProtocol, m_pszProtocol ) )
    {
        // this is us!
        // fire off a query for players on this server

        // get guidInstance from game
        GUID sessionInstanceGUID;
        if ( gameList->GetGUID( gameID, sessionInstanceGUID ) == false )
        {
            PAUSE(( "Unable to obtain sessionInstanceGUID for gameID %s.\n", gameID ));
            return false;
        }

		//
		// if it's a dedicated server, don't bother
		//
		const char *p;
		int iFlags;

		p = ServerBrowser::GetGameList()->GetDataField(gameID, GAMELIST_KEY_FLAGS);
		iFlags = (p) ? atoi(p) : 0;

		if (iFlags & NETFLAGS_DEDICATED_FLAG)
		{
			gameList->Lock();
			gameList->SetField( gameID, GAMELIST_KEY_SERVER_INFO_STATUS, "1" );
			gameList->Unlock();

			return true;
		}
		else
		{
			// add request for players to queue
			m_EnumPlayersQueue->AddRequest( gameList, gameID, m_lpDP, &sessionInstanceGUID );
    		return true;
		}
    }

    return false;

}

//========================================================================
//========================================================================
bool __stdcall LANServerBrowser::StartUpdate(void)
{
	m_StartUpdateTime = gos_GetHiResTime();

	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerBrowser::CancelActivity(void)
{
	// cancel the update of game sessions
	DPSESSIONDESC2 sessionDesc;
	memset( &sessionDesc, 0, sizeof(sessionDesc) );
	sessionDesc.dwSize=sizeof(sessionDesc);
	memcpy( &sessionDesc.guidApplication, &Environment.NetworkGUID, sizeof(GUID) );
	dplay4->EnumSessions( &sessionDesc, 0, StaticEnumSessionsCallback, 0, DPENUMSESSIONS_STOPASYNC | DPENUMSESSIONS_RETURNSTATUS );

	m_StartUpdateTime = -UPDATE_COMPLETE_TIME; // so getnetstatus will say we are no longer updating
	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerBrowser::GetNetStatus(void)
{
	if ( gos_GetHiResTime() >= m_StartUpdateTime + UPDATE_COMPLETE_TIME )
	{
		return false;
	}

	return true;
}




//========================================================================
//========================================================================
int __stdcall InitTCPIPServerBrowser(char *szIPAddress)
{
	LANServerBrowser * pSB = new LANServerBrowser();

	if (pSB == NULL)
		return 0;

	if (szIPAddress && szIPAddress[0])
	{
		strcpy(pSB->m_szAddress, szIPAddress);
		strcpy(pSB->m_szDescription, "IP");
		pSB->m_pszProtocol = GAMELIST_VALUE_PROTOCOL_INTERNET;
		pSB->m_AddressType = (void *)&DPSPGUID_TCPIP;
	}
	else
	{
		pSB->m_szAddress[0]=0;
		strcpy(pSB->m_szDescription, "LAN");
		pSB->m_pszProtocol = GAMELIST_VALUE_PROTOCOL_TCPIP_LAN;
		pSB->m_AddressType = (void *)&DPSPGUID_TCPIP;
	}

	pSB->Register();

	return atoi(pSB->GetBrowserHandle());
}



//========================================================================
//========================================================================
bool __stdcall InitIPXServerBrowser(void)
{
	LANServerBrowser *pSB = new LANServerBrowser();

	if (pSB == NULL)
		return NULL;

	strcpy(pSB->m_szDescription, "IPX");
	pSB->m_pszProtocol = GAMELIST_VALUE_PROTOCOL_IPX;
	pSB->m_AddressType = (void *)&DPSPGUID_IPX;

	pSB->Register();

	return true;
}





































































































int __stdcall LanUpdateList()
{

	if( !WaitingForEnum && (0==LastEnumTime || gos_GetElapsedTime(1)>LastEnumTime+3.0f) )
	{
		EnterCriticalSection( &EnumCriticalSection );
//
// Clear any games/players used last frame
//
		while( GameNames )
		{
			ListOfGames* pThis=GameNames;
			GameNames=GameNames->pNext;
			if( pThis->pPlayers )
			{
				ListOfNames* pTemp=pThis->pPlayers;
				while( pTemp )
				{
					ListOfNames* pThis=pTemp;			// Remove list of player names
					pTemp=pTemp->pNext;
					free( pThis );
				}
			}
			free( pThis );								// Remove gane name and information
		}
//
// Use previous enum while new enum running
//
		GameNames=EnumNames;
//
// Trigger enum
//
		EnumNames=0;
		WaitingForEnum=1;
		LeaveCriticalSection( &EnumCriticalSection );
		SetEvent( EnumEvent );
	}

	return 0;
};


int __stdcall LanCreateGame(char* GameName, char* PlayerName, int MaxPlayers, char* Password )
{
	return 0;
};


int __stdcall LanJoinGame( char* GameName, char* PlayerName, char* Password )
{
    return 0;
//	return gos_JoinGame( GameName, PlayerName, Password );
};


int __stdcall LanAddPlayer(DWORD dwItemID,char * PlayerName )
{
	return 0;
};

int __stdcall LanDestroyPlayer( DWORD dwItemID, char * PlayerName )
{
    return 0;
}


int __stdcall  DestroyLanGames()
{

//
// Make sure no player enumeration is still occuring
//
	EnterCriticalSection( &EnumCriticalSection );

//	GamesUpdateList=NULL;
//	GamesDisconnectNet=NULL;
//	GamesShutdownApp=NULL;
//	GamesCreateGame=NULL;
//	GamesJoinGame=NULL;
//	GamesAddPlayer=NULL;
//	GamesDestroyPlayer=NULL;
//
// Stop any game enumeration
//
	DPSESSIONDESC2 sessionDesc;
	memset( &sessionDesc, 0, sizeof(sessionDesc) );
	sessionDesc.dwSize=sizeof(sessionDesc);
	memcpy( &sessionDesc.guidApplication, &Environment.NetworkGUID, sizeof(GUID) );
	dplay4->EnumSessions( &sessionDesc, 0, EnumSessionsCallback, 0, DPENUMSESSIONS_STOPASYNC | DPENUMSESSIONS_RETURNSTATUS );

//
// Allow enum thread to start again
//
	LeaveCriticalSection( &EnumCriticalSection );


	return 0;
};

int _stdcall InitLanGames()
{
//	GamesUpdateList=LanUpdateList;
//	GamesDisconnectNet=DestroyLanGames;
//	GamesShutdownApp=DestroyLanGames;
//	GamesCreateGame=LanCreateGame;
//	GamesJoinGame=LanJoinGame;
//	GamesAddPlayer=LanAddPlayer;
//	GamesDestroyPlayer=LanDestroyPlayer;
	return 0;
};














#ifdef _DEBUG


//========================================================================
//========================================================================
typedef struct tagTHREADNAME_INFO
{
	DWORD	dwType;		// must be 0x1000
	char *	szName;		// pointer to name (in user addr space)
	DWORD	dwThreadID;	// thread ID (-1=caller thread)
	DWORD	dwFlags;	// reserved for future use, must be zero
} THREADNAME_INFO;


//========================================================================
//========================================================================
static void __stdcall SetThreadName(DWORD dwThreadID, char *szThreadName)
{
	THREADNAME_INFO info;

	info.dwType		= 0x1000;
	info.szName		= szThreadName;
	info.dwThreadID	= dwThreadID;
	info.dwFlags	= 0;

	__try
	{
		RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
	}
	__except (-1)
	{
	}
}


#endif










////MJL//////========================================================================
////MJL////// EnumPlayerList
////MJL//////
////MJL////// This is a linked list of requests to enumerate players. The list is
////MJL////// thread safe,
////MJL//////========================================================================
////MJL////class EnumPlayerList
////MJL////{
////MJL////public:
////MJL////	static bool AddRequest(Browse::PlayerList *pPlayerList, IDirectPlay4A * pDP, const GUID *pSessionInstance);
////MJL////	static bool Terminate(void);
////MJL////
////MJL////private:
////MJL////	void * operator new(size_t size) {
////MJL////		return malloc(size);
////MJL////	}
////MJL////	void operator delete( void* ptr )
////MJL////	{
////MJL////		free(ptr);
////MJL////	}
////MJL////	void operator delete[] ( void* ptr )
////MJL////	{
////MJL////		free(ptr);
////MJL////	}
////MJL////	static EnumPlayerList *		m_pHead;
////MJL////	static CRITICAL_SECTION		m_cs;
////MJL////	static HANDLE				m_hThread[MAX_ENUM_THREADS];
////MJL////	static int					m_iNumThreads;
////MJL////	static bool					m_bInitialized;
////MJL////	static bool					m_bKill;
////MJL////	static HANDLE				m_hWakeUpEvent;
////MJL////
////MJL////	static DWORD WINAPI EnumThreadProc(LPVOID lpParameter);
////MJL////	static BOOL FAR PASCAL StaticEnumPlayersCallback(DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext);
////MJL////
////MJL////	EnumPlayerList() {;}
////MJL////
////MJL////	//
////MJL////	// data for each request
////MJL////	//
////MJL////	Browse::PlayerList *		m_pPlayerList;
////MJL////	IDirectPlay4A *				m_pDP;
////MJL////	GUID						m_guidSessionInstance;
////MJL////	EnumPlayerList *			m_pNext;
////MJL////
////MJL////
////MJL////};
////MJL////
////MJL////
////MJL////
////MJL////
////MJL////
////MJL////
////MJL////
////MJL////
////MJL//////========================================================================
////MJL////// Static EnumPlayerList variables
////MJL//////========================================================================
////MJL////EnumPlayerList *	EnumPlayerList::m_pHead			= NULL;
////MJL////CRITICAL_SECTION	EnumPlayerList::m_cs			= {0};
////MJL////bool				EnumPlayerList::m_bInitialized	= false;
////MJL////bool				EnumPlayerList::m_bKill			= false;
////MJL////HANDLE				EnumPlayerList::m_hThread[]		= {0};
////MJL////HANDLE				EnumPlayerList::m_hWakeUpEvent	= NULL;
////MJL////int					EnumPlayerList::m_iNumThreads	= 0;
////MJL////
////MJL////
////MJL////
////MJL//////========================================================================
////MJL//////========================================================================
////MJL//////========================================================================
////MJL//////========================================================================
////MJL////bool EnumPlayerList::AddRequest(Browse::PlayerList *pPlayerList, IDirectPlay4A * pDP, const GUID *pSessionInstance)
////MJL////{
////MJL////	SetThreadName(GetCurrentThreadId(), "Main");
////MJL////
////MJL////	if (!m_bInitialized)
////MJL////	{
////MJL////		InitializeCriticalSection(&m_cs);
////MJL////		m_bInitialized = true;
////MJL////
////MJL////		m_hWakeUpEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
////MJL////		if (!m_hWakeUpEvent)
////MJL////		{
////MJL////			Terminate();
////MJL////			return false;
////MJL////		}
////MJL////
////MJL////		m_bKill = false;
////MJL////		for (m_iNumThreads=0 ; m_iNumThreads<NUM_ELEMENTS(m_hThread) ; m_iNumThreads++)
////MJL////		{
////MJL////			DWORD dwThreadId;
////MJL////			m_hThread[m_iNumThreads] = CreateThread(NULL, 0, EnumThreadProc, NULL, 0, &dwThreadId);
////MJL////
////MJL////			if (m_hThread == NULL)
////MJL////			{
////MJL////				Terminate();
////MJL////				return false;
////MJL////			}
////MJL////		}
////MJL////	}
////MJL////
////MJL////	EnterCriticalSection(&m_cs);
////MJL////
////MJL////	EnumPlayerList * pNext = m_pHead;
////MJL////
////MJL////	//
////MJL////	// first, check to see if there's already an EnumPlayers request in the
////MJL////	// list for the same session we're asking to add
////MJL////	//
////MJL////	EnumPlayerList *p = m_pHead;
////MJL////
////MJL////	while (p)
////MJL////	{
////MJL////		if (p->m_guidSessionInstance == *pSessionInstance)
////MJL////			break;
////MJL////
////MJL////		p=p->m_pNext;
////MJL////	}
////MJL////
////MJL////	if (!p)
////MJL////	{
////MJL////		//
////MJL////		// there was no request in the list for the same instance,
////MJL////		// so add one
////MJL////		//
////MJL////		m_pHead = new EnumPlayerList;
////MJL////		m_pHead->m_pNext = pNext;
////MJL////		m_pHead->m_pPlayerList			= pPlayerList;
////MJL////		m_pHead->m_pDP					= pDP;
////MJL////		m_pHead->m_pDP->AddRef();
////MJL////
////MJL////		m_pHead->m_guidSessionInstance	= *pSessionInstance;
////MJL////	}
////MJL////
////MJL////	LeaveCriticalSection(&m_cs);
////MJL////
////MJL////	SetEvent(m_hWakeUpEvent);
////MJL////	return true;
////MJL////}
////MJL////
////MJL//////========================================================================
////MJL//////========================================================================
////MJL////bool EnumPlayerList::Terminate(void)
////MJL////{
////MJL////	//
////MJL////	// wake up and kill all the threads
////MJL////	//
////MJL////	if (m_bInitialized)
////MJL////	{
////MJL////		//SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
////MJL////		//SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
////MJL////
////MJL////		EnterCriticalSection(&m_cs);
////MJL////
////MJL////		m_bKill = true;
////MJL////		while (m_iNumThreads)
////MJL////		{
////MJL////			int iThreadKilled;
////MJL////			SetEvent(m_hWakeUpEvent);
////MJL////			iThreadKilled = WaitForMultipleObjects(m_iNumThreads, m_hThread, FALSE, INFINITE) - WAIT_OBJECT_0;
////MJL////
////MJL////			CloseHandle(m_hThread[iThreadKilled]);
////MJL////
////MJL////			//
////MJL////			// now, copy the last still alive thread into this thread's handle
////MJL////			//
////MJL////			m_hThread[iThreadKilled] = m_hThread[m_iNumThreads - 1];
////MJL////			m_hThread[m_iNumThreads - 1] = NULL;
////MJL////
////MJL////			m_iNumThreads--;
////MJL////		}
////MJL////
////MJL////		if (m_hWakeUpEvent)
////MJL////		{
////MJL////			CloseHandle(m_hWakeUpEvent);
////MJL////			m_hWakeUpEvent = NULL;
////MJL////		}
////MJL////
////MJL////		gosASSERT(m_hThread[0] == NULL);
////MJL////		gosASSERT(m_hThread[1] == NULL);
////MJL////		gosASSERT(m_hThread[2] == NULL);
////MJL////		gosASSERT(m_hThread[3] == NULL);
////MJL////		gosASSERT(m_hThread[4] == NULL);
////MJL////		gosASSERT(m_hWakeUpEvent == NULL);
////MJL////
////MJL////		m_bInitialized = false;
////MJL////
////MJL////		//
////MJL////		// free up all memory used by the linked list
////MJL////		//
////MJL////		while (m_pHead)
////MJL////		{
////MJL////			EnumPlayerList *p = m_pHead->m_pNext;
////MJL////			delete m_pHead;
////MJL////			m_pHead = p;
////MJL////		}
////MJL////
////MJL////		LeaveCriticalSection(&m_cs);
////MJL////		DeleteCriticalSection(&m_cs);
////MJL////	}
////MJL////
////MJL////	return true;
////MJL////}
////MJL////
////MJL//////========================================================================
////MJL//////========================================================================
////MJL////DWORD WINAPI EnumPlayerList::EnumThreadProc(LPVOID lpParameter)
////MJL////{
////MJL////	SetThreadName(GetCurrentThreadId(), "Enum");
////MJL////	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
////MJL////
////MJL////	//
////MJL////	// wait until something wakes us up
////MJL////	//
////MJL////	while (true)
////MJL////	{
////MJL////		WaitForSingleObject(m_hWakeUpEvent, INFINITE);
////MJL////
////MJL////		if (m_bKill)
////MJL////			break;
////MJL////
////MJL////		EnumPlayerList * pThis = NULL;
////MJL////
////MJL////		EnterCriticalSection(&m_cs);
////MJL////		if (m_pHead)
////MJL////		{
////MJL////			pThis = m_pHead;
////MJL////			m_pHead = pThis->m_pNext;
////MJL////			pThis->m_pNext = NULL;
////MJL////		}
////MJL////		LeaveCriticalSection(&m_cs);
////MJL////
////MJL////		//
////MJL////		// see if we have a request to process
////MJL////		//
////MJL////		if (pThis)
////MJL////		{
////MJL////			//
////MJL////			// check to see if any of the players should be removed
////MJL////			//
////MJL////			if (true)
////MJL////			{
////MJL////				Browse::IndexedTableView * pIndexView = NULL;
////MJL////
////MJL////				pThis->m_pPlayerList->Lock();
////MJL////
////MJL////				pIndexView = pThis->m_pPlayerList->GetIndexedView();
////MJL////
////MJL////				for (int i=0 ; i<pIndexView->GetCount() ; i++)
////MJL////				{
////MJL////					char * playerID;
////MJL////
////MJL////					if ( pIndexView->GetIndex( i, &playerID ) == false )
////MJL////					{
////MJL////						PAUSE(( "Unable to find playerID for index %d", i ));
////MJL////						continue;
////MJL////					}
////MJL////					const char * playerUpdate = pThis->m_pPlayerList->GetDataField(playerID, PLAYERLIST_KEY_UPDATE_TIME );
////MJL////
////MJL////					if (!strcmp(playerUpdate, "old"))
////MJL////					{
////MJL////						// delete this player!
////MJL////						pThis->m_pPlayerList->Remove(playerID);
////MJL////						i--;
////MJL////					}
////MJL////					else
////MJL////					{
////MJL////						pThis->m_pPlayerList->SetField(playerID, PLAYERLIST_KEY_UPDATE_TIME, "old");
////MJL////					}
////MJL////				}
////MJL////
////MJL////				pThis->m_pPlayerList->Unlock();
////MJL////			}
////MJL////			pThis->m_pPlayerList->GetIndexedView()->GetCount();
////MJL////			pThis->m_pPlayerList->RemoveAll();
////MJL////
////MJL////#ifdef _DEBUG
////MJL////			OutputDebugString("Enumerating players...\n");
////MJL////#endif
////MJL////			if (FAILED(pThis->m_pDP->EnumPlayers(&pThis->m_guidSessionInstance, StaticEnumPlayersCallback, pThis, DPENUMPLAYERS_ALL | DPENUMPLAYERS_SESSION)))
////MJL////			{
////MJL////				// error!
////MJL////				__asm nop;
////MJL////			}
////MJL////
////MJL////			RELEASE(pThis->m_pDP);
////MJL////			delete pThis;
////MJL////		}
////MJL////	}
////MJL////
////MJL////	return 0;
////MJL////}
////MJL////
////MJL//////========================================================================
////MJL//////========================================================================
////MJL////BOOL FAR PASCAL EnumPlayerList::StaticEnumPlayersCallback(DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext)
////MJL////{
////MJL////	EnumPlayerList *pThis = (EnumPlayerList *)lpContext;
////MJL////
////MJL////	if ((dwPlayerType == DPPLAYERTYPE_PLAYER) && (lpName->lpszShortNameA != NULL))
////MJL////	{
////MJL////		pThis->m_pPlayerList->Lock();
////MJL////		pThis->m_pPlayerList->CreateEntry(lpName->lpszShortNameA);
////MJL////		pThis->m_pPlayerList->SetField(lpName->lpszShortNameA, PLAYERLIST_KEY_PLAYER_NAME, lpName->lpszShortNameA);
////MJL////		pThis->m_pPlayerList->SetField(lpName->lpszShortNameA, PLAYERLIST_KEY_UPDATE_TIME, "new");
////MJL////		pThis->m_pPlayerList->Unlock();
////MJL////	}
////MJL////
////MJL////	return TRUE;
////MJL////}
////MJL////









//========================================================================
//========================================================================
class LANServerAdvertiser : public ServerAdvertiser
{
	friend bool __stdcall InitLANServerAdvertiser(void);

private:
	// general use
	virtual	bool __stdcall Initialize(void);
	virtual	bool __stdcall Disconnect(void);
	virtual bool __stdcall Synchronize(void);
	virtual const char *__stdcall GetDescription(void);
	virtual bool __stdcall Release(void);


	// for servers
	virtual bool __stdcall CreateGame(const char *szGameName, const char *szPlayerName, int MaxPlayers, const char *szPassword, const GUID &guidInstance, DWORD dwFlags);
	virtual bool __stdcall CreatePlayer(DWORD dwItemID, const char *szPlayerName, bool bBot);
	virtual bool __stdcall RemovePlayer(DWORD dwItemID, const char *szPlayerName, bool bBot);
	virtual bool __stdcall SetItemValue(DWORD dwPlayer, const char * szName , const char * szValue);
	virtual bool __stdcall SetFlags(DWORD dwFlags);

	static int __stdcall EnumSessionsCallback( LPCDPSESSIONDESC2 lpSessionDesc, LPDWORD lpdwTimeOut, DWORD dwFlags, void* lpContext );

	void BuildSessionName(void);

	char					m_szFullSessionName[3000];
	char					m_szPlayerName[16][64];
	char					m_szClanName[16][64];
	char					m_szMapName[64];
	int						m_iNumPlayers;
	char					m_szGameName[64];
	DWORD					m_PlayerID[16];

	LANServerAdvertiser() : m_iNumPlayers(0)
	{;}


};

//========================================================================
//========================================================================
void LANServerAdvertiser::BuildSessionName(void)
{
	m_szFullSessionName[0] = 0;

	char *p;

	if ((p=strchr(m_szGameName, '\\')) != NULL) *p = 0;
	if ((p=strchr(m_szMapName, '\\')) != NULL) *p = 0;

	strncat(m_szFullSessionName, "\\g=", sizeof(m_szFullSessionName));
	strncat(m_szFullSessionName, m_szGameName, sizeof(m_szFullSessionName));
	strncat(m_szFullSessionName, "\\m=", sizeof(m_szFullSessionName));
	strncat(m_szFullSessionName, m_szMapName, sizeof(m_szFullSessionName));
	strncat(m_szFullSessionName, "\\v=", sizeof(m_szFullSessionName));
	strncat(m_szFullSessionName, Environment.version, sizeof(m_szFullSessionName));

	for (int i=0 ; i<m_iNumPlayers ; i++)
	{
		if ((p=strchr(m_szPlayerName[i], '\\')) != NULL) *p = 0;
		if ((p=strchr(m_szClanName[i], '\\')) != NULL) *p = 0;

		char string[256];
		sprintf((char *)string, "\\p%02d=%s\\c%02d=%s", i, m_szPlayerName[i], i, m_szClanName[i]);
		strncat(m_szFullSessionName, string, sizeof(m_szFullSessionName));
	}

	if (dplay4)
	{
		DPSESSIONDESC2 *pdpsd;
		DWORD dwSize;
		dplay4->GetSessionDesc(NULL, &dwSize);
		pdpsd = (DPSESSIONDESC2 *)gos_Malloc(dwSize);
		if (SUCCEEDED(dplay4->GetSessionDesc(pdpsd, &dwSize)))
		{
			pdpsd->lpszSessionNameA = (char *)m_szFullSessionName;
			dplay4->SetSessionDesc(pdpsd, 0);
		}

		gos_Free(pdpsd);
	}
	return;
}

//========================================================================
//========================================================================
bool __stdcall InitLANServerAdvertiser(void)
{
	LANServerAdvertiser *p;
	p = new LANServerAdvertiser();

	if (p == NULL)
		return false;

	p->Initialize();
	p->Register();

	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerAdvertiser::Release(void)
{
	Unregister();

	delete this;
	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerAdvertiser::Initialize(void)
{
	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerAdvertiser::Disconnect(void)
{
	return true;
}

//========================================================================
//========================================================================
const char *__stdcall LANServerAdvertiser::GetDescription(void)
{
	return "LAN";
}

//========================================================================
//========================================================================
bool __stdcall LANServerAdvertiser::Synchronize(void)
{
	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerAdvertiser::CreateGame(const char *szGameName, const char *szPlayerName, int MaxPlayers, const char *szPassword, const GUID &guidInstance, DWORD dwFlags)
{
	strncpy(m_szGameName, szGameName, sizeof(m_szGameName));
	m_szMapName[0]=0;

	if ((dwFlags & NETFLAGS_DEDICATED_FLAG)==0)
	{
		m_iNumPlayers = 1;

		strncpy(m_szPlayerName[0], szPlayerName, sizeof(m_szPlayerName[0]));
		FixSZString(m_szPlayerName[0]);
		m_szClanName[0][0] = 0;
		m_PlayerID[0] = (DWORD)gos_NetInformation( gos_MyID, 0, NULL );
	}

	BuildSessionName();
    return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerAdvertiser::CreatePlayer(DWORD dwItemID, const char *szPlayerName, bool bBot)
{
	if (bBot)
	{
		// if we're adding a bot, we don't really want to add a player, we really just want to
		// note it, and then reduce the max number of players truly available
		if (dplay4)
		{
			int mask = USER_FLAGS2_BOT_MASK;
			int shift = GetBottomBit(mask);

			DPSESSIONDESC2 *pdpsd;
			DWORD dwSize;
			dplay4->GetSessionDesc(NULL, &dwSize);
			pdpsd = (DPSESSIONDESC2 *)gos_Malloc(dwSize);
			if (SUCCEEDED(dplay4->GetSessionDesc(pdpsd, &dwSize)))
			{
				int iNumBots = (pdpsd->dwUser2 & mask) / (shift);
				iNumBots++;

				pdpsd->dwUser2 &= ~mask;
				pdpsd->dwUser2 |= (iNumBots*shift) & mask;
				pdpsd->dwMaxPlayers--;

				dplay4->SetSessionDesc(pdpsd, 0);
			}

			gos_Free(pdpsd);
		}


	}
	strncpy(m_szPlayerName[m_iNumPlayers], szPlayerName, sizeof(m_szPlayerName[m_iNumPlayers]));
	m_szClanName[m_iNumPlayers][0]=0;
	m_PlayerID[m_iNumPlayers]=dwItemID;
	m_iNumPlayers++;
	BuildSessionName();
	return true;
}

//========================================================================
//========================================================================
bool __stdcall LANServerAdvertiser::RemovePlayer(DWORD dwItemID, const char *szPlayerName, bool bBot)
{
	//
	// if we've got a player name, try to remove the player by name
	//
	int index=0;

	//
	// no player name, use the id
	//
	for (index=0 ; index<m_iNumPlayers ; index++)
	{
		if (m_PlayerID[index] == dwItemID)
			break;
	}
	if (index<m_iNumPlayers)
	{
		m_iNumPlayers--;
		strcpy(m_szPlayerName[index], m_szPlayerName[m_iNumPlayers]);
		strcpy(m_szClanName[index], m_szClanName[m_iNumPlayers]);
		m_PlayerID[index] = m_PlayerID[m_iNumPlayers];
		if (bBot)
		{
			// if we're adding a bot, we don't really want to add a player, we really just want to
			// note it, and then reduce the max number of players truly available
			if (dplay4)
			{
				int mask = USER_FLAGS2_BOT_MASK;
				int shift = GetBottomBit(mask);

				DPSESSIONDESC2 *pdpsd;
				DWORD dwSize;
				dplay4->GetSessionDesc(NULL, &dwSize);
				pdpsd = (DPSESSIONDESC2 *)gos_Malloc(dwSize);
				if (SUCCEEDED(dplay4->GetSessionDesc(pdpsd, &dwSize)))
				{
					int iNumBots = (pdpsd->dwUser2 & mask) / (shift);
					iNumBots--;

					pdpsd->dwUser2 &= ~mask;
					pdpsd->dwUser2 |= (iNumBots*shift) & mask;
					pdpsd->dwMaxPlayers++;

					dplay4->SetSessionDesc(pdpsd, 0);
				}

				gos_Free(pdpsd);
			}
		}
	}
	BuildSessionName();
	return true;
}


//========================================================================
//========================================================================
bool __stdcall LANServerAdvertiser::SetFlags(DWORD dwFlags)
{
	if (dplay4)
	{
		DPSESSIONDESC2 *pdpsd;
		DWORD dwSize;
		BuildSessionName();
		dplay4->GetSessionDesc(NULL, &dwSize);
		pdpsd = (DPSESSIONDESC2 *)gos_Malloc(dwSize);
		if (SUCCEEDED(dplay4->GetSessionDesc(pdpsd, &dwSize)))
		{
			pdpsd->dwUser1 = dwFlags;
			dplay4->SetSessionDesc(pdpsd, 0);
		}

		gos_Free(pdpsd);
	}

	return true;

}

//========================================================================
//========================================================================
bool __stdcall LANServerAdvertiser::SetItemValue(DWORD player, const char * Name , const char * Value)
{
	if (!strcmp(Name, GAMELIST_KEY_MAP_TYPE))
	{
		strncpy(m_szMapName, Value, sizeof(m_szMapName));
		BuildSessionName();
	}
	else if (!strcmp(Name, GAMELIST_KEY_GAME_TYPE))
	{
		int mask = USER_FLAGS2_GAME_MASK;
		int shift = GetBottomBit(mask);

		//
		// find the index in the list
		//
		int index;
		char szGameType[256];
		for (index=0 ; true ; index++)
		{
			Environment.GetSpecialGameData(0, index, sizeof(szGameType), szGameType);

			if (!strcmp(szGameType, Value))
				break;

			if (szGameType[0]==0)
				break;
		}
		if (szGameType[0]==0)
		{
			for (index=0 ; true ; index++)
			{
				Environment.GetSpecialGameData(0, index, sizeof(szGameType), szGameType);
				if (!stricmp(szGameType, Value))
					break;

				if (szGameType[0]==0)
					break;
			}

			if (szGameType[0]==0)
				index--;
		}

		// now, put the index in the right bit location
		index *= shift;
		index &= mask;

		//
		// set the session description
		//
		if (dplay4)
		{
			DPSESSIONDESC2 *pdpsd;
			DWORD dwSize;
			BuildSessionName();
			dplay4->GetSessionDesc(NULL, &dwSize);
			pdpsd = (DPSESSIONDESC2 *)gos_Malloc(dwSize);
			if (SUCCEEDED(dplay4->GetSessionDesc(pdpsd, &dwSize)))
			{
				pdpsd->dwUser2 &= ~mask;
				pdpsd->dwUser2 |= index;

				dplay4->SetSessionDesc(pdpsd, 0);
				gos_Free(pdpsd);
			}
		}
	}
	else if (!strcmp(Name, PLAYERLIST_KEY_CLAN_NAME))
	{
		//
		// find the player
		//
		int index=0;
		for (index=0 ; index<m_iNumPlayers ; index++)
		{
			if (player == (int)m_PlayerID[index])
				break;
		}
		if (index<m_iNumPlayers)
		{
			// we found the player
			strncpy(m_szClanName[index], Value, sizeof(m_szClanName[index]));
			FixSZString(m_szClanName[index]);
			BuildSessionName();
		}
	}

	return true;
}

int GetDirectPlayPort(void)
{
	int port;
	int size = sizeof(port);

	gos_LoadDataFromRegistry("DirectPlayPort", &port, (DWORD *)&size);
	if (size == 0)
	{
		port = 0;
		gos_SaveDataToRegistry("DirectPlayPort", &port, sizeof(port));
	}

	return port;
}
