//===========================================================================//
// File:        l4net.cpp                                                    //
// Project: MUNGA           Brick: Network Manager                           //
// Contents: Interface specification for network brick                       //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/02/95  GAC  Initial coding.                                            //
// 04/08/95  GAC  Netnow Removed, TCP/NetNub support added                   //
//                NOTE: old netnow code is saved in L4NetNow.*               //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//

#include "mungal4.h"
#pragma hdrstop

#include "l4app.h"
#include "l4host.h"
#include "l4net.h"
#include "..\munga\mission.h"
#include "..\munga\notation.h"
//#include <netnub.hpp>


#if defined(TRACE_SEND_PACKET)
	static BitTrace Send_Packet("Send Packet");
	#define SET_SEND_PACKET() Send_Packet.Set()
	#define CLEAR_SEND_PACKET() Send_Packet.Clear()
#else
	#define SET_SEND_PACKET()
	#define CLEAR_SEND_PACKET()
#endif

#if defined(TRACE_CHECK_BUFFERS)
	static BitTrace Check_Buffers("Check Buffers");
	#define SET_CHECK_BUFFERS() Check_Buffers.Set()
	#define CLEAR_CHECK_BUFFERS() Check_Buffers.Clear()
#else
	#define SET_CHECK_BUFFERS()
	#define CLEAR_CHECK_BUFFERS()
#endif

#if defined(TRACE_CALL_NETNUB)
	static BitTrace Call_Netnub("Call Netnub");
	#define SET_CALL_NETNUB() Call_Netnub.Set()
	#define CLEAR_CALL_NETNUB() Call_Netnub.Clear()
#else
	#define SET_CALL_NETNUB()
	#define CLEAR_CALL_NETNUB()
#endif

#if defined(TRACE_LOST_DATA)
	static BitTrace Lost_Data("Lost Data");
	#define SET_LOST_DATA() Lost_Data.Set()
	#define CLEAR_LOST_DATA() Lost_Data.Clear()
#else
	#define SET_LOST_DATA()
	#define CLEAR_LOST_DATA()
#endif

#define BATCHED_TRANSMIT	True		// true sends broadcast messages with a single call to netnub
#define BATCHED_RECEIVE		True		// true receives from all streams with a single call to netnub
#define REPORT_LOST_DATA	False		// Print a message if data is lost (may be toxic)

#if !defined(MESSAGE_BUFFERING)
	#define MESSAGE_BUFFERING 	True		// true uses munga level buffering for dropped packets
#endif

#define	CONSOLE_NET_PORT	1501		// Port number the console will connect on
#define	GAME_NET_PORT		1502		// Port number the game will connect on

char	GlobalEggFileName[80];   // NASTY HACK !!! should suffice till we get console up though

//struct RMREG  //real mode registers structure
//{
//unsigned long
//	edi,
//	esi,
//	ebp,
//	reserved,//0
//	ebx,
//	edx,
//	ecx,
//	eax;
//unsigned short
//	flags,	//0
//	es,
//	ds,
//	fs,		//0
//	gs,		//0
//	ip,
//	cs,
//	sp,
//	ss;
//};

//
// !!!! this stuff is also nasty but is currently required to be global for the
// assembly language stuff to work properly.
//
//Netcom_Ptr
//	Net_Common_Ptr = NULL;
//
//unsigned short
//	*Buffer_Length_Ptr;		// Where the network common buffer length field is located
//
//short
//	*Function_Ptr,				// Where the network common function field is located
//	offs,            			//	offset of Real Mode Netcom struct
//	baseadr,         			// segment of Real Mode Netcom struct
//	intno,
//	RMFunction_Off,			// Offset of function field for real mode structure
//	RMBuffer_Length_Off;		// Offset of buffer length field for real mode structure
//long
//	RMverno;                // Real mode version number
////
//// !!!! These are the assembyly code functions we will be calling
////
//extern "C"
//{
//	void mapRMBuff(void);   	//get selector for rm buffer
//	void getRMNumbers(void);   //get interr and version numbers from rm buffer
//	void getRMBuff(void);      //copy block from rm buffer
//	void setRMBuff(void);      //copy block to rm buffer
//};

//#############################################################################
// Shared Data Support
//
L4NetworkManager::SharedData
	L4NetworkManager::DefaultData(
		L4NetworkManager::GetClassDerivations(),
		L4NetworkManager::GetMessageHandlers()
	);

Derivation* L4NetworkManager::GetClassDerivations()
{
	static Derivation classDerivations(NetworkManager::GetClassDerivations(), "L4NetworkManager");
	return &classDerivations;
}

//#############################################################################
// Messaging support
//
const Receiver::HandlerEntry
	L4NetworkManager::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(L4NetworkManager,ReceiveEggFile),
	MESSAGE_ENTRY(L4NetworkManager,AcknowledgeEggFile),
	MESSAGE_ENTRY(L4NetworkManager,HostConnected),
	MESSAGE_ENTRY(L4NetworkManager,HostDisconnected)
};

Receiver::MessageHandlerSet& L4NetworkManager::GetMessageHandlers()
{
	static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(L4NetworkManager::MessageHandlerEntries), L4NetworkManager::MessageHandlerEntries, NetworkManager::GetMessageHandlers());
	return messageHandlers;
}

//
//#############################################################################
// Code for the network manager class
//#############################################################################
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for the L4NetworkManager
//
//WinSock support :ADB 01/06/07
L4NetworkManager::L4NetworkManager():
	NetworkManager(DefaultData),
	messageBuffer(this)
{
	unsigned long
		network_common_flat_address;

	network_common_flat_address =
		((L4Application *)application)->GetNetworkCommonFlatAddress();
	lastHostIteratorPosition				= 0;
	nextOpenHostID							= FirstLegalHostID+1;	// Reserve first legal id for console
	currentNetworkState						= NormalState;
	myConsoleHost							= NULL;
	eggAcknowledged							= False;
	networkStartupMode						= SlaveMode;
	numberOfMungaHostsConnected				= 0;
	numberOfConsoleHostsConnected			= 0;
	remoteHostCount							= 0x7FFFFFFF;
	wsaData = new WSADATA;

	//
	// If there is no net common, set things up for single user mode
	//
	if(network_common_flat_address == 0)
	{
		//Net_Common_Ptr	=	NULL;
		wsaData = NULL;

		//
		//---------------------------------------------------------------------
		// Retrieve the egg as specified on the command line, and send it as an
		// egg message
		//---------------------------------------------------------------------
		//
		const char* egg_name = l4_application->GetEggNotationFileName();
		if (!egg_name || !strlen(egg_name))
		{
			DEBUG_STREAM << "ERROR: No source exists for egg!\n" << std::flush;
			PostQuitMessage(AbortExitCodeID);
		}
		networkEggNotationFile = new NotationFile(egg_name);
		Register_Object(networkEggNotationFile);
		networkEggNotationFile->WriteFile("last.egg");
		currentNetworkState = NormalState;

		ReceiveEggFileMessage egg_message(-1, 10, "local egg", 10);
		application->Post(DefaultEventPriority, this, &egg_message);
		return;
	}

	//
	// Setup the local netcom structure and some pointers to things inside it
	//
	//Net_Common_Ptr 		= new Netcom;
	//Register_Pointer(Net_Common_Ptr);
	//Function_Ptr			= &Net_Common_Ptr->Function;
	//Buffer_Length_Ptr 	= &Net_Common_Ptr->Buffer_Length;
	
	int iResult = WSAStartup(MAKEWORD(2,2), wsaData);
	if(iResult != NO_ERROR)
	{
		DEBUG_STREAM << "ERROR: WSAStartup() failed with " << iResult << "!\n" << std::flush;
		WSACleanup();
		PostQuitMessage(AbortExitCodeID);
	}
	consoleListenerSocket = NULL;
	gameListenerSocket = NULL;

	//
	// Setup the global pointers that the netnub real/protected mode interface
	// requires to work.
	//
	//offs		= (short)(network_common_flat_address & 0x0000000f);
	//baseadr	= (short)(network_common_flat_address>>4);
	//mapRMBuff(); 					//get selector for baseadr
	//RMFunction_Off 				= (short)(offs
	//									+ sizeof(Net_Common_Ptr->Version_Number)
	//									+ sizeof(Net_Common_Ptr->Interrupt_Number));
	//RMBuffer_Length_Off 			= (short)(RMFunction_Off
	//									+ sizeof(Net_Common_Ptr->Function)
	//									+ sizeof(Net_Common_Ptr->Status));

	//getRMNumbers();				// get Interrupt_Number , Version_Number
	//intno			= Net_Common_Ptr->Interrupt_Number;
	//RMverno		= Net_Common_Ptr->Version_Number;

	//
	// Check for right network common number
	////
	//if(RMverno !=  NETCOM_VERSION)
	//{
	//	DEBUG_STREAM << "Netnub version " << RMverno
    //	<< ", code version " << NETCOM_VERSION << endl;
	//	Fail("MUNGA was compiled with an old version of netnub.h");
	//}

	//
	// Get my network address from the netnub (also verify's we are connected)
	//
	addresses = GetMyAddress();
	if(addresses == NULL)
	{
		DEBUG_STREAM<<"ERROR: GetMyAddress() failed!\n";
		WSACleanup();
		Fail("Unable to initialize the network");
	}

	//
	// Force us into reliable mode
	//
	Mode(NetworkManager::ReliableMode);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::MakeNotationFileEgg  This routine creates the console remote
// host
//
//WinSock support :ADB 01/06/07
void L4NetworkManager::CreateConsoleHost()
{
	//unsigned long console_socket;

	//
	// Die if there is no network common around
	//
	if (wsaData == NULL)
	{
		return;
	}

	unsigned short networkPort = (unsigned short)((L4Application *)application)->GetNetworkCommonFlatAddress();

	//
	// Initialize a bunch of stuff to sane states
	//
	nextOpenHostID					=	FirstLegalHostID+1;	// Reserve first legal id for console
	networkStartupMode				=	SlaveMode;				// We're a slave, (not a simulated console)
	currentNetworkState				=	ConsoleOnly;			// Only accept messages from the console
	numberOfConsoleHostsConnected	=	0;                   // Shouldn't be any consoles yet
	numberOfMungaHostsConnected		=	0;                   // No game machines either

	//
	// Post a listen for a console with an unknown network address
	// this will return a stream that the console will eventually be on
	//
	//WinSock support :ADB 01/06/07
	//console_socket	= OpenConnection(
	//	NETNUB_TCP_LISTEN,
	//	CONSOLE_NET_PORT,	// Local port
	//	0,						// Remote port (don't care in this case)
	//	0);					// Network address (don't care in this case)
	consoleListenerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(consoleListenerSocket == INVALID_SOCKET)
	{
		DEBUG_STREAM << "ERROR: Could not create console listener socket; socket() failed with " << WSAGetLastError() << "!\n" << std::flush;
		WSACleanup();
		PostQuitMessage(AbortExitCodeID);
		return;
	}
	sockaddr_in localEndpoint;
	memset(&localEndpoint, 0, sizeof(localEndpoint));
	localEndpoint.sin_family = AF_INET;
	localEndpoint.sin_port = htons(networkPort);
	localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY;
	if(bind(consoleListenerSocket, (sockaddr*)&localEndpoint, sizeof(localEndpoint)))
	{
		DEBUG_STREAM << "ERROR: Could not bind console listener socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush;
		WSACleanup();
		PostQuitMessage(AbortExitCodeID);
		return;
	}
	if(listen(consoleListenerSocket, 1))
	{
		DEBUG_STREAM << "ERROR: Could not listen on console listener socket; listen() failed with " << WSAGetLastError() << "!\n" << std::flush;
		WSACleanup();
		PostQuitMessage(AbortExitCodeID);
		return;
	}
	//set to non blocking
	unsigned long enable = 1;
	if(ioctlsocket(consoleListenerSocket, FIONBIO, &enable))
	{
		DEBUG_STREAM << "ERROR: Could not set console listener socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush;
		WSACleanup();
		PostQuitMessage(AbortExitCodeID);
		return;
	}

	//
	// Register the console host with the host manager, this will insure it gets
	// polled by CheckBuffers for connection.  On connect we'll get a message
	// routed to the network manager.  Note that this host has an unspecified
	// net address since we won't know it till the console connects.  This
	// must be the ONLY host we are listening for or things will fail!!!
	//
	SOCKADDR_IN address;
	address.sin_family = AF_INET;
	address.sin_addr.S_un.S_addr = NullNetworkAddress;
	myConsoleHost = new L4Host(
		FirstLegalHostID,
		ConsoleHostType,
		&address,		// Don't know the net address till it connects.
		INVALID_SOCKET,
		"Console");

	//
	// Do the usual stuff to the host, register it, and have the host manager adopt it
	//
	Register_Object(myConsoleHost);
	myConsoleHost->SetConnectStatus(L4Host::ListeningConnectionStatus);
	Check(application);
	Check(application->GetHostManager());
	application->GetHostManager()->AdoptRemoteHost(myConsoleHost);
	networkEggNotationFile = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::LoadMission  This routine is called when a mission starts to
// allow the network management system to do stuff (potentially establishing
// communications with all the participants and such like)
//
void
	L4NetworkManager::StartConnecting(Mission *mission)
{
	int listen;					// True if we should listen, false for connect
	//WinSock support :ADB 01/06/07
	//unsigned long
	SOCKET socket_ptr;			// temporary place to store the socket for a host
	SOCKADDR_IN net_address;	// temporary place to store resolved net address
	net_address.sin_family = AF_INET;
	L4Host *my_l4host;
	// the game port is the console listening port + 1
	unsigned short localGamePort = (unsigned short)((L4Application *)application)->GetNetworkCommonFlatAddress() + 1;

	//
	// This should be entered with no network connections to other game hosts up,
	// so we initialize this count to zero.
	//
	numberOfMungaHostsConnected = 0;

	//
	// Create the host iterator for the mission egg
	//
	Mission::HostIterator mission_host_iterator(mission);
	MissionHostData *mission_host_data;

	//
	// If there is no net common, set things up for single user mode
	//
	//WinSock support :ADB 01/06/07
	//if (Net_Common_Ptr == NULL)
	if(wsaData == NULL)
	{
		//
		// Make the local host for single user testing, we give the host ID#1
		// and assign the first symbolic name in the egg to it.
		//
		mission_host_data = mission_host_iterator.ReadAndNext();
		Check(mission_host_data);

		SOCKADDR_IN address;
		address.sin_family = AF_INET;
		address.sin_family = NullNetworkAddress;
		my_l4host = new L4Host(
			1,									// Host ID
			mission_host_data->GetHostType(),	// Host Type
			&address,							// Net Address
			INVALID_SOCKET,        				// Socket address
			mission_host_data->GetAddressString());
		Register_Object(my_l4host);
		Check(application);
		Check(application->GetHostManager());
		application->GetHostManager()->AdoptLocalHost(my_l4host);

		//
		//--------------------------------------------------------------------
		// Now, since this host creator is for stand-alone mode, send the load
		// mission message to the application
		//--------------------------------------------------------------------
		//
		Check(application);
		Application::Message
			load_message(
				Application::LoadMissionMessageID,
				sizeof(Application::Message)
			);
		application->Post(DefaultEventPriority, application, &load_message);
		return;
	}

	//
	// Iterate through all the host addresses in the egg and create all the host
	// structures, note that there MUST be an entry in the egg for us.
	//
	listen			=	False;
	remoteHostCount	=	0;
	int bufferSize = sizeof(SOCKADDR_IN);
	while ((mission_host_data = mission_host_iterator.ReadAndNext()) != NULL)
	{
		Check(mission_host_data);

		//
		// Try to resolve this address to an IP address
		//
		CString host_name(mission_host_data->GetAddressString());
		//WinSock support :ADB 01/06/07
		//VERIFY: are these IP addresses?
		//net_address	= ResolveAddress(host_name);
		//net_address = inet_addr((char *)host_name);
		WSAStringToAddressA((LPSTR)host_name, AF_INET, NULL, (LPSOCKADDR)&net_address, &bufferSize);
		if (net_address.sin_port == 0)
			net_address.sin_port = htons(localGamePort);

		//
		// See if this host is us or someone else
		//
		//WinSock support :ADB 01/06/07
		bool addressFound = false;
		for(int i=0; i<num_addresses; i++)
		{
			if(addresses[i] == net_address.sin_addr.S_un.S_addr && localGamePort == ntohs(net_address.sin_port))
			{
				addressFound = true;
				break;
			}
		}
		//if(net_address == address)
		if(addressFound)
		{
			//
			// Create the local host, socket is set to null since we can't send to
			// ourselves.
			//
			my_l4host = new L4Host(
				nextOpenHostID,
				mission_host_data->GetHostType(),
				//WinSock support :ADB 01/06/07
				//address,
				&net_address,
				INVALID_SOCKET,
				host_name);
			Register_Object(my_l4host);
			listen = True;
			Check(application);
			Check(application->GetHostManager());
			application->GetHostManager()->AdoptLocalHost(my_l4host);
		}
		else
		{
			//
			// See what we should do to connect to this remote host (open or listen)
			//
			remoteHostCount++;
			if(listen)
			{
				//WinSock support :ADB 01/06/07
				socket_ptr = OpenConnection(NETNUB_TCP_LISTEN, localGamePort, 0, net_address.sin_addr.S_un.S_addr);
			}
			else
				socket_ptr = OpenConnection(NETNUB_TCP_OPEN, localGamePort, ntohs(net_address.sin_port), net_address.sin_addr.S_un.S_addr);

			if (!listen && socket_ptr == INVALID_SOCKET)
				DEBUG_STREAM << "Could not open connection to " << host_name << ".\n" << std::flush;

			//
			// Now we can create the remote host and get the application to adopt
			// it
			//
			my_l4host = new L4Host(
				nextOpenHostID,
				mission_host_data->GetHostType(),
				&net_address,
				socket_ptr,
				host_name);
			Register_Object(my_l4host);
			my_l4host->SetConnectStatus(
				(listen)
				 ? L4Host::ListeningConnectionStatus
				 : L4Host::OpeningConnectionStatus
			);
			Check(application);
			Check(application->GetHostManager());
			application->GetHostManager()->AdoptRemoteHost(my_l4host);
		}
		nextOpenHostID++;
	}

	//
	// All the hosts are created, the connects/listens done.
	//
	if(networkStartupMode == SlaveMode)
	{
		//
		// We are a slave (got our egg from the network) so we will
		// acknowledge the egg now and wait for the connects to finish
		//
		#if defined(LAB_ONLY)
			DEBUG_STREAM << "Sending egg Acknowledgement\n" << flush;
		#endif
		AcknowledgeEggFileMessage myAcknowledgeEgg;
		Send(
			&myAcknowledgeEgg,
			NetworkClient::NetworkManagerClientID,
			myConsoleHost->GetHostID()
		);

		//
		//-----------------------------------------------------------------------
		// Send the load mission message to the application... if an egg was sent
		//-----------------------------------------------------------------------
		//
		if (numberOfMungaHostsConnected >= remoteHostCount)
		{
			Check(application);
			Application::Message
				load_message(
					Application::LoadMissionMessageID,
					sizeof(Application::Message)
				);
			application->Post(DefaultEventPriority, application, &load_message);
		}
	}

// Console simulator that would be here has been moved to end of file if needed

	else
	{
		Fail("host is in an illegal startup mode\n");
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::Shutdown  This routine is called when a mission ends to
// allow the network management system to do stuff (like disconnecting network
// connections between pods and so on)
//
Logical
	L4NetworkManager::Shutdown()
{
	Host
		*base_host;
	L4Host
		*my_l4host;
	unsigned long
		network_common_flat_address;

	network_common_flat_address = l4_application->GetNetworkCommonFlatAddress();

	if (networkEggNotationFile)
	{
		Unregister_Object(networkEggNotationFile);
		delete networkEggNotationFile;
		networkEggNotationFile = NULL;
	}

	//
	// If there is no net common, all we do is deregister the local host and return
	//
	if(network_common_flat_address == 0)
	{
		//
		// Deregister local host
		//
		base_host = application->GetHostManager()->OrphanLocalHost();
		my_l4host = Cast_Object(L4Host*, base_host);
		Check(my_l4host);
		Unregister_Object(my_l4host);
		delete my_l4host;
		return True;
	}

	//
	// If we get here there was a network setup, we must drop all connections
	// deregister all the remote hosts and then deregister the local host.
	// Start by closing all connections.
	//
	Check(application);
	HostManager *host_mgr = application->GetHostManager();
	Check(host_mgr);
	HostManager::RemoteHostIterator remote_hosts(host_mgr);
	while ((base_host = remote_hosts.ReadAndNext()) != NULL)
	{
		my_l4host = Cast_Object(L4Host*, base_host);
		Check(my_l4host);

		//
		//------------------------------
		// Don't delete the console host
		//------------------------------
		//
		if (my_l4host->GetHostType() == ConsoleHostType)
		{
			continue;
		}

		//
		// Don't do a close if the system says we're not connected (means close was
		// allready done by disconnect handler
		//
		if(my_l4host->GetConnectStatus() !=	Host::NoNetworkConnectionStatus)
		{
			CloseConnection(my_l4host->GetNetworkSocket());
		}
		// deregister this host with the host manager
		host_mgr->OrphanRemoteHost(my_l4host);
		Unregister_Object(my_l4host);
		delete my_l4host;
	}

	//
	// Deregister local host
	//
	base_host	=	host_mgr->OrphanLocalHost();
	if (base_host)
	{
		my_l4host	=	Cast_Object(L4Host*, base_host);
		Check(my_l4host);
		Unregister_Object(my_l4host);
		delete my_l4host;
	}

	//
	// Reinitialize this in case we start another game
	//
	remoteHostCount		= 0x7FFFFFFF;
	nextOpenHostID								=	FirstLegalHostID+1;	// Reserve first legal id for console
	networkStartupMode						=	SlaveMode;				// We're a slave, (not a simulated console)
	currentNetworkState						=	ConsoleOnly;			// Only accept messages from the console
	numberOfConsoleHostsConnected			=	0;                   // Shouldn't be any consoles yet
	numberOfMungaHostsConnected			=	0;                   // No game machines either
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the L4NetworkManager
//
L4NetworkManager::~L4NetworkManager()
{
	//WinSock support :ADB 01/06/07
	//if(Net_Common_Ptr)
	if(wsaData)
	{
		Check(application);
		HostManager *host_mgr = application->GetHostManager();
		Check(host_mgr);
		HostManager::RemoteHostIterator remote_hosts(host_mgr);
		Host *base_host;
		while ((base_host = remote_hosts.ReadAndNext()) != NULL)
		{
			L4Host *my_l4host = Cast_Object(L4Host*, base_host);
			Check(my_l4host);

			//
			// Don't do a close if the system says we're not connected (means close was
			// allready done by disconnect handler
			//
			if(my_l4host->GetConnectStatus() !=	Host::NoNetworkConnectionStatus)
			{
				CloseConnection(my_l4host->GetNetworkSocket());
			}
			// deregister this host with the host manager
			host_mgr->OrphanRemoteHost(my_l4host);
			Unregister_Object(my_l4host);
			delete my_l4host;
		}

		WSACleanup();
		delete wsaData;
		wsaData = NULL;
		//Unregister_Pointer(Net_Common_Ptr);
		//delete Net_Common_Ptr;
		//Net_Common_Ptr = NULL;
	}

	if(addresses)
		delete[] addresses;
	addresses = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::ReceiveEggFileMessageHandler
//
void
	L4NetworkManager::ReceiveEggFileMessageHandler(
		ReceiveEggFileMessage*		EggMessage
	)
{
	#if defined(LAB_ONLY)
		DEBUG_STREAM << "Received Egg message #" << EggMessage->sequenceNumber
		 << endl;
	#endif

	//
	// An egg sequence number of -1 means that the egg is posted locally, and
	// should just call create mission
	//
	if (EggMessage->sequenceNumber == -1)
	{
		application->CreateMission(networkEggNotationFile);
		return;
	}

	if (EggMessage->sequenceNumber == 0)
	{
		//
		// Make a buffer
		//
		eggTempBuffer	=	new char[EggMessage->notationFileLength];
		Register_Pointer(eggTempBuffer);
		eggTempNext		=	0;
	}

	//
	// Write the egg data into the buffer
	//
	memcpy(
		(eggTempBuffer+eggTempNext),
		EggMessage->notationData,
		EggMessage->thisMessageLength
	);
	eggTempNext +=	EggMessage->thisMessageLength;

	//
	// If we don't have all the data, return and wait for more
	//
	if(eggTempNext	<	EggMessage->notationFileLength)
	{
		return;
	}

	//
	// We've got all the data, make the notation file
	//
	networkEggNotationFile	=	new NotationFile();
	Register_Object(networkEggNotationFile);
	networkEggNotationFile->ReadText(eggTempBuffer, eggTempNext);
	#if defined(LAB_ONLY)
		DEBUG_STREAM << "Created egg\n";
	#endif
	networkEggNotationFile->WriteFile("last.egg");
	currentNetworkState	=	NormalState;

	//
	// Now turn the notation file into a mission
	//
	application->CreateMission(networkEggNotationFile);

	//
	// Get rid of the ram buffer now that we're done with it
	//
	Unregister_Pointer(eggTempBuffer);
	delete eggTempBuffer;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::AcknowledgeEggFileMessageHandler
//
void
	L4NetworkManager::AcknowledgeEggFileMessageHandler(
		AcknowledgeEggFileMessage* )
{
	#if defined(LAB_ONLY)
		DEBUG_STREAM << "\nReceived egg acknowledged message\n";
	#endif
	eggAcknowledged = True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::HostConnectedMessageHandler
//
void
	L4NetworkManager::HostConnectedMessageHandler(
		HostConnectedMessage* HostConnected
	)
{
	//
	// Get a pointer to the L4 host
	//
	Check(application);
	Check(application->GetHostManager());
	Host* connected_host =
		application->GetHostManager()->GetRemoteHost(HostConnected->hostID);
	L4Host* l4connected_host = Cast_Object(L4Host*, connected_host);
	Check(l4connected_host);

	//
	// Set the host connection status to on line and increment the
	// counter of hosts online.
	//
	l4connected_host->SetConnectStatus(L4Host::OnLineConnectionStatus);
	switch(l4connected_host->GetHostType())
	{
		case	GameMachineHostType:
			numberOfMungaHostsConnected++;
			DEBUG_STREAM << "Connected to GameMachineHost at ";
			break;
		case	CameraShipHostType:
			numberOfMungaHostsConnected++;
			DEBUG_STREAM << "Connected to CameraShipHost at ";
			break;
		case	MissionReviewHostType:
			numberOfMungaHostsConnected++;
			DEBUG_STREAM << "Connected to MissionReviewHost at ";
			break;
		case	ConsoleHostType:
			numberOfConsoleHostsConnected++;
			DEBUG_STREAM << "Connected to ConsoleHost at ";
			break;
		default:
			Fail("L4NetworkManager::HostConnectedMessageHandler - unknown host type");
			break;
	}

	// commenting out because Verify doesn't do anything anymore --RB 10/28/08
	////
	//// Cross check the network address with the one in the host (just in case)
	////
	//Verify(
	//	l4connected_host->GetNetworkAddress() == HostConnected->networkAddress
	//);

	char addressString[44];
	DWORD bufferSize = sizeof(addressString);
	WSAAddressToStringA((LPSOCKADDR)&HostConnected->networkAddress, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
	DEBUG_STREAM << addressString << std::endl << std::flush;

	if (numberOfMungaHostsConnected >= remoteHostCount)
	{
		DEBUG_STREAM << "All connections completed!\n" << std::flush;
		Marker("MUNGA MARKER--All connections completed!\n");

		//
		//--------------------------------------------------------------------
		// Now, since this host creator is for stand-alone mode, send the load
		// mission message to the application
		//--------------------------------------------------------------------
		//
		Check(application);
		Application::Message
			load_message(
				Application::LoadMissionMessageID,
				sizeof(Application::Message)
			);
		application->Post(DefaultEventPriority, application, &load_message);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::HostDisconnectedMessageHandler
// This message should eventually be generated in CheckBuffers, right now it
// is actually sent over the network and is assumed only to be sent by the
// console just prior to it disconnecting.  It clues us do locally close
// and destroy the console host so the game will start normally even if the
// console isn't around.
//
void L4NetworkManager::HostDisconnectedMessageHandler(HostDisconnectedMessage* HostDisconnected)
{
	//
	// Get a pointer to the L4 host
	//
	Check(application->GetHostManager());
	Host* connected_host		= application->GetHostManager()->GetRemoteHost(HostDisconnected->hostID);
	L4Host* l4connected_host	= Cast_Object(L4Host*, connected_host);
	Check(l4connected_host);
	//
	// Set the host connection status to off line, get the network address so we
	// can announce it later, then close the connection.
	//
	l4connected_host->SetConnectStatus(L4Host::NoNetworkConnectionStatus);
	SOCKADDR_IN temp_net_address = *l4connected_host->GetNetworkAddress();
	CloseConnection(HostDisconnected->streamPointer);
	//
	// See what type of host it was that disconnected
	//
	switch(l4connected_host->GetHostType())
	{
		case	GameMachineHostType:
			numberOfMungaHostsConnected--;
			DEBUG_STREAM<<"\nDisconnected from GameMachineHost at ";
			break;
		case	CameraShipHostType:
			numberOfMungaHostsConnected--;
			DEBUG_STREAM<<"\nDisconnected from CameraShipHost at ";
			break;
		case	MissionReviewHostType:
			numberOfMungaHostsConnected--;
			DEBUG_STREAM<<"\nDisconnected from MissionReviewHost at ";
			break;
		case	ConsoleHostType:
		{
			//
			// Knock down the number of consoles online
			//
			numberOfConsoleHostsConnected--;
			DEBUG_STREAM<<"\nDisconnected from ConsoleHost at ";
			//
			// Post a listen for a console so it can reconnect if it wants to.
			//
			shutdown(gameListenerSocket, SD_BOTH);
			closesocket(gameListenerSocket);
			gameListenerSocket = NULL;
			//unsigned long console_socket	=	OpenConnection(
			//	NETNUB_TCP_LISTEN,
			//	((L4Application *)application)->GetNetworkCommonFlatAddress(),			// Local port
			//	0,							// Remote port (don't care in this case)
			//	0);							// Network address (don't care in this case)
			//
			// Destroy the console host and recreate it, this resets all the
			// internal buffers and pointers so check_buffers won't go crazy
			//
			application->GetHostManager()->OrphanRemoteHost(l4connected_host);
			Unregister_Object(l4connected_host);
			delete l4connected_host;
#if 1
			CreateConsoleHost();
			/*SOCKADDR_IN address;
			address.sin_family = AF_INET;
			address.sin_family = NullNetworkAddress;
			l4connected_host = new L4Host(
				FirstLegalHostID,
				ConsoleHostType,
				&address,
				console_socket,
				"Console");
			Register_Object(l4connected_host);
			l4connected_host->SetConnectStatus(L4Host::ListeningConnectionStatus);
			application->GetHostManager()->AdoptRemoteHost(l4connected_host);
			myConsoleHost =  l4connected_host;*/
#else
			myConsoleHost = 0;
#endif
			break;
		}
		default:
			Fail("L4NetworkManager::HostDisconnectedMessageHandler - unknown host type");
			break;
	}

	char addressString[44];
	DWORD bufferSize = sizeof(addressString);
	WSAAddressToStringA((LPSOCKADDR)&temp_net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
	DEBUG_STREAM << addressString << "\n" << std::flush;
}

#if MESSAGE_BUFFERING
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::Send
//
void
	L4NetworkManager::Send(
		Message *message,
		ClientID client_ID,
		HostID host_ID
  )
{
	SET_SEND_PACKET();
	Check(this);
	Check(message);
	
	//
	//--------------------------------------------------------------------------
	// If the message buffer is not empty then add this new message into the
	// buffer.  Attempt to send a message.
	//--------------------------------------------------------------------------
	//	
	if (!messageBuffer.IsEmpty())
	{
		messageBuffer.AddSendRequest(host_ID, client_ID, message);
		messageBuffer.AttemptToSend();
	}
	
	//
	//--------------------------------------------------------------------------
	// The message buffer is empty so attempt to send the message.
	//--------------------------------------------------------------------------
	//	
	else if (!SendMessageToNetnub(message, client_ID, host_ID))
	{
		//
		// Message was dropped, add this new message into the buffer
		//
		messageBuffer.AddSendRequest(host_ID, client_ID, message);
	}
	CLEAR_SEND_PACKET();
}
#else
//WinSock support :ADB 01/06/07
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::Send  Handles sending a message to a specific network address
// which can NOT be us.
//
void L4NetworkManager::Send(
	Message		*message,
	ClientID	client,
	HostID		host_ID)
{
	int						packet_size;
	Host					*local_host;
	Host					*base_host;
	L4Host					*l4host;
	//SendPacketRequestPtr	send_packet_request;		// Format of the request we send to the NetNub
	//SendPacketReturnPtr	send_packet_return;
	HostManager				*our_host_manager;
	NetworkPacket			*my_temp_packet;			// pointer to a place to build a network message
	//
	// Mark entry to the routine with an analysis call
	//
	SET_SEND_PACKET();
	//
	// Check whatever incoming data needs checking, get a pointer to the host
	// we are going to send to and make sure that host is online.
	//
	Check_Pointer(message);
	Check(application);
	our_host_manager	=	application->GetHostManager();
	Check(our_host_manager);
	base_host	=	our_host_manager->GetRemoteHost(host_ID);
	l4host		=	Cast_Object(L4Host*, base_host);
	if(l4host->GetConnectStatus() != L4Host::OnLineConnectionStatus)
	{
		CLEAR_SEND_PACKET();
		return;
	}
	Verify(l4host->GetConnectStatus() == L4Host::OnLineConnectionStatus);
	//
	// figure out where to put the packet we're assembling
	//
	//send_packet_request	=	(SendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//send_packet_return	=  (SendPacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//my_temp_packet 		=	(NetworkPacket*)send_packet_request->Send_Data;
	//
	// Build a MUNGA network packet in the netnub common block
	//
	if((false) || (message->messageLength < 8) || (false))
	//if((message->messageLength > MAX_SEND_DATA_SIZE) || (message->messageLength < 8) || (message->messageLength+sizeof(NetworkPacketHeader) > MAX_SEND_DATA_SIZE))
	{
		char addressString[44];
		DWORD bufferSize = sizeof(addressString);
		WSAAddressToStringA((LPSOCKADDR)l4host->GetNetworkAddress(), sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
		DEBUG_STREAM << addressString << " Requested to send a " << message->messageLength << " size packet\n" << std::flush;
		Fail("Illegal transmit size\n");
	}
	//Verify(message->messageLength + sizeof(NetworkPacketHeader) <= MAX_SEND_DATA_SIZE);
	Verify(message->messageLength >= 8);
	my_temp_packet = (NetworkPacket*)malloc(message->messageLength + sizeof(NetworkPacketHeader));
	memcpy(&my_temp_packet->messageData, message, message->messageLength);
	my_temp_packet->clientID	=	client;
	my_temp_packet->gameID		=	gameID;
	my_temp_packet->timeStamp	=	Now();

	//
	// If the local host doesn't exist yet, we send back zero
	// this lets the console get status before the local host is actually created
	//
	local_host = our_host_manager->GetLocalHost();
	if(local_host == NULL)
	{
		my_temp_packet->fromHost 	=  0;
	}
	else
	{
		Check(local_host);
		my_temp_packet->fromHost 	=  local_host->GetHostID();
	}
	packet_size						=	message->messageLength + sizeof(NetworkPacketHeader);
	//
	// Fill in the NetNub common block with the rest of the data it needs for the call
	//
	//Net_Common_Ptr->Function	 		=	NETNUB_SEND_PACKET;
	//Net_Common_Ptr->Buffer_Length		=	(short)SEND_BUFFER_SIZE(packet_size);
	//send_packet_request->Socket_Ptr	=	l4host->GetNetworkSocket();
	//NetNub::SendCommand();
	send(l4host->GetNetworkSocket(), (char *)my_temp_packet, packet_size, 0);
	free(my_temp_packet);
	// Check for errors and abort if there were any
	//#if defined(TRACE_SEND_BUFFER)
	//l4host->sendBufferTrace->TakeSnapshot(send_packet_return->Send_Buffer_Used);
	//#endif
	//switch(Net_Common_Ptr->Status)
	//{
	//	case	NETNUB_OK:
	//		break;
	//	case	NETNUB_DATA_DUMPED:
	//	{
	//		SET_LOST_DATA();
	//		#if REPORT_LOST_DATA
	//		unsigned long temp_net_address;
	//		temp_net_address = l4host->GetNetworkAddress();
	//		DEBUG_STREAM<<"Data lost to ";
	//		DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<".";
	//		DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n";
	//		DEBUG_STREAM<<flush;
	//		#endif
	//		CLEAR_LOST_DATA();
	//		break;
	//	}
	//	case	NETNUB_STREAM_DISCONNECTED:
	//	{
	//		DEBUG_STREAM<<"Disconnect detected in send\n";
	//		DEBUG_STREAM<<flush;
	//		HostDisconnectedMessage myHostDisconnected(
	//			l4host->GetHostID(),
	//			l4host->GetNetworkSocket());
	//		NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
	//		Check(client);
	//		client->ReceiveNetworkPacket(NULL, &myHostDisconnected);
	//		break;
	//	}
	//	default:
	//	{
	//		unsigned long temp_net_address;
	//		temp_net_address = l4host->GetNetworkAddress();
	//		DEBUG_STREAM<<"Error "<<Net_Common_Ptr->Status<<" on ";
	//		DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<".";
	//		DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n";
	//		DEBUG_STREAM<<flush;
	//		Fail("NetNub error in transmit\n");
	//	}
	//}
	CLEAR_SEND_PACKET();
}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::SendMessageToNetnub
//
//WinSock support :ADB 01/06/07
Logical L4NetworkManager::SendMessageToNetnub(
		Message *message,
		ClientID client_ID,
		HostID host_ID)
{
	Check(this);
	Check(message);

	//
	//--------------------------------------------------------------------------
	// Get host manager, receiving host.  Verify that the host is online.
	//--------------------------------------------------------------------------
	//
	HostManager *host_manager;
	L4Host *receiving_host;
	
	Check(application);
	host_manager = application->GetHostManager();
	Check(host_manager);
	
	receiving_host = Cast_Object(L4Host*, host_manager->GetRemoteHost(host_ID));
	if (receiving_host->GetConnectStatus() != L4Host::OnLineConnectionStatus)
	{
		return True;
	}
	Verify(receiving_host->GetConnectStatus() == L4Host::OnLineConnectionStatus);

	//
	//--------------------------------------------------------------------------
	// Get the local host ID.  If the local host doesn't exist yet, we use zero,
	// this lets the console get status before the local host is actually 
	// created.
	//--------------------------------------------------------------------------
	//
	Host *local_host;
	HostID local_host_ID;
	
	if((local_host = host_manager->GetLocalHost()) == NULL)
	{
		local_host_ID = 0;
	}
	else
	{
		Check(local_host);
		local_host_ID = local_host->GetHostID();
	}

	//
	//--------------------------------------------------------------------------
	// Verify that the size of the message is within bounds.
	//--------------------------------------------------------------------------
	//
	long munga_network_message_size;
		
	munga_network_message_size = sizeof(NetworkPacketHeader) + message->messageLength;
	if((message->messageLength < 8) || (false))
	//if((message->messageLength < 8) || (munga_network_message_size > MAX_SEND_DATA_SIZE))
	{
		char addressString[44];
		DWORD bufferSize = sizeof(addressString);
		WSAAddressToStringA((LPSOCKADDR)local_host->GetNetworkAddress(), sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
			
		DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - " << addressString << "Requested to send a " << munga_network_message_size << " size message\n" << std::flush;
		Fail("L4NetworkManager::SendMessageToNetnub - Illegal transmit size\n");
		return True;
	}
	Verify(SEND_BUFFER_SIZE(munga_network_message_size) <= SHARED_MEMORY_SIZE);

	//
	//--------------------------------------------------------------------------
	// Build the send request and MUNGA network packet
	//--------------------------------------------------------------------------
	//
	//SendPacketRequestPtr send_packet_request;
	NetworkPacket *network_packet;
		
	Check_Pointer(Net_Common_Ptr);
	//send_packet_request = (SendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//network_packet	= (NetworkPacket*)send_packet_request->Send_Data;
	network_packet = (NetworkPacket*)malloc(munga_network_message_size);

	//send_packet_request->Socket_Ptr = receiving_host->GetNetworkSocket();
	network_packet->clientID = client_ID;
	network_packet->gameID = gameID;
	network_packet->fromHost = local_host_ID;
	network_packet->timeStamp = Now();
	
	Mem_Copy(
		&network_packet->messageData,
		message,
		message->messageLength,
		MAX_SEND_DATA_SIZE-sizeof(NetworkPacketHeader)
	);

	//
	//--------------------------------------------------------------------------
	// Netnub call
	//--------------------------------------------------------------------------
	//
	//Check_Pointer(Net_Common_Ptr);
	//#if defined(TRACE_SEND_BUFFER)
	//	SendPacketReturnPtr send_packet_return =
	//		(SendPacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//#endif
	//Net_Common_Ptr->Buffer_Length = 
	//	(unsigned short)SEND_BUFFER_SIZE(munga_network_message_size);
	//Net_Common_Ptr->Function = 
	//	NETNUB_SEND_PACKET;
	//NetNub::SendCommand();
	send(receiving_host->GetNetworkSocket(), (char *)network_packet, munga_network_message_size, 0);
	free(network_packet);
	
	#if defined(TRACE_SEND_BUFFER)
		receiving_host->sendBufferTrace->TakeSnapshot(
			send_packet_return->Send_Buffer_Used
		);
   #endif

	//
	//--------------------------------------------------------------------------
	// Check for errors
	//--------------------------------------------------------------------------
	//
	//switch(Net_Common_Ptr->Status)
	//{
	//case NETNUB_OK:
	//	return True;
	//	
	//case NETNUB_DATA_DUMPED:
	//	{
	//		SET_LOST_DATA();
	//		#if REPORT_LOST_DATA
	//			unsigned long 
	//				temp_net_address = receiving_host->GetNetworkAddress();
	//			DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - ";
	//			DEBUG_STREAM << "Data lost to ";
	//			DEBUG_STREAM << ((temp_net_address>>24) & 0xff)<<"."<<
	//				((temp_net_address>>16) & 0xff)<<".";
	//			DEBUG_STREAM << ((temp_net_address>>8) & 0xff)<<"."<<
	//				(temp_net_address & 0xff)<<"\n";
	//			DEBUG_STREAM << flush;
	//		#endif
	//		CLEAR_LOST_DATA();
	//	}
	//	return False;
	//	
	//case	NETNUB_STREAM_DISCONNECTED:
	//	{
	//		DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - ";
	//		DEBUG_STREAM<<"Disconnect detected in send\n";
	//		DEBUG_STREAM<<flush;
	//		
	//		HostDisconnectedMessage 
	//			myHostDisconnected(
	//				receiving_host->GetHostID(),
	//				receiving_host->GetNetworkSocket()
	//			);
	//			
	//		NetworkClient 
	//			*client = GetNetworkClientPointer(NetworkManagerClientID);
	//		Check(client);
	//		client->ReceiveNetworkPacket(NULL, &myHostDisconnected);
	//	}
	//	return True;
	//	
	//default:
	//	{
	//		unsigned long 
	//			temp_net_address = receiving_host->GetNetworkAddress();

	//		DEBUG_STREAM << "L4NetworkManager::SendMessageToNetnub - ";
	//		DEBUG_STREAM << "Error "<<Net_Common_Ptr->Status<<" on ";
	//		DEBUG_STREAM << ((temp_net_address>>24) & 0xff)<<"."<<
	//			((temp_net_address>>16) & 0xff)<<".";
	//		DEBUG_STREAM << ((temp_net_address>>8) & 0xff)<<"."<<
	//			(temp_net_address & 0xff)<<"\n";
	//		DEBUG_STREAM << flush;
	//		Fail("NetNub error in transmit\n");
	//	}
	//	break;
	//}
	return True;
}

#if MESSAGE_BUFFERING
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::ExclusiveBroadcast
//
void
	L4NetworkManager::ExclusiveBroadcast(
		Message *message,
		ClientID client_ID
	)
{
	SET_SEND_PACKET();
	Check(this);
	Check(message);
	
	//
	//--------------------------------------------------------------------------
	// If the message buffer is not empty then add this new message into the 
	// buffer.  One copy per host.
	//--------------------------------------------------------------------------
	//	
	if (!messageBuffer.IsEmpty())
	{
		Check(application);
		Check(application->GetHostManager());
		
		HostManager::RemoteHostIterator 
			remote_hosts(application->GetHostManager());
		Host
			*host;
		L4Host
			*l4host;
	
		while ((host = remote_hosts.ReadAndNext()) != NULL)
		{
			l4host = Cast_Object(L4Host*, host);
			Check(l4host);
			
			//
			// only non-console hosts that are online
			//
			if(
				(l4host->GetHostType() != ConsoleHostType) &&
				(l4host->GetConnectStatus() == L4Host::OnLineConnectionStatus)
			)
			{
				messageBuffer.AddSendRequest(
					l4host->GetHostID(), 
					client_ID, 
					message
				);
			}
		}
		messageBuffer.AttemptToSend();
		CLEAR_SEND_PACKET();
		return;
	}
	
	//
	//--------------------------------------------------------------------------
	// The message buffer is empty so attempt to send the message.
	//--------------------------------------------------------------------------
	//	
	DroppedMessageHostSocket 
		dropped_message_host_socket(NULL);
	
	SendBatchedMessageToNetnub(
		message, 
		client_ID, 
		&dropped_message_host_socket
	);
	
	//
	//--------------------------------------------------------------------------
	// For each host the message was dropped, add a send request message
	//--------------------------------------------------------------------------
	//
	DroppedMessageHostIterator
		iterator(&dropped_message_host_socket);
	L4Host
		*l4host;
		
	while((l4host = iterator.ReadAndNext()) != NULL)
	{
		Check(l4host);
		messageBuffer.AddSendRequest(
			l4host->GetHostID(), 
			client_ID, 
			message
		);
	}
	CLEAR_SEND_PACKET();
}
#else
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::ExclusiveBroadcast  Broadcasts a message to everyone on the
// network execpt us.  !!!! Reliable broadcasting is currently used,
// implimented by sending a point-to-point message to every destination.
//
void
	L4NetworkManager::ExclusiveBroadcast(
		Message *message, // what,
		ClientID client	// to
	)
{
#if !BATCHED_TRANSMIT
	Host
		*this_host;
	//
	// Iterate through the host list, resolving network addresses, sending and
	// making sure we don't send to ourselves.
	//
	Check(application);
	Check(application->GetHostManager());

	HostManager::RemoteHostIterator remote_hosts(application->GetHostManager());

	while ((this_host = remote_hosts.ReadAndNext()) != NULL)
	{
		Check(this_host);
		if(this_host->GetHostType() == ConsoleHostType)
		{
			continue;
		}

		Send(message, client, this_host->GetHostID());
	}
#else
	int
		i,
		host_count,				// Number of hosts we will be sending to
		packet_size;
	Host
		*this_host,
		*local_host;
	L4Host
		*l4_host_list[MULTIPLE_SEND_PACKET_MAX],
		*l4host;
	MultipleSendPacketReturnPtr
		multiple_send_packet_return;		// Format of the reply we get from the NetNub
	MultipleSendPacketRequestPtr
		multiple_send_packet_request;		// Format of the request we send to the NetNub
	HostManager
		*our_host_manager;
	NetworkPacket
		*my_temp_packet;			// pointer to a place to build a network message
	//
	// Mark entry to the routine with an analysis call
	//
	SET_SEND_PACKET();
	//
	// Make sure the application and host manager are valid
	//
	Check(application);
	Check_Pointer(message);
	our_host_manager	=	application->GetHostManager();
	Check(our_host_manager);
	//
	// Make a remote host iterator
	//
	HostManager::RemoteHostIterator remote_hosts(our_host_manager);
	multiple_send_packet_request 	= 	(MultipleSendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	multiple_send_packet_return 	= 	(MultipleSendPacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	my_temp_packet 					=	(NetworkPacket*)multiple_send_packet_request->Send_Data;
	//
	// Iterate through all the hosts we want to send to and add them to the
	// NETNUB_MULTIPLE_SEND command structure
	//
	host_count = 0;
	while ((this_host = remote_hosts.ReadAndNext()) != NULL)
	{
		// convert regular host to an L4host so we can get net info
		l4host	=	Cast_Object(L4Host*, this_host);
		Check(l4host);
		// only broadcast to non-console hosts that are online
		if((l4host->GetHostType() 			!= ConsoleHostType) &&
			(l4host->GetConnectStatus()	== L4Host::OnLineConnectionStatus))
		{
			// Remember this host so we can match up errors with it later
			l4_host_list[host_count]	=	l4host;
			// Put the socket pointer for this host into the request structure
			multiple_send_packet_request->Socket_Ptrs[host_count] =
				l4host->GetNetworkSocket();
			host_count++;
			if(host_count >= MULTIPLE_SEND_PACKET_MAX)
				Fail("Tried to send to too many destinations\n");
		}
	}
	//
	// Return if we didn't find anyone to send to
	//
	if(host_count == 0)
	{
		CLEAR_SEND_PACKET();
		return;
	}
	multiple_send_packet_request->Socket_Count = host_count;
	//
	// Check the packet to make sure it is of legal size
	//
	if(	(message->messageLength > MAX_SEND_DATA_SIZE)	||
			(message->messageLength < 8) ||
			(message->messageLength+sizeof(NetworkPacketHeader) > MAX_SEND_DATA_SIZE))
	{
		DEBUG_STREAM<<"Tried to send a "<<message->messageLength<<" size packet\n";
		DEBUG_STREAM<<flush;
		Fail("Illegal transmit size\n");
	}
	//
	// Copy the data over into the command to netnub
	//
	memcpy(&my_temp_packet->messageData, message, message->messageLength);
	my_temp_packet->clientID	=	client;
	my_temp_packet->gameID		=	gameID;
	my_temp_packet->timeStamp	=	Now();
	//
	// If the local host doesn't exist yet, we send back zero as the from address
	// this lets the console get status before the local host is actually created
	//
	local_host = our_host_manager->GetLocalHost();
	if(local_host == NULL)
	{
		my_temp_packet->fromHost 	=  0;
	}
	else
	{
		Check(local_host);
		my_temp_packet->fromHost 	=  local_host->GetHostID();
	}
	//
	// Fill in the NetNub common block with the rest of the data it needs for the call
	//
	packet_size								=	message->messageLength + sizeof(NetworkPacketHeader);
	Net_Common_Ptr->Function	 		=	NETNUB_MULTIPLE_SEND;
	Net_Common_Ptr->Buffer_Length		=	(short)MULTIPLE_SEND_BUFFER_SIZE(packet_size);
	NetNub::SendCommand();
	#if !defined(TRACE_SEND_BUFFER)
	if(Net_Common_Ptr->Status == NETNUB_OK)
	{
		CLEAR_SEND_PACKET();
		return;
	}
	#endif
	//
	// If we get this far an error was reported on one of the streams
	//
	for(i = 0; i<host_count; i++)
	{
		l4host	=	l4_host_list[i];
		#if defined(TRACE_SEND_BUFFER)
		l4host->sendBufferTrace->TakeSnapshot(multiple_send_packet_return->Send_Buffer_Used[i]);
		#endif
		switch(multiple_send_packet_return->Errors[i])
		{
			case	NETNUB_OK:
				break;
			case	NETNUB_DATA_DUMPED:
			{
				SET_LOST_DATA();
				#if REPORT_LOST_DATA
				unsigned long temp_net_address = l4host->GetNetworkAddress();
				DEBUG_STREAM<<"Data lost to  ";
				DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<".";
				DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n";
				DEBUG_STREAM<<flush;
				#endif
				CLEAR_LOST_DATA();
				break;
			}
			case	NETNUB_STREAM_DISCONNECTED:
			{
				DEBUG_STREAM<<"Disconnect detected in send\n";
				DEBUG_STREAM<<flush;
				HostDisconnectedMessage myHostDisconnected(
					l4host->GetHostID(),
					l4host->GetNetworkSocket());
				NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
				Check(client);
				client->ReceiveNetworkPacket(NULL, &myHostDisconnected);
				break;
			}
			default:
			{
				unsigned long temp_net_address;
				temp_net_address = l4host->GetNetworkAddress();
				DEBUG_STREAM<<"Error "<<Net_Common_Ptr->Status<<" on ";
				DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<".";
				DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n";
				DEBUG_STREAM<<flush;
				Fail("NetNub error in transmit\n");
			}
		}
	}
	CLEAR_SEND_PACKET();
#endif
}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::SendBatchedMessageToNetnub  
//
//WinSock support :ADB 01/06/07
void
	L4NetworkManager::SendBatchedMessageToNetnub(
		Message *message,
		ClientID client_ID,
		DroppedMessageHostSocket *dropped_message_host_socket
	)
{
	Check(this);
	Check(dropped_message_host_socket);
	
	//
	//--------------------------------------------------------------------------
	// If communication with netnub does not exist then return.
	//--------------------------------------------------------------------------
	//
	if(wsaData == NULL)
		return;
	
	//
	//--------------------------------------------------------------------------
	// Get host manager.
	//--------------------------------------------------------------------------
	//
	HostManager 
		*host_manager;
	
	Check(application);
	host_manager = application->GetHostManager();
	Check(host_manager);

	//
	//--------------------------------------------------------------------------
	// Get the local host ID.  If the local host doesn't exist yet, we use zero,
	// this lets the console get status before the local host is actually 
	// created.
	//--------------------------------------------------------------------------
	//
	Host 
		*local_host;
	HostID
		local_host_ID;
	
	if ((local_host = host_manager->GetLocalHost()) == NULL)
	{
		local_host_ID = 0;
	}
	else
	{
		Check(local_host);
		local_host_ID = local_host->GetHostID();
	}

	//
	//--------------------------------------------------------------------------
	// Verify that the size of the message is within bounds.
	//--------------------------------------------------------------------------
	//
	long
		munga_network_message_size;
		
	munga_network_message_size = sizeof(NetworkPacketHeader)+message->messageLength;
	//if((message->messageLength < 8) || (munga_network_message_size > MAX_SEND_DATA_SIZE))
	if((message->messageLength < 8) || (false))
	{
		DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - ";
		DEBUG_STREAM << "Requested to send a " << 
			munga_network_message_size << " size message\n";
		DEBUG_STREAM << std::flush;
		Fail("L4NetworkManager::SendBatchedMessageToNetnub - Illegal transmit size\n");
		return;
	}
	Verify(MULTIPLE_SEND_BUFFER_SIZE(munga_network_message_size) <= SHARED_MEMORY_SIZE);

	//
	//--------------------------------------------------------------------------
	// Build the send request and MUNGA network packet
	//--------------------------------------------------------------------------
	//
	//MultipleSendPacketRequestPtr send_packet_request;
	NetworkPacket *network_packet;
		
	Check_Pointer(Net_Common_Ptr);
	//send_packet_request = (MultipleSendPacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//network_packet	= (NetworkPacket*)send_packet_request->Send_Data;
	network_packet = (NetworkPacket*)malloc(munga_network_message_size);

	//
	// Fill in socket ptrs
	//
	HostManager::RemoteHostIterator remote_hosts(host_manager);
	Host *host;
	L4Host *l4host, *l4host_array[MULTIPLE_SEND_PACKET_MAX];
	int host_count;
	
	host_count = 0;
	while ((host = remote_hosts.ReadAndNext()) != NULL)
	{
		l4host = Cast_Object(L4Host*, host);
		Check(l4host);
		
		//
		// only broadcast to non-console hosts that are online
		//
		if(
			(l4host->GetHostType() != ConsoleHostType) &&
			(l4host->GetConnectStatus() == L4Host::OnLineConnectionStatus)
		)
		{
			if (host_count >= MULTIPLE_SEND_PACKET_MAX)
			{
				DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - ";	
				Fail("Tried to send to too many destinations\n");
				return;
			}

			//
			// Remember this host so we can match up errors with it later
			//
			Verify(0 <= host_count && host_count < MULTIPLE_SEND_PACKET_MAX);
			l4host_array[host_count] = l4host;
			
			//
			// Put the socket pointer for this host into the request structure
			//
			Verify(0 <= host_count && host_count < MULTIPLE_SEND_PACKET_MAX);
			//send_packet_request->Socket_Ptrs[host_count] = l4host->GetNetworkSocket();
			host_count++;
		}
	}
	
	//
	// Return if there are no hosts to send to
	//
	//if((send_packet_request->Socket_Count = host_count) == 0)
	if(host_count == 0)
		return;

	//
	// Fill out the munga network packet
	//
	network_packet->clientID = client_ID;
	network_packet->gameID = gameID;
	network_packet->fromHost = local_host_ID;
	network_packet->timeStamp = Now();
	
	Mem_Copy(
		&network_packet->messageData,
		message,
		message->messageLength,
		MAX_SEND_DATA_SIZE-sizeof(NetworkPacketHeader)
	);

	//
	//--------------------------------------------------------------------------
	// Netnub call
	//--------------------------------------------------------------------------
	//
	//MultipleSendPacketReturn
	//	*send_packet_return;
	//	
	//Check_Pointer(Net_Common_Ptr);
	//send_packet_return =
	//	(MultipleSendPacketReturn*)Net_Common_Ptr->Shared_Memory_Buffer;
	//Net_Common_Ptr->Buffer_Length = 
	//	(unsigned short)MULTIPLE_SEND_BUFFER_SIZE(munga_network_message_size);
	//Net_Common_Ptr->Function = 
	//	NETNUB_MULTIPLE_SEND;
	//NetNub::SendCommand();
	for(int i=0; i<host_count; i++)
	{
		send(l4host_array[i]->GetNetworkSocket(), (char *)network_packet, sizeof(network_packet), 0);
	}
	free(network_packet);
	
	//if (Net_Common_Ptr->Status == NETNUB_OK)
	//	return;
	//
	////
	////--------------------------------------------------------------------------
	//// Parse return
	////--------------------------------------------------------------------------
	////
	//for (int i = 0; i < host_count; i++)
	//{
	//	Verify(0 <= i && i < MULTIPLE_SEND_PACKET_MAX);
	//	l4host = l4host_array[i];
	//	Check(l4host);
	//	
	//	#if defined(TRACE_SEND_BUFFER)
	//		l4host->sendBufferTrace->TakeSnapshot(
	//			send_packet_return->Send_Buffer_Used[i]
	//		);
	//	#endif

	//	switch (send_packet_return->Errors[i])
	//	{
	//	case NETNUB_OK:
	//		break;
	//		
	//	case NETNUB_DATA_DUMPED:
	//		{
	//			SET_LOST_DATA();
	//			#if REPORT_LOST_DATA
	//				unsigned long 
	//					temp_net_address = l4host->GetNetworkAddress();
	//				
	//				DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - ";	
	//				DEBUG_STREAM << "Data lost to  ";
	//				DEBUG_STREAM << ((temp_net_address>>24) & 0xff)<<"."<<
	//					((temp_net_address>>16) & 0xff)<<".";
	//				DEBUG_STREAM << ((temp_net_address>>8) & 0xff)<<"."<<
	//					(temp_net_address & 0xff)<<"\n";
	//				DEBUG_STREAM << flush;
	//			#endif
	//			
	//			//
	//			// Add this host to the dropped message host socket
	//			//
	//			Check(dropped_message_host_socket);
	//			dropped_message_host_socket->Add(l4host);
	//			CLEAR_LOST_DATA();
	//		}
	//		break;
	//		
	//	case	NETNUB_STREAM_DISCONNECTED:
	//		{
	//			DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - ";	
	//			DEBUG_STREAM <<"Disconnect detected in send\n";
	//			DEBUG_STREAM <<flush;
	//			
	//			HostDisconnectedMessage 
	//				myHostDisconnected(
	//					l4host->GetHostID(),
	//					l4host->GetNetworkSocket()
	//				);
	//			NetworkClient 
	//				*client = GetNetworkClientPointer(NetworkManagerClientID);
	//			Check(client);
	//			client->ReceiveNetworkPacket(NULL, &myHostDisconnected);
	//		}
	//		break;
	//		
	//	default:
	//		{
	//			unsigned long 
	//				temp_net_address = l4host->GetNetworkAddress();
	//			DEBUG_STREAM << "L4NetworkManager::SendBatchedMessageToNetnub - ";	
	//			DEBUG_STREAM <<"Error "<<Net_Common_Ptr->Status<<" on ";
	//			DEBUG_STREAM <<((temp_net_address>>24) & 0xff)<<"."<<
	//				((temp_net_address>>16) & 0xff)<<".";
	//			DEBUG_STREAM <<((temp_net_address>>8) & 0xff)<<"."<<
	//				(temp_net_address & 0xff)<<"\n";
	//			DEBUG_STREAM <<flush;
	//			Fail("NetNub error in transmit\n");
	//		}
	//		break;
	//	}
	//}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::ExecuteBackground
//
Logical
	L4NetworkManager::ExecuteBackground()
{
	#if MESSAGE_BUFFERING
		Check(this);
		if (!messageBuffer.IsEmpty())
		{
			messageBuffer.AttemptToSend();		
			return True;
		}
		return False;
	#else
		return False;
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::CheckBuffers  Checks to see if there is a message for us
// buffered up in the network interface card and returns a pointer to it.
// NOTE: This thing should be using iterators to keep track of the people on
// line, waiting for connections and so on.
//
//WinSock support :ADB 01/06/07
Logical L4NetworkManager::CheckBuffers(NetworkPacket *network_packet)
{
	//char *current_receive_ptr;
	int
//		i,
//		status,
		host_count;
	Logical get_buffer_status;
	short space_left;
	Host *host;
	L4Host
		//*l4host,
		*remote_host;
		//*l4_host_list[MULTIPLE_SEND_PACKET_MAX];

	//MultipleReceivePacketRequestPtr multiple_receive_packet;
	//MultipleReceivePacketReturnPtr multiple_receive_packet_return;

	//SOCKET next_socket_ptr;

	SET_CHECK_BUFFERS();
	//
	// Get pointers to the receive packet request structure
	//
	//multiple_receive_packet			=	(MultipleReceivePacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//next_socket_ptr					=	multiple_receive_packet->Socket_Ptrs;
	//multiple_receive_packet_return	=  (MultipleReceivePacketReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//current_receive_ptr				=	multiple_receive_packet_return->Received_Data;
	//
	// Check some of the pointers we plan to use
	//
	Check(application);
	Check(application->GetHostManager());
	//
	// Make an iterator to take us through all the hosts
	//
	HostManager::RemoteHostIterator remote_hosts(application->GetHostManager());
	//
	// See if there is ANY data available from the MUNGA buffers before we go
	// looking for it from the network.
	//
	if(GetNextMungaPacket(network_packet,&remote_hosts))
	{
		CLEAR_CHECK_BUFFERS();
		return True;
	}
	//
	// Reset to the first host, then attempt to read from every host that has
	// room in it's buffers for a packet
	//
	host_count = 0;
	remote_hosts.First();
	while ((host = remote_hosts.ReadAndNext()) != NULL)
	{
		// get the host pointer and cast it over to an l4 host.  This is done so we can
		// bang directly on the pad buffers stored in the host. Yes, it's ugly but it will
		// be fixed in the future !!!! GAC
		remote_host = Cast_Object(L4Host*, host);
		Check(remote_host);
		//
		// Our first priority is to check connection status on any hosts that we haven't
		// connected to yet.  If we encounter a host who has connected we immediately
		// generate and return the appropriate message for transmission via routepacket
		//
		switch(remote_host->GetConnectStatus())
		{
			case L4Host::NoNetworkConnectionStatus:
				//
				// No connection at all, we skip polling this guy for data
				//
				continue;
			case L4Host::OpeningConnectionStatus:
			{
				SOCKADDR_IN stream_net_address, host_net_address;
				//
				// This checks a connection that was created with an active open.
				// If there wasn't a connection, continue on to the next guy
				//
				if(!CheckSocket(remote_host->GetNetworkSocket(), &stream_net_address))
					continue;

				//
				// Because we opened to a specific host address we should ALWAYS connect
				// to the host we expected.  If the address of the computer on the remote
				// doesn't match the one we opened to, we scream loudly and fail.
				//
				host_net_address = *remote_host->GetNetworkAddress();
				if(!(stream_net_address == host_net_address))
				{
					char addressString[44];
					DWORD bufferSize = sizeof(addressString);
					WSAAddressToStringA((LPSOCKADDR)&stream_net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);

					DEBUG_STREAM << "Host " << addressString << std::flush;

					bufferSize = sizeof(addressString);
					WSAAddressToStringA((LPSOCKADDR)&host_net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);

					DEBUG_STREAM << " client " << addressString << "\n";
					Fail("For an OPEN the host MUST match the client or something is really messed up\n");
				}
				//
				// Finish the process of opening the port up, then return False
				// as if no packet was received
				//
				HostConnectedMessage myHostConnect(
					remote_host->GetHostID(),
					stream_net_address,
					remote_host->GetNetworkSocket());
				NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
				Check(client);
				client->ReceiveNetworkPacket(NULL, &myHostConnect);
				CLEAR_CHECK_BUFFERS();
				return(False);
			}
			case  L4Host::ListeningConnectionStatus:
			{
				SOCKET tempSocket = INVALID_SOCKET;
				
				SOCKADDR_IN net_address;
				memset(&net_address, 0, sizeof(SOCKADDR_IN));
				net_address.sin_family = AF_INET;

				if(remote_host->GetHostType() == ConsoleHostType)
				{
					if((tempSocket = accept(consoleListenerSocket, NULL, 0)) != INVALID_SOCKET)
					{
						closesocket(consoleListenerSocket);
						consoleListenerSocket = INVALID_SOCKET;
					}
				}
				else
				{
					if((tempSocket = accept(gameListenerSocket, NULL, 0)) == INVALID_SOCKET)
					{
						continue;
					}
				}

				remote_host->SetNetworkSocket(tempSocket);
				if (!CheckSocket(remote_host->GetNetworkSocket(), &net_address))
					continue;

				char addressString[44];
				DWORD bufferSize = sizeof(addressString);
				WSAAddressToStringA((LPSOCKADDR)&net_address, sizeof(SOCKADDR_IN), NULL, addressString, &bufferSize);
				DEBUG_STREAM << "STATUS: socket accepted from " << addressString << "!\n" << std::flush;
				
				//
				// The Waterloo TCP package has a problem with making connections.  It always
				// seems to connect people to the last socket that was listened on regardless
				// of the port or net address settings.  Because of this we have to make sure
				// the connect we get belongs to the host we connected to, if it doesn't we
				// have to find the right host and potentially swap the streams around.  We
				// also have to have a special case to handle listening for the console.  Since
				// we don't know the console machine's address in advance we (!!!!for now)
				// assume we will only do a listen for the console if it's the only machine
				// we're going to listen for.
				//
				Host	*connect_host;
				L4Host	*connect_L4_host;
				SOCKET	swap_socket_ptr;
				swap_socket_ptr	= remote_host->GetNetworkSocket();
				if(net_address == *remote_host->GetNetworkAddress())
				{
					//
					// The address of the host that connected on this stream matched the one
					// we were expecting (ie: port/network address filtering worked) so we
					// just process it straight through.  This should handle a host pretending
					// to be a console and OPENing to another host.
					//
					HostConnectedMessage myHostConnect(
						remote_host->GetHostID(),
						net_address,
						remote_host->GetNetworkSocket());
					NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
					Check(client);
					client->ReceiveNetworkPacket(NULL, &myHostConnect);
				}
				else if(remote_host->GetHostType() == ConsoleHostType)
				{
					//
					// This handles a LISTEN for a console with an unspecified net address
					// Because the console uses a different port number than the game does
					// whatever connects to a console host should be correct.  So whoever
					// shows up on this host should be a console (or thinks it is)
					// NOTE: We actually set the host's net address here, since we didn't
					// know it in advance.
					//
					remote_host->SetNetworkAddress(&net_address);
					HostConnectedMessage myHostConnect(
						remote_host->GetHostID(),
						net_address,
						remote_host->GetNetworkSocket());
					NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
					Check(client);
					client->ReceiveNetworkPacket(NULL, &myHostConnect);
				}
				else
				{
					//
					// The address of the host that connected didn't match the one we were
					// expecting on this stream. So we have to find the right host and swap
					// streams with them.
					//
					HostManager::RemoteHostIterator connect_hosts(application->GetHostManager());
					while ((connect_host = connect_hosts.ReadAndNext()) != NULL)
					{
						connect_L4_host =	Cast_Object(L4Host*, connect_host);
						if(connect_L4_host->GetConnectStatus() != L4Host::ListeningConnectionStatus)
						{
							// skip hosts that are not currently listening since they are not
							// candidates for swapping.
							continue;
						}
						//
						// If the address of this guy matches the address of the person who just
						// connected, we swap their stream pointers
						//
						if(*connect_L4_host->GetNetworkAddress() == net_address)
						{
							remote_host->SetNetworkSocket(connect_L4_host->GetNetworkSocket());
							connect_L4_host->SetNetworkSocket(swap_socket_ptr);
							HostConnectedMessage myHostConnect(
								connect_L4_host->GetHostID(),
								net_address,
								connect_L4_host->GetNetworkSocket());
							NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
							Check(client);
							client->ReceiveNetworkPacket(NULL, &myHostConnect);
							CLEAR_CHECK_BUFFERS();
							return False;
						}
					}
				}
				CLEAR_CHECK_BUFFERS();
				return False;
			}
			case	L4Host::OnLineConnectionStatus:
				// OnLine hosts just fall through so they can be polled
				break;
			default:
				Fail("Host had illegal connection status\n");
				break;
		}
#if !BATCHED_RECEIVE
		//
		// If we get this far the host is on line, if there is room to do a receive
		// into this buffer then do one.
		//
		//unsigned long *next_status_ptr = &(multiple_receive_packet_return->Status[0]);
		space_left	= (short)(remote_host->pad_size - remote_host->pad_tail);
		if(space_left >= MAX_RECEIVE_DATA_SIZE)
		{
			// there was sufficient room for a receive to happen, so do it
			// Setup the request in the common area
			//Net_Common_Ptr->Function	 			=	NETNUB_RECEIVE_PACKET;
			//Net_Common_Ptr->Buffer_Length			=	sizeof(ReceivePacketRequest);
			//receive_packet_request					=	(ReceivePacketRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
			//receive_packet_request->Socket_Ptr	=	remote_host->GetNetworkSocket();
			// call the network server
			//NetShare();
			void* buffer = malloc(space_left);
			int received = recv(remote_host->GetNetworkSocket(), (char*)buffer, space_left, 0);
			// if we received data, copy it over into the buffer for this host
			//switch(Net_Common_Ptr->Status)
			if(received > 0)
			{
				//case	NETNUB_RECEIVED_PACKET:
					// copy the received data over to the host's data buffer
					Mem_Copy(
						&remote_host->pad_buffer[remote_host->pad_tail],
						//Net_Common_Ptr->Shared_Memory_Buffer,
						buffer,
						//Net_Common_Ptr->Buffer_Length,
						received,
						space_left);
					//remote_host->pad_tail += Net_Common_Ptr->Buffer_Length;
					remote_host->pad_tail += received;
					//break;
			}
				//case	NETNUB_OK:
			else if(received == SOCKET_ERROR)
			{
				//this indicates that no data is available and the socket is non blocking
					//break;
				DWORD error = WSAGetLastError();
				switch (error)
				{
				case WSAECONNRESET:
					// this will cause us to execute our disconnect code
					received = 0;
					break;
				case WSAEWOULDBLOCK:
					// this is expected when there is no data
					break;
				default:
					DEBUG_STREAM << "L4NetworkManager::CheckBuffers: Socket recv returned an unexpected error: WSAGetLastError = " << error << std::endl << std::flush;
				}
			}
			//	case	NETNUB_STREAM_DISCONNECTED:
			if(received == 0)
			{
				{
					HostDisconnectedMessage myHostDisconnected(
						remote_host->GetHostID(),
						remote_host->GetNetworkSocket());
					NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
					Check(client);
					client->ReceiveNetworkPacket(NULL, &myHostDisconnected);
					//break;
				}
			}
				//default:
			free(buffer);
		}
	}
#else
		//
		// If we get this far the host is on line, see if there is room to do a
		// receive into it's buffer.
		//
		space_left	= (short)(	remote_host->pad_size - remote_host->pad_tail);
		if(space_left >= MAX_RECEIVE_DATA_SIZE)
		{
			//
			// There is room, add it to the list of hosts
			//
			l4_host_list[host_count++]	=	remote_host;
			*(next_socket_ptr++)			=  remote_host->GetNetworkSocket();
//			DEBUG_STREAM<<"Add stream "<<remote_host->GetNetworkSocket()<<"\n";
		}
	}
	//
	// Finish the request to netnub, then send it down
	//
	if(host_count == 0)
	{
		CLEAR_CHECK_BUFFERS();
		return False;
	}
//	DEBUG_STREAM<<"Sending command to read "<<host_count<<" streams\n";
	multiple_receive_packet->Socket_Count	= host_count;
	Net_Common_Ptr->Function	 				=	NETNUB_MULTIPLE_RECEIVE;
	Net_Common_Ptr->Buffer_Length				=	sizeof(MultipleReceivePacketRequest);
	NetNub::SendCommand();
	//
	// If NETNUB_OK comes back in status, it means we have no data and can quit
	//
	if(Net_Common_Ptr->Status == NETNUB_OK)
	{
//		DEBUG_STREAM<<"No data on any stream\n";
		CLEAR_CHECK_BUFFERS();
		return(False);
	}
	//
	// One or more streams had data or errors
	//
	for(i = 0; i<host_count; i++)
	{
		l4host	=	l4_host_list[i];
		status	=  multiple_receive_packet_return->Status[i];
//		if(status != 0)
//			DEBUG_STREAM<<"stream "<<l4host->GetNetworkSocket()<<" status "<<status<<"\n";
		if(status == NETNUB_OK)
		{
			// No data or error here, go on to next host
			continue;
		}
		else if(status == NETNUB_STREAM_DISCONNECTED)
		{
			HostDisconnectedMessage myHostDisconnected(
				l4host->GetHostID(),
				l4host->GetNetworkSocket());
			NetworkClient *client = GetNetworkClientPointer(NetworkManagerClientID);
			Check(client);
			client->ReceiveNetworkPacket(NULL, &myHostDisconnected);
		}
		else if(status < 0)
		{
			// error of some kind, handle it
			DEBUG_STREAM<<"MUNGA reported network error "<<status<<"\n";
			Fail("MUNGA network error");
		}
		else
		{
			// there was data in the message
			// copy the received data over to the host's data buffer
			Mem_Copy(
				&l4host->pad_buffer[l4host->pad_tail],
				current_receive_ptr,
				status,
				(short)(l4host->pad_size - l4host->pad_tail));
			l4host->pad_tail +=	(Word)status;
			current_receive_ptr += status;
		}
	}
#endif
	//
	// Now loop through all the hosts again till we find one with a complete munga
	// message to be returned for processing by the host.
	//
	get_buffer_status = GetNextMungaPacket(network_packet,&remote_hosts);
	CLEAR_CHECK_BUFFERS();
	return(get_buffer_status);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// GetNextMungaPacket Checks the internal MUNGA packet assembly buffers to
// see if there are packets available on any open stream
//
Logical
	L4NetworkManager::GetNextMungaPacket(
		NetworkPacket *network_packet,
		HostManager::RemoteHostIterator *remote_hosts)
{
	short
		i,
		host_count,
		move_size,
		receive_packet_size;
	Host
		*host;
	L4Host
		*remote_host;
	NetworkPacket
		*incoming_packet;

	//
	// Loop through all the hosts and check their buffers
	//
	host_count = (short)remote_hosts->GetSize();
	for(
		i = 0;
		i < host_count;
		i++)
	{
		//
		// This causes us to start checking hosts where we left off the last time
		// it prevents any one host from always getting serviced first.
		//
		lastHostIteratorPosition++;
		if(lastHostIteratorPosition >= remote_hosts->GetSize())
		{
			lastHostIteratorPosition = 0;
		}
		if((host = remote_hosts->GetNth(lastHostIteratorPosition)) == NULL)
		{
			break;
		}
		//
		// get the host pointer and cast it over to an l4 host so we can look at it's buffers
		//
		remote_host = Cast_Object(L4Host*, host);
		Check(remote_host);
		//
		// If we're in ConsoleOnly state, don't return messages for non-console streams
		//
		if((currentNetworkState 		==	ConsoleOnly) &&
			(remote_host->GetHostType()	!=	ConsoleHostType))
			continue;
		//
		// does this host data buffer contain a complete munga message header
		//
		if(remote_host->pad_tail >= sizeof(NetworkPacket))
		{
			//
			// enough bytes for a complete message header, read the message length and
			// figure how many bytes the complete packet should be
			//
			incoming_packet		= (NetworkPacket*)remote_host->pad_buffer;
			receive_packet_size	= (short)(incoming_packet->messageData.messageLength + sizeof(NetworkPacketHeader));
			Verify(receive_packet_size > 0);
			// Are there enough bytes in the buffer to make up this packet?
			if(remote_host->pad_tail >= receive_packet_size)
			{
				// we have a complete packet, copy it into the output buffer and rejustify the host's
				// buffer (inefficient as hell but easy to write for now)
				Mem_Copy(
					network_packet,
					remote_host->pad_buffer,
					receive_packet_size,
					NETWORKMANAGER_BUFFER_SIZE);
				move_size = (short)(remote_host->pad_tail - receive_packet_size);
				// don't do the next step if the buffer is empty
				// we need to use memmove because the source and destination addresses overlap
				// and memcopy doesn't know how to deal with that.
				Verify(move_size < L4HOST_PAD_BUFFER_SIZE);
				if(move_size != 0)
				{
					memmove(
						remote_host->pad_buffer,
						&remote_host->pad_buffer[receive_packet_size],
						move_size);
				}
				remote_host->pad_tail -= receive_packet_size;
				// Return true (packet received)
				return True;
			}
			else
			{
//				cout<<"++++++++Waiting for "<<receive_packet_size<<" byte packet, got"<<remote_host->pad_tail<<"\n";
			}
		}
	}
	//
	// If we get here, all the streams were empty
	//
	return False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::RemovePacket  If the network implimentation requires us to
// free up a packet buffer, this routine would do it.
//
void
	L4NetworkManager::RemovePacket(NetworkPacket*)
{
	//
	// This function is not required for now
	//
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::NetShare  This is Gene's implementation of the protected
// to real mode interface.
//
//WinSock support :ADB 01/06/07
//void NetNub::SendCommand()
//{
//	struct RMREG
//		rmreg;
//	union	REGS
//		regs;
//	struct SREGS
//		sregs;
//
//	SET_CALL_NETNUB();
//	//
//	// Check a few things before making the DPMI call to the interrupt
//	//
//	Verify(Net_Common_Ptr->Buffer_Length <= SHARED_MEMORY_SIZE);
//	//
//	// This assembly routine copies the contents of the NetCommon down into real mode
//	//
//	setRMBuff();
//	//
//	// Setup for the call to the DPMI to simulate real mode interrupt
//	//
//	regs.x.eax		= 0x0300; 					// function of INT31
//	regs.h.bl		= intno;       			//
//	regs.h.bh		= 0;     					//flags
//	regs.x.ecx		= 0;     					//number of words to copy from protected mode to real mode stack
//	regs.x.edi		=(unsigned int)&rmreg;	//es:edi is an address of RMREG structure
//	segread(&sregs);
//	rmreg.reserved	= 0;							//default settings:
//	rmreg.fs			= 0;
//	rmreg.gs			= 0;
//	rmreg.flags		= 0;
//	rmreg.ss 		= 0;							//real mode stack is provided by DPMI unless ss:sp != 0
//	rmreg.sp 		= 0;
//	//
//	// Call the DPMI
//	//
//	int386x(0x31,&regs,&regs,&sregs);
//	//
//	// Check for an error
//	//
//	if (regs.x.cflag)
//	{
//		DEBUG_STREAM<<"int386x():ERROR="<<regs.x.eax<<endl;
//		Fail("Error calling netnub\n");
//	}
//	//
//	// Copy the real mode data back up into the protected mode common area
//	//
//	getRMBuff();
//	//
//	// Check the version number before returning
//	//
//	Verify(Net_Common_Ptr->Version_Number == NETCOM_VERSION);
//	CLEAR_CALL_NETNUB();
//}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~Encapsulations of netnub calls~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::GetMyAddress  This routine calls the netnub and returns
// our network address if everything is ok or zero if it's not.  It can also be
// used to tickle the netnub or to check status of the netnub.
//
//WinSock support :ADB 01/06/07
NetworkAddress* L4NetworkManager::GetMyAddress()
{
	char name[255];
	PHOSTENT hostinfo;

	if (gethostname(name, sizeof(name)) != 0)
	{
		DEBUG_STREAM << "ERROR: gethostname() failed!" << std::endl << std::flush;
		return NULL;
	}

	if ((hostinfo = gethostbyname(name)) == NULL)
	{
		DEBUG_STREAM << "ERROR: gethostbyname() failed!" << std::endl << std::flush;
		return NULL;
	}

	// count how many addresses we have
	for (num_addresses = 0; hostinfo->h_addr_list[num_addresses]; num_addresses++);
	
	NetworkAddress* myAddresses = new NetworkAddress[num_addresses + 1];
	for (int i=0; i<num_addresses; i++)
		myAddresses[i] = *((NetworkAddress*)hostinfo->h_addr_list[i]);

	// add 127.0.0.1 to list
	myAddresses[num_addresses++] = (NetworkAddress)0x0100007F;
	
	return myAddresses;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::ResolveAddress  This routine takes a CString containing
// a host name and makes the appropriate network calls to resolve it to a
// binary internet address.
//
//WinSock support :ADB 01/06/07
bool L4NetworkManager::ResolveAddress(CString host_name, SOCKADDR_IN *address)
{
	//if (strstr(host_name, ":"))
	{
		// this address contains a colon so we'll do things a little differently
		int bufferSize = sizeof(SOCKADDR_IN);
		WSAStringToAddressA((LPSTR)host_name, AF_INET, NULL, (LPSOCKADDR)address, &bufferSize);
		if (address->sin_port == 0)
			address->sin_port = htons(GAME_NET_PORT);
		return true;
	}

	addrinfo* hostAddrinfo = NULL;
	addrinfo aiHints;
	memset(&aiHints, 0, sizeof(aiHints));
	aiHints.ai_family = AF_INET;
	aiHints.ai_socktype = SOCK_STREAM;
	aiHints.ai_protocol = IPPROTO_TCP;

	int iResult = getaddrinfo((char*)host_name, NULL, &aiHints , &hostAddrinfo);
	if(iResult != 0)
	{
		DEBUG_STREAM<<"ERROR: getaddrinfo() failed with " << WSAGetLastError() << "!\n";
		return NULL;
	}

	addrinfo* addr = hostAddrinfo;
	while(addr != NULL)
	{
		if(addr->ai_addr->sa_family == AF_INET)
			break;
		addr = addr->ai_next;
	}

	memset(address, 0, sizeof(SOCKADDR_IN));
	*address = *((sockaddr_in*)&addr->ai_addr->sa_data);
	if (address->sin_port == 32778)
		address->sin_port = htons(CONSOLE_NET_PORT);

	freeaddrinfo(hostAddrinfo);
	
	//
	// Check the status returns and return the address if we got one ok
	//
	if(!address || address->sin_addr.S_un.S_addr == 0 || address->sin_port == 0)
	{
		DEBUG_STREAM << "Couldn't resolve " << host_name << " to a net address\n";
		Fail("unresolvable network address\n");
	}

	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::CheckSocket  This routine does checks a socket and returns
// the state of that socket (connected or not)
//
//WinSock support :ADB 01/06/07
bool L4NetworkManager::CheckSocket(SOCKET socket, SOCKADDR_IN *remoteEndpoint)
{
	if (remoteEndpoint)
	{
		int size = sizeof(SOCKADDR_IN);
		memset(remoteEndpoint, 0, size);
		if(!getpeername(socket, (sockaddr*)remoteEndpoint, &size))
			return true;
	}
	return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::OpenConnection  This routine does the appropriate calls to
// start the open process for another host.  You can do an active or passive open
// with this routine by passing the appropriate parameters in.  The arguments
// are geared to the typical TCP/IP connection parameters.  After calling this
// routine you must create the host yourself.
//
//WinSock support :ADB 01/06/07
//unsigned long OpenConnection(
SOCKET L4NetworkManager::OpenConnection(
		int connection_type,					// NETNUB_TCP_LISTEN or NETNUB_TCP_OPEN
		int local_port,
		int remote_port,
		int internet_address)
{
	//TCPOpenRequestPtr
	//	tcp_open_request;
	//TCPOpenReturnPtr
	//	tcp_open_return;
	//
	// Checkup everything
	//
	//Check_Pointer(Net_Common_Ptr);
	//
	// Make sure we are using a legal open mode
	//
	Verify(	(connection_type == NETNUB_TCP_LISTEN) ||
				(connection_type == NETNUB_TCP_OPEN));
	//
	// Format the open/listen request
	//
	//tcp_open_request							=	(TCPOpenRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//tcp_open_return							=	(TCPOpenReturnPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//Net_Common_Ptr->Function				=	(short)connection_type;
	//Net_Common_Ptr->Buffer_Length			=	sizeof(TCPOpenRequest);
	//tcp_open_request->Local_Port			=	(short)local_port;
	//tcp_open_request->Remote_Port			=	(short)remote_port;
	//tcp_open_request->Internet_Address	=	internet_address;
	//tcp_open_request->Socket_Ptr			=	0;			// !!! 0 causes netnub to allocate socket
	//NetNub::SendCommand();
	//if(Net_Common_Ptr->Status == NETNUB_ERROR)
	//{
	//	DEBUG_STREAM<<"NetNub TCP Open/Listen error #"<<tcp_open_return->Socket_Ptr<<" listening for ";
	//	DEBUG_STREAM<<((internet_address>>24) & 0xff)<<"."<<((internet_address>>16) & 0xff)<<".";
	//	DEBUG_STREAM<<((internet_address>>8) & 0xff)<<"."<<(internet_address & 0xff)<<"\n";
	//	Fail("Netnub error during open/listen\n");
	//}
	//return(tcp_open_return->Socket_Ptr);

	if(connection_type == NETNUB_TCP_OPEN)
	{
		DEBUG_STREAM << "Opening connection to " << inet_ntoa(*(in_addr*)&internet_address) << ":" << remote_port << "...\n" << std::flush;

		SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if(sock == INVALID_SOCKET)
		{
			DEBUG_STREAM << "ERROR: socket() failed with " << WSAGetLastError() << "!\n";
			return INVALID_SOCKET;
		}

		bool reuseAddr = true;
		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseAddr, sizeof(bool)))
		{
			DEBUG_STREAM << "ERROR: Could not set SO_REUSEADDR on socket; setsockopt() failed with " << WSAGetLastError() << "!\n" << std::flush;
			WSACleanup();
			PostQuitMessage(AbortExitCodeID);
			return INVALID_SOCKET;
		}

		sockaddr_in localEndpoint;
		memset(&localEndpoint, 0, sizeof(localEndpoint));
		localEndpoint.sin_family = AF_INET;
		localEndpoint.sin_port = htons(local_port);
		localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY;
		if(bind(sock, (sockaddr*)&localEndpoint, sizeof(localEndpoint)))
		{
			DEBUG_STREAM << "ERROR: Could not bind local socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush;
			WSACleanup();
			PostQuitMessage(AbortExitCodeID);
			return INVALID_SOCKET;
		}

		sockaddr_in remoteEndpoint;
		memset(&remoteEndpoint, 0, sizeof(remoteEndpoint));
		remoteEndpoint.sin_family = AF_INET;
		//VERIFY: Network vs Host byte order?
		remoteEndpoint.sin_addr.S_un.S_addr = internet_address;
		remoteEndpoint.sin_port = htons(remote_port);
		int wsaError, iResult;

		do
		{
			iResult = connect(sock, (sockaddr*)&remoteEndpoint, sizeof(remoteEndpoint));

			if (iResult != 0)
			{
				wsaError = WSAGetLastError();
			}
		} while (iResult != 0 && wsaError == 10061);
		
		if(iResult != 0)
		{
			DEBUG_STREAM << "ERROR: connect() failed with " << wsaError << "!\n" << std::flush;
			return INVALID_SOCKET;
		}

		//set to non blocking
		unsigned long enable = 1;
		if(ioctlsocket(sock, FIONBIO, &enable))
		{
			DEBUG_STREAM << "ERROR: Could not set actively opened socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush;
			WSACleanup();
			PostQuitMessage(AbortExitCodeID);
		}
		return sock;
	}
	else if(connection_type == NETNUB_TCP_LISTEN)
	{
		if(gameListenerSocket == NULL)
		{
			DEBUG_STREAM << "Starting to listen on port " << local_port << "...\n" << std::flush;

			gameListenerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
			if(gameListenerSocket == INVALID_SOCKET)
			{
				DEBUG_STREAM << "ERROR: Could not create game listener socket; socket() failed with " << WSAGetLastError() << "!\n" << std::flush;
				WSACleanup();
				PostQuitMessage(AbortExitCodeID);
			}

			bool reuseAddr = true;
			if (setsockopt(gameListenerSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseAddr, sizeof(bool)))
			{
				DEBUG_STREAM << "ERROR: Could not set SO_REUSEADDR on game listener socket; setsockopt() failed with " << WSAGetLastError() << "!\n" << std::flush;
				WSACleanup();
				PostQuitMessage(AbortExitCodeID);
				return INVALID_SOCKET;
			}

			sockaddr_in localEndpoint;
			memset(&localEndpoint, 0, sizeof(localEndpoint));
			localEndpoint.sin_family = AF_INET;
			localEndpoint.sin_port = htons(local_port);
			localEndpoint.sin_addr.S_un.S_addr = INADDR_ANY;
			if(bind(gameListenerSocket, (sockaddr*)&localEndpoint, sizeof(localEndpoint)))
			{
				DEBUG_STREAM << "ERROR: Could not bind game listener socket; bind() failed with " << WSAGetLastError() << "!\n" << std::flush;
				WSACleanup();
				PostQuitMessage(AbortExitCodeID);
				return INVALID_SOCKET;
			}
			if(listen(gameListenerSocket, 25))
			{
				DEBUG_STREAM << "ERROR: Could not listen on game listener socket; listen() failed with " << WSAGetLastError() << "!\n" << std::flush;
				WSACleanup();
				PostQuitMessage(AbortExitCodeID);
				return INVALID_SOCKET;
			}
			//set to non blocking
			unsigned long enable = 1;
			if(ioctlsocket(gameListenerSocket, FIONBIO, &enable))
			{
				DEBUG_STREAM << "ERROR: Could not set game listener socket to nonblocking; ioctlsocket() failed with " << WSAGetLastError() << "!\n" << std::flush;
				WSACleanup();
				PostQuitMessage(AbortExitCodeID);
				return INVALID_SOCKET;
			}
		}
		else
			DEBUG_STREAM << "Listen requested on port " << local_port << " but gameListenerSocket already existed!\n" << std::flush;
		
		return INVALID_SOCKET;
	}
	else
		return INVALID_SOCKET;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::CloseConnection  This routine does the appropriate calls to
// close a connection to another host, it really should wait for an error return
// but doesn't right now.
//
//WinSock support :ADB 01/06/07
void L4NetworkManager::CloseConnection(SOCKET socket_ptr)  // socket address from netnub (to close)
{
	//TCPCloseRequestPtrclose_request;
	//
	// Checkup everything
	//
	Check_Pointer(Net_Common_Ptr);
	//
	// Do the actual close
	//
	//Net_Common_Ptr->Function		=	NETNUB_TCP_CLOSE;
	//Net_Common_Ptr->Buffer_Length	=	sizeof(TCPCloseRequest);
	//close_request						=	(TCPCloseRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//close_request->Socket_Ptr		=	socket_ptr;
	//NetNub::SendCommand();
	shutdown(socket_ptr, SD_BOTH);
	closesocket(socket_ptr);
	// Check for errors
	//if(Net_Common_Ptr->Status != NETNUB_OK)
	//{
	//	DEBUG_STREAM<<"NetNub close error "<<Net_Common_Ptr->Status<<" on stream "<<socket_ptr<<"\n";
	//	return;
	//}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::Mode  does mode settings on the network interface
//
//WinSock support :ADB 01/06/07
void L4NetworkManager::Mode(NetworkMode myMode)
{
	//SetSwitchesRequestPtr set_switches_request;
	//
	// No netnub present?  Just return
   //
	//if(Net_Common_Ptr == NULL)
	if(wsaData == NULL)
	{
		return;
	}
	//
	// Checkup everything
	//
	//Check_Pointer(Net_Common_Ptr);
	//
	// Copy the text over (including the 0 terminator)
	//
	//set_switches_request = (SetSwitchesRequestPtr)Net_Common_Ptr->Shared_Memory_Buffer;
	//switch(myMode)
	//{
	//	case	ReliableMode:
	//		set_switches_request->blocking_switch = True;
	//		break;
	//	case	UnreliableMode:
	//		set_switches_request->blocking_switch = False;
	//		break;
	//}
	//Net_Common_Ptr->Function		=	NETNUB_SET_SWITCHES;
	//Net_Common_Ptr->Buffer_Length	=	sizeof(SetSwitchesRequest);
	//NetNub::SendCommand();
	//// Check for errors
	//if(Net_Common_Ptr->Status != NETNUB_OK)
	//{
	//	DEBUG_STREAM<<"NetNub mode call failed\n";
	//	return;
	//}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager::Marker  This routine does the appropriate calls to send
// a text marker down to netnub
//
//WinSock support :ADB 01/06/07
void L4NetworkManager::Marker(char *marker_text)  // text message to send down
{
	//int
	//	marker_text_size;
	////
	//// No netnub present?  Just return
 //  //
	//if(Net_Common_Ptr == NULL)
	//{
	//	return;
	//}
	////
	//// Checkup everything
	////
	//Check_Pointer(Net_Common_Ptr);
	////
	//// Copy the text over (including the 0 terminator)
	////
	//marker_text_size	=	strlen(marker_text)+1;
	//Mem_Copy(
	//	(char*)Net_Common_Ptr->Shared_Memory_Buffer,
	//	marker_text,
	//	marker_text_size,
	//	SHARED_MEMORY_SIZE);
	//Net_Common_Ptr->Function		=	NETNUB_MARKER_TEXT;
	//Net_Common_Ptr->Buffer_Length	=	(short)marker_text_size;
	//NetNub::SendCommand();
	//// Check for errors
	//if(Net_Common_Ptr->Status != NETNUB_OK)
	//{
	//	DEBUG_STREAM<<"NetNub marker text call failed\n";
	//	return;
	//}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	L4NetworkManager::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}

#ifdef TEST_CLASS
#include "l4net.tcp"
#endif


#if 0
	else if(networkStartupMode ==	MasterMode && remoteHostCount > 0)
	{
		enum ConsoleState
		{
			GetNextHostConsoleState,
			OpenNextHostConsoleState,
			WaitForConnectConsoleState,
			SendEggConsoleState,
			WaitForEggAckConsoleState,
			WaitForGameConnectConsoleState,
			CloseStreamConsoleState,
			WaitForAllHostsConsoleState,
			DoneConsoleState
		};
		long
			size_of_notation_text;
		int
			game_machine_up_count;
		ConsoleState
			consoleState;
		NetworkAddress
			temp_net_address;
		char
			*temp_notation_text;
		Host
			*host;
		NotationFile
			*temp_notation_file;
		//
		// Master must send the egg to the other hosts and wait for each to acknowledge
		// it's processing before going on to the next host.  First we turn the notation
		// file into text so we can send it.
		//
		temp_notation_file = new NotationFile(GlobalEggFileName);
		Register_Object(temp_notation_file);
		if(temp_notation_file->PageCount() == 0)
		{
			Fail("Tried to startup with an empty notation file\n");
		}
		size_of_notation_text	=	temp_notation_file->SizeOfText();
		temp_notation_text		=	new char[size_of_notation_text];
		Register_Pointer(temp_notation_text);
		temp_notation_file->WriteText(
			temp_notation_text,
			size_of_notation_text);
		//
		// Now setup and run the state machine till everyone has been connected.
		//
		HostManager::RemoteHostIterator remote_hosts(application->GetHostManager());
		currentNetworkState	=	ConsoleOnly;
		eggAcknowledged		=	False;
		consoleState			=	GetNextHostConsoleState;
		while ( consoleState !=  DoneConsoleState && remoteHostCount > 0)
		{
			//
			// Poll the network (the arrival times of these measages are complicated
			// so I don't advise messing with the order of this loop casually)
			//
			RoutePacket();
			//
			// Do what we're supposed to based on our current state
			//
			switch(consoleState)
			{
				case GetNextHostConsoleState:
					//
					// Get the next game machine host
					//
					Tell("GetNextHostConsoleState--Getting next host\n");
					game_machine_up_count = numberOfMungaHostsConnected;
					remote_hosts.First();
					while((host = remote_hosts.ReadAndNext()) != NULL)
					{
						L4Host*	an_l4host	=	Cast_Object(L4Host*, host);
						if(an_l4host->GetHostType()		==	ConsoleHostType ||
							an_l4host->GetConnectStatus()	==	L4Host::NoNetworkConnectionStatus ||
							an_l4host->GetConnectStatus()	==	L4Host::OnLineConnectionStatus)
							continue;
						if(an_l4host->GetHostType() == GameMachineHostType)
							break;
					}
					if(host == NULL)
					{
						consoleState	=	WaitForAllHostsConsoleState;
					}
					else
					{
						consoleState	=  OpenNextHostConsoleState;
					}
					break;
				case	OpenNextHostConsoleState:
					// Get the network address of this game machine host and open it
					temp_net_address = host->GetNetworkAddress();
					Tell("OpenNextHostConsoleState--Opening console to ");
					Tell(((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<".");
					Tell(((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n");
					socket_ptr	=	OpenConnection(
						NETNUB_TCP_OPEN,
						0,
						CONSOLE_NET_PORT,
						temp_net_address);
					// Create a console host for this guy
					myConsoleHost = new L4Host(
						FirstLegalHostID,
						ConsoleHostType,
						temp_net_address,
						socket_ptr,
						"Console");
					Register_Object(myConsoleHost);
					// Register the console host with the host manager
					Check(application);
					Check(application->GetHostManager());
					application->GetHostManager()->AdoptRemoteHost(myConsoleHost);
					myConsoleHost->SetConnectStatus(L4Host::OpeningConnectionStatus);
					numberOfConsoleHostsConnected = 0;
					consoleState						= WaitForConnectConsoleState;
					break;
				case	WaitForConnectConsoleState:
					Tell("C"<<numberOfMungaHostsConnected);
					if(numberOfConsoleHostsConnected != 0)
					{
						consoleState	=	SendEggConsoleState;
						Tell("\nWaitForConnectConsoleState--Connected\n");
					}
					break;
				case	SendEggConsoleState:
				{
					int
						bytes_left,
						next_send,
						sequence;
					next_send	=	0;
					sequence		=	0;
//					Verify(size_of_notation_text < 1000);
					while(next_send < size_of_notation_text)
					{
						Tell("Sent Egg message #"<<sequence<<"\n");
						bytes_left = size_of_notation_text - next_send;
						if(bytes_left > 1000)
							bytes_left = 1000;
						ReceiveEggFileMessage myEggMessage(
							sequence,
							size_of_notation_text,
							temp_notation_text + next_send,
							bytes_left);
						Send(
							&myEggMessage,
							NetworkClient::NetworkManagerClientID,
							myConsoleHost->GetHostID());
						next_send += bytes_left;
						sequence++;
					}
					eggAcknowledged	=	False;
					consoleState		=  WaitForEggAckConsoleState;
					break;
				}
				case	WaitForEggAckConsoleState:
					Tell("E"<<numberOfMungaHostsConnected);
					if(eggAcknowledged)
					{
						consoleState	=	WaitForGameConnectConsoleState;
						DEBUG_STREAM<<"\nWaitForEggAckConsoleState--Got one from";
						DEBUG_STREAM<<((temp_net_address>>24) & 0xff)<<"."<<((temp_net_address>>16) & 0xff)<<".";
						DEBUG_STREAM<<((temp_net_address>>8) & 0xff)<<"."<<(temp_net_address & 0xff)<<"\n";
					}
					break;
				case WaitForGameConnectConsoleState:
					Tell("G"<<numberOfMungaHostsConnected);
					if(game_machine_up_count < numberOfMungaHostsConnected)
					{
						consoleState	=	CloseStreamConsoleState;
						Tell("\nWaitForGameConnectConsoleState--Got one\n");
					}
					break;
				case	CloseStreamConsoleState:
				{
					Tell("CloseStreamConsoleState -- closing console\n");
#if 0
					HostDisconnectedMessage myHostDisconnected;
					Send(
						&myHostDisconnected,
						NetworkClient::NetworkManagerClientID,
						myConsoleHost->GetHostID());
#endif
					Check(myConsoleHost);
					CloseConnection(myConsoleHost->GetNetworkSocket());
					// deregister this host with the host manager
					application->GetHostManager()->OrphanRemoteHost(myConsoleHost);
					Unregister_Object(myConsoleHost);
					delete myConsoleHost;
					myConsoleHost	=	NULL;
					consoleState	=	GetNextHostConsoleState;
					break;
				}
				case	WaitForAllHostsConsoleState:
					Tell("W"<<numberOfMungaHostsConnected);
					if(numberOfMungaHostsConnected >= remoteHostCount)
					{
						consoleState	=	DoneConsoleState;
						Tell("\WaitForAllHostsConsoleState--done connecting\n");
					}
					break;
			}
		}
		Unregister_Pointer(temp_notation_text);
		delete temp_notation_text;
		Unregister_Object(temp_notation_file);
		delete temp_notation_file;
		currentNetworkState	=	NormalState;
	}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~ MessageQueue__SendRequest ~~~~~~~~~~~~~~~~~~~~~~~~~~

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MessageQueue__SendRequest::MessageQueue__SendRequest(
	NetworkClient::ClientID client_ID,
	Receiver::Message *message
)
{
	//
	// Set client ID
	//
	clientID = client_ID;
	
	//
	// Store the message
	//
	Check(message);
	messageToSend = (Receiver::Message*)new char[message->messageLength];
	Register_Pointer(messageToSend);
	Mem_Copy(
		messageToSend,
		message,
		message->messageLength,
		message->messageLength
	);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MessageQueue__SendRequest::~MessageQueue__SendRequest()
{
	Unregister_Pointer(messageToSend);
	delete messageToSend;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	MessageQueue__SendRequest::TestInstance() const
{
	Check_Pointer(messageToSend);
	return True;
}

//~~~~~~~~~~~~~~~~~~~~ HostMessageBuffer__MessageQueue ~~~~~~~~~~~~~~~~~~~~~~~~

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
HostMessageBuffer__MessageQueue::HostMessageBuffer__MessageQueue(
	HostID host_ID
):
	sendRequestSocket(NULL)
{
	hostID = host_ID;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
HostMessageBuffer__MessageQueue::~HostMessageBuffer__MessageQueue()
{
	ChainIteratorOf<SendRequest*>
		iterator(&sendRequestSocket);
	iterator.DeletePlugs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	HostMessageBuffer__MessageQueue::TestInstance() const
{
	Check(&sendRequestSocket);
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HostMessageBuffer__MessageQueue::AddSendRequest(
		NetworkClient::ClientID client_ID,
		Receiver::Message *message
	)
{
	Check(this);
	Check(message);
	
	//
	// Create new send request and add it to the socket
	//
	SendRequest
		*send_request;
		
	send_request = new SendRequest(client_ID, message);
	Register_Object(send_request);
	sendRequestSocket.Add(send_request);
}	

//~~~~~~~~~~~~~~~~~~~~ L4NetworkManager__MessageBuffer ~~~~~~~~~~~~~~~~~~~~~~~~

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4NetworkManager__MessageBuffer::L4NetworkManager__MessageBuffer(
	L4NetworkManager *network_manager
):
	messageQueueSocket(NULL, True)
{
	networkManager = network_manager;
	currentQueueIndex = 0;
	bufferSize = 0;
	#ifdef LAB_ONLY
		messageCount = 0;
		maxBufferSize = 0;
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4NetworkManager__MessageBuffer::~L4NetworkManager__MessageBuffer()
{
	TableIteratorOf<MessageQueue*, HostID>
		iterator(&messageQueueSocket);	
	iterator.DeletePlugs();
	
	#ifdef LAB_ONLY
		cout << "L4NetworkManager__MessageBuffer::" <<
			"~L4NetworkManager__MessageBuffer" <<
			" messageCount=" << messageCount << "\n";;
		cout << "L4NetworkManager__MessageBuffer::" <<
			"~L4NetworkManager__MessageBuffer" <<
			" maxBufferSize=" << maxBufferSize << "\n";;
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	L4NetworkManager__MessageBuffer::TestInstance() const
{
	Check(&messageQueueSocket);
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	L4NetworkManager__MessageBuffer::AddSendRequest(
		HostID host_ID,
		NetworkClient::ClientID client_ID,
		Receiver::Message *message
	)
{
	Check(this);
	Check(message);
	
	//
	// Get this hosts message queue
	//
	MessageQueue
		*message_queue;
		
	if ((message_queue = messageQueueSocket.Find(host_ID)) == NULL)
	{
		message_queue = new MessageQueue(host_ID);
		Register_Object(message_queue);
		messageQueueSocket.AddValue(message_queue, host_ID);
	}
	Check(message_queue);
	
	//
	// Add the send request
	//
	message_queue->AddSendRequest(client_ID, message);
	bufferSize++;
	#ifdef LAB_ONLY
		messageCount++;
		maxBufferSize = Max(maxBufferSize, bufferSize);
	#endif
}	
	
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	L4NetworkManager__MessageBuffer::AttemptToSend()
{
	Check(this);
	
	//
	//--------------------------------------------------------------------------
	// Verify that there exist non-empty queues and that the buffer size is
	// correct
	//--------------------------------------------------------------------------
	//
	#if DEBUG_LEVEL>0
	{
		TableIteratorOf<MessageQueue*, HostID>
			iterator(&messageQueueSocket);
		MessageQueue
			*message_queue;
		CollectionSize
			counter = 0;
		
		while ((message_queue = iterator.ReadAndNext()) != NULL)
		{
			Check(message_queue);
			ChainIteratorOf<MessageQueue::SendRequest*>
				message_iterator(&message_queue->sendRequestSocket);
			counter += message_iterator.GetSize();
		}
		Verify(counter > 0);
		Verify(counter == bufferSize);
	}
	#endif

	//
	//--------------------------------------------------------------------------
	// Increment the index to the next non-empty queue
	//--------------------------------------------------------------------------
	//
	TableIteratorOf<MessageQueue*, HostID>
		queue_iterator(&messageQueueSocket);
	MessageQueue
		*message_queue;
	MessageQueue::SendRequest
		*send_request;
			
	do
	{
		//
		// Increment the counter
		//
		if (++currentQueueIndex >= queue_iterator.GetSize())
			currentQueueIndex = 0;	
		
		//
		// Get the message queue
		//	
		message_queue = queue_iterator.GetNth(currentQueueIndex);
		Check(message_queue);
		
		//
		// Get the message
		//
		ChainIteratorOf<MessageQueue::SendRequest*>
			message_iterator(&message_queue->sendRequestSocket);
		send_request = message_iterator.GetCurrent();
	}
	while (send_request == NULL);
	Check(send_request);

	//
	//--------------------------------------------------------------------------
	// Attempt to send
	//--------------------------------------------------------------------------
	//
	Check(networkManager);
	if (
		networkManager->SendMessageToNetnub(
			send_request->messageToSend,
			send_request->clientID,
			message_queue->hostID
		)
	)
	{
		Unregister_Object(send_request);
		delete send_request;
		bufferSize--;
	}
}	
