/*
	Lan Wars.
	Copyright (c) 2009, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//=====================================================================================================================
/// @brief		This helper class handles sending and receiving of data for the server.
///				It is contained within the GameSession class itself.
//=====================================================================================================================

#include "Precompiled.h"
#include "Game/GameSessionDirector_Server.h"
#include "Game/GameSession.h"
#include "Game/GameSession.h"
#include "Game/GameCore.h"
#include "Game/GameObjectManager.h"
#include "Game/GamePacketCenter.h"
#include "Game/GamePacket.h"
#include "Game/GamePeer.h"
#include "Graphics/Camera3D.h"
#include "GameObjects/GameObjectBase.h"
#include "GameObjects/GameMap.h"
#include "GameObjects/GamePlayer.h"
#include "Doom/DoomLevel.h"
#include "Doom/DoomCollision.h"
#include "Doom/DoomPPNode.h"

using namespace Game;

//=====================================================================================================================
// Constants
//=====================================================================================================================

//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
/// During area of interest management we check if one sector in the level is visible from another
/// using doom's reject map. This is done by getting the sector our client is in, and the sector the 
/// other client is in, and then doing the reject lookup. In addition to this however, we add some 
/// lienency to the algorithm by checking sectors within a certain radius of the object we are determining
/// is in scope. This is that radius:
//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

const float GameSessionDirector_Server::AOI_RADIUS = 32;

//=====================================================================================================================
/// @brief		Creates this helper object
/// @param		session - session we belong to
/// @pre		We must be connected
/// @pre		We must be a server connection
//=====================================================================================================================

GameSessionDirector_Server::GameSessionDirector_Server( GameSession & session ) : m_session(session)
{
	// Make sure ok:
	
	ENSURE( m_session.IsConnected()	, "We must be connected"			)
	ENSURE( m_session.IsServer()	, "We must be a server connection"	)

	// We are a server: start the game
	
	InitialiseWorld();
}				

//=====================================================================================================================
/// @brief		Does an update of the server session
/// @pre		The server must be connected
//=====================================================================================================================

void GameSessionDirector_Server::Update()
{
	// Make sure ok
	
	ENSURE( m_session.IsConnected() , "The server must be connected" )
	
	// See how many packets are incoming:
	
	unsigned received_packets = m_session.m_packet_center->GetIncomingCount();
	
	// Only if we got packets:
	
	if ( received_packets > 0 )
	{
		// Makeup an array for all the packets:
		
		std::vector<GamePacket> incoming(received_packets);	
		
		// Read in all the packets:
		
		for ( unsigned i = 0 ; i < received_packets ; ++i ) m_session.m_packet_center->ReadPacket(incoming[i]);
		
		// Sort all the packets:
		
		std::sort(incoming.begin(),incoming.end());
		
		// This is the address of the client we are currently processing:
		
		NetworkAddress current_client = NetworkAddress::InvalidAddress();
		
		// This is the packet to send out to the current client:
		
		GamePacket current_packet;
		
		// Run through all the incoming packets:
		
		for ( unsigned i = 0 ; i < incoming.size() ; ++i )
		{
			// See if this client has not been processed in this loop yet:
			
			if ( incoming[i].GetAddress() != current_client )
			{
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				// Dispatch the current packet to the current address: done with that client. 
				// This will also re-clear the packet when done, as well as sending other data
				// such as create/delete messages and other remote object's data to the client.
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				
				DispatchPacket(current_packet,current_client);
				
				// Save the current client
				
				current_client = incoming[i].GetAddress();
			}
			
			// Begin reading the packet from the client
			
			Receive( incoming[i] , current_packet );

			// If this is a client packet then reset the timeout:

			if ( m_session.IsClientAddress( incoming[i].GetAddress() ) )
			{
				// Get this client: reset the time since the last receive

				m_session.GetSendingClient(incoming[i].GetAddress())->SetLastReceiveTime();
			}
		}
		
		//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
		// Dispatch the current packet to the current address: done with that client. 
		// This will also re-clear the packet when done, as well as sending other data
		// such as create/delete messages and other remote object's data to the client.
		//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
		
		DispatchPacket(current_packet,current_client);
	}
}

//=====================================================================================================================
/// @brief		Initialises the game world
/// @pre		We must be connected
/// @pre		The game core object must exist
/// @pre		The game object manager must exist
//=====================================================================================================================

void GameSessionDirector_Server::InitialiseWorld()
{	
	// Make sure ok:
	
	ENSURE( m_session.IsConnected()								, "We must be connected"				)
	ENSURE( GameCore::GetSingleton() != 0						, "The game core object must exist"		)
	ENSURE( GameCore::GetSingleton()->GetObjectManager() != 0	, "The game object manager must exist"	)
	
	// Get this object:
	
	GameObjectManager * m = GameCore::GetSingleton()->GetObjectManager();
	
	// Make sure the level is cleared:
	
	m->Clear();

	// Make the map:
	
	{
		// Make the map:
	
		GameMap * map = new GameMap();
		
		// Add to the object manager:
		
		m->AddObject(map);
		
		// Done:
		
		map->RemoveReference();
	}
	
	// Pick a start point for the camera:
	
	GameCore::GetSingleton()->GetCamera()->SetPosition( GameMap::GetSingleton().PickStartPosition() );	
	
	// Send the level ready message:
	
	m->SendGlobalEvent( GameObjectManager::GOM_LEVEL_READY , 0 );	
}

//=====================================================================================================================
/// @brief		Dispatches a packet to the given client if the packet is valid and if the client is valid. 
///				Clears the packet aftewords also so it can be reused.
/// @param		packet - packet to dispatch
/// @param		client - client to dispatch the packet to
//=====================================================================================================================

void GameSessionDirector_Server::DispatchPacket( GamePacket & packet , const NetworkAddress & client )
{
	// Only if the client is valid:
	
	if ( client.IsValid() )
	{
		// Only if we are still connected:
		
		if ( m_session.IsConnected() )
		{
			// Set the destination address of the packet:
			
			packet.SetAddress(client);

			// See if this is a client address:

			if ( m_session.IsClientAddress(client) )
			{
				// Client. Get the client peer

				GamePeer & client_peer = * m_session.GetSendingClient(client);

				// Write all pending create object requests to the client

				client_peer.SendCreateRequests(packet);
				
				// Send object data to the client
				
				SendWorldData(packet,client_peer);

				// Write all pending delete object requests to the client

				client_peer.SendDeleteRequests(packet);
					
				// Do not send if the packet is empty:
				
				if ( packet.GetBitCount() > 0 )
				{
					// Terminate the packet to the client:
					
					packet.WriteChunkId(GamePacketChunk::PKCKID_END);

					// Do the dispatch:
					
					m_session.m_packet_center->WritePacket(packet);

					// Reset the time since we last sent a packet to this client:

					client_peer.SetLastSendTime();
				}
			}
			else
			{
				// Not a client packet: do not send if we have nothing written to it
				
				if ( packet.GetBitCount() >= 1 )
				{			
					// Terminate the packet to the client:
					
					packet.WriteChunkId(GamePacketChunk::PKCKID_END);

					// Non-client. Just do the dispatch:
					
					m_session.m_packet_center->WritePacket(packet);
				}
			}
		}
	}
	
	// Clear the packet:
	
	packet.Clear();
}

//=====================================================================================================================
/// @brief		Sends world data to the given client. This includes data for it's own object as well as data for
///				any other objects in the world that are in scope to the client.
/// @param		packet - packet to write to
/// @param		client - client we are dealing with
/// @pre		The GameCore object must exist
/// @pre		The GameCore object must have a GameObjectManager
/// @todo		Only send updates for remote objects that are in scope
//=====================================================================================================================

void GameSessionDirector_Server::SendWorldData( GamePacket & packet , GamePeer & client )
{
	// Make sure ok:
	
	ENSURE( GameCore::GetSingleton()						, "The GameCore object must exist"						)
	ENSURE( GameCore::GetSingleton()->GetObjectManager()	, "The GameCore object must have a GameObjectManager"	)
	
	// If this is true then we perform area of interest management: if for some reason the client has no control object then don't do aoi
	
	bool do_aoi_management = false;
	
	// If this is true then we send data to the client:
	
	bool do_send = false;
	
	// If we are to do aoi management then this is the position of the client's object:
	
	Vec3f client_pos = Vec3f::Zero();
	
	// This is the doom level we use to do aoi
	
	const DoomLevel * level = 0;
	
	// See if we are to send:
	
	if ( GetTickCount() - client.GetLastSendTime() > GameSession::GetSingleton().GetSettings().m_server_cpg_interval )
	{
		// Time to send an update
		
		do_send = true;
	}
	
	//-----------------------------------------------------------------------------------------------------------------
	// If we are sending the client remote objects then send a packet to signifiy this so the client can clear scopes
	// on all objects and only set scope for objects which it has received data for
	//-----------------------------------------------------------------------------------------------------------------
	
	if ( do_send ) packet.WriteChunkId(GamePacketChunk::PKCKID_REMOTE_UPDATE);

	//-----------------------------------------------------------------------------------------------------------------
	// See if the client has a control object: if so then send an update and do AOI management
	//-----------------------------------------------------------------------------------------------------------------
	
	if ( do_send && client.GetControlObject() )
	{
		// Send data for it's control object
		
		packet.WriteChunkId(GamePacketChunk::PKCKID_GAME_DATA);
		packet.WritePackedUint(client.GetControlObject()->GetNetId());
		
		// Send:
		
		client.GetControlObject()->NetSend(packet,NetObject::SEND_CONTEXT_SERVER_TO_CLIENT);
		
		// Do aoi management if client is controlling a positional object:
		
		if ( dynamic_cast<PositionalObject*>(client.GetControlObject()) != 0 )
		{		
			// Only if we can find the doom map:
			
			GameMap * map = GameCore::GetSingleton()->GetObjectManager()->FindByType<GameMap>("GameMap");
			
			// See if there:
			
			if ( map )
			{	
				// Do area of interest management:
				
				do_aoi_management	= true;
				client_pos			= ((PositionalObject*)(client.GetControlObject()))->GetPosition();
				level				= &map->GetLevel();
			}
		}
	}
	
	//-----------------------------------------------------------------------------------------------------------------
	// Send data for all other objects:
	//-----------------------------------------------------------------------------------------------------------------
	
	std::vector<GameObjectBase*> net_objects; GameCore::GetSingleton()->GetObjectManager()->GetNetObjects(net_objects);
	
	// Only if we are doing a remote object update:
	
	if ( do_send )
	{
		// Do the send for all the remote objects:

		for ( unsigned i = 0 ; i < net_objects.size() ; ++i )
		{
			// Get this net object
			
			GameObjectBase * object = net_objects[i];
			
			// This should never happen:
			
			ENSURE( object != 0 , "Null net object given by the GameObjectManager" )
			
			// Only send if not the player's object, and if a valid net object
			
			if ( object->IsNetworkedObject()	== false						) continue;
			if ( object->GetOwnerPeerId()		== client.GetPeerId()			) continue;
			if ( object->GetNetId()				== NetObject::NET_ID_INVALID	) continue;
			
			// If this is positional object then we need to do aoi for this object:
			
			bool in_scope = true;
			
			if ( dynamic_cast<PositionalObject*>(object) != 0 )
			{
				// Positional object: see if we are to do aoi
				
				if ( do_aoi_management )
				{
					in_scope = IsObjectInAreaOfInterest(*level,client_pos,((PositionalObject*)(object))->GetPosition());
				}
			}
			
			// If the object is not in scope then do not do AOI
			
			if ( in_scope == false ) 
			{
				continue;
			}

			// Send data for it's control object
			
			packet.WriteChunkId(GamePacketChunk::PKCKID_GAME_DATA);
			packet.WritePackedUint(object->GetNetId());
			
			// Do the send:
			
			bool sent = object->NetSend(packet,NetObject::SEND_CONTEXT_REMOTE_TO_CLIENT);
			
			// See if anything was sent:
			
			if ( sent == false )
			{
				// Rewind the packet position: object did not send anything
				
				packet.RewindBits(GamePacketChunk::CHUNK_ID_BITS);
				
				// Need to figure out how many bits the id uses:
				
				unsigned highest_bit = 0;
				
				// Find the highest bit of the id:
				
				for ( unsigned i = 0 ; i < 32 ; ++i )
				{
					if ( (object->GetNetId() & ( 1 << i )) != 0 ) highest_bit = i;
				}
				
				// Rewind by the number of bits for the packed uint:
				
				packet.RewindBits( 5 + ( highest_bit + 1 ) );
			}
		}
	}
}

//=====================================================================================================================
/// @brief		Does all receiving for the server
/// @param		in - incoming packet
/// @param		out - outgoing packet, in case we need to write acknowledgements
/// @pre		We must be connected
//=====================================================================================================================

void GameSessionDirector_Server::Receive( GamePacket & in , GamePacket & out )
{
	// Make sure ok
	
	ENSURE( m_session.IsConnected() , "We must be connected" )
	
	// Continue reading the packet until we reach the end of the packet:
	
	bool continue_reading = true;
	
	// Keep reading the packet:
	
	while ( continue_reading )
	{
		// Read the id of the next chunk:
		
		GamePacketChunk::ChunkID id = GamePacketChunk::PKCKID_INVALID;
		
		// Do the read: if it failed then abort
		
		if ( in.ReadChunkId(id) == false )
		{	
			continue_reading = false; continue;
		}
		
		// See what the chunk id is:
		
		switch ( id )
		{				
			// If reading of object data from the packet goes wrong then discard the packet contents
			
			case GamePacketChunk::PKCKID_DISCONNECT:	continue_reading = HandlePacketChunk_Disconnect(in);				break;
			case GamePacketChunk::PKCKID_DELETE_ACK:	continue_reading = HandlePacketChunk_DeleteAcknowledgement(in);		break;
			case GamePacketChunk::PKCKID_CREATE_ACK:	continue_reading = HandlePacketChunk_CreateAcknowledgement(in);		break;
			case GamePacketChunk::PKCKID_JOIN:			continue_reading = HandlePacketChunk_JoinRequest(in,out);			break;
			case GamePacketChunk::PKCKID_GAME_DATA:		continue_reading = HandlePacketChunk_ObjectData(in);				break;
		
			// Unknown / Unsupported / Terminating chunk. Stop reading.
		
			default: continue_reading = false;
		}
		
		// If we are no longer connected then do no more reading:
		
		if ( m_session.IsConnected() == false ) continue_reading = false;
	}
}

//=====================================================================================================================
/// @brief		Creates the control object for a given client
/// @param		client - client we are creating a control object for
/// @pre		The client must have no control object currently
/// @pre		We must be connected
/// @pre		The map must exist in the game world
/// @pre		The GameCore object must exist
/// @pre		The GameCore object must have a GameObjectManager
//=====================================================================================================================

void GameSessionDirector_Server::CreateClientControlObject( GamePeer & client )
{
	// Make sure ok:
	
	ENSURE( m_session.IsConnected()							, "We must be connected"								)
	ENSURE( client.GetControlObject() == 0					, "The client must have no control object currently"	)
	ENSURE( GameCore::GetSingleton()						, "The GameCore object must exist"						)
	ENSURE( GameCore::GetSingleton()->GetObjectManager()	, "The GameCore object must have a GameObjectManager"	)
	
	// Get the game object manager:
	
	GameObjectManager & mgr = * GameCore::GetSingleton()->GetObjectManager();
	
	// Get the map
	
	GameMap * map = mgr.FindByType<GameMap>("GameMap");
	
	// This should exist:
	
	ENSURE( map != 0 , "The map must exist in the game world" )
	
	// Pick a spawn position for the player
	
	Vec3f spawn_point = map->PickStartPosition();
	
	// Create a game player object:
	
	GamePlayer * player = new GamePlayer( client.GetPeerId() , spawn_point , 0 , 0 );
	
	// Add to the game:
	
	mgr.AddObject(player);
	
	// Set as the control object for the client:
	
	client.SetControlObject(player);
	
	// Done with this
	
	player->RemoveReference();
}

//=====================================================================================================================
/// @brief		Handles a disconnect packet chunk from the client
/// @param		in - incoming packet
/// @returns	True if the packet was handled successfully
//=====================================================================================================================

bool GameSessionDirector_Server::HandlePacketChunk_Disconnect( GamePacket & in )
{
	// See if this is froma client that is connected:
	
	if ( m_session.IsClientAddress( in.GetAddress() ) )
	{
		// Remove this peer from the game:
		
		m_session.RemoveClient( * m_session.GetSendingClient(in.GetAddress()) );
		
		// This message was processed ok
		
		return true;
	}
	else
	{
		// Not from a regonised client: return false
		
		return false;
	}
}

//=====================================================================================================================
/// @brief		Handles a join packet chunk from the client
/// @param		in - incoming packet
/// @param		out - outgoing packet for sending repsonse
/// @returns	True if the packet was handled successfully
//=====================================================================================================================

bool GameSessionDirector_Server::HandlePacketChunk_JoinRequest( GamePacket & in , GamePacket & out )
{
	// See if the client is already connected:
	
	if ( m_session.IsClientAddress( in.GetAddress() ) )
	{
		// Get the sending client:
		
		GamePeer * sender = m_session.GetSendingClient(in.GetAddress());
	
		// Client address: already connected. Just send another ack in case it didn't get through.
		
		out.WriteChunkId(GamePacketChunk::PKCKID_JOIN_ACK);
		out.WritePackedUint(sender->GetPeerId());
	}
	else
	{
		// Not a client address: we need to make a new client if the client list is not full
		
		if ( m_session.m_peers.size() < GameSession::MAX_CLIENTS )
		{
			// Add the client:

			GamePeer * client = m_session.AddClient(in.GetAddress());
			
			// This should always be the case:
			
			ENSURE( client != 0 , "Session did not create a client object" )
			
			// Send an acknowledgement to this client that they can join
			
			out.WriteChunkId(GamePacketChunk::PKCKID_JOIN_ACK);
			out.WritePackedUint(client->GetPeerId());
			
			// Make this peer's control object:
			
			CreateClientControlObject(*client);
		}
		else
		{
			// Can't accomodate this client. Send a connection refused message.
			
			out.WriteChunkId(GamePacketChunk::PKCKID_JOIN_REFUSE);
		}
	}
	
	// This message always get's processed ok
	
	return true;
}

//=====================================================================================================================
/// @brief		Handles a game object data packet chunk from the client
/// @param		in - incoming packet
/// @returns	True if the packet was handled successfully
//=====================================================================================================================

bool GameSessionDirector_Server::HandlePacketChunk_ObjectData( GamePacket & in )
{
	// Only if this is from a recognised client address:
	
	if ( m_session.IsClientAddress(in.GetAddress()) )
	{
		// Ok: read the id of the net object the packet is for
		
		unsigned net_id = 0; 

		// Do it:
		
		if ( in.ReadPackedUint(net_id) )
		{
			// If this is a non-networked object or object with an invalid net id then ignore:
			
			if ( net_id == NetObject::NET_ID_INVALID || net_id == NetObject::NET_ID_NONE )
			{
				return false;
			}
		
			// Get this client
			
			GamePeer & client = * m_session.GetSendingClient(in.GetAddress());
		
			// Get the client's control object:
			
			GameObjectBase * control_object = client.GetControlObject();
			
			// Only if the client has a control object:
			
			if ( control_object )
			{
				// Make sure the net id of the packet matches the net id of the control object here: the client can't change other peoples objects!
				
				if ( net_id == control_object->GetNetId() )
				{
					// Allow the control object to read it's data from the client:
					
					return control_object->NetReceive(in,NetObject::RECV_CONTEXT_CLIENT_TO_SERVER);
				}
				else
				{
					// Client trying to send updates for an object it does not own: fail
					
					return false;
				}
			}
			else
			{
				// Client has no control object: can't be sending data
				
				return false;
			}
		}
		else
		{
			// Fail: couldn't read the net id of the object
			
			return false;
		}	
	}
	else
	{
		// Fail: not from one of our clients
		
		return false;
	}
}

//=====================================================================================================================
/// @brief		Handles a create message acknowledgement from the client
/// @param		in - incoming packet
/// @returns	True if the packet was handled successfully
//=====================================================================================================================

bool GameSessionDirector_Server::HandlePacketChunk_CreateAcknowledgement( GamePacket & in )
{
	// Read the net id of which object this is for:
	
	unsigned net_id = 0;
	
	// Do it:
	
	if ( in.ReadPackedUint(net_id) )
	{
		// If this is a non-networked object or object with an invalid net id then ignore:
		
		if ( net_id == NetObject::NET_ID_INVALID || net_id == NetObject::NET_ID_NONE )
		{
			return false;
		}	
	
		// Only if this is from a client we regonize:

		if ( m_session.IsClientAddress(in.GetAddress()) )
		{
			// Get the client peer:

			GamePeer & client = * m_session.GetSendingClient(in.GetAddress());

			// This request was received ok: remove it from re-transmission

			client.AcknowledgeCreateRequest(net_id);

			// Success:
			
			return true;
		}
		else
		{
			// Not from a client we recongize: junk the packet

			return false;
		}
	}
	else
	{
		// Failed: return false
		
		return false;
	}
}

//=====================================================================================================================
/// @brief		Handles a delete message acknowledgement from the client
/// @param		in - incoming packet
/// @returns	True if the packet was handled successfully
/// @todo		Handle delete acknowledgements on the server.
//=====================================================================================================================

bool GameSessionDirector_Server::HandlePacketChunk_DeleteAcknowledgement( GamePacket & in )
{
	// Read the net id of which object this is for:
	
	unsigned net_id = 0;
	
	// Do it:
	
	if ( in.ReadPackedUint(net_id) )
	{
		// If this is a non-networked object or object with an invalid net id then ignore:
		
		if ( net_id == NetObject::NET_ID_INVALID || net_id == NetObject::NET_ID_NONE )
		{
			return false;
		}	
	
		// Only if this is from a client we regonize:

		if ( m_session.IsClientAddress(in.GetAddress()) )
		{
			// Get the client peer:

			GamePeer & client = * m_session.GetSendingClient(in.GetAddress());

			// This request was received ok: remove it from re-transmission

			client.AcknowledgeDeleteRequest(net_id);

			// Success:
			
			return true;
		}
		else
		{
			// Not from a client we recongize: junk the packet

			return false;
		}
	}
	else
	{
		// Failed: return false
		
		return false;
	}
}

//=====================================================================================================================
/// @brief		Perfoms an area of interest check against the two points on the map
/// @param		level - doom level we are using for the aoi check
/// @param		client_pos - position of the client in the world
/// @param		obj_pos - position of the object in the world
/// @returns	True if the given object position is within our area of interest.
//=====================================================================================================================

bool GameSessionDirector_Server::IsObjectInAreaOfInterest( const DoomLevel & level , const Vec3f & client_pos , const Vec3f & obj_pos ) const
{
	// Get the root node in the BSP tree of the level:
	
	const DoomPPNode & node = level.GetRootNode();
	
	// Perform this check recursively and return the result:
	
	return AoiCheckRecursive(level,node,client_pos,obj_pos);
}

//=====================================================================================================================
/// @brief		Recursively performs area of interest checks by going through the BSP tree in the level
/// @param		level - level we are checking against
/// @param		node - node we are checking against
/// @param		client_pos - position of the client
/// @param		obj_pos - position of the object
/// @returns	True if the object position is visible from the client position
//=====================================================================================================================

bool GameSessionDirector_Server::AoiCheckRecursive( const DoomLevel & level , const DoomPPNode & node , const Vec3f & client_pos , const Vec3f & obj_pos ) const
{
	// Make sure this is ok:

	ENSURE( level.GetSubsector(obj_pos)	, "Object position not in a subsector"	)

	// If true then the object is visible:
	
	bool visible = false;

	// Get the distance to the node's plane:
	
	float dist = node.m_normal_3d.Dot(client_pos) + node.m_minus_n_dot_p;
	
	// See what side of the plane we are on:
	
	if ( dist >= 0 )
	{
		//-------------------------------------------------------------------------------------------------------------
		// On the right side of the plane
		//-------------------------------------------------------------------------------------------------------------
		
		// See if we are close enough to check the left side also:
		
		if ( dist <= AOI_RADIUS )
		{
			// See if we have a left child node:
			
			if ( node.m_left_child )
			{
				// This should never be the case:
				
				ENSURE( node.m_left_subsector == 0 , "Invalid BSP tree node" )			
			
				// Have a left child: recursively go down through the tree
				
				visible = visible || AoiCheckRecursive( level , * node.m_left_child , client_pos , obj_pos );
			}
			else
			{
				// This should never be the case:
				
				ENSURE( node.m_left_subsector , "Invalid BSP tree node" )
			
				// No left child: get the sector that the left subsector is in
				
				const DoomSector & sector1 = level.GetSector(*node.m_left_subsector);
				const DoomSector & sector2 = level.GetSector(*level.GetSubsector(obj_pos));
				
				// See if visible from each other:
				
				visible = visible || level.GetCollision().IsPotentiallyVisible(sector1,sector2);
			}
		}
		
		// See if we have a right child node:
		
		if ( node.m_right_child )
		{
			// This should never be the case:
			
			ENSURE( node.m_right_subsector == 0	, "Invalid BSP tree node" )
			
			// Have a right child: recursively go down through the tree
			
			visible = visible || AoiCheckRecursive( level , * node.m_right_child , client_pos , obj_pos );
		}
		else
		{
			// This should never be the case:
			
			ENSURE( node.m_right_subsector , "Invalid BSP tree node" )
		
			// No left child: get the sector that the right subsector is in
			
			const DoomSector & sector1 = level.GetSector(*node.m_right_subsector);
			const DoomSector & sector2 = level.GetSector(*level.GetSubsector(obj_pos));
			
			// See if visible from each other:
			
			visible = visible || level.GetCollision().IsPotentiallyVisible(sector1,sector2);			
		}
	}
	else
	{
		//-------------------------------------------------------------------------------------------------------------
		// On the left side of the plane
		//-------------------------------------------------------------------------------------------------------------
		
		// See if we are close enough to check the right side also:
		
		if ( dist >= -AOI_RADIUS )
		{		
			// See if we have a right child node:
			
			if ( node.m_right_child )
			{
				// This should never be the case:
				
				ENSURE( node.m_right_subsector == 0	, "Invalid BSP tree node" )
				
				// Have a right child: recursively go down through the tree
				
				visible = visible || AoiCheckRecursive( level , * node.m_right_child , client_pos , obj_pos );
			}
			else
			{
				// This should never be the case:
				
				ENSURE( node.m_right_subsector , "Invalid BSP tree node" )
			
				// No left child: get the sector that the right subsector is in
				
				const DoomSector & sector1 = level.GetSector(*node.m_right_subsector);
				const DoomSector & sector2 = level.GetSector(*level.GetSubsector(obj_pos));
				
				// See if visible from each other:
				
				visible = visible || level.GetCollision().IsPotentiallyVisible(sector1,sector2);			
			}		
		}
		
		// See if we have a left child node:
		
		if ( node.m_left_child )
		{
			// This should never be the case:
			
			ENSURE( node.m_left_subsector == 0 , "Invalid BSP tree node" )
		
			// Have a left child: recursively go down through the tree
			
			visible = visible || AoiCheckRecursive( level , * node.m_left_child , client_pos , obj_pos );
		}
		else
		{
			// This should never be the case:
			
			ENSURE( node.m_left_subsector , "Invalid BSP tree node" )
		
			// No left child: get the sector that the left subsector is in
			
			const DoomSector & sector1 = level.GetSector(*node.m_left_subsector);
			const DoomSector & sector2 = level.GetSector(*level.GetSubsector(obj_pos));
			
			// See if visible from each other:
			
			visible = visible || level.GetCollision().IsPotentiallyVisible(sector1,sector2);
		}			
	}
	
	// Return if visible
	
	return visible;
}
