// This file is part of ZetaGlest <https://github.com/ZetaGlest>
//
// Copyright (C) 2018  The ZetaGlest team
//
// ZetaGlest is a fork of MegaGlest <https://megaglest.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>

#include "client_interface.h"

#include "logger.h"
#include "window.h"

#include "platform_util.h"
#include "network_manager.h"
#include "game_util.h"
#include "conversion.h"
#include "config.h"
#include "lang.h"
#include "config.h"
#include <stdexcept>
#include <cassert>
#include "shared_const.h"
#include "leak_dumper.h"
#include "game.h"

using namespace std;
using namespace Shared;
using namespace Shared::Platform;
using namespace Shared::Util;

#ifdef WIN32
#define snprintf _snprintf
#endif

namespace Game {
	const bool debugClientInterfacePerf = false;

	const int ClientInterface::messageWaitTimeout = 10000;	//10 seconds
	const int ClientInterface::waitSleepTime = 10;
	const int ClientInterface::maxNetworkCommandListSendTimeWait = 5;

	// =====================================================
	//	class ClientInterfaceThread
	// =====================================================

	ClientInterfaceThread::ClientInterfaceThread(ClientInterface *client) : BaseThread() {
		this->clientInterface = client;
		this->uniqueID = "ClientInterfaceThread";
	}

	ClientInterfaceThread::~ClientInterfaceThread() {
		this->clientInterface = NULL;
	}

	void ClientInterfaceThread::setQuitStatus(bool value) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s] Line: %d value = %d\n", __FILE__, __FUNCTION__, __LINE__, value);

		BaseThread::setQuitStatus(value);

		if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s] Line: %d\n", __FILE__, __FUNCTION__, __LINE__);
	}

	bool ClientInterfaceThread::canShutdown(bool deleteSelfIfShutdownDelayed) {
		bool ret = (getExecutingTask() == false);
		if (ret == false && deleteSelfIfShutdownDelayed == true) {
			setDeleteSelfOnExecutionDone(deleteSelfIfShutdownDelayed);
			deleteSelfIfRequired();
			signalQuit();
		}

		return ret;
	}

	void ClientInterfaceThread::execute() {
		RunningStatusSafeWrapper runningStatus(this);
		try {
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d]\n", __FILE__, __FUNCTION__, __LINE__);
			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] ****************** STARTING worker thread this = %p\n", __FILE__, __FUNCTION__, __LINE__, this);

			// Set socket to non blocking
			if (clientInterface != NULL && clientInterface->getSocket(true) != NULL) {
				clientInterface->getSocket(true)->setBlock(false);
			}

			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("ClientInterfaceThread::exec Line: %d\n", __LINE__);

			time_t clientSimulationLagStartTime = 0;
			Chrono chrono;
			for (; this->clientInterface != NULL;) {

				if (getQuitStatus() == true) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d]\n", __FILE__, __FUNCTION__, __LINE__);
					break;
				}

				ExecutingTaskSafeWrapper safeExecutingTaskMutex(this);

				if (debugClientInterfacePerf == true) printf("START === Client thread\n");

				//printf("ClientInterfaceThread::exec Line: %d\n",__LINE__);

				uint64 loopCount = 0;
				if (debugClientInterfacePerf == true) {
					chrono.start();
				}
				while (this->getQuitStatus() == false &&
					clientInterface != NULL) {
					//printf("ClientInterfaceThread::exec Line: %d this->getQuitStatus(): %d\n",__LINE__,this->getQuitStatus());

					// START: Test simulating lag for the client
					int simulateLag = Config::getInstance().getInt("SimulateClientLag", "0");
					if (simulateLag > 0) {
						if (clientSimulationLagStartTime == 0) {
							clientSimulationLagStartTime = time(NULL);
						}
						if (difftime((long int) time(NULL), clientSimulationLagStartTime) <= Config::getInstance().getInt("SimulateClientLagDurationSeconds", "0")) {
							sleep(simulateLag);
						}
					}
					// END: Test simulating lag for the client

					clientInterface->updateNetworkFrame();

					//printf("ClientInterfaceThread::exec Line: %d this->getQuitStatus(): %d\n",__LINE__,this->getQuitStatus());

					if (debugClientInterfacePerf == true) {
						loopCount++;
						if (chrono.getMillis() >= 1000) {
							printf("Client thread loopCount = %llu\n", (long long unsigned int)loopCount);

							loopCount = 0;
							//sleep(0);
							chrono.start();
						}
					}
				}

				if (debugClientInterfacePerf == true) {
					printf("END === Client thread\n");
				}

				//printf("ClientInterfaceThread::exec Line: %d\n",__LINE__);

				if (getQuitStatus() == true) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d]\n", __FILE__, __FUNCTION__, __LINE__);
					break;
				}

				//printf("ClientInterfaceThread::exec Line: %d\n",__LINE__);
			}

			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("ClientInterfaceThread::exec Line: %d\n", __LINE__);
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d]\n", __FILE__, __FUNCTION__, __LINE__);
			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] ****************** ENDING worker thread this = %p\n", __FILE__, __FUNCTION__, __LINE__, this);
		} catch (const exception &ex) {

			SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] Error [%s]\n", __FILE__, __FUNCTION__, __LINE__, ex.what());
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d]\n", __FILE__, __FUNCTION__, __LINE__);

			if (clientInterface == NULL || clientInterface->getSocket(true) == NULL || clientInterface->getSocket(true)->isConnected() == true) {
				throw game_runtime_error(ex.what());
			}
		} catch (...) {
			char szBuf[8096] = "";
			snprintf(szBuf, 8096, "In [%s::%s %d] UNKNOWN error\n", __FILE__, __FUNCTION__, __LINE__);
			SystemFlags::OutputDebug(SystemFlags::debugError, szBuf);
			throw game_runtime_error(szBuf);
		}

		if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s] Line: %d\n", __FILE__, __FUNCTION__, __LINE__);
	}

	// =====================================================
	//	class ClientInterface
	// =====================================================

	ClientInterface::ClientInterface() : GameNetworkInterface() {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] constructor for %p\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, this);

		networkCommandListThreadAccessor = new Mutex(CODE_AT_LINE);
		networkCommandListThread = NULL;
		cachedPendingCommandsIndex = 0;
		cachedLastPendingFrameCount = 0;
		timeClientWaitedForLastMessage = 0;

		flagAccessor = new Mutex(CODE_AT_LINE);

		clientSocket = NULL;
		sessionKey = 0;
		launchGame = false;
		introDone = false;

		this->joinGameInProgress = false;
		this->joinGameInProgressLaunch = false;
		this->readyForInGameJoin = false;
		this->resumeInGameJoin = false;

		quitThreadAccessor = new Mutex(CODE_AT_LINE);
		setQuitThread(false);

		playerIndex = -1;
		gameSettingsReceivedCount = 0;
		setGameSettingsReceived(false);
		gameSettingsReceivedCount = 0;
		connectedTime = 0;
		port = 0;
		serverFTPPort = 0;

		gotIntro = false;
		lastNetworkCommandListSendTime = 0;
		currentFrameCount = 0;
		lastSentFrameCount = 0;
		clientSimulationLagStartTime = 0;

		networkGameDataSynchCheckOkMap = false;
		networkGameDataSynchCheckOkTile = false;
		networkGameDataSynchCheckOkTech = false;
		this->setNetworkGameDataSynchCheckTechMismatchReport("");
		this->setReceivedDataSynchCheck(false);
	}

	void ClientInterface::shutdownNetworkCommandListThread(MutexSafeWrapper &safeMutexWrapper) {
		if (networkCommandListThread != NULL) {
			//printf("START === shutdownNetworkCommandListThread\n");
			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

			setQuitThread(true);
			networkCommandListThread->signalQuit();
			safeMutexWrapper.ReleaseLock(true);

			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

			Chrono chronoElapsed(true);
			for (; chronoElapsed.getMillis() <= 10000;) {
				safeMutexWrapper.Lock();
				if (networkCommandListThread != NULL &&
					networkCommandListThread->canShutdown(false) == false &&
					networkCommandListThread->getRunningStatus() == true) {

					safeMutexWrapper.ReleaseLock(true);
					if (chronoElapsed.getMillis() % 1000 == 0) {
						sleep(1);
						if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);
					}
				} else {
					safeMutexWrapper.ReleaseLock(true);
					break;
				}
				//printf("%s Line: %d\n",__FUNCTION__,__LINE__);
			}

			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n chronoElapsed.getMillis(): %lld", __FUNCTION__, __LINE__, (long long int)chronoElapsed.getMillis());
			//printf("A === shutdownNetworkCommandListThread\n");

			safeMutexWrapper.Lock();
			if (networkCommandListThread != NULL &&
				networkCommandListThread->canShutdown(true)) {

				if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

				delete networkCommandListThread;
				networkCommandListThread = NULL;
			} else {
				networkCommandListThread = NULL;
			}

			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);
			//printf("END === shutdownNetworkCommandListThread\n");
		}
	}

	ClientInterface::~ClientInterface() {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] destructor for %p\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, this);
		//printf("START === Client destructor\n");

		if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

		MutexSafeWrapper safeMutex(networkCommandListThreadAccessor, CODE_AT_LINE);

		if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

		shutdownNetworkCommandListThread(safeMutex);
		//printf("A === Client destructor\n");

		if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

		if (clientSocket != NULL &&
			clientSocket->isConnected() == true) {

			if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

			Lang &lang = Lang::getInstance();
			const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
			for (unsigned int langIndex = 0; langIndex < languageList.size(); ++langIndex) {

				string sQuitText = "has chosen to leave the game!";
				if (lang.hasString("PlayerLeftGame", languageList[langIndex]) == true) {
					sQuitText = lang.getString("PlayerLeftGame", languageList[langIndex]);
				}

				if (clientSocket != NULL && clientSocket->isConnected() == true) {
					sendTextMessage(sQuitText, -1, false, languageList[langIndex]);
				}
			}
		}

		if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

		//printf("B === Client destructor\n");

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

		close(false);

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

		if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

		if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

		//printf("C === Client destructor\n");

		networkCommandListThreadAccessor = NULL;
		safeMutex.ReleaseLock(false, true);

		if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);

		delete flagAccessor;
		flagAccessor = NULL;
		//printf("END === Client destructor\n");

		delete quitThreadAccessor;
		quitThreadAccessor = NULL;

		if (SystemFlags::VERBOSE_MODE_ENABLED) printf("%s Line: %d\n", __FUNCTION__, __LINE__);
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
	}

	bool ClientInterface::getQuitThread() {
		MutexSafeWrapper safeMutex(quitThreadAccessor, CODE_AT_LINE);
		return this->quitThread;
	}
	void ClientInterface::setQuitThread(bool value) {
		MutexSafeWrapper safeMutex(quitThreadAccessor, CODE_AT_LINE);
		this->quitThread = value;
	}

	bool ClientInterface::getQuit() {
		MutexSafeWrapper safeMutex(quitThreadAccessor, CODE_AT_LINE);
		return this->quit;
	}
	void ClientInterface::setQuit(bool value) {
		MutexSafeWrapper safeMutex(quitThreadAccessor, CODE_AT_LINE);
		this->quit = value;
	}

	bool ClientInterface::getJoinGameInProgress() {
		MutexSafeWrapper safeMutex(flagAccessor, CODE_AT_LINE);
		return joinGameInProgress;
	}
	bool ClientInterface::getJoinGameInProgressLaunch() {
		MutexSafeWrapper safeMutex(flagAccessor, CODE_AT_LINE);
		return joinGameInProgressLaunch;
	}

	bool ClientInterface::getReadyForInGameJoin() {
		MutexSafeWrapper safeMutex(flagAccessor, CODE_AT_LINE);
		return readyForInGameJoin;
	}

	bool ClientInterface::getResumeInGameJoin() {
		MutexSafeWrapper safeMutex(flagAccessor, CODE_AT_LINE);
		return resumeInGameJoin;
	}

	void ClientInterface::connect(const Ip &ip, int port) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] START\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

		this->ip = ip;
		this->port = port;

		MutexSafeWrapper safeMutex(networkCommandListThreadAccessor, CODE_AT_LINE);
		shutdownNetworkCommandListThread(safeMutex);

		delete clientSocket;
		clientSocket = NULL;

		safeMutex.ReleaseLock();

		clientSocket = new ClientSocket();
		clientSocket->setBlock(false);
		clientSocket->connect(ip, port);
		connectedTime = time(NULL);
		//clientSocket->setBlock(true);

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] END - socket = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, clientSocket->getSocketId());
	}

	void ClientInterface::reset() {
		if (getSocket() != NULL) {
			Lang &lang = Lang::getInstance();
			const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
			for (unsigned int langIndex = 0; langIndex < languageList.size(); ++langIndex) {

				string sQuitText = "has chosen to leave the game!";
				if (lang.hasString("PlayerLeftGame", languageList[langIndex]) == true) {
					sQuitText = lang.getString("PlayerLeftGame", languageList[langIndex]);
				}
				sendTextMessage(sQuitText, -1, false, languageList[langIndex]);
			}
			close();
		}
	}

	void ClientInterface::update() {
		bool wasConnected = this->isConnected();
		if (gotIntro == true &&
			wasConnected == false) {
			string playerNameStr = getHumanPlayerName();

			Lang &lang = Lang::getInstance();

			char szBuf1[8096] = "";
			string statusTextFormat = lang.getString("PlayerDisconnected");
			snprintf(szBuf1, 8096, statusTextFormat.c_str(), playerNameStr.c_str());

			DisplayErrorMessage(szBuf1);
			//setQuit(true);
			return;
		}

		try {
			NetworkMessageCommandList networkMessageCommandList(currentFrameCount);
			for (int index = 0; index < GameConstants::maxPlayers; ++index) {
				networkMessageCommandList.setNetworkPlayerFactionCRC(index, this->getNetworkPlayerFactionCRC(index));
			}

			//send as many commands as we can
			while (requestedCommands.empty() == false) {
				if (networkMessageCommandList.addCommand(&requestedCommands.back())) {
					requestedCommands.pop_back();
				} else {
					break;
				}
			}

			double lastSendElapsed = difftime((long int) time(NULL), lastNetworkCommandListSendTime);

			// If we are on a frame that should send packets or we have commands
			// to send now, send it now.
			if ((currentFrameCount >= this->gameSettings.getNetworkFramePeriod() &&
				currentFrameCount % this->gameSettings.getNetworkFramePeriod() == 0) ||
				networkMessageCommandList.getCommandCount() > 0) {

				if (lastSentFrameCount < currentFrameCount ||
					networkMessageCommandList.getCommandCount() > 0) {

					lastSentFrameCount = currentFrameCount;
					sendMessage(&networkMessageCommandList);

					lastNetworkCommandListSendTime = time(NULL);
					lastSendElapsed = 0;
				}
			}

			// If we have not sent anything for maxNetworkCommandListSendTimeWait
			// seconds, send one now.
			if (lastNetworkCommandListSendTime > 0 &&
				lastSendElapsed >= ClientInterface::maxNetworkCommandListSendTimeWait) {

				lastSentFrameCount = currentFrameCount;
				sendMessage(&networkMessageCommandList);
				lastNetworkCommandListSendTime = time(NULL);
			}

			// Possible cause of out of sync since we have more commands that need
			// to be sent in this frame
			if (requestedCommands.empty() == false) {
				if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] WARNING / ERROR, requestedCommands.size() = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, requestedCommands.size());

				printf("May go out of sync: client requestedCommands.size() = %zd\n", requestedCommands.size());
			}
		} catch (const game_runtime_error &ex) {
			SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] Error [%s]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, ex.what());
			SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] Error [%s]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, ex.what());

			if (this->isConnected() == false) {
				if (gotIntro == false || wasConnected == false) {
					string sErr = string(extractFileFromDirectoryPath(__FILE__).c_str()) + "::" + string(__FUNCTION__) + " network error: " + string(ex.what());
					DisplayErrorMessage(sErr);
				}

				setQuit(true);
			} else {
				throw game_runtime_error(ex.what());
			}
		}
	}

	std::string ClientInterface::getIpAddress(bool mutexLock) {
		string result = "";
		//MutexSafeWrapper safeMutexSlot((mutexLock == true ? mutexSocket : NULL),CODE_AT_LINE);
		if (clientSocket != NULL) {
			result = clientSocket->getIpAddress();
		}
		return result;
	}

	std::string ClientInterface::getServerIpAddress() {
		return this->ip.getString();
	}

	void ClientInterface::updateLobby() {
		Chrono chrono;
		if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();

		NetworkMessageType networkMessageType = getNextMessageType();

		if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
		if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

		switch (networkMessageType) {
			case nmtInvalid:
				break;

			case nmtIntro:
			{
				NetworkMessageIntro networkMessageIntro;
				if (receiveMessage(&networkMessageIntro)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					gotIntro = true;
					sessionKey = networkMessageIntro.getSessionId();
					versionString = networkMessageIntro.getVersionString();
					playerIndex = networkMessageIntro.getPlayerIndex();
					serverName = networkMessageIntro.getName();
					serverUUID = networkMessageIntro.getPlayerUUID();
					serverPlatform = networkMessageIntro.getPlayerPlatform();
					serverFTPPort = networkMessageIntro.getFtpPort();

					if (playerIndex < 0 || playerIndex >= GameConstants::maxPlayers) {
						printf("playerIndex < 0 || playerIndex >= GameConstants::maxPlayers\n");
						return;
					}

					MutexSafeWrapper safeMutexFlags(flagAccessor, CODE_AT_LINE);
					this->joinGameInProgress = (networkMessageIntro.getGameInProgress() != 0);
					this->joinGameInProgressLaunch = false;
					safeMutexFlags.ReleaseLock();

					//printf("Client got intro playerIndex = %d\n",playerIndex);

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] got NetworkMessageIntro, networkMessageIntro.getGameState() = %d, versionString [%s], sessionKey = %d, playerIndex = %d, serverFTPPort = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkMessageIntro.getGameState(), versionString.c_str(), sessionKey, playerIndex, serverFTPPort);

					//check consistency
					bool compatible = checkVersionCompatibility(GameVersionString, networkMessageIntro.getVersionString());

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] got NetworkMessageIntro, networkMessageIntro.getGameState() = %d, versionString [%s], sessionKey = %d, playerIndex = %d, serverFTPPort = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkMessageIntro.getGameState(), versionString.c_str(), sessionKey, playerIndex, serverFTPPort);

					if (compatible == false) {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

						string playerNameStr = getHumanPlayerName();
						string sErr = "Server and client binary mismatch!\nYou have to use the exactly same binaries!\n\nServer: " + networkMessageIntro.getVersionString() +
							"\nClient: " + GameVersionString + " player [" + playerNameStr + "]";
						printf("%s\n", sErr.c_str());

						sendTextMessage("Server and client binary mismatch", -1, true, "");
						sendTextMessage(" Server:" + networkMessageIntro.getVersionString(), -1, true, "");
						sendTextMessage(" Client: " + GameVersionString, -1, true, "");
						sendTextMessage(" Client player [" + playerNameStr + "]", -1, true, "");
					}

					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

					if (networkMessageIntro.getGameState() == nmgstOk) {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

						//send intro message
						Lang &lang = Lang::getInstance();
						NetworkMessageIntro sendNetworkMessageIntro(
							sessionKey, GameVersionString,
							getHumanPlayerName(),
							-1,
							nmgstOk,
							this->getSocket()->getConnectedIPAddress(),
							serverFTPPort,
							lang.getLanguage(),
							networkMessageIntro.getGameInProgress(),
							Config::getInstance().getString("PlayerId", ""),
							getPlatformNameString());
						sendMessage(&sendNetworkMessageIntro);

						//printf("Got intro sending client details to server\n");

						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

						if (clientSocket == NULL ||
							clientSocket->isConnected() == false) {

							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

							string sErr = "Disconnected from server during intro handshake.";
							DisplayErrorMessage(sErr);
							setQuit(true);
							close();

							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
							return;
						} else {
							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

							introDone = true;
						}
					} else if (networkMessageIntro.getGameState() == nmgstNoSlots) {
						string sErr = "Cannot join the server because there are no open slots for new players.";
						DisplayErrorMessage(sErr);
						setQuit(true);
						close();
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
						return;
					} else {
						string sErr = "Unknown response from server: " + intToStr(networkMessageIntro.getGameState());
						DisplayErrorMessage(sErr);
						setQuit(true);
						close();
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
						return;
					}
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();
				}
			}
			break;

			case nmtPing:
			{
				if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtPing\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

				NetworkMessagePing networkMessagePing;
				if (receiveMessage(&networkMessagePing)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
					this->setLastPingInfo(networkMessagePing);
				}
			}
			break;

			case nmtSynchNetworkGameData:
			{
				NetworkMessageSynchNetworkGameData networkMessageSynchNetworkGameData;

				if (receiveMessage(&networkMessageSynchNetworkGameData)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] got NetworkMessageSynchNetworkGameData, getTechCRCFileCount() = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkMessageSynchNetworkGameData.getTechCRCFileCount());

					this->setLastPingInfoToNow();

					networkGameDataSynchCheckOkMap = false;
					networkGameDataSynchCheckOkTile = false;
					networkGameDataSynchCheckOkTech = false;
					this->setNetworkGameDataSynchCheckTechMismatchReport("");
					this->setReceivedDataSynchCheck(false);

					uint32 tilesetCRC = 0;
					uint32 techCRC = 0;
					uint32 mapCRC = 0;
					vector<std::pair<string, uint32> > vctFileList;

					try {
						Config &config = Config::getInstance();
						string scenarioDir = "";
						if (gameSettings.getScenarioDir() != "") {

							scenarioDir = gameSettings.getScenarioDir();
							if (EndsWith(scenarioDir, ".xml") == true) {
								scenarioDir = scenarioDir.erase(scenarioDir.size() - 4, 4);
								scenarioDir = scenarioDir.erase(scenarioDir.size() - gameSettings.getScenario().size(), gameSettings.getScenario().size() + 1);
							}

							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] gameSettings.getScenarioDir() = [%s] gameSettings.getScenario() = [%s] scenarioDir = [%s]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, gameSettings.getScenarioDir().c_str(), gameSettings.getScenario().c_str(), scenarioDir.c_str());
						}

						// check the checksum's
						tilesetCRC = getFolderTreeContentsCheckSumRecursively(config.getPathListForType(ptTilesets, scenarioDir), string("/") + networkMessageSynchNetworkGameData.getTileset() + string("/*"), ".xml", NULL);

						this->setNetworkGameDataSynchCheckOkTile((tilesetCRC == networkMessageSynchNetworkGameData.getTilesetCRC()));

						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] tilesetCRC info, local = %d, remote = %d, networkMessageSynchNetworkGameData.getTileset() = [%s]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, tilesetCRC, networkMessageSynchNetworkGameData.getTilesetCRC(), networkMessageSynchNetworkGameData.getTileset().c_str());

						//tech, load before map because of resources
						techCRC = getFolderTreeContentsCheckSumRecursively(config.getPathListForType(ptTechs, scenarioDir), string("/") + networkMessageSynchNetworkGameData.getTech() + string("/*"), ".xml", NULL);

						this->setNetworkGameDataSynchCheckOkTech((techCRC == networkMessageSynchNetworkGameData.getTechCRC()));

						if (this->getNetworkGameDataSynchCheckOkTech() == false) {

							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

							string pathSearchString = "/" + networkMessageSynchNetworkGameData.getTech() + "/*";
							vctFileList = getFolderTreeContentsCheckSumListRecursively(config.getPathListForType(ptTechs, scenarioDir), pathSearchString, ".xml", NULL);

							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

							string report = networkMessageSynchNetworkGameData.getTechCRCFileMismatchReport(vctFileList);
							this->setNetworkGameDataSynchCheckTechMismatchReport(report);

						}
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] techCRC info, local = %d, remote = %d, networkMessageSynchNetworkGameData.getTech() = [%s]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, techCRC, networkMessageSynchNetworkGameData.getTechCRC(), networkMessageSynchNetworkGameData.getTech().c_str());

						//map
						Checksum checksum;
						string file = Config::getMapPath(networkMessageSynchNetworkGameData.getMap(), scenarioDir, false);
						if (file != "") {
							checksum.addFile(file);
							mapCRC = checksum.getSum();
						}
						this->setNetworkGameDataSynchCheckOkMap((mapCRC == networkMessageSynchNetworkGameData.getMapCRC()));
						this->setReceivedDataSynchCheck(true);

						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] mapCRC info, local = %d, remote = %d, file = [%s]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, mapCRC, networkMessageSynchNetworkGameData.getMapCRC(), file.c_str());
					} catch (const runtime_error &ex) {
						SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] Error [%s]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, ex.what());
						string sErr = ex.what();
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] error during processing, sErr = [%s]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, sErr.c_str());

						DisplayErrorMessage(sErr);
					}

					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					NetworkMessageSynchNetworkGameDataStatus sendNetworkMessageSynchNetworkGameDataStatus(mapCRC, tilesetCRC, techCRC, vctFileList);
					sendMessage(&sendNetworkMessageSynchNetworkGameDataStatus);
				}
			}
			break;

			case nmtSynchNetworkGameDataFileCRCCheck:
			{
				NetworkMessageSynchNetworkGameDataFileCRCCheck networkMessageSynchNetworkGameDataFileCRCCheck;
				if (receiveMessage(&networkMessageSynchNetworkGameDataFileCRCCheck)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					this->setLastPingInfoToNow();

					Checksum checksum;
					string file = networkMessageSynchNetworkGameDataFileCRCCheck.getFileName();
					checksum.addFile(file);
					uint32 fileCRC = checksum.getSum();

					if (fileCRC != networkMessageSynchNetworkGameDataFileCRCCheck.getFileCRC()) {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtSynchNetworkGameDataFileCRCCheck localCRC = %d, remoteCRC = %d, file [%s]\n",
							extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, fileCRC,
							networkMessageSynchNetworkGameDataFileCRCCheck.getFileCRC(),
							networkMessageSynchNetworkGameDataFileCRCCheck.getFileName().c_str());

						// Here we initiate a download of missing or mismatched content

						NetworkMessageSynchNetworkGameDataFileGet sendNetworkMessageSynchNetworkGameDataFileGet(networkMessageSynchNetworkGameDataFileCRCCheck.getFileName());
						sendMessage(&sendNetworkMessageSynchNetworkGameDataFileGet);

						FileTransferInfo fileInfo;
						fileInfo.hostType = eClient;
						fileInfo.serverIP = this->ip.getString();
						fileInfo.serverPort = this->port;
						fileInfo.fileName = networkMessageSynchNetworkGameDataFileCRCCheck.getFileName();

						FileTransferSocketThread *fileXferThread = new FileTransferSocketThread(fileInfo);
						fileXferThread->start();
					}

					if (networkMessageSynchNetworkGameDataFileCRCCheck.getFileIndex() < networkMessageSynchNetworkGameDataFileCRCCheck.getTotalFileCount()) {
						NetworkMessageSynchNetworkGameDataFileCRCCheck sendNetworkMessageSynchNetworkGameDataFileCRCCheck(
							networkMessageSynchNetworkGameDataFileCRCCheck.getTotalFileCount(),
							networkMessageSynchNetworkGameDataFileCRCCheck.getFileIndex() + 1,
							0,
							"");
						sendMessage(&sendNetworkMessageSynchNetworkGameDataFileCRCCheck);
					}

					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();
				}
			}
			break;

			case nmtText:
			{
				NetworkMessageText networkMessageText;
				if (receiveMessage(&networkMessageText)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					this->setLastPingInfoToNow();

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtText\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

					ChatMsgInfo msg(networkMessageText.getText().c_str(), networkMessageText.getTeamIndex(), networkMessageText.getPlayerIndex(), networkMessageText.getTargetLanguage());
					this->addChatInfo(msg);
				}
			}
			break;

			case nmtMarkCell:
			{
				NetworkMessageMarkCell networkMessageMarkCell;
				if (receiveMessage(&networkMessageMarkCell)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					this->setLastPingInfoToNow();
					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtMarkCell\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

					MarkedCell msg(networkMessageMarkCell.getTarget(),
						networkMessageMarkCell.getFactionIndex(),
						networkMessageMarkCell.getText().c_str(),
						networkMessageMarkCell.getPlayerIndex());
					this->addMarkedCell(msg);
				}
			}
			break;
			case nmtUnMarkCell:
			{
				NetworkMessageUnMarkCell networkMessageMarkCell;
				if (receiveMessage(&networkMessageMarkCell)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					this->setLastPingInfoToNow();
					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtMarkCell\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

					UnMarkedCell msg(networkMessageMarkCell.getTarget(),
						networkMessageMarkCell.getFactionIndex());
					this->addUnMarkedCell(msg);
				}
			}
			break;
			case nmtHighlightCell:
			{
				NetworkMessageHighlightCell networkMessageHighlightCell;
				if (receiveMessage(&networkMessageHighlightCell)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					this->setLastPingInfoToNow();
					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtHighlightCell\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

					MarkedCell msg(networkMessageHighlightCell.getTarget(),
						networkMessageHighlightCell.getFactionIndex(),
						"none", -1);
					this->setHighlightedCell(msg);
				}
			}
			break;

			case nmtLaunch:
			case nmtBroadCastSetup:
			{
				//printf("#1 Got new game setup playerIndex = %d!\n",playerIndex);

				NetworkMessageLaunch networkMessageLaunch;
				if (receiveMessage(&networkMessageLaunch, networkMessageType)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					this->setLastPingInfoToNow();

					if (networkMessageLaunch.getMessageType() == nmtLaunch) {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Lined: %d] got nmtLaunch\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
					} else if (networkMessageLaunch.getMessageType() == nmtBroadCastSetup) {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Lined: %d] got nmtBroadCastSetup\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
					} else {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Lined: %d] got networkMessageLaunch.getMessageType() = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkMessageLaunch.getMessageType());

						printf("In [%s::%s Line: %d] Invalid networkMessageLaunch.getMessageType() = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkMessageLaunch.getMessageType());
						//throw game_runtime_error(szBuf);
					}

					networkMessageLaunch.buildGameSettings(&gameSettings);

					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					//printf("Client got game settings playerIndex = %d lookingfor match...\n",playerIndex);

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Lined: %d] got networkMessageLaunch.getMessageType() = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkMessageLaunch.getMessageType());
					//replace server player by network
					for (int factionIndex = 0; factionIndex < gameSettings.getFactionCount(); ++factionIndex) {

						//printf("Faction = %d start location = %d faction name = %s\n",i,gameSettings.getStartLocationIndex(factionIndex),gameSettings.getFactionTypeName(factionIndex).c_str());

						//replace by network
						if (gameSettings.getFactionControl(factionIndex) == ctHuman) {
							gameSettings.setFactionControl(factionIndex, ctNetwork);
						}

						//printf("factionIndex = %d gameSettings.getStartLocationIndex(factionIndex) = %d playerIndex = %d, gameSettings.getFactionControl(factionIndex) = %d\n",factionIndex,gameSettings.getStartLocationIndex(factionIndex),playerIndex,gameSettings.getFactionControl(i));

						//set the faction index
						if (gameSettings.getStartLocationIndex(factionIndex) == playerIndex) {
							//printf("Setting my factionindex to: %d for playerIndex: %d\n",i,playerIndex);

							gameSettings.setThisFactionIndex(factionIndex);

							//printf("Client got game settings playerIndex = %d factionIndex = %d control = %d name = %s\n",playerIndex,factionIndex,gameSettings.getFactionControl(factionIndex),gameSettings.getFactionTypeName(i).c_str());
							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] gameSettings.getThisFactionIndex(factionIndex) = %d, playerIndex = %d, factionIndex = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, gameSettings.getThisFactionIndex(), playerIndex, factionIndex);
						}
					}

					if (networkMessageLaunch.getMessageType() == nmtLaunch) {
						launchGame = true;
					} else if (networkMessageLaunch.getMessageType() == nmtBroadCastSetup) {
						setGameSettingsReceived(true);
					}

					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();
				}
			}
			break;
			case nmtPlayerIndexMessage:
			{
				PlayerIndexMessage playerIndexMessage(-1);
				if (receiveMessage(&playerIndexMessage)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					this->setLastPingInfoToNow();
					playerIndex = playerIndexMessage.getPlayerIndex();

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] got nmtPlayerIndexMessage, playerIndex = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, playerIndex);
				}

				//printf("Got player index changed msg: %d\n",playerIndex);
			}
			break;

			case nmtReady:
			{
				NetworkMessageReady networkMessageReady;
				if (receiveMessage(&networkMessageReady)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					this->setLastPingInfoToNow();
					MutexSafeWrapper safeMutexFlags(flagAccessor, CODE_AT_LINE);
					this->readyForInGameJoin = true;
				}

				//printf("ClientInterface got nmtReady this->readyForInGameJoin: %d\n",this->readyForInGameJoin);
			}
			break;

			case nmtCommandList:
			{

				//make sure we read the message
				//time_t receiveTimeElapsed = time(NULL);
				NetworkMessageCommandList networkMessageCommandList;
				bool gotCmd = receiveMessage(&networkMessageCommandList);
				if (gotCmd == false) {
					printf("Server has interrupted network connection...\n");
					return;
				}
				if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
				if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

				this->setLastPingInfoToNow();
			}
			break;

			case nmtQuit:
			{
				//time_t receiveTimeElapsed = time(NULL);
				NetworkMessageQuit networkMessageQuit;
				bool gotCmd = receiveMessage(&networkMessageQuit);
				if (gotCmd == false) {
					printf("Server has interrupted connection...\n");
					return;
				}
				if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
				if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

				this->setLastPingInfoToNow();
				setQuit(true);
				close();
			}
			break;

			case nmtLoadingStatusMessage:
			{
				NetworkMessageLoadingStatus networkMessageLoadingStatus(nmls_NONE);
				if (receiveMessage(&networkMessageLoadingStatus)) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
					if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

					this->setLastPingInfoToNow();
					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
				}
			}
			break;

			default:
			{
				string sErr = string(extractFileFromDirectoryPath(__FILE__).c_str()) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType);
				//throw game_runtime_error(string(extractFileFromDirectoryPath(__FILE__).c_str()) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType));
				sendTextMessage("Unexpected network message: " + intToStr(networkMessageType), -1, true, "");
				DisplayErrorMessage(sErr);
				sleep(1);

				setQuit(true);
				close();
			}
		}

		if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] took msecs: %lld\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis());
		if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();

		if (clientSocket != NULL && clientSocket->isConnected() == true &&
			gotIntro == false && difftime((long int) time(NULL), connectedTime) > GameConstants::maxClientConnectHandshakeSecs) {
			if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] difftime(time(NULL),connectedTime) = %f\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, difftime((long int) time(NULL), connectedTime));
			close();
		}

		//if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,chrono.getMillis());
		//if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start();
	}

	void ClientInterface::updateNetworkFrame() {
		this->updateFrame(NULL);
	}

	void ClientInterface::updateFrame(int *checkFrame) {
		//printf("#1 ClientInterface::updateFrame\n");

		//printf("In updateFrame: %d\n",(checkFrame ? *checkFrame : -1));

		if (isConnected() == true && getQuitThread() == false) {
			//printf("#2 ClientInterface::updateFrame\n");

			uint64 loopCount = 0;
			Chrono chronoPerf;
			if (debugClientInterfacePerf == true) {
				chronoPerf.start();
			}

			int waitMicroseconds = (checkFrame == NULL ? 10 : 0);

			bool done = false;
			while (done == false && getQuitThread() == false) {
				//printf("BEFORE Client get networkMessageType\n");


				//wait for the next message
				NetworkMessageType networkMessageType = waitForMessage(waitMicroseconds);

				//printf("AFTER Client got networkMessageType = %d\n",networkMessageType);

				//check we have an expected message
				//NetworkMessageType networkMessageType= getNextMessageType();

				//printf("Got Network networkMessageType: %d\n",networkMessageType);

				switch (networkMessageType) {
					case nmtCommandList:
					{

						//make sure we read the message
						//time_t receiveTimeElapsed = time(NULL);
						NetworkMessageCommandList networkMessageCommandList;
						bool gotCmd = receiveMessage(&networkMessageCommandList);
						if (gotCmd == false) {
							SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] error retrieving nmtCommandList returned false!\n", __FILE__, __FUNCTION__, __LINE__);
							printf("Network connection has been interrupted...\n");
							/*if (isConnected() == false) {
								setQuit(true);
								close();
							}*/
							return;
						}

						//printf("Client Thread getFrameCount(): %d getCommandCount(): %d\n",networkMessageCommandList.getFrameCount(),networkMessageCommandList.getCommandCount());

						MutexSafeWrapper safeMutex(networkCommandListThreadAccessor, CODE_AT_LINE);
						cachedLastPendingFrameCount = networkMessageCommandList.getFrameCount();
						//printf("cachedLastPendingFrameCount = %lld\n",(long long int)cachedLastPendingFrameCount);

						//check that we are in the right frame
						if (checkFrame != NULL) {
							if (networkMessageCommandList.getFrameCount() != *checkFrame) {
								string sErr = "Player: " + getHumanPlayerName() +
									" got a Network synchronization error, frame counts do not match, server frameCount = " +
									intToStr(networkMessageCommandList.getFrameCount()) + ", local frameCount = " +
									intToStr(*checkFrame);
								sendTextMessage(sErr, -1, true, "");
								DisplayErrorMessage(sErr);
								sleep(1);

								setQuit(true);
								close();
								return;
							}
							for (int index = 0; index < GameConstants::maxPlayers; ++index) {
								printf("Frame: %d faction: %d local CRC: %u Remote CRC: %u\n", *checkFrame, index, getNetworkPlayerFactionCRC(index), networkMessageCommandList.getNetworkPlayerFactionCRC(index));

								if (networkMessageCommandList.getNetworkPlayerFactionCRC(index) != getNetworkPlayerFactionCRC(index)) {
									string sErr = "Player: " + getHumanPlayerName() +
										" got a Network CRC error, CRC's do not match, server CRC = " +
										uIntToStr(networkMessageCommandList.getNetworkPlayerFactionCRC(index)) + ", local CRC = " +
										uIntToStr(getNetworkPlayerFactionCRC(index));
									sendTextMessage(sErr, -1, true, "");
									DisplayErrorMessage(sErr);
									sleep(1);

									setQuit(true);
									close();
									return;
								}
							}
						}

						cachedPendingCommands[networkMessageCommandList.getFrameCount()].reserve(networkMessageCommandList.getCommandCount());

						// give all commands
						for (int i = 0; i < networkMessageCommandList.getCommandCount(); ++i) {
							//pendingCommands.push_back(*networkMessageCommandList.getCommand(i));

							//if(networkMessageCommandList.getCommand(i)->getNetworkCommandType() == nctPauseResume) {
								//printf("Network cmd type: %d [%d] frame: %d\n",networkMessageCommandList.getCommand(i)->getNetworkCommandType(),nctPauseResume,networkMessageCommandList.getFrameCount());
							//}

							cachedPendingCommands[networkMessageCommandList.getFrameCount()].push_back(*networkMessageCommandList.getCommand(i));

							if (cachedPendingCommandCRCs.find(networkMessageCommandList.getFrameCount()) == cachedPendingCommandCRCs.end()) {
								cachedPendingCommandCRCs[networkMessageCommandList.getFrameCount()].reserve(GameConstants::maxPlayers);
								for (int index = 0; index < GameConstants::maxPlayers; ++index) {
									cachedPendingCommandCRCs[networkMessageCommandList.getFrameCount()].push_back(networkMessageCommandList.getNetworkPlayerFactionCRC(index));
								}
							}
						}
						safeMutex.ReleaseLock();

						done = true;
					}
					break;

					case nmtPing:
					{
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtPing\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

						NetworkMessagePing networkMessagePing;
						if (receiveMessage(&networkMessagePing)) {
							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
							this->setLastPingInfo(networkMessagePing);
						}
					}
					break;

					case nmtQuit:
					{
						NetworkMessageQuit networkMessageQuit;
						bool gotCmd = receiveMessage(&networkMessageQuit);
						if (gotCmd == false) {
							//SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] error retrieving nmtQuit returned false!\n", __FILE__, __FUNCTION__, __LINE__);
							printf("Network connection has been interrupted...\n");
							/*setQuit(true);
							if (isConnected() == false) {
								close();
								return;
							}*/
							done = true;
							NetworkManager::getInstance().setNetworkRole(nrServer);
							//gameSettings.setMasterserver_admin(getSessionKey());
							//gameSettings.setMasterserver_admin_faction_index(getPlayerIndex());
						}
						if (!done) {
							setQuit(true);
							done = true;
						}
					}
					break;

					case nmtText:
					{
						NetworkMessageText networkMessageText;
						bool gotCmd = receiveMessage(&networkMessageText);
						if (gotCmd == false) {

							SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] error retrieving nmtText returned false!\n", __FILE__, __FUNCTION__, __LINE__);
							if (isConnected() == false) {

								setQuit(true);
								close();
								return;
							}

							throw game_runtime_error("error retrieving nmtText returned false!");
						}

						ChatMsgInfo msg(networkMessageText.getText().c_str(), networkMessageText.getTeamIndex(), networkMessageText.getPlayerIndex(), networkMessageText.getTargetLanguage());
						this->addChatInfo(msg);
					}
					break;

					case nmtMarkCell:
					{
						NetworkMessageMarkCell networkMessageMarkCell;
						if (receiveMessage(&networkMessageMarkCell)) {
							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtMarkCell\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

							MarkedCell msg(networkMessageMarkCell.getTarget(),
								networkMessageMarkCell.getFactionIndex(),
								networkMessageMarkCell.getText().c_str(),
								networkMessageMarkCell.getPlayerIndex());
							this->addMarkedCell(msg);
						}
					}
					break;

					case nmtUnMarkCell:
					{
						NetworkMessageUnMarkCell networkMessageMarkCell;
						if (receiveMessage(&networkMessageMarkCell)) {
							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtMarkCell\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

							UnMarkedCell msg(networkMessageMarkCell.getTarget(),
								networkMessageMarkCell.getFactionIndex());
							this->addUnMarkedCell(msg);
						}
					}
					break;
					case nmtHighlightCell:
					{
						NetworkMessageHighlightCell networkMessageHighlightCell;
						if (receiveMessage(&networkMessageHighlightCell)) {
							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] got nmtHighlightCell\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

							MarkedCell msg(networkMessageHighlightCell.getTarget(),
								networkMessageHighlightCell.getFactionIndex(),
								"none", -1);
							this->setHighlightedCell(msg);
						}
					}
					break;


					case nmtLaunch:
					case nmtBroadCastSetup:
					{
						//printf("#2 Got new game setup playerIndex = %d!\n",playerIndex);

						NetworkMessageLaunch networkMessageLaunch;
						if (receiveMessage(&networkMessageLaunch, networkMessageType)) {

							if (networkMessageLaunch.getMessageType() == nmtLaunch) {
								if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Lined: %d] got nmtLaunch\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
							} else if (networkMessageLaunch.getMessageType() == nmtBroadCastSetup) {
								if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Lined: %d] got nmtBroadCastSetup\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
							} else {
								if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Lined: %d] got networkMessageLaunch.getMessageType() = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkMessageLaunch.getMessageType());

								char szBuf[1024] = "";
								snprintf(szBuf, 1023, "In [%s::%s Line: %d] Invalid networkMessageLaunch.getMessageType() = %d", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkMessageLaunch.getMessageType());
								throw game_runtime_error(szBuf);
							}

							networkMessageLaunch.buildGameSettings(&gameSettings);

							if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Lined: %d] got networkMessageLaunch.getMessageType() = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkMessageLaunch.getMessageType());
							//replace server player by network
							for (int i = 0; i < gameSettings.getFactionCount(); ++i) {
								//replace by network
								if (gameSettings.getFactionControl(i) == ctHuman) {
									gameSettings.setFactionControl(i, ctNetwork);
								}

								//printf("i = %d gameSettings.getStartLocationIndex(i) = %d playerIndex = %d!\n",i,gameSettings.getStartLocationIndex(i),playerIndex);

								//set the faction index
								if (gameSettings.getStartLocationIndex(i) == playerIndex) {
									//printf("Setting my factionindex to: %d for playerIndex: %d\n",i,playerIndex);

									gameSettings.setThisFactionIndex(i);
									if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] gameSettings.getThisFactionIndex(i) = %d, playerIndex = %d, i = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, gameSettings.getThisFactionIndex(), playerIndex, i);
								}
							}
						}
					}
					break;


					case nmtLoadingStatusMessage:
						break;

					case nmtInvalid:
						break;

					default:
					{
						sendTextMessage("Unexpected message in client interface: " + intToStr(networkMessageType), -1, true, "");
						DisplayErrorMessage(string(extractFileFromDirectoryPath(__FILE__).c_str()) + "::" + string(__FUNCTION__) + " Unexpected message in client interface: " + intToStr(networkMessageType));
						sleep(1);

						setQuit(true);
						close();
						done = true;
					}
					break;
				}

				if (isConnected() == false && getQuit() == true) {
					done = true;
				}

				if (debugClientInterfacePerf == true) {
					loopCount++;
					if (chronoPerf.getMillis() >= 1000) {
						printf("Client updateFrame loopCount = %llu\n", (long long unsigned int)loopCount);

						loopCount = 0;
						//sleep(0);
						chronoPerf.start();
					}
				}
			}

			if (done == true) {
				MutexSafeWrapper safeMutex(networkCommandListThreadAccessor, CODE_AT_LINE);
				cachedPendingCommandsIndex++;
			}
		}
		//printf("#3 ClientInterface::updateFrame\n");
	}

	uint64 ClientInterface::getCachedLastPendingFrameCount() {
		MutexSafeWrapper safeMutex(networkCommandListThreadAccessor, CODE_AT_LINE);
		uint64 result = cachedLastPendingFrameCount;
		return result;
	}

	int64 ClientInterface::getTimeClientWaitedForLastMessage() {
		MutexSafeWrapper safeMutex(networkCommandListThreadAccessor, CODE_AT_LINE);
		uint64 result = timeClientWaitedForLastMessage;
		return result;
	}

	bool ClientInterface::getNetworkCommand(int frameCount, int currentCachedPendingCommandsIndex) {
		bool result = false;
		bool waitForData = false;
		uint64 copyCachedLastPendingFrameCount = 0;
		uint64 waitCount = 0;
		uint64 frameCountAsUInt64 = frameCount;
		timeClientWaitedForLastMessage = 0;

		//printf("In getNetworkCommand: %d [%d]\n",frameCount,currentCachedPendingCommandsIndex);

		if (getQuit() == false && getQuitThread() == false) {

			Chrono chrono;
			MutexSafeWrapper safeMutex(NULL, CODE_AT_LINE);

			for (; getQuit() == false && getQuitThread() == false;) {

				if (safeMutex.isValidMutex() == false) {
					safeMutex.setMutex(networkCommandListThreadAccessor, CODE_AT_LINE);
				} else {
					safeMutex.Lock();
				}
				copyCachedLastPendingFrameCount = cachedLastPendingFrameCount;

				if (cachedPendingCommands.find(frameCount) != cachedPendingCommands.end()) {

					Commands &frameCmdList = cachedPendingCommands[frameCount];

					//printf("In getNetworkCommand frameCmdList.size(): %d\n",(int)frameCmdList.size());

					if (frameCmdList.empty() == false) {
						for (int index = 0; index < (int) frameCmdList.size(); ++index) {
							pendingCommands.push_back(frameCmdList[index]);
						}
						cachedPendingCommands[frameCount].clear();

						if (frameCount >= 0) {
							for (int index = 0; index < GameConstants::maxPlayers; ++index) {
								//printf("X**X Frame: %d faction: %d local CRC: %u Remote CRC: %u\n",frameCount,index,getNetworkPlayerFactionCRC(index),cachedPendingCommandCRCs[frameCount][index]);

								if (cachedPendingCommandCRCs[frameCount][index] != getNetworkPlayerFactionCRC(index)) {

									printf("X**X Frame: %d faction: %d local CRC: %u Remote CRC: %u\n", frameCount, index, getNetworkPlayerFactionCRC(index), cachedPendingCommandCRCs[frameCount][index]);

									string sErr = "Player: " + getHumanPlayerName() +
										" got a Network CRC error, CRC's do not match, server CRC = " +
										uIntToStr(cachedPendingCommandCRCs[frameCount][index]) + ", local CRC = " +
										uIntToStr(getNetworkPlayerFactionCRC(index));
									sendTextMessage(sErr, -1, true, "");
									DisplayErrorMessage(sErr);
									sleep(1);

									setQuit(true);
									close();
								}
							}
						}
						cachedPendingCommandCRCs.erase(frameCount);
					}
					if (waitForData == true) {
						timeClientWaitedForLastMessage = chrono.getMillis();
						chrono.stop();
					}
					safeMutex.ReleaseLock(true);

					result = true;
					break;
				} else {
					safeMutex.ReleaseLock(true);
					// No data for this frame
					if (waitForData == false) {
						if (SystemFlags::VERBOSE_MODE_ENABLED) printf("Client waiting for packet for frame: %d, copyCachedLastPendingFrameCount = %lld\n", frameCount, (long long int)copyCachedLastPendingFrameCount);
						chrono.start();
					}
					if (copyCachedLastPendingFrameCount > frameCountAsUInt64) {
						break;
					}

					if (waitForData == false) {
						waitForData = true;
						sleep(0);
					}

					waitCount++;
					//printf("Client waiting for packet for frame: %d, currentCachedPendingCommandsIndex = %d, cachedPendingCommandsIndex = %lld\n",frameCount,currentCachedPendingCommandsIndex,(long long int)cachedPendingCommandsIndex);
				}
			}
		}
		if (waitForData == true) {
			if (SystemFlags::VERBOSE_MODE_ENABLED) printf("Client waiting for packet FINISHED for frame: %d, copyCachedLastPendingFrameCount = %lld waitCount = %llu\n", frameCount, (long long int)copyCachedLastPendingFrameCount, (long long unsigned int)waitCount);
		}

		return result;
	}

	void ClientInterface::updateKeyframe(int frameCount) {
		currentFrameCount = frameCount;

		//printf("In updateKeyFrame: %d\n",currentFrameCount);

		if (getQuit() == false && getQuitThread() == false) {
			if (networkCommandListThread == NULL) {
				static string mutexOwnerId = string(extractFileFromDirectoryPath(__FILE__).c_str()) + string("_") + intToStr(__LINE__);
				networkCommandListThread = new ClientInterfaceThread(this);
				networkCommandListThread->setUniqueID(mutexOwnerId);
				networkCommandListThread->start();

				sleep(0);
			}

			getNetworkCommand(frameCount, cachedPendingCommandsIndex);
		}
	}

	bool ClientInterface::isMasterServerAdminOverride() {
		return gameSettings.getMasterserver_admin() == this->getSessionKey();
	}

	void ClientInterface::waitUntilReady(Checksum* checksum) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

		MutexSafeWrapper safeMutexFlags(flagAccessor, CODE_AT_LINE);
		bool signalServerWhenReadyToStartJoinedGame = this->readyForInGameJoin;
		this->readyForInGameJoin = false;
		safeMutexFlags.ReleaseLock();

		Logger &logger = Logger::getInstance();

		Chrono chrono;
		chrono.start();

		// FOR TESTING ONLY - delay to see the client count up while waiting
		//sleep(5000);

		//clientSocket->setBlock(true);
		//send ready message
		NetworkMessageReady networkMessageReady;
		sendMessage(&networkMessageReady);

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

		NetworkMessageLoadingStatus networkMessageLoadingStatus(nmls_NONE);

		Lang &lang = Lang::getInstance();

		int64 lastMillisCheck = 0;
		uint64 waitLoopIterationCount = 0;
		uint64 MAX_LOOP_COUNT_BEFORE_SLEEP = 100;
		MAX_LOOP_COUNT_BEFORE_SLEEP = Config::getInstance().getInt("NetworkClientLoopGameLoadingCap", intToStr(MAX_LOOP_COUNT_BEFORE_SLEEP).c_str());
		if (MAX_LOOP_COUNT_BEFORE_SLEEP == 0) {
			MAX_LOOP_COUNT_BEFORE_SLEEP = 1;
		}
		int sleepMillis = Config::getInstance().getInt("NetworkClientLoopGameLoadingCapSleepMillis", "10");

		//wait until we get a ready message from the server
		while (true) {
			// FOR TESTING ONLY - delay to see the client count up while waiting
			//sleep(2000);

			waitLoopIterationCount++;
			if (waitLoopIterationCount > 0 &&
				waitLoopIterationCount % MAX_LOOP_COUNT_BEFORE_SLEEP == 0) {
				sleep(sleepMillis);
				waitLoopIterationCount = 0;
			}

			if (isConnected() == false) {
				if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
				string sErr = lang.getString("ServerDisconnected");
				//DisplayErrorMessage(sErr);

				if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

				setQuit(true);
				close();

				if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
				return;
			}
			NetworkMessageType networkMessageType = getNextMessageType();

			// consume old messages from the lobby
			bool discarded = shouldDiscardNetworkMessage(networkMessageType);
			if (discarded == false) {
				if (networkMessageType == nmtReady) {
					if (receiveMessage(&networkMessageReady)) {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
						break;
					}
				} else if (networkMessageType == nmtLoadingStatusMessage) {
					if (receiveMessage(&networkMessageLoadingStatus)) {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
					}
				} else if (networkMessageType == nmtQuit) {
					NetworkMessageQuit networkMessageQuit;
					if (receiveMessage(&networkMessageQuit)) {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

						DisplayErrorMessage(lang.getString("GameCancelledByUser"));

						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

						setQuit(true);
						close();

						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
						return;

					}
				} else if (networkMessageType == nmtCommandList) {
					//make sure we read the message
					NetworkMessageCommandList networkMessageCommandList;
					bool gotCmd = receiveMessage(&networkMessageCommandList);
					if (gotCmd == false) {
						printf("Server has interrupted network connection...\n");
						return;
					}
				} else if (networkMessageType == nmtInvalid) {
					if (chrono.getMillis() > readyWaitTimeout) {
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

						Lang &lang = Lang::getInstance();
						const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
						for (unsigned int i = 0; i < languageList.size(); ++i) {
							string sErr = "Timeout waiting for server";
							if (lang.hasString("TimeoutWaitingForServer", languageList[i]) == true) {
								sErr = lang.getString("TimeoutWaitingForServer", languageList[i]);
							}
							bool echoLocal = lang.isLanguageLocal(lang.getLanguage());
							sendTextMessage(sErr, -1, echoLocal, languageList[i]);

							if (echoLocal) {
								DisplayErrorMessage(sErr);
							}
						}

						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

						sleep(1);
						setQuit(true);
						close();

						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
						return;
					} else {
						if (chrono.getMillis() % 100 == 0) {
							lastMillisCheck = (chrono.getMillis() / 1000);

							char szBuf[8096] = "";
							string updateTextFormat = "Waiting for network: %lld seconds elapsed (maximum wait time: %d seconds)";
							if (lang.hasString("NetworkGameClientLoadStatus") == true) {
								updateTextFormat = lang.getString("NetworkGameClientLoadStatus");
							}

							string waitForHosts = "";
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER1_CONNECTED) == nmls_PLAYER1_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER1_READY) != nmls_PLAYER1_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(0);
								}
							}
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER2_CONNECTED) == nmls_PLAYER2_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER2_READY) != nmls_PLAYER2_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(1);
								}
							}
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER3_CONNECTED) == nmls_PLAYER3_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER3_READY) != nmls_PLAYER3_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(2);
								}
							}
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER4_CONNECTED) == nmls_PLAYER4_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER4_READY) != nmls_PLAYER4_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(3);
								}
							}
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER5_CONNECTED) == nmls_PLAYER5_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER5_READY) != nmls_PLAYER5_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(4);
								}
							}
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER6_CONNECTED) == nmls_PLAYER6_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER6_READY) != nmls_PLAYER6_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(5);
								}
							}
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER7_CONNECTED) == nmls_PLAYER7_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER7_READY) != nmls_PLAYER7_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(6);
								}
							}
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER8_CONNECTED) == nmls_PLAYER8_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER8_READY) != nmls_PLAYER8_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(7);
								}
							}
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER9_CONNECTED) == nmls_PLAYER9_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER9_READY) != nmls_PLAYER9_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(7);
								}
							}
							if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER10_CONNECTED) == nmls_PLAYER10_CONNECTED) {
								if ((networkMessageLoadingStatus.getStatus() & nmls_PLAYER10_READY) != nmls_PLAYER10_READY) {
									if (waitForHosts != "") {
										waitForHosts += ", ";
									}
									waitForHosts += gameSettings.getNetworkPlayerNameByPlayerIndex(7);
								}
							}

							if (waitForHosts == "") {
								waitForHosts = lang.getString("Server");
							}
							snprintf(szBuf, 8096, updateTextFormat.c_str(), (long long int)lastMillisCheck, int(readyWaitTimeout / 1000));

							char szBuf1[8096] = "";
							string statusTextFormat = "Waiting for players: %s";
							if (lang.hasString("NetworkGameStatusWaiting") == true) {
								statusTextFormat = lang.getString("NetworkGameStatusWaiting");
							}
							snprintf(szBuf1, 8096, statusTextFormat.c_str(), waitForHosts.c_str());

							logger.add(szBuf, true, szBuf1);

							sleep(0);
						}
					}
				} else {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
					sendTextMessage("Unexpected network message: " + intToStr(networkMessageType), -1, true, "");

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

					DisplayErrorMessage(string(extractFileFromDirectoryPath(__FILE__).c_str()) + "::" + string(__FUNCTION__) + " Unexpected network message: " + intToStr(networkMessageType));

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

					sleep(1);
					setQuit(true);
					close();

					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
					return;
				}

				::Shared::Platform::Window::handleEvent();
				// sleep a bit
				sleep(waitSleepTime);
			}
		}

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

		//check checksum
		if (getJoinGameInProgress() == false &&
			networkMessageReady.getChecksum() != checksum->getSum()) {

			if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

			Lang &lang = Lang::getInstance();
			const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
			for (unsigned int langIndex = 0; langIndex < languageList.size(); ++langIndex) {

				string sErr = "Checksum error, you don't have the same data as the server";
				if (lang.hasString("CheckSumGameLoadError", languageList[langIndex]) == true) {
					sErr = lang.getString("CheckSumGameLoadError", languageList[langIndex]);
				}
				bool echoLocal = lang.isLanguageLocal(lang.getLanguage());
				sendTextMessage(sErr, -1, echoLocal, languageList[langIndex]);

				string playerNameStr = "Player with error is: " + getHumanPlayerName();
				if (lang.hasString("CheckSumGameLoadPlayer", languageList[langIndex]) == true) {
					playerNameStr = lang.getString("CheckSumGameLoadPlayer", languageList[langIndex]) + " " + getHumanPlayerName();
				}
				sendTextMessage(playerNameStr, -1, echoLocal, languageList[langIndex]);

				string sErr1 = "Client Checksum: " + intToStr(checksum->getSum());
				if (lang.hasString("CheckSumGameLoadClient", languageList[langIndex]) == true) {
					sErr1 = lang.getString("CheckSumGameLoadClient", languageList[langIndex]) + " " + intToStr(checksum->getSum());
				}

				sendTextMessage(sErr1, -1, echoLocal, languageList[langIndex]);

				string sErr2 = "Server Checksum: " + intToStr(networkMessageReady.getChecksum());
				if (lang.hasString("CheckSumGameLoadServer", languageList[langIndex]) == true) {
					sErr2 = lang.getString("CheckSumGameLoadServer", languageList[langIndex]) + " " + intToStr(networkMessageReady.getChecksum());
				}
				sendTextMessage(sErr2, -1, echoLocal, languageList[langIndex]);

				if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d %s %s %s\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, sErr.c_str(), sErr1.c_str(), sErr2.c_str());

				if (echoLocal == true) {
					if (Config::getInstance().getBool("NetworkConsistencyChecks")) {
						// error message and disconnect only if checked
						if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

						string niceError = sErr + string("\n") + sErr1 + string("\n") + sErr2;
						DisplayErrorMessage(niceError);
					}
				}
			}

			if (Config::getInstance().getBool("NetworkConsistencyChecks")) {
				// error message and disconnect only if checked
				if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

				sleep(1);
				setQuit(true);

				if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

				close();
			}
			return;
		}

		MutexSafeWrapper safeMutexFlags2(flagAccessor, CODE_AT_LINE);
		this->joinGameInProgress = false;
		this->joinGameInProgressLaunch = false;

		//printf("Client signalServerWhenReadyToStartJoinedGame = %d\n",signalServerWhenReadyToStartJoinedGame);
		if (signalServerWhenReadyToStartJoinedGame == true) {

			Lang &lang = Lang::getInstance();
			const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
			for (unsigned int langIndex = 0; langIndex < languageList.size(); ++langIndex) {

				string sText = "Player: %s is joining the game now.";
				if (lang.hasString("JoinPlayerToCurrentGameLaunchDone", languageList[langIndex]) == true) {
					sText = lang.getString("JoinPlayerToCurrentGameLaunchDone", languageList[langIndex]);
				}

				if (clientSocket != NULL && clientSocket->isConnected() == true) {
					string playerNameStr = getHumanPlayerName();
					char szBuf[8096] = "";
					snprintf(szBuf, 8096, sText.c_str(), playerNameStr.c_str());

					sendTextMessage(szBuf, -1, false, languageList[langIndex]);
				}
			}

			this->resumeInGameJoin = true;
			safeMutexFlags2.ReleaseLock();
		} else {
			safeMutexFlags2.ReleaseLock();
			// delay the start a bit, so clients have more room to get messages
			// This is to ensure clients don't start ahead of the server and thus
			// constantly freeze because they are waiting for the server to catch up
			sleep(120);
		}

		// This triggers LAG update packets to begin as required
		lastNetworkCommandListSendTime = time(NULL);

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] END\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);
	}

	void ClientInterface::sendResumeGameMessage() {
		NetworkMessageReady networkMessageReady;
		sendMessage(&networkMessageReady);
	}

	void ClientInterface::sendTextMessage(const string &text, int teamIndex, bool echoLocal,
		string targetLanguage) {

		string humanPlayerName = getHumanPlayerName();
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] humanPlayerName = [%s] playerIndex = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, humanPlayerName.c_str(), playerIndex);

		NetworkMessageText networkMessageText(text, teamIndex, playerIndex, targetLanguage);
		sendMessage(&networkMessageText);

		if (echoLocal == true) {
			ChatMsgInfo msg(networkMessageText.getText().c_str(), networkMessageText.getTeamIndex(), networkMessageText.getPlayerIndex(), targetLanguage);
			this->addChatInfo(msg);
		}
	}

	void ClientInterface::sendMarkCellMessage(Vec2i targetPos, int factionIndex, string note, int playerIndex) {
		string humanPlayerName = getHumanPlayerName();
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] humanPlayerName = [%s] playerIndex = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, humanPlayerName.c_str(), playerIndex);

		NetworkMessageMarkCell networkMessageMarkCell(targetPos, factionIndex, note, playerIndex);
		sendMessage(&networkMessageMarkCell);
	}

	void ClientInterface::sendHighlightCellMessage(Vec2i targetPos, int factionIndex) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] playerIndex = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, playerIndex);

		NetworkMessageHighlightCell networkMessageHighlightCell(targetPos, factionIndex);
		sendMessage(&networkMessageHighlightCell);
	}

	void ClientInterface::sendUnMarkCellMessage(Vec2i targetPos, int factionIndex) {
		string humanPlayerName = getHumanPlayerName();
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] humanPlayerName = [%s] playerIndex = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, humanPlayerName.c_str(), playerIndex);

		NetworkMessageUnMarkCell networkMessageMarkCell(targetPos, factionIndex);
		sendMessage(&networkMessageMarkCell);
	}

	void ClientInterface::sendPingMessage(int32 pingFrequency, int64 pingTime) {
		NetworkMessagePing networkMessagePing(pingFrequency, pingTime);
		sendMessage(&networkMessagePing);
	}

	string ClientInterface::getNetworkStatus() {
		std::string label = Lang::getInstance().getString("Server") + ": " + serverName;
		//float pingTime = getThreadedPingMS(getServerIpAddress().c_str());
		char szBuf[8096] = "";
		snprintf(szBuf, 8096, "%s", label.c_str());

		return szBuf;
	}

	NetworkMessageType ClientInterface::waitForMessage(int waitMicroseconds) {
		// Debug!
	/*
		sendTextMessage("Timeout waiting for message",-1);
		DisplayErrorMessage("Timeout waiting for message");
		quit= true;
		close();
		return;
	*/

		uint64 loopCount = 0;
		Chrono chronoPerf;
		if (debugClientInterfacePerf == true) {
			chronoPerf.start();
		}

		Chrono chrono;
		chrono.start();

		NetworkMessageType msg = nmtInvalid;
		while (msg == nmtInvalid &&
			getQuitThread() == false) {

			msg = getNextMessageType(waitMicroseconds);
			if (msg == nmtInvalid) {
				if (getSocket() == NULL || (chrono.getMillis() % 250 == 0 && isConnected() == false)) {
					if (getQuit() == false) {
						Lang &lang = Lang::getInstance();
						printf("\n%s\n", lang.getString("ServerDisconnected").c_str());
						setQuit(true);
					}
					close();
					return msg;
				}

				if (chrono.getMillis() > messageWaitTimeout) {
					if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

					Lang &lang = Lang::getInstance();
					const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
					for (unsigned int langIndex = 0; langIndex < languageList.size(); ++langIndex) {

						string msg = "Timeout waiting for message.";
						if (lang.hasString("TimeoutWaitingForMessage", languageList[langIndex]) == true) {
							msg = lang.getString("TimeoutWaitingForMessage", languageList[langIndex]);
						}

						sendTextMessage(msg, -1, lang.isLanguageLocal(languageList[langIndex]), languageList[langIndex]);
						if (lang.isLanguageLocal(languageList[langIndex]) == true) {
							DisplayErrorMessage(msg);
						}
					}

					sleep(1);
					setQuit(true);
					close();
					return msg;
				}
				// Sleep every x milli-seconds we wait to let other threads work
				else if (chrono.getMillis() % 2 == 0) {
					sleep(1);
				} else {
					sleep(0);
				}
			}

			if (debugClientInterfacePerf == true) {
				loopCount++;
				if (chronoPerf.getMillis() >= 100) {
					printf("Client waitForMessage loopCount = %llu\n", (long long unsigned int)loopCount);

					loopCount = 0;
					chronoPerf.start();
				}
			}
		}

		if (SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 1) SystemFlags::OutputDebug(SystemFlags::debugPerformance, "In [%s::%s Line: %d] waiting took %lld msecs, msg = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, chrono.getMillis(), msg);

		return msg;
	}

	void ClientInterface::quitGame(bool userManuallyQuit) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] userManuallyQuit = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, userManuallyQuit);

		if (clientSocket != NULL && userManuallyQuit == true) {
			if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

			Lang &lang = Lang::getInstance();
			const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
			for (unsigned int langIndex = 0; langIndex < languageList.size(); ++langIndex) {

				string msg = "has chosen to leave the game!";
				if (lang.hasString("PlayerLeftGame", languageList[langIndex]) == true) {
					msg = lang.getString("PlayerLeftGame", languageList[langIndex]);
				}

				sendTextMessage(msg, -1, lang.isLanguageLocal(languageList[langIndex]), languageList[langIndex]);
			}
			sleep(1);
			close();
		}

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Lined: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
	}

	void ClientInterface::close(bool lockMutex) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] START, clientSocket = %p\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, clientSocket);

		MutexSafeWrapper safeMutex(NULL, CODE_AT_LINE);
		if (lockMutex == true) {
			safeMutex.setMutex(networkCommandListThreadAccessor, CODE_AT_LINE);
		}
		shutdownNetworkCommandListThread(safeMutex);

		delete clientSocket;
		clientSocket = NULL;

		safeMutex.ReleaseLock();

		connectedTime = 0;
		gotIntro = false;

		MutexSafeWrapper safeMutexFlags(flagAccessor, CODE_AT_LINE);
		this->joinGameInProgress = false;
		this->joinGameInProgressLaunch = false;
		this->readyForInGameJoin = false;

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] END\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
	}

	void ClientInterface::close() {
		close(true);
	}

	void ClientInterface::discoverServers(DiscoveredServersInterface *cb) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

		ClientSocket::discoverServers(cb);

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
	}
	void ClientInterface::stopServerDiscovery() {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

		ClientSocket::stopBroadCastClientThread();

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
	}

	void ClientInterface::sendSwitchSetupRequest(string selectedFactionName, int8 currentSlotIndex,
		int8 toSlotIndex, int8 toTeam, string networkPlayerName,
		NetworkPlayerStatusType networkPlayerStatus, int8 flags,
		string language) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] networkPlayerName [%s] flags = %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__, networkPlayerName.c_str(), flags);
		SwitchSetupRequest message = SwitchSetupRequest(selectedFactionName,
			currentSlotIndex, toSlotIndex, toTeam, networkPlayerName,
			networkPlayerStatus, flags, language);
		sendMessage(&message);
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d]\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
	}

	bool ClientInterface::shouldDiscardNetworkMessage(NetworkMessageType networkMessageType) {
		bool discard = false;

		switch (networkMessageType) {
			case nmtIntro:
			{
				discard = true;
				NetworkMessageIntro msg = NetworkMessageIntro();
				this->receiveMessage(&msg);
			}
			break;
			case nmtPing:
			{
				discard = true;
				NetworkMessagePing msg = NetworkMessagePing();
				this->receiveMessage(&msg);
				this->setLastPingInfo(msg);
			}
			break;
			case nmtLaunch:
			{
				discard = true;
				NetworkMessageLaunch msg = NetworkMessageLaunch();
				this->receiveMessage(&msg);
			}
			break;
			case nmtText:
			{
				if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s Line: %d] got nmtText\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
				discard = true;
				NetworkMessageText netMsg = NetworkMessageText();
				this->receiveMessage(&netMsg);

				ChatMsgInfo msg(netMsg.getText().c_str(), netMsg.getTeamIndex(), netMsg.getPlayerIndex(), netMsg.getTargetLanguage());
				this->addChatInfo(msg);
			}
			break;

			case nmtMarkCell:
			{
				discard = true;
				NetworkMessageMarkCell networkMessageMarkCell;
				receiveMessage(&networkMessageMarkCell);

				MarkedCell msg(networkMessageMarkCell.getTarget(),
					networkMessageMarkCell.getFactionIndex(),
					networkMessageMarkCell.getText().c_str(),
					networkMessageMarkCell.getPlayerIndex());
				this->addMarkedCell(msg);
			}
			break;

			case nmtUnMarkCell:
			{
				discard = true;
				NetworkMessageUnMarkCell networkMessageMarkCell;
				receiveMessage(&networkMessageMarkCell);

				UnMarkedCell msg(networkMessageMarkCell.getTarget(),
					networkMessageMarkCell.getFactionIndex());
				this->addUnMarkedCell(msg);
			}
			break;

			case nmtHighlightCell:
			{
				discard = true;
				NetworkMessageHighlightCell networkMessageHighlightCell;
				receiveMessage(&networkMessageHighlightCell);

				MarkedCell msg(networkMessageHighlightCell.getTarget(),
					networkMessageHighlightCell.getFactionIndex(),
					"none", -1);
				this->setHighlightedCell(msg);
			}
			break;

			case nmtSynchNetworkGameData:
			{
				discard = true;
				NetworkMessageSynchNetworkGameData msg = NetworkMessageSynchNetworkGameData();
				this->receiveMessage(&msg);
			}
			break;
			case nmtSynchNetworkGameDataStatus:
			{
				discard = true;
				NetworkMessageSynchNetworkGameDataStatus msg = NetworkMessageSynchNetworkGameDataStatus();
				this->receiveMessage(&msg);
			}
			break;
			case nmtSynchNetworkGameDataFileCRCCheck:
			{
				discard = true;
				NetworkMessageSynchNetworkGameDataFileCRCCheck msg = NetworkMessageSynchNetworkGameDataFileCRCCheck();
				this->receiveMessage(&msg);
			}
			break;
			case nmtSynchNetworkGameDataFileGet:
			{
				discard = true;
				NetworkMessageSynchNetworkGameDataFileGet msg = NetworkMessageSynchNetworkGameDataFileGet();
				this->receiveMessage(&msg);
			}
			break;
			case nmtSwitchSetupRequest:
			{
				discard = true;
				SwitchSetupRequest msg = SwitchSetupRequest();
				this->receiveMessage(&msg);
			}
			break;
			case nmtBroadCastSetup:
			{
				discard = true;
				NetworkMessageLaunch msg = NetworkMessageLaunch();
				this->receiveMessage(&msg);
			}
			break;

			case nmtPlayerIndexMessage:
			{
				discard = true;
				PlayerIndexMessage msg = PlayerIndexMessage(0);
				this->receiveMessage(&msg);
			}
			break;

			default:
			break;
		}

		return discard;
	}

	string ClientInterface::getHumanPlayerName(int index) {
		string  result = Config::getInstance().getString("NetPlayerName", Socket::getHostName().c_str());

		if (index >= 0 || gameSettings.getThisFactionIndex() >= 0) {
			if (index < 0) {
				index = gameSettings.getThisFactionIndex();
			}
			if (gameSettings.getNetworkPlayerName(index) != "") {
				result = gameSettings.getNetworkPlayerName(index);
			}
		}

		return result;
	}

	void ClientInterface::setGameSettings(GameSettings *serverGameSettings) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] START\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);

		gameSettings = *serverGameSettings;

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] END\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__);
	}

	void ClientInterface::broadcastGameSetup(const GameSettings *gameSettings) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

		NetworkMessageLaunch networkMessageLaunch(gameSettings, nmtBroadCastSetup);
		sendMessage(&networkMessageLaunch);

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
	}

	void ClientInterface::broadcastGameStart(const GameSettings *gameSettings) {
		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);

		MutexSafeWrapper safeMutexFlags(flagAccessor, CODE_AT_LINE);
		if (this->joinGameInProgress == true) {
			this->joinGameInProgressLaunch = true;
		}
		safeMutexFlags.ReleaseLock();

		//printf("Sending game launch joinGameInProgress: %d\n",joinGameInProgress);

		NetworkMessageLaunch networkMessageLaunch(gameSettings, nmtLaunch);
		sendMessage(&networkMessageLaunch);

		if (SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork, "In [%s::%s] Line: %d\n", extractFileFromDirectoryPath(__FILE__).c_str(), __FUNCTION__, __LINE__);
	}

	void ClientInterface::setGameSettingsReceived(bool value) {
		//printf("In [%s:%s] Line: %d gameSettingsReceived = %d value = %d, gameSettingsReceivedCount = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,gameSettingsReceived,value,gameSettingsReceivedCount);
		gameSettingsReceived = value;
		gameSettingsReceivedCount++;
	}

} //end namespace
