//===========================================================================//
// File:	network.cc                                                       //
// Project: MUNGA           Brick: Network Manager                           //
// Contents: Implementation details for network class                        //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 11/01/94  JMA  Initial coding.                                            //
// 03/02/95  GAC  Added comments and functionality to support real net       //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//

#include "AdeptHeaders.hpp"

#include "Network.hpp"
#include "Connection.hpp"
#include "Application.hpp"
#include "EntityManager.hpp"
#include "NetStatCollector.hpp"
#include <MissionLang\Resource.h>
#include <gameos\Network.hpp>

#include <dplay.h> // jcem for mission replay
#include <gameos\mreplay.h> // jcem for mission replay

#ifdef _DEBUG
int g_nLocalPacketLevel = 0;
#endif // _DEBUG

//#############################################################################
//###########################    InBox   ##############################
//#############################################################################

InBox::ClassData*
	InBox::DefaultData = NULL;

//
//#############################################################################
//#############################################################################
//
void
	InBox::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			InBoxClassID,
			"Adept::InBox",
			Receiver::DefaultData,
			0,
			NULL
		);
	Register_Object(DefaultData);


}

//
//#############################################################################
//#############################################################################
//
void
	InBox::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//
//#############################################################################
//#############################################################################
//
InBox::InBox(
	ClassData *class_data,
	int box_ID
):
	Receiver(class_data)
{
	boxID = box_ID;
}

//
//#############################################################################
//#############################################################################
//
InBox::~InBox()
{
}

//
//#############################################################################
//#############################################################################
//
void
	InBox::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

//
//#############################################################################
//#############################################################################
//
void
	InBox::ReceiveNetworkPacket(
		const _NetPacket* packet,
		const Message *message
	)
{
	Check_Object(message);
	Dispatch(message);
}

//
//#############################################################################
//#############################################################################
//
void
	InBox::Send(
		const Message *message,
		Connection *connection
	)
{
	Check_Object(this);
	Check_Object(message);
	Check_Object(connection);

	if (connection == Connection::Local)
	{
		ReceiveNetworkPacket(NULL, message);
	}
	else
	{
		SendIt(connection->GetNetworkAddress(), message);
	}
}

//
//#############################################################################
//#############################################################################
//
void
	InBox::SendIt(
		DWORD destination,
		const Message *message
	)
{
	Check_Object(this);
	Check_Object(message);

	
	STOP(("NO LONGER SUPPORTED"));

	// use history if you need the old functionality.

}

//#############################################################################


QuedPacket::QuedPacket(int connection, int type, MemoryStream *packet_data):
	Plug(DefaultData)
{
	connectionID = connection;
	messageType = type;
	
	packetData = NULL;

	if (packet_data != NULL)
	{
		int size = packet_data->GetBufferBytesUsed();
		BYTE *mem_buffer = new BYTE[size];

		packetData = new MemoryStream(mem_buffer,size);
		packetData->WriteBytes(packet_data->GetPointer(), size);
		packetData->Rewind();
	}
}

//#############################################################################

QuedPacket::~QuedPacket()
{

	if (packetData != NULL)
	{
		packetData->Rewind();

		BYTE *mem_buffer = (BYTE*)packetData->GetPointer();
		delete[] mem_buffer;

		delete packetData;
	}
}


//#############################################################################
//##########################    Network   ##############################
//#############################################################################

Network::ClassData*
	Network::DefaultData = NULL;


DWORD
	Network::BroadcastAddress;
DWORD
	Network::ServerAddress;
DWORD
	Network::MyAddress;
bool Network::bUseNewUI = false;

int
	Network::DefaultPacketSize = 512;
int
	Network::HashMessageTypeFlags[255];


//
//#############################################################################
//#############################################################################
//
void
	Network::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			NetworkClassID,
			"Adept::Network",
			InBox::DefaultData,
			0, NULL
		);
	Register_Object(DefaultData);

	Connection::Hermit = new Connection(0, -1, "HERMIT");
	Register_Object(Connection::Hermit);

	
	for (int i = 0; i < 255; ++i)
	{
		HashMessageTypeFlags[i] = NoHash;
	}
}

//
//#############################################################################
//#############################################################################
//
void
	Network::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	// the hermit lives and breathes at all times
	Unregister_Object(Connection::Hermit);
	delete Connection::Hermit;
}

//
//#############################################################################
//#############################################################################
//
Network::Network():
	InBox(DefaultData, NetworkBoxID),
	localPacketQue(NULL)
{
	//
	//---------------------------------------------------------------------
	// See if we are a networked game.  If not, we need to create the local
	// connection ourself and set the server connection to Null
	//---------------------------------------------------------------------
	//


	// Start up the stat collectors...
//#ifdef _DEBUG // jcem - mapClientCRC will be changed if serverKey changed...
    serverKey = 1000;
//#else // !_DEBUG
//	serverKey = Random::GetInt();
//#endif // _DEBUG
	
	netStatCollector = new NetStatCollector[NetStatCount];
	

	netStatCollector[IncomingPacketCountStat].SetName("I-PPS");
	netStatCollector[OutgoingPacketCountStat].SetName("O-PPS");

	netStatCollector[IncomingTotalPacketSizeStat].SetName("I-Total");
	netStatCollector[OutgoingTotalPacketSizeStat].SetName("O-Total");

	netStatCollector[IncomingProtocolHeaderBitSizeStat].SetName("I-ProtHdr");
	netStatCollector[OutgoingProtocolHeaderBitSizeStat].SetName("O-ProtHdr");
	
	netStatCollector[IncomingHeaderBitSizeStat].SetName("I-GameHdr");
	netStatCollector[OutgoingHeaderBitSizeStat].SetName("O-GameHdr");
	netStatCollector[IncomingSystemMessageBitSizeStat].SetName("I-System");
	netStatCollector[OutgoingSystemMessageBitSizeStat].SetName("O-System");
	netStatCollector[IncomingWeaponMessageBitSizeStat].SetName("I-Weapon");
	netStatCollector[OutgoingWeaponMessageBitSizeStat].SetName("O-Weapon");
	
	netStatCollector[OutgoingMechMovementBitSizeStat].SetName("O-MechMvt");
	netStatCollector[IncomingMechMovementBitSizeStat].SetName("I-MechMvt");

	netStatCollector[MovementConfirmationBitSizeStat].SetName("Mvt-Conf");	

	netStatCollector[MechInternalBitSizeStat].SetName("O-MechInt");
	netStatCollector[BuildingDamageBitSizeStat].SetName("O-Building");
	netStatCollector[VehicleBitSizeStat].SetName("O-Vehicle");



	for (int i = 0; i < Maximum_Connection_Numbers; ++i)
	{

		packetsSent[i] = 0;
		packetsDropped[i] = 0;


		m_bigMessageCollectors[i].m_bigMessageSize = 0;
		m_bigMessageCollectors[i].m_bigMessageType = -1;
		m_bigMessageCollectors[i].m_bigPacketCRC = 0;
		m_bigMessageCollectors[i].m_bigMessage = NULL;
	}

	Check_Object(EntityManager::GetInstance());
	EntityManager::GetInstance()->Reset();



	for (int j = 0; j < Maximum_Connection_Numbers; ++j)
	{
		activeConnections[j] = NULL;
	}

	activeConnections[0] = Connection::Hermit;

	if (!reinterpret_cast<DWORD>(gos_NetInformation(gos_Networking, 0)))
	{
		startedGosNet = false;

		amIServer = true;

		EntityManager::GetInstance()->StartServer();
	

		Verify(!Connection::Local);
		Verify(!Connection::Server);
		Connection::Local = new Connection(1, 0, "");

		Register_Object(Connection::Local);
		Connection::Server = Connection::Local;

		activeConnections[1] = Connection::Local;

		Application::GetInstance()->ConnectClient(1, Connection::Local);


		return;
	}

	startedGosNet = true;

	//
	//----------------------------------
	// Get the other network information
	//----------------------------------
	//
	DWORD player_count =
		reinterpret_cast<DWORD>(gos_NetInformation(gos_NumberOfPlayers, 0));
	MyAddress =
		reinterpret_cast<DWORD>(gos_NetInformation(gos_MyID, 0));
	BroadcastAddress =
		reinterpret_cast<DWORD>(gos_NetInformation(gos_AllID, 0));
	ServerAddress =
		reinterpret_cast<DWORD>(gos_NetInformation(gos_ServerID, 0));

	//
	//--------------------------------------------------
	// Create a new connection for each connected player
	//--------------------------------------------------
	//

	Verify(!Connection::Local);
	Verify(!Connection::Server);
	nextConnectionID = 2;
	currentPlayerCount = 0;

	if (reinterpret_cast<DWORD>(gos_NetInformation(gos_AmITheServer, 0)))
	{
		BYTE i;
		for (i=1; i<=player_count; ++i)
		{

			//
			//-------------------------------------------------------
			// Get the player name and figure out the connection type
			//-------------------------------------------------------
			//
			const char* player_name =
				reinterpret_cast<const char*>(gos_NetInformation(gos_PlayerName, i-1));
			DWORD net_id =
				reinterpret_cast<DWORD>(gos_NetInformation(gos_PlayerID, i-1));

			//
			//----------------------------------------------------------------
			// Build the connection, then stick it into the table and pointers
			//----------------------------------------------------------------
			//
			Connection *connection = new Connection(i, net_id, player_name);
			Register_Object(connection);

			//SPEW(("jerryeds", "GAIN CONNECTION: - %x : %d %d", connection, connection->GetID(), connection->GetNetworkAddress()));

			if (net_id == MyAddress)
			{
				Connection::Local = connection;
				Connection::Server = connection;
				connection->connectionID = 1;
			}
			else
			{
		
				Verify(nextConnectionID >= 0);
				Verify(nextConnectionID < Maximum_Connection_Numbers);
				connection->connectionID = (BYTE)nextConnectionID;
				Network::GetInstance()->AddConnectionToStats(connection->GetID());
				++nextConnectionID;
			}


			activeConnections[i] = connection;

			Application::GetInstance()->ConnectClient(connection->connectionID, connection);

			
		}


		currentPlayerCount = player_count;
		nextConnectionID = i;
		Verify(nextConnectionID < Maximum_Connection_Numbers);
	
		Check_Object(Connection::Local);
	}

	//
	//--------------------------------------------------------------------------
	// If we are the server, we need to send a message to everybody else to tell
	// them our connection order
	//--------------------------------------------------------------------------
	//
	
	if (reinterpret_cast<DWORD>(gos_NetInformation(gos_AmITheServer, 0)))
	{
		EntityManager::GetInstance()->StartServer();
	
		amIServer = true;
		Connection::Server = Connection::Local;
		SendConnections();
	}
	else
	{

		EntityManager::GetInstance()->StartClient();
	

		amIServer = false;
	}



}


//
//#############################################################################
//#############################################################################
//

const char *Network::GetGameName()
{
	Check_Object(this);
	Verify(amIServer);
	

	const char* game_name = reinterpret_cast<const char*>(gos_NetInformation(gos_NameOfGame, NULL));

	return game_name;

}

//
//#############################################################################
//#############################################################################
//

void Network::AdvanceStats()
{
	if (Application::GetInstance()->applicationMode != Application::EditorMode)
	{

	for (int i = 0; i < NetStatCount; ++i)
	{
		netStatCollector[i].AdvanceTime();
	}
	}
}

//
//#############################################################################
//#############################################################################
//

void Network::AddConnectionToStats(int id)
{
	for (int i = 0; i < NetStatCount; ++i)
	{
		netStatCollector[i].AddConnection(id);
	}
}

//
//#############################################################################
//#############################################################################
//

void Network::RemoveConnectionToStats(int id)
{
	for (int i = 0; i < NetStatCount; ++i)
	{
		netStatCollector[i].RemoveConnection(id);
	}
}


//
//#############################################################################
//#############################################################################
//
Network::~Network()
{
	
	EntityManager::GetInstance()->StopServer();
	EntityManager::GetInstance()->StopClient();


	if (startedGosNet)
	{
		gos_NetEndGame();
	}

	Verify(Connection::Hermit);
	Connection::Hermit->DeleteChildren();

	for (unsigned i=1; i<Maximum_Connection_Numbers; ++i)
	{
		m_bigMessageCollectors[i].m_bigMessageSize = 0;
		m_bigMessageCollectors[i].m_bigMessageType = -1;
		m_bigMessageCollectors[i].m_bigPacketCRC = 0;
		
		if (m_bigMessageCollectors[i].m_bigMessage != NULL)
		{
			Check_Object(m_bigMessageCollectors[i].m_bigMessage);
			delete m_bigMessageCollectors[i].m_bigMessage;
			m_bigMessageCollectors[i].m_bigMessage = NULL;
		}

		if (activeConnections[i] != NULL)
		{
			Application::GetInstance()->DisconnectClient(i, Connection::Local);
			Connection *connection = activeConnections[i];
			Unregister_Object(connection);
			delete connection;
			connection = NULL;
		}
	}
	Connection::Local = NULL;
	Connection::Server = NULL;
	//Connection::Hermit = NULL; // this guy stays!

	delete[] netStatCollector;

}

//
//#############################################################################
//#############################################################################
//

void Network::Recycle(void)
{
	


	Connection::Hermit->DeleteChildren();

	// this is bad news. 
	// we should only delete packet ques when we leave the server
	// that happens when the network is destructed

	for (unsigned i=1; i<Maximum_Connection_Numbers; ++i)
	{
#if 0
		m_bigMessageCollectors[i].m_bigMessageSize = 0;
		m_bigMessageCollectors[i].m_bigMessageType = -1;
		m_bigMessageCollectors[i].m_bigPacketCRC = 0;
		
		if (m_bigMessageCollectors[i].m_bigMessage != NULL)
		{
			Check_Object(m_bigMessageCollectors[i].m_bigMessage);
			delete m_bigMessageCollectors[i].m_bigMessage;
			m_bigMessageCollectors[i].m_bigMessage = NULL;
		}
#endif
		if (activeConnections[i] != NULL)
		{
			Connection *connection = activeConnections[i];
			connection->DeleteChildren();
		}
	}


}

//
//#############################################################################
//#############################################################################
//
void
	Network::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

//
//#############################################################################
//#############################################################################
//
void
	Network::ReceiveNetworkPacket(
		const _NetPacket* packet,
		const Message *message
	)
{
	Check_Pointer(packet);

	//
	//----------------------------------
	// Find out who this message is from
	//----------------------------------
	//
	Connection *connection = FindConnection(packet->FromID);
	Check_Object(connection);


	

	//
	//-------------------------------------------------------------------------
	// If we are not already starting a file transfer for this connection, look
	// to find the type of thing to do
	//-------------------------------------------------------------------------
	//
	if (!connection->dataBuffer)
	{
		Verify(!connection->bytesToTransfer);
		DataTransferMessage *data =
			Cast_Pointer(DataTransferMessage*, packet->pData);

		//
		//-------------------------------------------------
		// Otherwise, we must be requesting a file transfer
		//-------------------------------------------------
		//
		
		connection->bytesToTransfer = data->dataLength;
		connection->dataBuffer = new BYTE[data->dataLength];
		Register_Pointer(connection->dataBuffer);
		connection->dataPointer = connection->dataBuffer;
		connection->dataTransferBox = data->inBox;
	
	}

	//
	//------------------------------------------
	// Otherwise, process the next chunk of data
	//------------------------------------------
	//
	else
	{
		Verify(connection->dataPointer >= connection->dataBuffer);
		size_t len = packet->Length;

		Verify(connection->bytesToTransfer >= len);
		Mem_Copy(
			connection->dataPointer,
			packet->pData,
			len,
			connection->bytesToTransfer
		);
		connection->bytesToTransfer -= len;
		connection->dataPointer += len;

		//
		//--------------------------------------------------------------------
		// If we are done with the transfer, send the completed message to the
		// desired inbox
		//--------------------------------------------------------------------
		//
		if (!connection->bytesToTransfer)
		{
			_NetPacket spoof(*packet);
			spoof.Type = connection->dataTransferBox;
			spoof.pData = connection->dataBuffer;
			InBox *box = FindInBox(static_cast<BoxID>(spoof.Type));
			Check_Object(box);
			box->ReceiveNetworkPacket(&spoof, static_cast<Message*>(spoof.pData));
			Unregister_Pointer(connection->dataBuffer);
			delete[] connection->dataBuffer;
			connection->dataBuffer = NULL;
		}
	}
}

//define HUNT_PACKET_CORUPTION


//
//#############################################################################
//#############################################################################
//
bool
	Network::RoutePacket()
{
	Check_Object(this);

	//
	//---------------------------------
	// See if we have a packet to route
	//---------------------------------
	//
	NetPacket *packet = gos_NetGetMessage();
	if (!packet)
	{
		//SPEW(("jerryeds", "%f : NO PACKET", gos_GetElapsedTime()));
		return false;
	}
	//SPEW(("jerryeds", "%f : PACKET", gos_GetElapsedTime()));


	#if defined(LAB_ONLY)
		MWGameInfo::g_lastPacketType[MWGameInfo::nextPacketHistoryIndex] = packet->Type;
		MWGameInfo::g_lastPacketSize[MWGameInfo::nextPacketHistoryIndex] = packet->Length;
		MWGameInfo::g_lastPacketInbound[MWGameInfo::nextPacketHistoryIndex] = true;
		MWGameInfo::g_lastPacketTime[MWGameInfo::nextPacketHistoryIndex] = gos_GetElapsedTime();

		{
			Check_Object(Application::GetInstance());
			Connection *connection = FindConnection(packet->FromID);

			if (connection != NULL)
			{
				MWGameInfo::g_lastPacketAddress[MWGameInfo::nextPacketHistoryIndex] = connection->GetID();
			}
		}

		++MWGameInfo::nextPacketHistoryIndex;

		if (MWGameInfo::nextPacketHistoryIndex >= MWGameInfo::Packet_History_Count)
				MWGameInfo::nextPacketHistoryIndex = 0;

	#endif


	Check_Object(Application::GetInstance());
	Connection *connection = FindConnection(packet->FromID);

	if (connection != NULL)
	{
		

		Network::GetInstance()->netStatCollector[Network::IncomingPacketCountStat].AddToStatistic(connection->GetID(),1);
		Network::GetInstance()->netStatCollector[Network::IncomingProtocolHeaderBitSizeStat].AddToStatistic(connection->GetID(),42*8);
		Network::GetInstance()->netStatCollector[Network::IncomingTotalPacketSizeStat].AddToStatistic(connection->GetID(),packet->Length*8);

		
		Adept::g_lastPacketFromPlayer[connection->GetID()] = gos_GetElapsedTime();
	}

	//
	//----------------------------------------------------------
	// We have a packet, so route it to the appropiate place
	//----------------------------------------------------------
	//

	if (packet->Type >= GameMessageStartID && packet->Type < GameMessageStopID)
	{
		Check_Object(Application::GetInstance());
		Connection *connection = FindConnection(packet->FromID);

		// some of our children types do not require a connection
		// and instead create them.  so we do that testing lower
		int id = -1;

		if (connection != NULL)
		{
			Check_Object(connection);
			id = connection->GetID();
		}

		if (packet->Length > 0)
		{
			MemoryStream message(packet->pData, packet->Length);
			ReceiveMessage(id, packet->Type, &message);
		}
		else if (packet->pData != NULL)
		{
			MemoryStream message(packet->pData, 256);
			ReceiveMessage(id, packet->Type, &message);
		}
		else
		{	
			if(packet->pData != NULL)
				STOP(("EMPTY PACKET HAS DATA"));
			ReceiveMessage(id, packet->Type, NULL);
		}
		return true;
	}


	switch (packet->Type)
	{
		case BigPacketStartBufferBoxID:
			{
				if (connection == NULL)
				{
					return true;
				}

				Verify(m_bigMessageCollectors[connection->GetID()].m_bigMessage == NULL);
	
				Check_Pointer(packet->pData);
				Verify(packet->Length > 0);
				MemoryStream message(packet->pData, packet->Length);

				m_bigMessageCollectors[connection->GetID()].m_bigMessageType = 0;
				m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0;
				m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0;
				message.ReadBytes(&m_bigMessageCollectors[connection->GetID()].m_bigMessageType, 1);
				message.ReadBytes(&m_bigMessageCollectors[connection->GetID()].m_bigMessageSize, 4);
				message.ReadBytes(&m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC, 4);

				m_bigMessageCollectors[connection->GetID()].m_bigMessage = new DynamicMemoryStream(m_bigMessageCollectors[connection->GetID()].m_bigMessageSize);
				m_bigMessageCollectors[connection->GetID()].m_bigMessage->WriteBytes(message.GetPointer(), message.GetBytesRemaining());

				message.Rewind();

#ifdef HUNT_PACKET_CORUPTION
				DWORD crc = 0;
				for (int i = 0; i < message.GetSize(); ++i)
				{
					BYTE data;
					message.ReadBytes(&data, 1);
					crc += data;
				}
				message.Rewind();
				SPEW(("jerryeds", "START CRC %x", crc));
#endif

				Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connection->GetID(),3);
			}
			return true;

		case BigPacketPartBufferBoxID:
			{
				Check_Object(m_bigMessageCollectors[connection->GetID()].m_bigMessage);

				Check_Object(Application::GetInstance());
				Connection *connection = FindConnection(packet->FromID);
				
				if (connection == NULL)
				{
					return true;
				}



				Check_Pointer(packet->pData);
				Verify(packet->Length > 0);

				MemoryStream message(packet->pData, packet->Length);
				m_bigMessageCollectors[connection->GetID()].m_bigMessage->WriteBytes(message.GetPointer(), message.GetBytesRemaining());

		
				message.Rewind();
#ifdef HUNT_PACKET_CORUPTION
				DWORD crc = 0;
				for (int i = 0; i < message.GetSize(); ++i)
				{
					BYTE data;
					message.ReadBytes(&data, 1);
					crc += data;
				}
				message.Rewind();
				SPEW(("jerryeds", "PART CRC %x", crc));
#endif				

			}
			return true;

		case BigPacketEndBufferBoxID:
			{

				Check_Object(Application::GetInstance());
				Connection *connection = FindConnection(packet->FromID);
				
				if (connection == NULL)
				{
					return true;
				}


				Check_Object(m_bigMessageCollectors[connection->GetID()].m_bigMessage);

				Check_Pointer(packet->pData);
				Verify(packet->Length > 0);
				MemoryStream message(packet->pData, packet->Length);

				m_bigMessageCollectors[connection->GetID()].m_bigMessage->WriteBytes(message.GetPointer(), message.GetBytesRemaining());
				m_bigMessageCollectors[connection->GetID()].m_bigMessage->Rewind();

				message.Rewind();
#ifdef HUNT_PACKET_CORUPTION
				DWORD little_crc = 0;
				for (int j = 0; j < message.GetSize(); ++j)
				{
					BYTE data;
					message.ReadBytes(&data, 1);
					little_crc += data;
				}
				message.Rewind();
				SPEW(("jerryeds", "END CRC %x", little_crc));
#endif

				DWORD crc = 0;
				for (int i = 0; i < m_bigMessageCollectors[connection->GetID()].m_bigMessage->GetSize(); ++i)
				{
					BYTE data;
					m_bigMessageCollectors[connection->GetID()].m_bigMessage->ReadBytes(&data, 1);
					crc += data;
				}
				m_bigMessageCollectors[connection->GetID()].m_bigMessage->Rewind();

				if ( m_bigMessageCollectors[connection->GetID()].m_bigMessage->GetSize() < m_bigMessageCollectors[connection->GetID()].m_bigMessageSize)
				{
					delete m_bigMessageCollectors[connection->GetID()].m_bigMessage;
					m_bigMessageCollectors[connection->GetID()].m_bigMessage = NULL;
					m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0;
					m_bigMessageCollectors[connection->GetID()].m_bigMessageType = -1;
					m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0;

					RemovePlayer(connection->GetID());
					return true;
					//STOP(("Network::RoutePacket::BIG PACKET WRONG SIZE : SMALLER"));
				}

				if ( m_bigMessageCollectors[connection->GetID()].m_bigMessage->GetSize() > m_bigMessageCollectors[connection->GetID()].m_bigMessageSize)
				{
					delete m_bigMessageCollectors[connection->GetID()].m_bigMessage;
					m_bigMessageCollectors[connection->GetID()].m_bigMessage = NULL;
					m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0;
					m_bigMessageCollectors[connection->GetID()].m_bigMessageType = -1;
					m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0;

					RemovePlayer(connection->GetID());
					return true;
					//STOP(("Network::RoutePacket::BIG PACKET WRONG SIZE : LARGER"));
				}

				if (crc != m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC)
				{
					delete m_bigMessageCollectors[connection->GetID()].m_bigMessage;
					m_bigMessageCollectors[connection->GetID()].m_bigMessage = NULL;
					m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0;
					m_bigMessageCollectors[connection->GetID()].m_bigMessageType = -1;
					m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0;

					RemovePlayer(connection->GetID());
					return true;
					//STOP(("Network::RoutePacket::BIG PACKET GOT CORRUPTED :  From : %d  Type : %d", connection->GetID(), m_bigMessageCollectors[connection->GetID()].m_bigMessageType));
				}

				ReceiveMessage(connection->GetID(), m_bigMessageCollectors[connection->GetID()].m_bigMessageType, m_bigMessageCollectors[connection->GetID()].m_bigMessage);

				delete m_bigMessageCollectors[connection->GetID()].m_bigMessage;
				m_bigMessageCollectors[connection->GetID()].m_bigMessage = NULL;
				m_bigMessageCollectors[connection->GetID()].m_bigMessageSize = 0;
				m_bigMessageCollectors[connection->GetID()].m_bigMessageType = -1;
				m_bigMessageCollectors[connection->GetID()].m_bigPacketCRC = 0;


			}
			return true;

		case NetworkBoxID:
		case ConnectionBoxID:	
		case ApplicationBoxID:
			//SPEW(("jerryeds","BOX Message"));
			{
				STOP(("BOX MESSAGES DISABLED"));
				//
				//----------------------------------
				// Find out who this message is from
				//----------------------------------
				//
				Connection *connection = FindConnection(packet->FromID);
				if (connection == NULL)
						return true;
				// no messages from "us" or from the "hermit"
				if (connection == Connection::Local)
					return true;
				if (connection == Connection::Hermit)
					return true;

				Check_Object(connection);

				InBox *box = FindInBox(static_cast<BoxID>(packet->Type));
				if (box)
				{
					Check_Object(box);
					box->ReceiveNetworkPacket(packet, static_cast<Message*>(packet->pData));
				}
			}
			return true;

	

		case gosNet_PlayerAdded:
			
			
			if (reinterpret_cast<DWORD>(gos_NetInformation(gos_AmITheServer, 0)))
			{
				//SPEW(("jerryeds","PLAYER ADDED"));
				
				DWORD net_id = packet->FromID;
				if(FindConnection(net_id) == NULL)
				{
					const char* player_name = reinterpret_cast<const char*>(gos_NetInformation(gos_PlayerName, reinterpret_cast<DWORD>(gos_NetInformation(gos_PlayerNumber,packet->FromID))));
					DWORD net_id = packet->FromID;

                    net_AddPlayer(player_name, net_id);

                    connection = FindConnection(net_id);
                    gosASSERT(connection);
                    if (g_pfn_ADDREMOVE_CONNECTION) {
                        (*g_pfn_ADDREMOVE_CONNECTION)(true, connection);
                    }


				}
				SendConnections();
			}
			return true;

		case gosNet_PlayerDeleted:

			
			if (reinterpret_cast<DWORD>(gos_NetInformation(gos_AmITheServer, 0)))
			{
				
				//SPEW(("jerryeds","PLAYER DELETED"));
				// send the clients the remove command

				Connection *connection = FindConnection(packet->FromID);

				if (connection)
				{
                    if (g_pfn_ADDREMOVE_CONNECTION) {
                        (*g_pfn_ADDREMOVE_CONNECTION)(false, connection);
                    }

                    net_RemovePlayer(packet->FromID);

					SendConnections();
				}
				return true;
			}
			else
			{

				Connection *connection = FindConnection(packet->FromID);
				
				if (connection == Connection::Server)
				{
					//
					// we're the client, so on a player deleted message,
					// end the game
					//
                    if (g_pfn_SESSION_LOST) {
                        (*g_pfn_SESSION_LOST)();
                    }
					Application::GetInstance()->SessionLost();
					Check_Object(Application::GetInstance());
					Application::GetInstance()->QueStopGame(true);
				}
                return true;
			}

		case gosNet_GameEnded:
            if (g_pfn_SESSION_LOST) {
                (*g_pfn_SESSION_LOST)();
            }
			Application::GetInstance()->SessionLost();
			Check_Object(Application::GetInstance());
			Application::GetInstance()->QueStopGame(true);

			return true;
	}		



	return true;

}

void Network::net_AddPlayer(const char* player_name, DWORD net_id)
{
    gosASSERT(reinterpret_cast<DWORD>(gos_NetInformation(gos_AmITheServer, 0)));
    gosASSERT(FindConnection(net_id) == NULL);

	++currentPlayerCount;

	int break_tester = 0;
	while (activeConnections[nextConnectionID] != NULL)
	{
		++nextConnectionID;
		if (nextConnectionID >= Maximum_Connection_Numbers)
			nextConnectionID = 2;

		++break_tester;

		if (break_tester >= Maximum_Connection_Numbers)
			STOP(("NO CONNECTIONS LEFT"));
	}


	Verify(activeConnections[nextConnectionID] == NULL);

	Connection *connection = new Connection((BYTE)nextConnectionID, net_id, player_name);
	Register_Object(connection);
	Network::GetInstance()->AddConnectionToStats(connection->GetID());
			
	Application::GetInstance()->ConnectClient(nextConnectionID, connection);

	activeConnections[nextConnectionID] = connection;
	++nextConnectionID;

	if (true)
	{
		// MLYONS: added system chat message for player joining
		char szString[256];
		sprintf(szString, Application::GetInstance()->GetLocString(IDS_ADEPT_NET_PLAYER_HAS_CONNECTED), player_name);
		Application::GetInstance()->SendChat(1, 0, 0, szString);
	}
}

void Network::net_RemovePlayer(DWORD net_id)
{
    gosASSERT(reinterpret_cast<DWORD>(gos_NetInformation(gos_AmITheServer, 0)));
	//SPEW(("jerryeds","PLAYER DELETED"));
	// send the clients the remove command

	--currentPlayerCount;
	Connection *connection = FindConnection(net_id);
    gosASSERT(connection);

	Check_Object(connection);

	if (true)
	{
		// MLYONS: added system chat message for player leaving
		char szString[256];
		sprintf(szString, Application::GetInstance()->GetLocString(IDS_ADEPT_NET_PLAYER_HAS_DISCONNECTED), connection->GetConnectionName());
		Application::GetInstance()->SendChat(1, 0, 0, szString);
	}

	BYTE id = connection->GetID();

	Check_Object(Application::GetInstance());


	if (m_bigMessageCollectors[id].m_bigMessage != NULL)
	{
		delete m_bigMessageCollectors[id].m_bigMessage;
		m_bigMessageCollectors[id].m_bigMessage = NULL;
		m_bigMessageCollectors[id].m_bigMessageSize = 0;
		m_bigMessageCollectors[id].m_bigMessageType = -1;
		m_bigMessageCollectors[id].m_bigPacketCRC = 0;

	}

	Application::GetInstance()->DisconnectClient(id, connection);

	
	Unregister_Object(connection);
	delete connection;
	connection = NULL;


	//fry deathrow
	while(EntityManager::GetInstance()->FryDeathRow())
	{
	}

	activeConnections[id] = NULL;
}

//#############################################################################


void Network::RemovePlayer(int connection_id)
{
	Connection *connection = FindConnection(connection_id);

	if (connection)
	{
		Check_Object(connection);

		if (true)
		{
			// MLYONS: added system chat message for player leaving
			char szString[256];
			sprintf(szString, Application::GetInstance()->GetLocString(IDS_ADEPT_NET_PLAYER_HAS_DISCONNECTED), connection->GetConnectionName());
			Application::GetInstance()->SendChat(1, 0, 0, szString);
		}

		BYTE id = connection->GetID();

		Check_Object(Application::GetInstance());


		if (m_bigMessageCollectors[id].m_bigMessage != NULL)
		{
			delete m_bigMessageCollectors[id].m_bigMessage;
			m_bigMessageCollectors[id].m_bigMessage = NULL;
			m_bigMessageCollectors[id].m_bigMessageSize = 0;
			m_bigMessageCollectors[id].m_bigMessageType = -1;
			m_bigMessageCollectors[id].m_bigPacketCRC = 0;

		}

		Application::GetInstance()->DisconnectClient(id, connection);

		
		Unregister_Object(connection);
		delete connection;
		connection = NULL;


		//fry deathrow
		while(EntityManager::GetInstance()->FryDeathRow())
		{
		}

		activeConnections[id] = NULL;


		SendConnections();
	}
}

//#############################################################################


bool
	Network::RouteLocalPacket()
{
	ChainIteratorOf<QuedPacket *> packets(&localPacketQue);
	QuedPacket *packet;
	if ((packet = packets.GetCurrent()) != NULL)
	{
#ifdef _DEBUG
        g_nLocalPacketLevel++;
#endif // _DEBUG
		ReceiveMessage(packet->connectionID, packet->messageType, packet->packetData);
#ifdef _DEBUG
        g_nLocalPacketLevel--;
#endif // _DEBUG

		packets.Remove();
		delete packet;
	}

	if (packets.GetCurrent())
		return true;

	return false;

}
//
//#############################################################################
//#############################################################################
//

void Network::ReceiveMessage(int connection_id, int message_type, Stuff::MemoryStream *message)
{
	
	
	if (message_type == FullConnectionListMessageID)		
	{
		//
		//--------------------------------------------
		// Create a new server list in the right order
		//--------------------------------------------
		//

		//Network::GetInstance()->netStatCollector[Network::IncomingSystemMessageBitSizeStat].AddToStatistic(Connection::Server->GetID(),message->GetCurrentBitCount()*8);

		// read the player count
		DWORD player_count;
		*message >> player_count;
		*message >> serverKey;


		// make a "lost list"
		// this list assumes everyone has been dropped from the game.
		// as the player is found in the stream we mark him as not lost
		// anyone at the end of the process who is still not marked is
		// no longer in the game...

		// this method is used so that only one connection message is needed
		// and multiple players can be both removed and added with one message.

		currentPlayerCount = player_count;
		bool lost_list[Maximum_Connection_Numbers];
		for (int i = 1; i < Maximum_Connection_Numbers; ++i)
		{
			lost_list[i] = true;
		}

		bool connect_server = false;

		// read each player from the stream



		for (i=1; i<=player_count; ++i)
		{
			BYTE  connection_id;
			DWORD address;
			*message >> connection_id;
			*message >> address;

			MString player_name;
			*message >> player_name;


			Verify(connection_id < Maximum_Connection_Numbers);

			// look for that player
			Connection *connection = activeConnections[connection_id];
			if (connection == NULL)
			{
				//we have a new player, create his connection

			
				connection = new Connection(connection_id, address, player_name);
				Register_Object(connection);

				Application::GetInstance()->ConnectClient(connection_id, connection);

				Verify(activeConnections[connection_id] == NULL);
				activeConnections[connection_id] = connection;

				lost_list[connection_id] = false;

				//if (address == MyAddress)
				//	Connection::Local = connection;
				//else
				
				Network::GetInstance()->AddConnectionToStats(connection->GetID());

				if (connection_id == 1)
				{
					Connection::Server = connection;
					Network::GetInstance()->netStatCollector[Network::IncomingSystemMessageBitSizeStat].AddToStatistic(Connection::Server->GetID(),message->GetBufferBytesUsed()*8);
				
					connect_server = true;
				}

			}
			else
			{
				// we found this player so mark him as still in the game
				Verify(address == connection->GetNetworkAddress());
				lost_list[connection_id] = false;
			}

				
		

			Check_Object(connection);
		}


		BYTE local_player;
		*message >> local_player;

		if (Connection::Local == NULL)
		{
			Connection::Local = activeConnections[local_player];
			Network::GetInstance()->RemoveConnectionToStats(local_player);
		}



		if (connect_server)
		{
			Application::GetInstance()->ConnectServer();
		}

		// now go thru the lost list and remove everyone who was lost...
		for (i = 1; i < Maximum_Connection_Numbers; ++i)
		{
			if (lost_list[i] && activeConnections[i] != NULL)
			{
				// connection is now gone.

			
				if (activeConnections[i] != Connection::Local)
				{
					Application::GetInstance()->DisconnectClient(i, activeConnections[i]);

					Unregister_Object(activeConnections[i]);
					delete activeConnections[i];
					activeConnections[i] = NULL;
				}

				
			}
		}

		// fry death row to be doubly sure they are gone
		while(EntityManager::GetInstance()->FryDeathRow())
		{
		}
		
		Check_Object(Connection::Local);
		Check_Object(Connection::Server);
#if defined(_DEBUG) && 0 // jcem - for MR test
  {
	OutputDebugString(":: FullConnectionListMessageID\n");
  }
#endif // _DEBUG && ...

		return;

	}

	// if we need to throw this away...
	Connection *connection = Network::GetInstance()->GetConnection(connection_id);

	if (connection == NULL)
	{
		return;
	}


	bool delete_message = false;

	if (message && HashMessageTypeFlags[message_type] != NoHash)
	{

		MemoryStream sub_message(message->GetPointer(), message->GetSize());
		//comp the hash..
		
		MD5_CTX resource_context;
		GUID resource_hash;
		unsigned char * buff=(unsigned char *) &resource_hash;
		
		MD5Init(&resource_context);
		
		MD5Update(&resource_context, (unsigned char *) &serverKey, 4);

		if (HashMessageTypeFlags[message_type] == GameSpecificHash)
		{
			int number = Application::GetInstance()->currentGameNumber^0x43215202;
			MD5Update(&resource_context, (unsigned char *) &number, 4);
			//SPEW(("jerryeds", "GAME : %d", number));

			number = Connection::Server->GetLastReplicatorIDUsed();
			MD5Update(&resource_context, (unsigned char *) &number, 4);
			//SPEW(("jerryeds", "ID : %d", number));
		}

		MD5Update(&resource_context, (unsigned char *) connection->GetConnectionName(), strlen(connection->GetConnectionName()));

		//SPEW(("jerryeds", "NAME : %s", connection->GetConnectionName()));

		MD5Update(&resource_context, (unsigned char *) message->GetPointer(), message->GetSize()-2);
		MD5Final(buff, &resource_context);


		WORD part_1 = (WORD)buff[0];
		WORD part_2 = (WORD)buff[2];
		WORD part_3 = (WORD)buff[4];
		WORD part_4 = (WORD)buff[6];
		WORD part_5 = (WORD)buff[8];
		WORD part_6 = (WORD)buff[10];
		WORD part_7 = (WORD)buff[12];
		WORD part_8 = (WORD)buff[14];

		WORD total_crc;
		total_crc = part_1;
		total_crc ^= part_2;
		total_crc ^= part_3;
		total_crc ^= part_4;
		total_crc ^= part_5;
		total_crc ^= part_6;
		total_crc ^= part_7;
		total_crc ^= part_8;

		message->AdvancePointer(message->GetSize()-2);

		WORD comp_crc = 0;
		message->ReadBytes(&comp_crc, 2);


		//SPEW(("jerryeds", "CRC : %d", total_crc));
		//SPEW(("jerryeds", "COMPCRC : %d", comp_crc));

		if (comp_crc != total_crc)
		{
			SPEW(("jerryeds", "CORRUPTED PACKET"));
			return;
		}

		Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connection_id,16);

		message->Rewind();

		delete_message = true;
		Stuff::MemoryStream *new_stream = new Stuff::MemoryStream(message->GetPointer(), message->GetSize()-2);
		message = new_stream;
	}



	if (message_type >= FirstDirectApplicationMessageID && message_type < LastDirectApplicationMessageID)
	{
		// application messages are system messages...
		if (message != NULL)
			Network::GetInstance()->netStatCollector[Network::IncomingSystemMessageBitSizeStat].AddToStatistic(connection_id,message->GetBufferBytesUsed()*8);

		Application::GetInstance()->ReceiveDirectMessage(connection_id, message_type, message);	
	}
	else if (message_type >= FirstDictionaryPageMessageID && message_type < LastDictionaryPageMessageID)
	{	
		EntityManager::GetInstance()->UpdateClientEntites(connection_id, message_type, message);
	}
	else
	{
		STOP(("UNKNOWN MESSAGE TYPE"));
	}


	if (delete_message)
	{
		delete message;
	}
	
}


//
//#############################################################################
//#############################################################################
//
InBox*
	Network::FindInBox(BoxID box_id)
{
	Check_Object(this);
	switch (box_id)
	{
	case NetworkBoxID:
		return this;

	case ConnectionBoxID:
		return Connection::Local;

	case ApplicationBoxID:
		Check_Object(Application::GetInstance());
		return Application::GetInstance();

	}
	return NULL;
}

//
//#############################################################################
//#############################################################################
//
Connection*
	Network::FindConnection(DWORD net_address)
{
	Check_Object(this);

	for (BYTE i=1; i<Maximum_Connection_Numbers; ++i)
	{
		if (activeConnections[i] != NULL)
		{
			Check_Object(activeConnections[i]);
			if (activeConnections[i]->GetNetworkAddress() == net_address)
				return activeConnections[i];
		}
	}
	return NULL;
}


//#############################################################################


Connection*
	Network::GetConnection(int host_ID)
{

	Check_Object(this);

	if (host_ID < 0 || host_ID >= Maximum_Connection_Numbers) // jcem host_ID > Maximum_Connection_Numbers
		return NULL;

	return activeConnections[host_ID];

}


//#############################################################################


void Network::RemoveConnection(int host_ID)
{
	if (activeConnections[host_ID] != NULL)
	{
		Unregister_Object(activeConnections[host_ID]);
		delete activeConnections[host_ID];
		activeConnections[host_ID] = NULL;
	}
};


//
//#############################################################################
//#############################################################################
//

void
	Network::SendConnections()
{
	Check_Object(this);

	// go ahead and get the max we could use. 
	// It will only send what is actually used.

	for (BYTE j = 1; j < Maximum_Connection_Numbers; ++j)
	{
		if (activeConnections[j] != NULL && activeConnections[j] != Connection::Local)
		{


			DynamicMemoryStream stream(sizeof(DWORD)+(currentPlayerCount*(sizeof(DWORD)+256)));
			
			stream << currentPlayerCount;
			stream << serverKey;

			BYTE i;
			for (i=1; i<Maximum_Connection_Numbers; ++i)
			{
				//SPEW(("jerryeds", "SENDNG ADDRESS : %d %d", activeConnections[i]->GetID(), activeConnections[i]->GetNetworkAddress()));
				if (activeConnections[i] != NULL)
				{
					stream << activeConnections[i]->GetID();
					stream << activeConnections[i]->GetNetworkAddress();
					MString name = activeConnections[i]->GetConnectionName();
					stream << name;
				}
			}
			

			stream << j;

			stream.Rewind();



			Network::GetInstance()->netStatCollector[Network::OutgoingSystemMessageBitSizeStat].AddToStatistic(j,1);
		
			Check_Object(activeConnections[j]);
			SendNetMessage(j, FullConnectionListMessageID, &stream, true);
		}
	}


}


//#############################################################################

void
	Network::SendNetMessage(int connection_id, int message_type, Stuff::MemoryStream *message, bool reliable)
{
        // jcem for mission review
    if (g_nMR && (connection_id == 1)) {
        if (!(*g_pfn_LOCALSENDNETMESSAGE)(message_type, message))
            return;
    }

	#ifdef GROUP_ADEPT_NETWORK
		LOG_BLOCK("Game Logic::Network::Send Message");
	#endif

	Connection *connection = Network::GetInstance()->GetConnection(connection_id);
	Check_Object(connection);

	_NetPacket packet;
	packet.ToID = connection->GetNetworkAddress();
	packet.Type = static_cast<BYTE>(message_type);

	bool deletemessage = false;

	if (message)
	{
		
		message->WriteByteAlign();
		message->Rewind();


		// Calculate a hash on this packet
		if (HashMessageTypeFlags[message_type] != NoHash)
		{
			//add a hash..
			
			MD5_CTX resource_context;
			GUID resource_hash;
			unsigned char * buff=(unsigned char *) &resource_hash;
			
			MD5Init(&resource_context);
			
			MD5Update(&resource_context, (unsigned char *) &serverKey, 4);

			if (HashMessageTypeFlags[message_type] == GameSpecificHash)
			{
				int number = Application::GetInstance()->currentGameNumber^0x43215202;
				MD5Update(&resource_context, (unsigned char *) &number, 4);

				//SPEW(("jerryeds", "GAME : %d", number));

				number = Connection::Server->GetLastReplicatorIDUsed();
				MD5Update(&resource_context, (unsigned char *) &number, 4);

				//SPEW(("jerryeds", "ID : %d", number));

			}

			MD5Update(&resource_context, (unsigned char *) Connection::Local->GetConnectionName(), strlen(Connection::Local->GetConnectionName()));
			
			//SPEW(("jerryeds", "NAME : %s", Connection::Local->GetConnectionName()));

			MD5Update(&resource_context, (unsigned char *) message->GetPointer(), message->GetBufferBytesUsed());
			MD5Final(buff, &resource_context);


			WORD part_1 = (WORD)buff[0];
			WORD part_2 = (WORD)buff[2];
			WORD part_3 = (WORD)buff[4];
			WORD part_4 = (WORD)buff[6];
			WORD part_5 = (WORD)buff[8];
			WORD part_6 = (WORD)buff[10];
			WORD part_7 = (WORD)buff[12];
			WORD part_8 = (WORD)buff[14];

			WORD total_crc;
			total_crc = part_1;
			total_crc ^= part_2;
			total_crc ^= part_3;
			total_crc ^= part_4;
			total_crc ^= part_5;
			total_crc ^= part_6;
			total_crc ^= part_7;
			total_crc ^= part_8;

			//SPEW(("jerryeds", "CRC : %d", total_crc));

			deletemessage = true;
			Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connection_id,16);


			BYTE *data = new BYTE[message->GetBufferBytesUsed()+2];
			MemoryStream *new_stream = new MemoryStream(data, message->GetBufferBytesUsed()+2);

			new_stream->WriteBytes(message->GetPointer(), message->GetBufferBytesUsed());
			new_stream->WriteBytes(&total_crc, 2);
			new_stream->Rewind();
			message = new_stream;


		}

		packet.pData = message->GetPointer();
		packet.Length = (WORD)message->GetBufferBytesUsed();

	}
	else
	{
		packet.pData = NULL;
		packet.Length = static_cast<BYTE>(0);
	}



	if (reliable)
	{
		packet.Flags = gosNetFlag_guaranteed;
	}
	else
	{
		packet.Flags = 0;
	}
	
	if (Connection::Local != NULL && connection_id == Connection::Local->GetID())
	{
		// if this message is a local message than just repeat it...
		
		QuedPacket *packet = new QuedPacket(connection_id, message_type, message);
		localPacketQue.Add(packet);

	}
	else if (message != NULL && message->GetBufferBytesUsed() > Network::DefaultPacketSize)
	{
		if (reliable)
		{
			SendBigMessage(connection_id, message_type, message, true);
			return;
		}
		else
		{
			STOP(("Network::SendMessage - Message Larger than max network size and not reliable!"));
		}
	}
	else
	{
		// add gos packet overhead
		Network::GetInstance()->netStatCollector[Network::OutgoingPacketCountStat].AddToStatistic(connection_id,1);
		Network::GetInstance()->netStatCollector[Network::OutgoingProtocolHeaderBitSizeStat].AddToStatistic(connection_id,42*8);
		Network::GetInstance()->netStatCollector[Network::OutgoingTotalPacketSizeStat].AddToStatistic(connection_id,packet.Length*8);

		Adept::g_lastPacketToPlayer[connection_id] = gos_GetElapsedTime();

		#if defined(LAB_ONLY)
			MWGameInfo::g_lastPacketType[MWGameInfo::nextPacketHistoryIndex] = packet.Type;
			MWGameInfo::g_lastPacketSize[MWGameInfo::nextPacketHistoryIndex] = packet.Length;
			MWGameInfo::g_lastPacketInbound[MWGameInfo::nextPacketHistoryIndex] = false;
			MWGameInfo::g_lastPacketTime[MWGameInfo::nextPacketHistoryIndex] = gos_GetElapsedTime();

			if (connection != NULL)
			{
				MWGameInfo::g_lastPacketAddress[MWGameInfo::nextPacketHistoryIndex] = connection->GetID();
			}
			


			++MWGameInfo::nextPacketHistoryIndex;

			if (MWGameInfo::nextPacketHistoryIndex >= MWGameInfo::Packet_History_Count)
				MWGameInfo::nextPacketHistoryIndex = 0;

		#endif


		#if defined(LAB_ONLY)
		
			if (message_type >= FirstDictionaryPageMessageID && message_type <= LastDictionaryPageMessageID)
			{
				if (packet.Length == 0)
					STOP(("INVALID DICTIONARY PAGE LENGTH"));
			}

		#endif

		{
			#ifdef GROUP_ADEPT_NETWORK
			LOG_BLOCK("Game Logic::Network::GOS Send Message");
			#endif

			if (!reliable)
			{
				++packetsSent[connection_id]; 
				packetsDropped[connection_id] += !gos_NetSendMessage(&packet);
			}
			else
			{
				
				gos_NetSendMessage(&packet);
			}
		}
	}
	
	
	if (deletemessage)
	{
		message->Rewind();
		BYTE *data = (BYTE *)message->GetPointer();
		delete[] data;
		delete message;
	}

}


//#############################################################################

void
	Network::SendBigMessage(int connection, int message_type, Stuff::MemoryStream *message, bool reliable)
{
	
	size_t bytes_remaining = message->GetBufferBytesUsed();
	bool first_message = true;

	do
	{
		size_t chunk_size = bytes_remaining;
		Max_Clamp(chunk_size, Network::DefaultPacketSize-2);

		
		if (first_message) // first packet
		{
			chunk_size;
			
			Stuff::DynamicMemoryStream small_stream(chunk_size);
			
			int size = message->GetBufferBytesUsed();


			DWORD crc = 0;
			for (int i = 0; i < message->GetSize(); ++i)
			{
				BYTE data;
				message->ReadBytes(&data, 1);
				crc += data;
			}
			message->Rewind();




			small_stream.WriteBytes(&message_type,1);
			small_stream.WriteBytes(&size, 4);
			small_stream.WriteBytes(&crc, 4);

			small_stream.WriteBytes(message->GetPointer(), chunk_size-7);
			small_stream.Rewind();

			message->AdvancePointer(chunk_size-7);

			first_message = false;
			Verify(bytes_remaining > 0);
			SendNetMessage(connection, BigPacketStartBufferBoxID, &small_stream, true);
			bytes_remaining -= chunk_size-7;
			Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connection,3);


			small_stream.Rewind();
			crc = 0;
			for (i = 0; i < small_stream.GetSize(); ++i)
			{
				BYTE data;
				small_stream.ReadBytes(&data, 1);
				crc += data;
			}
			small_stream.Rewind();
			//SPEW(("jerryeds", "START CRC %x", crc));


			

		}
		else if (bytes_remaining-chunk_size > 0) // if we still have some space left after this chunk
		{
			Stuff::MemoryStream small_stream(message->GetPointer(), chunk_size);
			small_stream.AdvancePointer(chunk_size);
			small_stream.Rewind();
			message->AdvancePointer(chunk_size);
			SendNetMessage(connection, BigPacketPartBufferBoxID, &small_stream, true);
			bytes_remaining -= chunk_size;

			small_stream.Rewind();
			DWORD crc = 0;
			for (int i = 0; i < small_stream.GetSize(); ++i)
			{
				BYTE data;
				small_stream.ReadBytes(&data, 1);
				crc += data;
			}
			small_stream.Rewind();
			//SPEW(("jerryeds", "PART CRC %x", crc));


		}
		else // we are out of pieces so this is the last packet
		{
			Stuff::MemoryStream small_stream(message->GetPointer(), chunk_size);
			small_stream.AdvancePointer(chunk_size);
			small_stream.Rewind();
			message->AdvancePointer(chunk_size);
			SendNetMessage(connection, BigPacketEndBufferBoxID, &small_stream, true);
			bytes_remaining -= chunk_size;

			small_stream.Rewind();
			DWORD crc = 0;
			for (int i = 0; i < small_stream.GetSize(); ++i)
			{
				BYTE data;
				small_stream.ReadBytes(&data, 1);
				crc += data;
			}
			small_stream.Rewind();
			//SPEW(("jerryeds", "END CRC %x", crc));
		}
	}
	while (bytes_remaining > 0);

}
