/*
 *  Original work: Copyright (c) 2014, Oculus VR, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  RakNet License.txt file in the licenses directory of this source tree. An additional grant 
 *  of patent rights can be found in the RakNet Patents.txt file in the same directory.
 *
 *
 *  Modified work: Copyright (c) 2017, SLikeSoft UG (haftungsbeschränkt)
 *
 *  This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
 *  license found in the license.txt file in the root directory of this source tree.
 */

#include "Lobby2Server.h"
#include "slikenet/assert.h"
#include "slikenet/MessageIdentifiers.h"

//#define __INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN

#ifdef __INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN
#include "RoomsPlugin.h"
#endif

using namespace SLNet;

int Lobby2Server::UserCompByUsername( const RakString &key, Lobby2Server::User * const &data )
{
	if (key < data->userName)
		return -1;
	if (key==data->userName)
		return 0;
	return 1;
}

Lobby2Server::Lobby2Server()
{
	DataStructures::OrderedList<SystemAddress, SystemAddress>::IMPLEMENT_DEFAULT_COMPARISON();
	DataStructures::OrderedList<RakString, RakString>::IMPLEMENT_DEFAULT_COMPARISON();
	roomsPlugin=0;
	roomsPluginAddress=UNASSIGNED_SYSTEM_ADDRESS;
}
Lobby2Server::~Lobby2Server()
{
	Clear();
}
void Lobby2Server::SendMsg(Lobby2Message *msg, const DataStructures::List<SystemAddress> &recipients)
{
	SLNet::BitStream bs;
	bs.Write((MessageID)ID_LOBBY2_SEND_MESSAGE);
	bs.Write((MessageID)msg->GetID());
	msg->Serialize(true,true,&bs);
	SendUnifiedToMultiple(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, recipients);
}
void Lobby2Server::Update(void)
{
	while (threadActionQueue.Size())
	{
		threadActionQueueMutex.Lock();
		if (threadActionQueue.Size())
		{
			ThreadAction ta = threadActionQueue.Pop();
			threadActionQueueMutex.Unlock();
			if (ta.action==L2MID_Client_Logoff)
			{
				OnLogoff(&ta.command, false);
			}
			else if (ta.action==L2MID_Client_Login)
			{
				OnLogin(&ta.command, false);
			}
			else if (ta.action==L2MID_Client_ChangeHandle)
			{
				OnChangeHandle(&ta.command, false);
			}
		}
		else
		{
			threadActionQueueMutex.Unlock();
			break;
		}
	}

	if (threadPool.HasOutputFast() && threadPool.HasOutput())
	{
		Lobby2ServerCommand c = threadPool.GetOutput();
		c.lobby2Message->ServerPostDBMemoryImpl(this, c.callingUserName);
		if (c.returnToSender)
		{
			for (unsigned long i=0; i < callbacks.Size(); i++)
			{
				if (c.lobby2Message->callbackId==(uint32_t)-1 || c.lobby2Message->callbackId==callbacks[i]->callbackId)
					c.lobby2Message->CallCallback(callbacks[i]);
			}


			SLNet::BitStream bs;
			bs.Write((MessageID)ID_LOBBY2_SEND_MESSAGE);
			bs.Write((MessageID)c.lobby2Message->GetID());
			c.lobby2Message->Serialize(true,true,&bs);
			// Have the ID to send to, but not the address. The ID came from the thread, such as notifying another user
			if (c.callerSystemAddresses.Size()==0)
			{
				unsigned int i;
				if (c.callerUserId!=0)
				{
					for (i=0; i < users.Size(); i++)
					{
						if (users[i]->callerUserId==c.callerUserId)
						{
							c.callerSystemAddresses=users[i]->systemAddresses;
							c.callerGuids=users[i]->guids;		

							/*
							if (c.requiredConnectionAddress!=UNASSIGNED_SYSTEM_ADDRESS)
							{
								// This message refers to another user that has to be logged on for it to be sent
								bool objectExists;
								unsigned int idx;
								idx = users.GetIndexFromKey(c.callerSystemAddress,&objectExists);
								if (objectExists==false)
								{
									if (c.deallocMsgWhenDone)
										SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
									return;
								}
							}
							*/
							break;
						}
					}
				}
				if (c.callerSystemAddresses.Size()==0 && c.callingUserName.IsEmpty()==false)
				{
					for (i=0; i < users.Size(); i++)
					{
						if (users[i]->callerUserId==c.callerUserId)
						{
							c.callerSystemAddresses=users[i]->systemAddresses;
							c.callerGuids=users[i]->guids;
							break;
						}
					}
				}
			}
			else
			{
				bool objectExists;
				unsigned int idx;
				idx = users.GetIndexFromKey(c.callingUserName,&objectExists);
				if (objectExists && 
					c.callingUserName.IsEmpty()==false &&
					users[idx]->userName!=c.callingUserName)
				{
					// Different user, same IP address. Abort the send.
					if (c.deallocMsgWhenDone)
						SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
					return;
				}
			}
			SendUnifiedToMultiple(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, c.callerSystemAddresses);
		}
		if (c.deallocMsgWhenDone)
			SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
	}
}
PluginReceiveResult Lobby2Server::OnReceive(Packet *packet)
{
	RakAssert(packet);

	switch (packet->data[0]) 
	{
	case ID_LOBBY2_SEND_MESSAGE:
		OnMessage(packet);
		return RR_STOP_PROCESSING_AND_DEALLOCATE;
	}

	return RR_CONTINUE_PROCESSING;
}
void Lobby2Server::OnShutdown(void)
{
	Clear();
}
void Lobby2Server::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
{
	(void)rakNetGUID;
	(void)lostConnectionReason;

	unsigned int index = GetUserIndexBySystemAddress(systemAddress);

	// If systemAddress is a user, then notify his friends about him logging off
	if (index!=-1)
	{
		bool found=false;
		User *user = users[index];
		for (unsigned int i=0; i < user->systemAddresses.Size(); i++)
		{
			if (user->systemAddresses[i]==systemAddress)
			{
				found=true;
				user->systemAddresses.RemoveAtIndexFast(i);
				break;
			}
		}

		if (found && user->systemAddresses.Size()==0)
		{
			// Log this logoff due to closed connection
			Lobby2Message *lobby2Message = msgFactory->Alloc(L2MID_Client_Logoff);
			Lobby2ServerCommand command;
			command.lobby2Message=lobby2Message;
			command.deallocMsgWhenDone=true;
			command.returnToSender=true;
			command.callerUserId=users[index]->callerUserId;
			command.server=this;
			ExecuteCommand(&command);

			RemoveUser(index);
		}		
	}
}
void Lobby2Server::OnMessage(Packet *packet)
{
	SLNet::BitStream bs(packet->data,packet->length,false);
	bs.IgnoreBytes(1); // ID_LOBBY2_SEND_MESSAGE
	MessageID msgId;
	bs.Read(msgId);
	Lobby2MessageID lobby2MessageID = (Lobby2MessageID) msgId;
	unsigned int index;
	Lobby2Message *lobby2Message = msgFactory->Alloc(lobby2MessageID);
	if (lobby2Message)
	{
		lobby2Message->Serialize(false,false,&bs);
		Lobby2ServerCommand command;
		command.lobby2Message=lobby2Message;
		command.deallocMsgWhenDone=true;
		command.returnToSender=true;
		index=GetUserIndexBySystemAddress(packet->systemAddress);
		if (index!=-1)
		{
			command.callingUserName=users[index]->userName;
			command.callerUserId=users[index]->callerUserId;
		}
		else
		{
			if (lobby2Message->RequiresLogin())
			{
				SLNet::BitStream bs2;
				bs2.Write((MessageID)ID_LOBBY2_SEND_MESSAGE);
				bs2.Write((MessageID)lobby2Message->GetID());
				lobby2Message->resultCode=L2RC_NOT_LOGGED_IN;
				lobby2Message->Serialize(true,true,&bs2);
				SendUnified(&bs2,packetPriority, RELIABLE_ORDERED, orderingChannel, packet->systemAddress, false);
				return;
			}
			command.callerUserId=0;
		}
		command.callerSystemAddresses.Push(packet->systemAddress,__FILE__,__LINE__);
		command.callerGuids.Push(packet->guid,__FILE__,__LINE__);
		command.server=this;
		ExecuteCommand(&command);
	}
	else
	{
		SLNet::BitStream out;
		out.Write((MessageID)ID_LOBBY2_SERVER_ERROR);
		out.Write((unsigned char) L2SE_UNKNOWN_MESSAGE_ID);
		out.Write((unsigned int) msgId);
		SendUnified(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, packet->systemAddress, false);
	}
}
void Lobby2Server::Clear(void)
{
	ClearAdminAddresses();
	ClearRankingAddresses();
	ClearUsers();
	ClearConnections();

	threadPool.StopThreads();
	RakAssert(threadPool.NumThreadsWorking()==0);

	unsigned i;
	Lobby2ServerCommand c;
	for (i=0; i < threadPool.InputSize(); i++)
	{
		c = threadPool.GetInputAtIndex(i);
		if (c.deallocMsgWhenDone && c.lobby2Message)
			SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
	}
	threadPool.ClearInput();
	for (i=0; i < threadPool.OutputSize(); i++)
	{
		c = threadPool.GetOutputAtIndex(i);
		if (c.deallocMsgWhenDone && c.lobby2Message)
			SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
	}
	threadPool.ClearOutput();

	threadActionQueueMutex.Lock();
	threadActionQueue.Clear(__FILE__, __LINE__);
	threadActionQueueMutex.Unlock();
}
void Lobby2Server::AddAdminAddress(SystemAddress addr)
{
	adminAddresses.Insert(addr,addr,false, __FILE__, __LINE__ );
}
bool Lobby2Server::HasAdminAddress(const DataStructures::List<SystemAddress> &addresses)
{
	if (addresses.Size()==0)
		return true;

	unsigned int j;
	for (j=0; j < addresses.Size(); j++)
	{
		if (adminAddresses.HasData(addresses[j]))
			return true;
	}
	return false;
}
void Lobby2Server::RemoveAdminAddress(SystemAddress addr)
{
	adminAddresses.RemoveIfExists(addr);
}
void Lobby2Server::ClearAdminAddresses(void)
{
	adminAddresses.Clear(false, __FILE__, __LINE__);
}
void Lobby2Server::AddRankingAddress(SystemAddress addr)
{
	rankingAddresses.Insert(addr,addr,false, __FILE__, __LINE__ );
}
bool Lobby2Server::HasRankingAddress(const DataStructures::List<SystemAddress> &addresses)
{
	if (addresses.Size()==0)
		return true;

	unsigned int j;
	for (j=0; j < addresses.Size(); j++)
	{
		if (rankingAddresses.HasData(addresses[j]))
			return true;
	}
	return false;
}
void Lobby2Server::RemoveRankingAddress(SystemAddress addr)
{
	rankingAddresses.RemoveIfExists(addr);
}
void Lobby2Server::ClearRankingAddresses(void)
{
	rankingAddresses.Clear(false, __FILE__, __LINE__);
}
void Lobby2Server::ExecuteCommand(Lobby2ServerCommand *command)
{
	//SLNet::BitStream out;
	if (command->lobby2Message->PrevalidateInput()==false)
	{
		SendMsg(command->lobby2Message, command->callerSystemAddresses);
		if (command->deallocMsgWhenDone)
			msgFactory->Dealloc(command->lobby2Message);
		return;
	}

	if (command->lobby2Message->RequiresAdmin() && HasAdminAddress(command->callerSystemAddresses)==false)
	{
		command->lobby2Message->resultCode=L2RC_REQUIRES_ADMIN;
		SendMsg(command->lobby2Message, command->callerSystemAddresses);
		//SendUnifiedToMultiple(&out,packetPriority, RELIABLE_ORDERED, orderingChannel, command->callerSystemAddresses);
		if (command->deallocMsgWhenDone)
			msgFactory->Dealloc(command->lobby2Message);
		return;
	}

	if (command->lobby2Message->RequiresRankingPermission() && HasRankingAddress(command->callerSystemAddresses)==false)
	{
		command->lobby2Message->resultCode=L2RC_REQUIRES_ADMIN;
		SendMsg(command->lobby2Message, command->callerSystemAddresses);
		//SendUnifiedToMultiple(&out,packetPriority, RELIABLE_ORDERED, orderingChannel, command->callerSystemAddresses);
		if (command->deallocMsgWhenDone)
			msgFactory->Dealloc(command->lobby2Message);
		return;
	}

	if (command->lobby2Message->ServerPreDBMemoryImpl(this, command->callingUserName)==true)
	{
		SendMsg(command->lobby2Message, command->callerSystemAddresses);
		if (command->deallocMsgWhenDone)
			msgFactory->Dealloc(command->lobby2Message);
		return;
	}

	command->server=this;
	AddInputCommand(*command);
}
void Lobby2Server::SetRoomsPlugin(RoomsPlugin *rp)
{
	roomsPlugin=rp;
	roomsPluginAddress=UNASSIGNED_SYSTEM_ADDRESS;
}
void Lobby2Server::SetRoomsPluginAddress(SystemAddress address)
{
	roomsPluginAddress=address;
	roomsPlugin=0;
}
void Lobby2Server::ClearUsers(void)
{
	unsigned int i;
	for (i=0; i < users.Size(); i++)
		SLNet::OP_DELETE(users[i], __FILE__, __LINE__);
	users.Clear(false, __FILE__, __LINE__);
}
void Lobby2Server::LogoffFromRooms(User *user)
{
	// Remove from the room too
#if defined(__INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN)
	// Tell the rooms plugin about the logoff event
	if (roomsPlugin)
	{
		roomsPlugin->LogoffRoomsParticipant(user->userName, UNASSIGNED_SYSTEM_ADDRESS);
	}
	else if (roomsPluginAddress!=UNASSIGNED_SYSTEM_ADDRESS)
	{
		SLNet::BitStream bs;
		RoomsPlugin::SerializeLogoff(user->userName,&bs);
		SendUnified(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, roomsPluginAddress, false);
	}
#endif

}
void Lobby2Server::SendRemoteLoginNotification(SLNet::RakString handle, const DataStructures::List<SystemAddress>& recipients)
{
	Notification_Client_RemoteLogin notification;
	notification.handle=handle;
	notification.resultCode=L2RC_SUCCESS;
	SendMsg(&notification, recipients);
}
void Lobby2Server::OnLogin(Lobby2ServerCommand *command, bool calledFromThread)
{
	if (calledFromThread)
	{
		ThreadAction ta;
		ta.action=L2MID_Client_Login;
		ta.command=*command;
		threadActionQueueMutex.Lock();
		threadActionQueue.Push(ta, __FILE__, __LINE__ );
		threadActionQueueMutex.Unlock();
		return;
	}

	bool objectExists;
	unsigned int insertionIndex = users.GetIndexFromKey(command->callingUserName, &objectExists);
	if (objectExists)
	{
		User * user = users[insertionIndex];
		if (user->allowMultipleLogins==false)
		{
			SendRemoteLoginNotification(user->userName, user->systemAddresses);
			LogoffFromRooms(user);

			// Already logged in from this system address.
			// Delete the existing entry, which will be reinserted.
			SLNet::OP_DELETE(user,_FILE_AND_LINE_);
			users.RemoveAtIndex(insertionIndex);
		}
		else
		{
			if (user->systemAddresses.GetIndexOf(command->callerSystemAddresses[0])==(unsigned int) -1)
			{
				// Just add system address and guid already in use to the list for this user
				user->systemAddresses.Push(command->callerSystemAddresses[0], __FILE__, __LINE__);
				user->guids.Push(command->callerGuids[0], __FILE__, __LINE__);
			}
			return;
		}
	}
	else
	{
		// Different username, from the same IP address or RakNet instance
		unsigned int idx2 = GetUserIndexByGUID(command->callerGuids[0]);
		unsigned int idx3 = GetUserIndexBySystemAddress(command->callerSystemAddresses[0]);
		if (idx2!=(unsigned int) -1)
		{
			User * user = users[idx2];
			if (user->allowMultipleLogins==true)
				return;
			SendRemoteLoginNotification(user->userName, user->systemAddresses);
			LogoffFromRooms(user);

			SLNet::OP_DELETE(user,__FILE__,__LINE__);
			users.RemoveAtIndex(idx2);

			insertionIndex = users.GetIndexFromKey(command->callingUserName, &objectExists);
		}
		else if (idx3!=(unsigned int) -1)
		{
			User * user = users[idx3];
			if (user->allowMultipleLogins==true)
				return;
			SendRemoteLoginNotification(user->userName, user->systemAddresses);
			LogoffFromRooms(user);

			SLNet::OP_DELETE(user,__FILE__,__LINE__);
			users.RemoveAtIndex(idx3);

			insertionIndex = users.GetIndexFromKey(command->callingUserName, &objectExists);
		}
	}


	User *user = SLNet::OP_NEW<User>( __FILE__, __LINE__ );
	user->userName=command->callingUserName;
	user->systemAddresses=command->callerSystemAddresses;
	user->guids=command->callerGuids;
	user->callerUserId=command->callerUserId;
	user->allowMultipleLogins=((Client_Login*)command->lobby2Message)->allowMultipleLogins;
	users.InsertAtIndex(user, insertionIndex, __FILE__, __LINE__ );

#if defined(__INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN)
	// Tell the rooms plugin about the login event
	if (roomsPlugin)
	{
		roomsPlugin->LoginRoomsParticipant(user->userName, user->systemAddresses[0], user->guids[0], UNASSIGNED_SYSTEM_ADDRESS);
	}
	else if (roomsPluginAddress!=UNASSIGNED_SYSTEM_ADDRESS)
	{
		SLNet::BitStream bs;
		RoomsPlugin::SerializeLogin(user->userName,user->systemAddresses[0], user->guids[0], &bs);
		SendUnified(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, roomsPluginAddress, false);
	}
#endif
}
void Lobby2Server::OnLogoff(Lobby2ServerCommand *command, bool calledFromThread)
{
	if (calledFromThread)
	{
		ThreadAction ta;
		ta.action=L2MID_Client_Logoff;
		ta.command=*command;
		threadActionQueueMutex.Lock();
		threadActionQueue.Push(ta, __FILE__, __LINE__ );
		threadActionQueueMutex.Unlock();
		return;
	}
	RemoveUser(command->callingUserName);
}
void Lobby2Server::OnChangeHandle(Lobby2ServerCommand *command, bool calledFromThread)
{
	if (calledFromThread)
	{
		ThreadAction ta;
		ta.action=L2MID_Client_ChangeHandle;
		ta.command=*command;
		threadActionQueueMutex.Lock();
		threadActionQueue.Push(ta, __FILE__, __LINE__ );
		threadActionQueueMutex.Unlock();
		return;
	}

	unsigned int i;
	SLNet::RakString oldHandle;
	for (i=0; i < users.Size(); i++)
	{
		if (users[i]->callerUserId==command->callerUserId)
		{
			oldHandle=users[i]->userName;
			users[i]->userName=command->callingUserName;
			break;
		}
	}

	if (oldHandle.IsEmpty())
		return;

#if defined(__INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN)
	// Tell the rooms plugin about the handle change
	if (roomsPlugin)
	{
		roomsPlugin->ChangeHandle(oldHandle, command->callingUserName);
	}
	else if (roomsPluginAddress!=UNASSIGNED_SYSTEM_ADDRESS)
	{
		SLNet::BitStream bs;
		RoomsPlugin::SerializeChangeHandle(oldHandle,command->callingUserName,&bs);
		SendUnified(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, roomsPluginAddress, false);
	}
#endif
}
void Lobby2Server::RemoveUser(RakString userName)
{
	bool objectExists;
	unsigned int index = users.GetIndexFromKey(userName, &objectExists);
	if (objectExists)
		RemoveUser(index);
}
void Lobby2Server::RemoveUser(unsigned int index)
{
	User *user = users[index];

	Lobby2ServerCommand command;
	Notification_Friends_StatusChange *notification = (Notification_Friends_StatusChange *) GetMessageFactory()->Alloc(L2MID_Notification_Friends_StatusChange);
	notification->otherHandle=user->userName;
	notification->op=Notification_Friends_StatusChange::FRIEND_LOGGED_OFF;
	notification->resultCode=L2RC_SUCCESS;
	command.server=this;
	command.deallocMsgWhenDone=true;
	command.lobby2Message=notification;
	command.callerUserId=user->callerUserId;
	command.callingUserName=user->userName;
	ExecuteCommand(&command);

	unsigned i;
	i=0;
	threadPool.LockInput();
	while (i < threadPool.InputSize())
	{
		command = threadPool.GetInputAtIndex(i);
		if (command.lobby2Message->CancelOnDisconnect()&& command.callerSystemAddresses.Size()>0 && user->systemAddresses.GetIndexOf(command.callerSystemAddresses[0])!=(unsigned int)-1)
		{
			if (command.deallocMsgWhenDone)
				SLNet::OP_DELETE(command.lobby2Message, __FILE__, __LINE__);
			threadPool.RemoveInputAtIndex(i);
		}
		else
			i++;
	}
	threadPool.UnlockInput();
	LogoffFromRooms(user);

	SLNet::OP_DELETE(user,__FILE__,__LINE__);
	users.RemoveAtIndex(index);

}
unsigned int Lobby2Server::GetUserIndexBySystemAddress(SystemAddress systemAddress) const
{
	unsigned int idx1,idx2;
	for (idx1=0; idx1 < users.Size(); idx1++)
	{
		for (idx2=0; idx2 < users[idx1]->systemAddresses.Size(); idx2++)
		{
			if (users[idx1]->systemAddresses[idx2]==systemAddress)
				return idx1;
		}
	}
	return (unsigned int) -1;
}
unsigned int Lobby2Server::GetUserIndexByGUID(RakNetGUID guid) const
{
	unsigned int idx1,idx2;
	for (idx1=0; idx1 < users.Size(); idx1++)
	{
		for (idx2=0; idx2 < users[idx1]->guids.Size(); idx2++)
		{
			if (users[idx1]->guids[idx2]==guid)
				return idx1;
		}
	}
	return (unsigned int) -1;
}
unsigned int Lobby2Server::GetUserIndexByUsername(SLNet::RakString userName) const
{
	unsigned int idx;
	bool objectExists;
	idx = users.GetIndexFromKey(userName,&objectExists);
	if (objectExists)
		return idx;
	return (unsigned int) -1;
}
void Lobby2Server::StopThreads(void)
{
	threadPool.StopThreads();
}
void Lobby2Server::SetConfigurationProperties(ConfigurationProperties c)
{
	configurationProperties=c;
}
const Lobby2Server::ConfigurationProperties *Lobby2Server::GetConfigurationProperties(void) const
{
	return &configurationProperties;
}
void Lobby2Server::GetUserOnlineStatus(UsernameAndOnlineStatus &userInfo) const
{
	unsigned int idx = GetUserIndexByUsername(userInfo.handle);
	if (idx!=-1)
	{
		userInfo.isOnline=true;
		userInfo.presence=users[idx]->presence;
	}
	else
	{
		userInfo.isOnline=false;
		userInfo.presence.status=Lobby2Presence::NOT_ONLINE;
		userInfo.presence.isVisible=false;
	}
}
void Lobby2Server::SetPresence(const SLNet::Lobby2Presence &presence, SLNet::RakString userHandle)
{
	unsigned int index = GetUserIndexByUsername(userHandle);

	if (index!=-1)
	{
		User *user = users[index];
		user->presence=presence;

		// Push notify presence update to friends
		Lobby2ServerCommand command;
		Notification_Friends_PresenceUpdate *notification = (Notification_Friends_PresenceUpdate *) GetMessageFactory()->Alloc(L2MID_Notification_Friends_PresenceUpdate);
		notification->newPresence=presence;
		notification->otherHandle=user->userName;
		notification->resultCode=L2RC_SUCCESS;
		command.server=this;
		command.deallocMsgWhenDone=true;
		command.lobby2Message=notification;
		command.callerUserId=user->callerUserId;
		command.callingUserName=user->userName;
		ExecuteCommand(&command);
	}
}
void Lobby2Server::GetPresence(SLNet::Lobby2Presence &presence, SLNet::RakString userHandle)
{
	unsigned int userIndex = GetUserIndexByUsername(userHandle);
	if (userIndex!=-1)
	{
		presence=users[userIndex]->presence;
	}
	else
	{
		presence.status=Lobby2Presence::NOT_ONLINE;
	}
}
void Lobby2Server::SendUnifiedToMultiple( const SLNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char curOrderingChannel, const DataStructures::List<SystemAddress> systemAddresses )
{
	for (unsigned int i=0; i < systemAddresses.Size(); i++)
		SendUnified(bitStream,priority,reliability,curOrderingChannel,systemAddresses[i],false);
}
