// GameServDlg.cpp : implementation file
//


// Includes...

#include "stdafx.h"
#include "coolserv.h"
#include "GameServDlg.h"
#include "NetStart.h"
#include "Sparam.h"
#include "Utils.h"
#include "mmsystem.h"


// Defines...

#define DEFAULT_PORT	27888
#define USE_POSTMSG		0


// Debug...

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


// Macros...

#define DEC_TIMER(t, d)		if (d >= t) t = 0; else t -= d;


// Externs...

extern	ServerInfo		g_ServerInfo;
extern	ServerOptions	g_ServerOptions;
extern	NetGame			g_NetGame;
extern	BOOL	 		g_bEmptyExit;


// Globals...

DWORD	g_timeCur   = 0;
DWORD	g_timeLast  = 0;
DWORD	g_timeDelta = 0;
DWORD	g_timeStart = 0;

DWORD	g_dwPosted  = 0;
DWORD	g_dwHandled = 0;
DWORD	g_dwRemoved = 0;

BOOL	g_bDoEmptyExit    = FALSE;
int		g_nEmptyExitDelay = 60;
DWORD	g_timerEmptyExit  = 0;
BOOL	g_bNoExitConfirm  = FALSE;

CString	g_sFullTcpIpAddress;
WORD	g_wPort = 0;

#define SERVER_VERSION	"2.2"
#define DEFAULT_PORT	27888

CString	g_sSpyGameName;
CString	g_sSpyGameVer;
CString	g_sSpyGameKey;
CString g_sWebGameName;
CString	g_sWebGameVer;
CString g_sWebRegUrl;


// Statics...

static	DWORD	s_timerWebRegUpdate  = 400; 

static	BOOL	s_bThreadDone = FALSE;


// Classes...

class CGameServerAppHandler : public ServerAppHandler
{
public:
	DRESULT ShellMessageFn(char* pInfo);
	DRESULT ConsoleOutputFn(char* pInfo);
	DRESULT	OutOfMemory();
	DRESULT ProcessPacket(char *pData, DDWORD dataLen, DBYTE senderAddr[4], D_WORD senderPort);
};


// Globals...

CGameServDlg*			g_pDialog = NULL;
CGameServerAppHandler	g_AppHandler;


// Prototypes...

DWORD WINAPI ThreadUpdate(LPVOID lpParameter);


// Functions...

DRESULT CGameServerAppHandler::ShellMessageFn(char* pInfo)
{
	if (g_pDialog)
	{
		g_pDialog->OnShellMessage(pInfo);
	}

	return(LT_OK);
}

DRESULT CGameServerAppHandler::ConsoleOutputFn(char* pInfo)
{
	if (g_pDialog)
	{
		g_pDialog->OnConsoleOutput(pInfo);
	}

	return(LT_OK);
}

DRESULT CGameServerAppHandler::OutOfMemory()
{
	if (g_pDialog)
	{
		g_pDialog->OnOutOfMemory();
	}

	return(LT_OK);
}

DRESULT CGameServerAppHandler::ProcessPacket(char *pData, DDWORD dataLen, DBYTE senderAddr[4], D_WORD senderPort)
{
	if(g_pDialog)
	{
		g_pDialog->OnProcessNetPacket(pData, dataLen, senderAddr, senderPort);
	}

	return LT_OK;
}





// ----------------------------------------------------------------------- //
// Replacement for MFC's trace.  Doesn't ASSERT.
// ----------------------------------------------------------------------- //

void SS_Trace(const char *pMsg, ...)
{
	#ifdef _DEBUG
		char buf[1024*16];
		va_list marker;

		va_start(marker, pMsg);
		vsprintf(buf, pMsg, marker);
		va_end(marker);

		OutputDebugString(buf);
	#endif
}


/////////////////////////////////////////////////////////////////////////////
// CGameServSendHandler
/////////////////////////////////////////////////////////////////////////////

void CGameServSendHandler::SendTo(const void *pData, unsigned long len, const char *sAddr, unsigned long port)
{
	ServerInterface *pMgr;

	if(m_pDlg && (pMgr = m_pDlg->GetServerMgr()))
	{
		pMgr->SendTo(pData, len, sAddr, port);
	}
}



/////////////////////////////////////////////////////////////////////////////
// CGameServDlg dialog

CGameServDlg::CGameServDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CGameServDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CGameServDlg)
	//}}AFX_DATA_INIT

	Clear();

	m_pConfigFilename = NULL;
	g_pDialog = this;
	m_SendHandler.SetDlg(this);

	m_sGameSpyGameMode = "openplaying";
	m_sGameSpyGameType = "deathmatch";
}


void CGameServDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CGameServDlg)
	DDX_Control(pDX, IDC_PLAYERS_LIST, m_lbPlayers);
	DDX_Control(pDX, IDC_LEVELS_LIST, m_lbLevels);
	DDX_Control(pDX, IDC_CONSOLE_WINDOW, m_edConsole);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CGameServDlg, CDialog)
	//{{AFX_MSG_MAP(CGameServDlg)
	ON_WM_SHOWWINDOW()
	ON_WM_CLOSE()
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_QUIT, OnQuit)
	ON_BN_CLICKED(IDC_SERVER, OnServer)
	ON_BN_CLICKED(IDC_CONSOLE_SEND, OnConsoleSend)
	ON_BN_CLICKED(IDC_CONSOLE_CLEAR, OnConsoleClear)
	ON_BN_CLICKED(IDC_COMMANDS_NEXTLEVEL, OnCommandsNextLevel)
	ON_BN_CLICKED(IDC_PLAYERS_BOOT, OnPlayersBoot)
	ON_LBN_SELCHANGE(IDC_PLAYERS_LIST, OnSelchangePlayersList)
	ON_WM_DESTROY()
	ON_BN_CLICKED(IDC_COMMANDS_OPTIONS, OnCommandsOptions)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


BOOL CGameServDlg::Init()
{
	if(!m_bCSInitted)
	{
		InitializeCriticalSection(&m_CS);
		m_bCSInitted = TRUE;
	}
	
	// Display a console message...

	WriteConsoleString(IDS_CONSOLE_INITSERVER);


	// Get the server interface...

	m_pServerMgr = GetServerInterface();
	if (!m_pServerMgr) return(FALSE);


	// Set our app handler...

	m_pServerMgr->SetAppHandler(&g_AppHandler);


	// Set the tab stops for the player list box...

	int nTabs[10] = { 30, 116, 181, 84, 86, 88, 90, 92, 94, 96 };

	m_lbPlayers.SetTabStops(10, nTabs);


	// Load some strings...

	g_sSpyGameName.LoadString(IDS_SPYGAMENAME);
	g_sSpyGameVer.LoadString(IDS_SPYGAMEVER);
	g_sSpyGameKey.LoadString(IDS_SPYGAMEKEY);
	g_sWebGameName.LoadString(IDS_WEBGAMENAME);
	g_sWebGameVer.LoadString(IDS_WEBGAMEVER);
	g_sWebRegUrl.LoadString(IDS_WEBREGURL);


	// All done...

	WriteConsoleString(IDS_CONSOLE_SERVERINITED);
	return(TRUE);
}

void CGameServDlg::Term()
{
	if(m_bCSInitted)
	{
		DeleteCriticalSection(&m_CS);
		m_bCSInitted = FALSE;
	}

	Clear();
	g_pDialog = NULL;

	Sleep(2);
	SS_Trace("Updates posted  = %lu\n", g_dwPosted);
	SS_Trace("Updates handled = %lu\n", g_dwHandled);
	SS_Trace("Updates removed = %lu\n", g_dwRemoved);
	SS_Trace("Updates per sec = %i\n", (int)((float)(g_dwHandled / (float)(timeGetTime() - g_timeStart)) * 1000));
}

void CGameServDlg::Clear()
{
	m_bCSInitted = FALSE;
	memset(&m_CS, 0, sizeof(m_CS));

	m_pServerMgr = NULL;
	g_pDialog    = NULL;

	m_bRunning       = FALSE;
	m_bLevelChanging = FALSE;

	m_timeServerRunning = 0;
	m_timeServerStart   = 0;
	m_timeServerLast    = 0;

	m_timeLevelRunning = 0;
	m_timeLevelStart   = 0;
	m_timeLevelLast    = 0;

	m_cPlayers  = 0;
	m_nCurLevel = 0;

	m_hThread    = NULL;
	m_dwThreadID = NULL;

	m_sCurLevel.Empty();
}

BOOL CGameServDlg::StartServer()
{
	// Sanity checks...

	if (!m_pServerMgr) return(FALSE);


	// Set the debug level...

	m_pServerMgr->RunConsoleString("debuglevel 0");


	// Set the server options...

	NetStart_RunServerOptions(m_pServerMgr, &g_ServerOptions);


	// Look at some console vars...

	HCONSOLEVAR hVar = NULL;

	DRESULT dr = m_pServerMgr->GetConsoleVar("WebRegUpdate", &hVar, NULL);
	if (dr == LT_OK && hVar)
	{
		float fVal = 0;
		dr = m_pServerMgr->GetVarValueFloat(hVar, &fVal);
		if (dr == LT_OK && fVal > 1)
		{
			s_timerWebRegUpdate = (DWORD)fVal;
		}
	}

	hVar = NULL;
	dr   = m_pServerMgr->GetConsoleVar("EmptyExit", &hVar, NULL);
	if (dr == LT_OK && hVar)
	{
		float fVal = 0;
		dr = m_pServerMgr->GetVarValueFloat(hVar, &fVal);
		if (dr == LT_OK && fVal >= 1)
		{
			g_bEmptyExit = TRUE;
		}
	}

	hVar = NULL;
	dr   = m_pServerMgr->GetConsoleVar("EmptyExitDelay", &hVar, NULL);
	if (dr == LT_OK && hVar)
	{
		float fVal = 0;
		dr = m_pServerMgr->GetVarValueFloat(hVar, &fVal);
		if (dr == LT_OK && fVal >= 1)
		{
			g_nEmptyExitDelay = (int)fVal;
		}
	}

	hVar = NULL;
	dr   = m_pServerMgr->GetConsoleVar("SpyGameType", &hVar, NULL);
	if (dr == LT_OK && hVar)
	{
		char sTemp[64] = { "" };
		dr = m_pServerMgr->GetVarValueString(hVar, sTemp, 64);
		if (dr == LT_OK && sTemp[0] != '\0')
		{
			m_sGameSpyGameType = sTemp;
		}
	}

	hVar = NULL;
	dr   = m_pServerMgr->GetConsoleVar("SpyGameMode", &hVar, NULL);
	if (dr == LT_OK && hVar)
	{
		char sTemp[64] = { "" };
		dr = m_pServerMgr->GetVarValueString(hVar, sTemp, 64);
		if (dr == LT_OK && sTemp[0] != '\0')
		{
			m_sGameSpyGameMode = sTemp;
		}
	}


	// Host the game...

	CWaitCursor wc;

	NetHost nh;

	memset(&nh, 0, sizeof(nh));

	nh.m_dwMaxPlayers = g_ServerInfo.m_dwMaxPlayers;
	nh.m_Port         = NetStart_GetPort();

	CString sHost;
	sHost.LoadString(IDS_APPNAME);

	Sparam_Add(nh.m_sName, NST_GAMENAME,  g_ServerInfo.m_sName);
	Sparam_Add(nh.m_sName, NST_GAMEHOST,  sHost);
	Sparam_Add(nh.m_sName, NST_GAMELEVEL, g_NetGame.m_sLevels[0]);
	Sparam_Add(nh.m_sName, NST_GAMETYPE,  g_NetGame.m_byType);

	if (m_pServerMgr->HostGame(&nh) != DTRUE)
	{
		WriteConsoleString(IDS_CONSOLE_UNABLETOHOST);
		StopServer();
		return(FALSE);
	}


	// Start the world...

	StartGameRequest req;
	memset(&req, 0, sizeof(req));

	req.m_Type = STARTGAME_HOST;
	req.m_pGameInfo   = &g_NetGame;
	req.m_GameInfoLen = sizeof(NetGame_t);

	strcpy(req.m_WorldName, g_NetGame.m_sLevels[0]);

	if (m_pServerMgr->StartWorld(&req) != DTRUE)
	{
		WriteConsoleString(IDS_CONSOLE_UNABLETOSTARTWORLD);
		WriteConsoleString(req.m_WorldName);
		StopServer();
		return(FALSE);
	}


	// Update the service info with the tcp/ip address if available...

	g_ServerInfo.m_sAddress[0] = '\0';
	g_wPort = 0;

	m_pServerMgr->GetTcpIpAddress(g_ServerInfo.m_sAddress, 128, g_wPort);

	CString sService    = g_ServerInfo.m_sService;
	g_sFullTcpIpAddress = "";
	if (g_ServerInfo.m_sAddress[0] != '\0')
	{
		sService += " ";
		g_sFullTcpIpAddress = g_ServerInfo.m_sAddress;
		if (g_wPort > 0 && g_wPort != DEFAULT_PORT)
		{
			char sTemp[32];
			wsprintf(sTemp, ":%i", g_wPort);
			g_sFullTcpIpAddress += sTemp;
		}
		sService += "[";
		sService += g_sFullTcpIpAddress;
		sService += "]";
	}
	SetDlgItemText(IDC_SERVER_SERVICE, sService);


	// Tell the server shell to update the game info parameters...

	m_pServerMgr->SendToServerShell("GAMEINIT");


	// Tell the server that this is a stand-alone server hosted game...

	m_pServerMgr->SendToServerShell("SERVHOST");


	// Init some stuff now that we're running...

	m_bRunning        = TRUE;
	m_timeServerStart = timeGetTime();
	g_timeStart       = timeGetTime();
	m_timeLevelStart  = m_timeServerStart;


	// Init the GameSpy manager...

	if (g_ServerInfo.m_bUseGameSpy)
	{
		if (m_GameSpyMgr.Init(g_sSpyGameName, g_sSpyGameVer, g_sSpyGameKey, 0, g_wPort, (g_wPort + 149), GSMF_USEGAMEPORTFORHEARTBEAT))
		{
			m_GameSpyMgr.SetSendHandler(&m_SendHandler);
		}
		else
		{
			g_ServerInfo.m_bUseGameSpy = FALSE;
		}
	}


	// Create the thread to do our updating...

	m_hThread = CreateThread(NULL, 0, ThreadUpdate, (void*)g_pDialog, 0, &m_dwThreadID);


	// All done...

	WriteConsoleString(IDS_CONSOLE_SERVERRUNNING);
	return(TRUE);
}

BOOL CGameServDlg::StopServer()
{
	// Unregister the server if necessary...

	if (m_bRunning)
	{
		UnregisterServer();
	}


	// Terminate the GameSpy manager...

	m_GameSpyMgr.Term();


	// Flag that we are done...

	m_bRunning = FALSE;


	// Wait for the thread to stop...

	if (m_hThread)
	{
		Sleep(0);
		DWORD dwRet = WaitForSingleObject(m_hThread, 1000);
		Sleep(0);
		
		if (dwRet == WAIT_OBJECT_0)
		{
			CloseHandle(m_hThread);
		}
		else
		{
			TerminateThread(m_hThread, 0);
		}

		m_hThread = NULL;
	}


	// All done...

	return(TRUE);
}

void CGameServDlg::WriteConsoleString(LPCTSTR pMsg, ...)
{
	TCHAR		str[500];
	va_list		marker;
	int			nLen;

	static int nMax = 500;

	if(m_edConsole.GetLineCount() > nMax)
	{
		// Nuke the oldest 75%.
		int iLine = (m_edConsole.GetLineCount()*75) / 100;
		int iChar = m_edConsole.LineIndex(iLine);
		
		m_edConsole.SetRedraw(FALSE);
		m_edConsole.SetSel(0, iChar);
		m_edConsole.ReplaceSel("", FALSE);
		m_edConsole.SetRedraw(TRUE);
	}

	va_start(marker, pMsg);
	vsprintf(str, pMsg, marker);
	va_end(marker);

	int len = strlen(str);
	if (len <= 0) return;
	if (len > 0 && str[len-1] < 32) str[len-1] = '\0';

	strcat(str, "\r\n");

	nLen = m_edConsole.SendMessage(EM_GETLIMITTEXT, 0, 0);
	m_edConsole.SetSel(nLen, nLen);
	m_edConsole.ReplaceSel(str);
}

void CGameServDlg::WriteConsoleString(int nStringID)
{
	CString sMsg;
	if (!sMsg.LoadString(nStringID)) return;
	WriteConsoleString(sMsg);
}

/////////////////////////////////////////////////////////////////////////////
// CGameServDlg message handlers

BOOL CGameServDlg::OnInitDialog() 
{
	// Let the base class do it's thing...

	CDialog::OnInitDialog();


	// Fill in some of the text info...

	SetDlgItemText(IDC_SERVER_NAME, g_ServerInfo.m_sName);
	SetDlgItemText(IDC_SERVER_TIME, "0:00");

	CString sService(g_ServerInfo.m_sService);
	if (g_ServerInfo.m_sAddress[0] != '\0')
	{
		sService += " (";
		sService += g_ServerInfo.m_sAddress;
		sService += ")";
	}
	SetDlgItemText(IDC_SERVER_SERVICE, sService);

	SetDlgItemText(IDC_GAME_PLAYERS, "0");
	SetDlgItemText(IDC_GAME_CURLEVEL, g_NetGame.m_sLevels[0]);
	SetDlgItemText(IDC_GAME_TIME, "0:00");

	m_sCurLevel = g_NetGame.m_sLevels[0];

	if (g_NetGame.m_byNumLevels > 1) SetDlgItemText(IDC_GAME_NEXTLEVEL, g_NetGame.m_sLevels[1]);
	else SetDlgItemText(IDC_GAME_NEXTLEVEL, "");


	CString sBuf;
	if (g_NetGame.m_byEnd == NGE_FRAGS) sBuf.Format("%i Frags", g_NetGame.m_dwEndFrags);
	else if (g_NetGame.m_byEnd == NGE_TIME) sBuf.Format("%i Minutes", g_NetGame.m_dwEndTime);
	else if (g_NetGame.m_byEnd == NGE_FRAGSANDTIME) sBuf.Format("%i Frags or %i Minutes", g_NetGame.m_dwEndFrags, g_NetGame.m_dwEndTime);
	else sBuf = "None. Level never ends";
	SetDlgItemText(IDC_GAME_GOAL, sBuf);

	for (int i = 0; i < g_NetGame.m_byNumLevels; i++)
	{
		m_lbLevels.AddString(g_NetGame.m_sLevels[i]);
	}


	// Set the icon...

	HICON hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	SetIcon(hIcon, TRUE);
	SetIcon(hIcon, FALSE);


	for(i=0; i < 30; i++)
	{
		if(SetTimer((UINT)i, 500, NULL))
			break;
	}

	// All done...

	return(TRUE);
}

BOOL CGameServDlg::OnCommand(WPARAM wParam, LPARAM lParam) 
{
	switch (wParam)
	{
		case ID_INIT:
		{
			if (!Init())
			{
				return(TRUE);
			}

			if (!StartServer())
			{
				return(TRUE);
			}

			return(TRUE);
		}

		case ID_UPDATE:
		{
			OnUpdate();
			return(TRUE);
		}
	}
	
	return(CDialog::OnCommand(wParam, lParam));
}

void CGameServDlg::OnShowWindow(BOOL bShow, UINT nStatus) 
{
	static BOOL bNeedInit = TRUE;

	CDialog::OnShowWindow(bShow, nStatus);

	if (bShow && bNeedInit)
	{
		bNeedInit = FALSE;
		PostMessage(WM_COMMAND, ID_INIT);
	}
}

void CGameServDlg::UpdateServerMgr(DWORD timeDelta)
{
	// Sanity checks...

	if (!m_pServerMgr) return;
	if (!IsRunning()) return;


	// Let the server update...

	if (m_pServerMgr->Update(0) != DTRUE)
	{
		WriteServerError();
		StopServer();
	}
}

void CGameServDlg::WriteServerError()
{
	char str[256];

	if (m_pServerMgr)
	{
		m_pServerMgr->GetErrorString(str, sizeof(str)-1);
		WriteConsoleString(str);
	}
}

void CGameServDlg::OnClose() 
{
	CDialog::OnClose();
}

void CGameServDlg::OnTimer(UINT nIDEvent) 
{
	// Call the update function...

	//OnUpdate();
	UpdateTimers();
}


void CGameServDlg::OnUpdate()
{
	// Increment our counter...

	g_dwHandled++;


	// If we're currently changing levels, just return...

	if (IsLevelChanging()) return;


	// Get the current time...

	g_timeCur = timeGetTime();
	if (g_timeLast == 0) g_timeLast = g_timeCur;


	// Calculate the delta time...

	g_timeDelta = g_timeCur - g_timeLast;
	g_timeLast  = g_timeCur;


	// Update everything if we're running...

	if (IsRunning())
	{
		// Update the empty exit...

		if (g_bDoEmptyExit)
		{
			DEC_TIMER(g_timerEmptyExit, g_timeDelta);
			if (g_timerEmptyExit == 0)
			{
				g_bNoExitConfirm = TRUE;
				PostMessage(WM_CLOSE);
			}
		}


		// Update the server manager...

		UpdateServerMgr(g_timeDelta);
		if (!IsRunning()) return;


		// Update the game spy manager...

		UpdateGameSpyMgr(g_timeDelta);


		// Update the server registration...

		UpdateServerRegistration(g_timeDelta);
	}
}

void CGameServDlg::UpdateTimers()
{
	DWORD timeCur;

	if(!IsRunning())
		return;

	timeCur = timeGetTime();

	// Update the server time...

	m_timeServerRunning = timeCur - m_timeServerStart;
	if ((m_timeServerLast / 1000) != (m_timeServerRunning / 1000))
	{
		SetDlgItemText(IDC_SERVER_TIME, TimeToString(m_timeServerRunning));
		m_timeServerLast = m_timeServerRunning;
	}


	// Update the game/level time...

	m_timeLevelRunning = timeCur - m_timeLevelStart;
	if ((m_timeLevelLast / 1000) != (m_timeLevelRunning / 1000))
	{
		SetDlgItemText(IDC_GAME_TIME, TimeToString(m_timeLevelRunning));
		m_timeLevelLast = m_timeLevelRunning;
	}


	// Remove any update messages in the queue...

	RemoveMessage(WM_TIMER, 500);
}


void CGameServDlg::OnQuit() 
{
	PostMessage(WM_COMMAND, IDCANCEL);
}

void CGameServDlg::OnServer() 
{
	PostMessage(WM_COMMAND, IDCANCEL);
}

BOOL CGameServDlg::UpdateServerRegistration(DWORD timeDelta)
{
	// Sanity checks...

	if (!m_pServerMgr) return(FALSE);
	if (!g_ServerInfo.m_bRegisterServer) return(FALSE);
	if (g_ServerInfo.m_sAddress[0] == '\0') return(FALSE);


	// Check if it's really time to update...

	static DWORD timerUpdate = 7000;

	DEC_TIMER(timerUpdate, timeDelta);
	if (timerUpdate != 0) return(FALSE);
	else timerUpdate = s_timerWebRegUpdate * 1000;


	// Build the info string...

	CString sInfo;

	sInfo.Format("game=%s&name=%s&ip=%s&players=%i&version=%s", g_sWebGameName, g_ServerInfo.m_sName, g_sFullTcpIpAddress, m_pServerMgr->GetNumClients(), g_sWebGameVer);


	// Send the server info the web page...

	SendWebInfo(sInfo, FALSE);


	// All done...

	return(TRUE);
}

BOOL CGameServDlg::UpdateGameSpyMgr(DWORD timeDelta)
{
	// Sanity checks...

	if (!m_pServerMgr) return(FALSE);
	if (!g_ServerInfo.m_bUseGameSpy) return(FALSE);


	// Update the GameSpy manager...

	m_GameSpyMgr.Update();


	// All done...

	return(TRUE);
}

BOOL CGameServDlg::UnregisterServer()
{
	// Sanity checks...

	if (!g_ServerInfo.m_bRegisterServer) return(FALSE);
	if (g_ServerInfo.m_sAddress[0] == '\0') return(FALSE);


	// Build the info string...

	CString sInfo;

	sInfo.Format("game=%s&name=%s&ip=%s&players=-1&version=%s", g_sWebGameName, g_ServerInfo.m_sName, g_sFullTcpIpAddress, g_sWebGameVer);


	// Send the server info the web page...

	SendWebInfo(sInfo, FALSE);


	// All done...

	return(TRUE);
}

void CGameServDlg::OnShellMessage(char* pMsg)
{
	// Sanity checks...

	if (!pMsg) return;


	// Figure out what type of message this if...

	char sTemp[128];

	if (!Sparam_Get(sTemp, pMsg, NST_GENERICMESSAGE))
	{
		return;
	}

	switch (atoi(sTemp))
	{
		case NGM_STANDARDUPDATE:	OnStandardUpdate(pMsg); break;
		case NGM_LEVELCHANGING:		OnLevelChanging(pMsg); break;
		case NGM_LEVELCHANGED:		OnLevelChangeUpdate(pMsg); break;
		case NGM_LEVELCHANGESTOP:	OnLevelChangeStop(); break;
		case NGM_CONSOLEMSG:		OnConsoleMessageUpdate(pMsg); break;
	}
}


int ClientIDToIndex(ServerInterface *pInterface, DWORD id)
{
	int i, count;
	ClientInfo info;

	count = pInterface->GetNumClients();
	for(i=0; i < count; i++)
	{
		pInterface->GetClientInfo(i, &info);
		if(info.m_ClientID == id)
			return i;
	}
	return -1;
}


void CGameServDlg::OnStandardUpdate(char* pMsg)
{
	// Update the level info...

	char sTemp[128];

	if (Sparam_Get(sTemp, pMsg, NST_CURLEVEL))
	{
		SetDlgItemText(IDC_GAME_CURLEVEL, sTemp);
	}

	if (Sparam_Get(sTemp, pMsg, NST_NEXTLEVEL))
	{
		SetDlgItemText(IDC_GAME_NEXTLEVEL, sTemp);
	}


	// Get the current player selection info...

	CString sCurSel;
	CString sCurSelName;
	int     nCurSel = m_lbPlayers.GetCurSel();

	if (nCurSel != LB_ERR)
	{
		m_lbPlayers.GetText(nCurSel, sCurSel);

		int i = 0;

		while (sCurSel[i] >= 32)
		{
			sCurSelName += sCurSel[i];
			i++;
		}
	}


	// Update the player info...

	m_lbPlayers.ResetContent();

	int count    = 0;
	int cPlayers = 0;
	int nNewSel  = nCurSel;

	if (Sparam_Get(sTemp, pMsg, NST_PLRCOUNT))
	{
		count = atoi(sTemp);
	}

	for (int i = 1; i <= count; i++)
	{
		char sBase[32];
		char sName[64];

		int j = i-1;

		wsprintf(sBase, "%s%i", NST_PLRNAME_BASE, i);

		if (Sparam_Get(sName, pMsg, sBase))
		{
			strcpy(m_aPis[j].sName, sName);

			wsprintf(sBase, "%s%i", NST_PLRFRAG_BASE, i);

			if (Sparam_Get(sTemp, pMsg, sBase))
			{
				m_aPis[j].nFrags = atoi(sTemp);

				wsprintf(sBase, "%s%i", NST_PLRID_BASE, i);

				if (Sparam_Get(sTemp, pMsg, sBase))
				{
					m_aPis[j].dwID = atoi(sTemp);
				}
				else
				{
					m_aPis[j].dwID = 0;
				}
			}
		}

		int clientIndex = ClientIDToIndex(m_pServerMgr, m_aPis[j].dwID);
		m_aPis[j].dwPing = 0;
		if(clientIndex != -1)
		{
			ClientInfo info;

			if(m_pServerMgr->GetClientInfo(clientIndex, &info))
			{
				m_aPis[j].dwPing = (DWORD)(info.m_Ping * 1000.0f);
			}
		}

		cPlayers++;
	}


	// Sort the players...

	PLAYERINFO TempPi;

	for (i = 0; i < count - 1; i++)
	{
		for (int j = i+1; j < count; j++)
		{
			BOOL bSwap = FALSE;

			if (m_aPis[i].nFrags < m_aPis[j].nFrags) bSwap = TRUE;

			if (m_aPis[i].nFrags == m_aPis[j].nFrags)
			{
				if (strcmp(m_aPis[i].sName, m_aPis[j].sName) > 0) bSwap = TRUE;
			}

			if (bSwap)
			{
				memcpy(&TempPi,  &m_aPis[i], sizeof(PLAYERINFO));
				memcpy(&m_aPis[i], &m_aPis[j], sizeof(PLAYERINFO));
				memcpy(&m_aPis[j], &TempPi,  sizeof(PLAYERINFO));
			}
		}
	}


	// Add the players to the list box...

	nNewSel = LB_ERR;

	for (i = 0; i < count; i++)
	{
		CString sPlayer;
		sPlayer.Format("(%d)\t%s\t%i", m_aPis[i].dwPing, m_aPis[i].sName, m_aPis[i].nFrags);
		int nIndex = m_lbPlayers.AddString(sPlayer);

		if (nIndex != LB_ERR)
		{
			m_lbPlayers.SetItemData(nIndex, m_aPis[i].dwID);

			if (!sCurSelName.IsEmpty() && strcmp(m_aPis[i].sName, sCurSelName) == 0) nNewSel = nIndex;
		}
	}


	// Reselect the selected player if necessary...

	if (nNewSel != LB_ERR)
	{
		m_lbPlayers.SetCurSel(nNewSel);
	}

	CWnd* pWnd = GetDlgItem(IDC_PLAYERS_BOOT);
	if (pWnd)
	{
		pWnd->EnableWindow(nNewSel != LB_ERR);
	}


	// Check for empty exit...

	if (g_bEmptyExit)
	{
		if (m_cPlayers > 0 && cPlayers == 0)
		{
			WriteConsoleString(IDS_CONSOLE_LASTPLAYERLEFT);
			g_bDoEmptyExit   = TRUE;
			g_timerEmptyExit = g_nEmptyExitDelay * 1000;
		}
		else
		{
			if (cPlayers >= 1) g_bDoEmptyExit = FALSE;
		}
	}


	// Set the number of players...

	m_cPlayers = cPlayers;

	SetDlgItemInt(IDC_GAME_PLAYERS, cPlayers);
}

void CGameServDlg::OnLevelChanging(char* pMsg)
{
	// Flag the the level is changing...
	m_bLevelChanging = TRUE;
	EnterCriticalSection(&m_CS);
}

void CGameServDlg::OnLevelChangeUpdate(char* pMsg)
{
	OnLevelChangeStop();

	// Update the level info...

	char sTemp[128];

	if (Sparam_Get(sTemp, pMsg, NST_CURLEVEL))
	{
		SetDlgItemText(IDC_GAME_CURLEVEL, sTemp);
		m_sCurLevel = sTemp;
	}

	if (Sparam_Get(sTemp, pMsg, NST_NEXTLEVEL))
	{
		SetDlgItemText(IDC_GAME_NEXTLEVEL, sTemp);
	}


	// Reset the level timer...

	m_timeLevelStart = timeGetTime();
}

void CGameServDlg::OnLevelChangeStop()
{
	// Flag the level is done changing and is now changed...
	m_bLevelChanging = FALSE;
	LeaveCriticalSection(&m_CS);
}

void CGameServDlg::OnConsoleMessageUpdate(char* pMsg)
{
	char sTemp[128];

	if (Sparam_Get(sTemp, pMsg, NST_CONSOLEMSG))
	{
		WriteConsoleString(sTemp);
	}
}

void CGameServDlg::OnConsoleOutput(char* pMsg)
{
	WriteConsoleString(pMsg);
}

void CGameServDlg::OnConsoleSend() 
{
	if (!m_pServerMgr) return;

	char sCmd[128];
	sCmd[0] = '\0';
	if (GetDlgItemText(IDC_CONSOLE_COMMAND, sCmd, 120) == 0) return;

	WriteConsoleString(sCmd);
	SetDlgItemText(IDC_CONSOLE_COMMAND, "");

	m_pServerMgr->RunConsoleString(sCmd);

	UpdateConsoleVars();
}

void CGameServDlg::OnConsoleClear() 
{
	SetDlgItemText(IDC_CONSOLE_WINDOW, "");
}

BOOL CGameServDlg::DestroyWindow() 
{
	StopServer();
	Clear();
	return(CDialog::DestroyWindow());
}

void CGameServDlg::OnOutOfMemory()
{
	AfxMessageBox(IDS_ERROR_MEMORY);
	g_bNoExitConfirm = TRUE;
	SendMessage(WM_COMMAND, IDCANCEL);
	exit(0);
}

void CGameServDlg::OnCommandsNextLevel() 
{
	if (m_pServerMgr)
	{
		m_pServerMgr->SendToServerShell("NEXTLEVEL");
	}
}

void CGameServDlg::OnPlayersBoot() 
{
	int nCurSel = m_lbPlayers.GetCurSel();
	if (nCurSel == LB_ERR) return;

	DWORD dwID = m_lbPlayers.GetItemData(nCurSel);

	m_pServerMgr->BootClient(dwID);
}

void CGameServDlg::OnSelchangePlayersList() 
{
	int nCurSel = m_lbPlayers.GetCurSel();

	CWnd* pWnd = GetDlgItem(IDC_PLAYERS_BOOT);
	if (pWnd)
	{
		pWnd->EnableWindow(nCurSel != LB_ERR);
	}
}

DWORD WINAPI ThreadUpdate(LPVOID lpParameter)
{
	// Sanity checks...

	if (!lpParameter) return(0);


	// Call the update until we are done...

	CGameServDlg* pDlg = (CGameServDlg*)lpParameter;
	HWND           hWnd = pDlg->GetSafeHwnd();

	while (pDlg->IsRunning())
	{
		DWORD timeNow = timeGetTime();

		if (timeNow - g_timeCur >= 8 && !pDlg->IsLevelChanging())
		{

#if USE_POSTMSG
			pDlg->RemoveMessage(WM_TIMER, 500);
			pDlg->PostMessage(WM_TIMER);
			g_dwPosted++;
#else
			EnterCriticalSection(pDlg->GetCS());
			pDlg->OnUpdate();
			LeaveCriticalSection(pDlg->GetCS());
#endif

		}

		Sleep(9);
	}


	// All done...

	s_bThreadDone = TRUE;
	SS_Trace("Thread is exiting...\n");
	return(TRUE);
}

void CGameServDlg::OnCancel() 
{
	if (g_bNoExitConfirm)
	{
		StopServer();
		CDialog::OnCancel();
		return;
	}

	CString str, title;

	str.LoadString(IDS_CHECKEXIT);
	title.LoadString(IDS_APPNAME);

	if (MessageBox(str, title, MB_YESNO | MB_ICONQUESTION) == IDYES)
	{
		StopServer();
		CDialog::OnCancel();
	}
}

void CGameServDlg::OnDestroy() 
{
	StopServer();
	CDialog::OnDestroy();
}

void CGameServDlg::RemoveMessage(int nMsg, int nMax)
{
	MSG msg;

	for (int i = 0; i < nMax; i++)
	{
		if (!PeekMessage(&msg, GetSafeHwnd(), nMsg, nMsg, PM_REMOVE))
		{
			return;
		}

		g_dwRemoved++;
	}
}

PLAYERINFO* CGameServDlg::GetFirstPlayerInfo()
{
	if (m_cPlayers <= 0) return(NULL);

	m_iPlayer = -1;

	return(GetNextPlayerInfo());
}

PLAYERINFO* CGameServDlg::GetNextPlayerInfo()
{
	m_iPlayer++;
	if (m_iPlayer >= m_cPlayers) return(NULL);

	return(&m_aPis[m_iPlayer]);
}

void CGameServDlg::OnCommandsOptions() 
{
	NetStart_DoOptionsDialog(GetSafeHwnd(), m_pServerMgr, &g_ServerOptions);
}

void CGameServDlg::OnProcessNetPacket(char* sData, DWORD nLen, BYTE senderAddr[4], DWORD senderPort)
{
	char sAddr[128];

	if (g_ServerInfo.m_bUseGameSpy)
	{
		if(nLen > 0 && sData[0] == '\\')
		{
			sprintf(sAddr, "%d.%d.%d.%d", senderAddr[0], senderAddr[1], senderAddr[2], senderAddr[3]);
			m_GameSpyMgr.OnQuery(sAddr, senderPort, sData, nLen);
		}
	}
}

void CGameServDlg::UpdateConsoleVars()
{
	HCONSOLEVAR hVar = NULL;
	DRESULT     dr   = m_pServerMgr->GetConsoleVar("SpyGameType", &hVar, NULL);
	if (dr == LT_OK && hVar)
	{
		char sTemp[64] = { "" };
		dr = m_pServerMgr->GetVarValueString(hVar, sTemp, 64);
		if (dr == LT_OK && sTemp[0] != '\0')
		{
			m_sGameSpyGameType = sTemp;
		}
	}

	hVar = NULL;
	dr   = m_pServerMgr->GetConsoleVar("SpyGameMode", &hVar, NULL);
	if (dr == LT_OK && hVar)
	{
		char sTemp[64] = { "" };
		dr = m_pServerMgr->GetVarValueString(hVar, sTemp, 64);
		if (dr == LT_OK && sTemp[0] != '\0')
		{
			m_sGameSpyGameMode = sTemp;
		}
	}
}
