/*
 *  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 "RPC3.h"
#include "slikenet/memoryoverride.h"
#include "slikenet/assert.h"
#include "slikenet/StringCompressor.h"
#include "slikenet/BitStream.h"
#include "slikenet/peerinterface.h"
#include "slikenet/MessageIdentifiers.h"
#include "slikenet/NetworkIDManager.h"
#include <stdlib.h>

using namespace SLNet;

// int RPC3::RemoteRPCFunctionComp( const RPC3::RPCIdentifier &key, const RemoteRPCFunction &data )
// {
// 	return strcmp(key.C_String(), data.identifier.C_String());
// }
int SLNet::RPC3::LocalSlotObjectComp( const LocalSlotObject &key, const LocalSlotObject &data )
{
	if (key.callPriority>data.callPriority)
		return -1;
	if (key.callPriority==data.callPriority)
	{
		if (key.registrationCount<data.registrationCount)
			return -1;
		if (key.registrationCount==data.registrationCount)
			return 0;
		return 1;
	}

	return 1;
}
RPC3::RPC3()
{
	currentExecution[0]=0;
	networkIdManager=0;
	outgoingTimestamp=0;
	outgoingPriority=HIGH_PRIORITY;
	outgoingReliability=RELIABLE_ORDERED;
	outgoingOrderingChannel=0;
	outgoingBroadcast=true;
	incomingTimeStamp=0;
	nextSlotRegistrationCount=0;
}

RPC3::~RPC3()
{
	Clear();
}

void RPC3::SetNetworkIDManager(NetworkIDManager *idMan)
{
	networkIdManager=idMan;
}

bool RPC3::UnregisterFunction(const char *uniqueIdentifier)
{
	// unused parameters
	(void)uniqueIdentifier;

	return false;
}

bool RPC3::IsFunctionRegistered(const char *uniqueIdentifier)
{
	DataStructures::HashIndex i = GetLocalFunctionIndex(uniqueIdentifier);
	return i.IsInvalid()==false;
}

void RPC3::SetTimestamp(SLNet::Time timeStamp)
{
	outgoingTimestamp=timeStamp;
}

void RPC3::SetSendParams(PacketPriority priority, PacketReliability reliability, char orderingChannel)
{
	outgoingPriority=priority;
	outgoingReliability=reliability;
	outgoingOrderingChannel=orderingChannel;
}

void RPC3::SetRecipientAddress(const SystemAddress &systemAddress, bool broadcast)
{
	outgoingSystemAddress=systemAddress;
	outgoingBroadcast=broadcast;
}

void RPC3::SetRecipientObject(NetworkID networkID)
{
	outgoingNetworkID=networkID;
}

SLNet::Time RPC3::GetLastSenderTimestamp(void) const
{
	return incomingTimeStamp;
}

SystemAddress RPC3::GetLastSenderAddress(void) const
{
	return incomingSystemAddress;
}

RakPeerInterface *RPC3::GetRakPeer(void) const
{
	return rakPeerInterface;
}

const char *RPC3::GetCurrentExecution(void) const
{
	return (const char *) currentExecution;
}

bool RPC3::SendCallOrSignal(RakString uniqueIdentifier, char parameterCount, SLNet::BitStream *serializedParameters, bool isCall)
{	
	SystemAddress systemAddr;
//	unsigned int outerIndex;
//	unsigned int innerIndex;

	if (uniqueIdentifier.IsEmpty())
		return false;

	SLNet::BitStream bs;
	if (outgoingTimestamp!=0)
	{
		bs.Write((MessageID)ID_TIMESTAMP);
		bs.Write(outgoingTimestamp);
	}
	bs.Write((MessageID)ID_RPC_PLUGIN);
	bs.Write(parameterCount);
	if (outgoingNetworkID!=UNASSIGNED_NETWORK_ID && isCall)
	{
		bs.Write(true);
		bs.Write(outgoingNetworkID);
	}
	else
	{
		bs.Write(false);
	}
	bs.Write(isCall);
	// This is so the call SetWriteOffset works
	bs.AlignWriteToByteBoundary();
	BitSize_t writeOffset = bs.GetWriteOffset();
	if (outgoingBroadcast)
	{
		unsigned systemIndex;
		for (systemIndex=0; systemIndex < rakPeerInterface->GetMaximumNumberOfPeers(); systemIndex++)
		{
			systemAddr=rakPeerInterface->GetSystemAddressFromIndex(systemIndex);
			if (systemAddr!= SLNet::UNASSIGNED_SYSTEM_ADDRESS && systemAddr!=outgoingSystemAddress)
			{
// 				if (GetRemoteFunctionIndex(systemAddr, uniqueIdentifier, &outerIndex, &innerIndex, isCall))
// 				{
// 					// Write a number to identify the function if possible, for faster lookup and less bandwidth
// 					bs.Write(true);
// 					if (isCall)
// 						bs.WriteCompressed(remoteFunctions[outerIndex]->operator [](innerIndex).functionIndex);
// 					else
// 						bs.WriteCompressed(remoteSlots[outerIndex]->operator [](innerIndex).functionIndex);
// 				}
// 				else
// 				{
// 					bs.Write(false);
					StringCompressor::Instance()->EncodeString(uniqueIdentifier, 512, &bs, 0);
//				}

				bs.WriteCompressed(serializedParameters->GetNumberOfBitsUsed());

				// serializedParameters->PrintBits();

				bs.WriteAlignedBytes((const unsigned char*) serializedParameters->GetData(), serializedParameters->GetNumberOfBytesUsed());
				SendUnified(&bs, outgoingPriority, outgoingReliability, outgoingOrderingChannel, systemAddr, false);

				// Start writing again after ID_AUTO_RPC_CALL
				bs.SetWriteOffset(writeOffset);
			}
		}
	}
	else
	{
		systemAddr = outgoingSystemAddress;
		if (systemAddr!= SLNet::UNASSIGNED_SYSTEM_ADDRESS)
		{
// 			if (GetRemoteFunctionIndex(systemAddr, uniqueIdentifier, &outerIndex, &innerIndex, isCall))
// 			{
// 				// Write a number to identify the function if possible, for faster lookup and less bandwidth
// 				bs.Write(true);
// 				if (isCall)
// 					bs.WriteCompressed(remoteFunctions[outerIndex]->operator [](innerIndex).functionIndex);
// 				else
// 					bs.WriteCompressed(remoteSlots[outerIndex]->operator [](innerIndex).functionIndex);
// 			}
// 			else
// 			{
// 				bs.Write(false);
				StringCompressor::Instance()->EncodeString(uniqueIdentifier, 512, &bs, 0);
//			}

			bs.WriteCompressed(serializedParameters->GetNumberOfBitsUsed());
			bs.WriteAlignedBytes((const unsigned char*) serializedParameters->GetData(), serializedParameters->GetNumberOfBytesUsed());
			SendUnified(&bs, outgoingPriority, outgoingReliability, outgoingOrderingChannel, systemAddr, false);
		}
		else
			return false;
	}
	return true;
}

void RPC3::OnAttach(void)
{
	outgoingSystemAddress= SLNet::UNASSIGNED_SYSTEM_ADDRESS;
	outgoingNetworkID=UNASSIGNED_NETWORK_ID;
	incomingSystemAddress= SLNet::UNASSIGNED_SYSTEM_ADDRESS;
}

PluginReceiveResult RPC3::OnReceive(Packet *packet)
{
	SLNet::Time timestamp=0;
	unsigned char packetIdentifier, packetDataOffset;
	if ( ( unsigned char ) packet->data[ 0 ] == ID_TIMESTAMP )
	{
		if ( packet->length > sizeof( unsigned char ) + sizeof(SLNet::Time ) )
		{
			packetIdentifier = ( unsigned char ) packet->data[ sizeof( unsigned char ) + sizeof(SLNet::Time ) ];
			// Required for proper endian swapping
			SLNet::BitStream tsBs(packet->data+sizeof(MessageID),packet->length-1,false);
			tsBs.Read(timestamp);
			packetDataOffset=sizeof( unsigned char )*2 + sizeof(SLNet::Time );
		}
		else
			return RR_STOP_PROCESSING_AND_DEALLOCATE;
	}
	else
	{
		packetIdentifier = ( unsigned char ) packet->data[ 0 ];
		packetDataOffset=sizeof( unsigned char );
	}

	switch (packetIdentifier)
	{
	case ID_RPC_PLUGIN:
		incomingTimeStamp=timestamp;
		incomingSystemAddress=packet->systemAddress;
		OnRPC3Call(packet->systemAddress, packet->data+packetDataOffset, packet->length-packetDataOffset);
		return RR_STOP_PROCESSING_AND_DEALLOCATE;
// 	case ID_AUTO_RPC_REMOTE_INDEX:
// 		OnRPCRemoteIndex(packet->systemAddress, packet->data+packetDataOffset, packet->length-packetDataOffset);
// 		return RR_STOP_PROCESSING_AND_DEALLOCATE;		
	}

	return RR_CONTINUE_PROCESSING;
}

void RPC3::OnRPC3Call(const SystemAddress &systemAddress, unsigned char *data, unsigned int lengthInBytes)
{
	SLNet::BitStream bs(data,lengthInBytes,false);

	DataStructures::HashIndex functionIndex;
	LocalRPCFunction *lrpcf;
	char parameterCount;
	NetworkIDObject *networkIdObject;
	NetworkID networkId;
	bool hasNetworkId=false;
//	bool hasFunctionIndex=false;
//	unsigned int functionIndex;
	BitSize_t bitsOnStack;
	char strIdentifier[512];
	incomingExtraData.Reset();
	bs.Read(parameterCount);
	bs.Read(hasNetworkId);
	if (hasNetworkId)
	{
		SLNET_VERIFY(bs.Read(networkId));
		RakAssert(networkId!=UNASSIGNED_NETWORK_ID);
		if (networkIdManager==0)
		{
			// Failed - Tried to call object member, however, networkIDManager system was never registered
			SendError(systemAddress, RPC_ERROR_NETWORK_ID_MANAGER_UNAVAILABLE, "");
			return;
		}
		networkIdObject = networkIdManager->GET_OBJECT_FROM_ID<NetworkIDObject*>(networkId);
		if (networkIdObject==0)
		{
			// Failed - Tried to call object member, object does not exist (deleted?)
			SendError(systemAddress, RPC_ERROR_OBJECT_DOES_NOT_EXIST, "");
			return;
		}
	}
	else
	{
		networkIdObject=0;
	}
	bool isCall;
	bs.Read(isCall);
	bs.AlignReadToByteBoundary();
//	bs.Read(hasFunctionIndex);
//	if (hasFunctionIndex)
//		bs.ReadCompressed(functionIndex);
//	else
		StringCompressor::Instance()->DecodeString(strIdentifier,512,&bs,0);
	bs.ReadCompressed(bitsOnStack);
	SLNet::BitStream serializedParameters;
	if (bitsOnStack>0)
	{
		serializedParameters.AddBitsAndReallocate(bitsOnStack);
		// BITS_TO_BYTES is correct, why did I change this?
		bs.ReadAlignedBytes(serializedParameters.GetData(), BITS_TO_BYTES(bitsOnStack));
		serializedParameters.SetWriteOffset(bitsOnStack);
	}
// 	if (hasFunctionIndex)
// 	{
// 		if (
// 			(isCall==true && functionIndex>localFunctions.Size()) ||
// 			(isCall==false && functionIndex>localSlots.Size())
// 			)
// 		{
// 			// Failed - other system specified a totally invalid index
// 			// Possible causes: Bugs, attempts to crash the system, requested function not registered
// 			SendError(systemAddress, RPC_ERROR_FUNCTION_INDEX_OUT_OF_RANGE, "");
// 			return;
// 		}
// 	}
// 	else
	{
		// Find the registered function with this str
		if (isCall)
		{
// 			for (functionIndex=0; functionIndex < localFunctions.Size(); functionIndex++)
// 			{
// 				bool isObjectMember = boost::fusion::get<0>(localFunctions[functionIndex].functionPointer);
// 				//		boost::function<_RPC3::InvokeResultCodes (_RPC3::InvokeArgs)> functionPtr = boost::fusion::get<0>(localFunctions[functionIndex].functionPointer);
// 
// 				if (isObjectMember == (networkIdObject!=0) &&
// 					strcmp(localFunctions[functionIndex].identifier.C_String(), strIdentifier)==0)
// 				{
// 					// SEND RPC MAPPING
// 					SLNet::BitStream outgoingBitstream;
// 					outgoingBitstream.Write((MessageID)ID_AUTO_RPC_REMOTE_INDEX);
// 					outgoingBitstream.Write(hasNetworkId);
// 					outgoingBitstream.WriteCompressed(functionIndex);
// 					StringCompressor::Instance()->EncodeString(strIdentifier,512,&outgoingBitstream,0);
// 					outgoingBitstream.Write(isCall);
// 					SendUnified(&outgoingBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false);
// 					break;
// 				}
// 			}


			functionIndex = localFunctions.GetIndexOf(strIdentifier);
			if (functionIndex.IsInvalid())
			{
				SendError(systemAddress, RPC_ERROR_FUNCTION_NOT_REGISTERED, strIdentifier);
				return;
			}
			lrpcf = localFunctions.ItemAtIndex(functionIndex);

			bool isObjectMember = boost::fusion::get<0>(lrpcf->functionPointer);
			if (isObjectMember==true && networkIdObject==0)
			{
				// Failed - Calling C++ function as C function
				SendError(systemAddress, RPC_ERROR_CALLING_CPP_AS_C, strIdentifier);
				return;
			}

			if (isObjectMember==false && networkIdObject!=0)
			{
				// Failed - Calling C function as C++ function
				SendError(systemAddress, RPC_ERROR_CALLING_C_AS_CPP, strIdentifier);
				return;
			}
		}
		else
		{
			functionIndex = localSlots.GetIndexOf(strIdentifier);
			if (functionIndex.IsInvalid())
			{
				SendError(systemAddress, RPC_ERROR_FUNCTION_NOT_REGISTERED, strIdentifier);
				return;
			}
		}
	}

	if (isCall)
	{
		/*bool isObjectMember = */ boost::fusion::get<0>(lrpcf->functionPointer);
		boost::function<_RPC3::InvokeResultCodes (_RPC3::InvokeArgs)> functionPtr = boost::fusion::get<1>(lrpcf->functionPointer);
		//	int arity = boost::fusion::get<2>(localFunctions[functionIndex].functionPointer);
		//	if (isObjectMember)
		//		arity--; // this pointer
		if (functionPtr==0)
		{
			// Failed - Function was previously registered, but isn't registered any longer
			SendError(systemAddress, RPC_ERROR_FUNCTION_NO_LONGER_REGISTERED, strIdentifier);
			return;
		}

		// Boost doesn't support this for class members
		//	if (arity!=parameterCount)
		//	{
		//		// Failed - The number of parameters that this function has was explicitly specified, and does not match up.
		//		SendError(systemAddress, RPC_ERROR_INCORRECT_NUMBER_OF_PARAMETERS, localFunctions[functionIndex].identifier);
		//		return;
		//	}

		_RPC3::InvokeArgs functionArgs;
		functionArgs.bitStream=&serializedParameters;
		functionArgs.networkIDManager=networkIdManager;
		functionArgs.caller=this;
		functionArgs.thisPtr=networkIdObject;
		
		// serializedParameters.PrintBits();

		functionPtr(functionArgs);
	}
	else
	{
		InvokeSignal(functionIndex, &serializedParameters, false);
	}

}
void RPC3::InterruptSignal(void)
{
	interruptSignal=true;
}
void RPC3::InvokeSignal(DataStructures::HashIndex functionIndex, SLNet::BitStream *serializedParameters, bool temporarilySetUSA)
{
	if (functionIndex.IsInvalid())
		return;

	SystemAddress lastIncomingAddress=incomingSystemAddress;
	if (temporarilySetUSA)
		incomingSystemAddress= SLNet::UNASSIGNED_SYSTEM_ADDRESS;
	interruptSignal=false;
	LocalSlot *localSlot = localSlots.ItemAtIndex(functionIndex);
	unsigned int i;
	_RPC3::InvokeArgs functionArgs;
	functionArgs.bitStream=serializedParameters;
	functionArgs.networkIDManager=networkIdManager;
	functionArgs.caller=this;
	i=0;
	while (i < localSlot->slotObjects.Size())
	{
		if (localSlot->slotObjects[i].associatedObject!=UNASSIGNED_NETWORK_ID)
		{
			functionArgs.thisPtr = networkIdManager->GET_OBJECT_FROM_ID<NetworkIDObject*>(localSlot->slotObjects[i].associatedObject);
			if (functionArgs.thisPtr==0)
			{
				localSlot->slotObjects.RemoveAtIndex(i);
				continue;
			}
		}
		else
			functionArgs.thisPtr=0;
		functionArgs.bitStream->ResetReadPointer();

		// #med - review whether the call is actually required at all
		boost::fusion::get<0>(localSlot->slotObjects[i].functionPointer);
		boost::function<_RPC3::InvokeResultCodes (_RPC3::InvokeArgs)> functionPtr = boost::fusion::get<1>(localSlot->slotObjects[i].functionPointer);
		if (functionPtr==0)
		{
			if (temporarilySetUSA==false)
			{
				// Failed - Function was previously registered, but isn't registered any longer
				SendError(lastIncomingAddress, RPC_ERROR_FUNCTION_NO_LONGER_REGISTERED, localSlots.KeyAtIndex(functionIndex).C_String());
			}
			return;
		}
		functionPtr(functionArgs);

		// Not threadsafe
		if (interruptSignal==true)
			break;

		i++;
	}

	if (temporarilySetUSA)
		incomingSystemAddress=lastIncomingAddress;
}
// void RPC3::OnRPCRemoteIndex(const SystemAddress &systemAddress, unsigned char *data, unsigned int lengthInBytes)
// {
// 	// A remote system has given us their internal index for a particular function.
// 	// Store it and use it from now on, to save bandwidth and search time
// 	bool objectExists;
// 	RakString strIdentifier;
// 	unsigned int insertionIndex;
// 	unsigned int remoteIndex;
// 	RemoteRPCFunction newRemoteFunction;
// 	SLNet::BitStream bs(data,lengthInBytes,false);
// 	RPCIdentifier identifier;
// 	bool isObjectMember;
// 	bool isCall;
// 	bs.Read(isObjectMember);
// 	bs.ReadCompressed(remoteIndex);
// 	bs.Read(strIdentifier);
// 	bs.Read(isCall);
// 
// 	if (strIdentifier.IsEmpty())
// 		return;
// 
// 	DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, RPC3::RemoteRPCFunctionComp> *theList;
// 	if (
// 		(isCall==true && remoteFunctions.Has(systemAddress)) ||
// 		(isCall==false && remoteSlots.Has(systemAddress))
// 		)
// 	{
// 		if (isCall==true)
// 			theList = remoteFunctions.Get(systemAddress);
// 		else
// 			theList = remoteSlots.Get(systemAddress);
// 		insertionIndex=theList->GetIndexFromKey(identifier, &objectExists);
// 		if (objectExists==false)
// 		{
// 			newRemoteFunction.functionIndex=remoteIndex;
// 			newRemoteFunction.identifier = strIdentifier;
// 			theList->InsertAtIndex(newRemoteFunction, insertionIndex, _FILE_AND_LINE_ );
// 		}
// 	}
// 	else
// 	{
// 		theList = SLNet::OP_NEW<DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, RPC3::RemoteRPCFunctionComp> >(_FILE_AND_LINE_);
// 
// 		newRemoteFunction.functionIndex=remoteIndex;
// 		newRemoteFunction.identifier = strIdentifier;
// 		theList->InsertAtEnd(newRemoteFunction, _FILE_AND_LINE_ );
// 
// 		if (isCall==true)
// 			remoteFunctions.SetNew(systemAddress,theList);
// 		else
// 			remoteSlots.SetNew(systemAddress,theList);
// 	}
// }

void RPC3::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
{
	// unused parameters
	(void)systemAddress;
	(void)rakNetGUID;
	(void)lostConnectionReason;

// 	if (remoteFunctions.Has(systemAddress))
// 	{
// 		DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, RPC3::RemoteRPCFunctionComp> *theList = remoteFunctions.Get(systemAddress);
// 		delete theList;
// 		remoteFunctions.Delete(systemAddress);
// 	}
// 
// 	if (remoteSlots.Has(systemAddress))
// 	{
// 		DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, RPC3::RemoteRPCFunctionComp> *theList = remoteSlots.Get(systemAddress);
// 		delete theList;
// 		remoteSlots.Delete(systemAddress);
// 	}
}

void RPC3::OnShutdown(void)
{
	// Not needed, and if the user calls Shutdown inadvertantly, it unregisters his functions
	// Clear();
}

void RPC3::Clear(void)
{
	unsigned j;
// 	for (j=0; j < remoteFunctions.Size(); j++)
// 	{
// 		DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, RPC3::RemoteRPCFunctionComp> *theList = remoteFunctions[j];
// 		SLNet::OP_DELETE(theList,_FILE_AND_LINE_);
// 	}
// 	for (j=0; j < remoteSlots.Size(); j++)
// 	{
// 		DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, RPC3::RemoteRPCFunctionComp> *theList = remoteSlots[j];
// 		SLNet::OP_DELETE(theList,_FILE_AND_LINE_);
// 	}

	DataStructures::List<SLNet::RakString> keyList;
	DataStructures::List<LocalSlot*> outputList;
	localSlots.GetAsList(outputList,keyList,_FILE_AND_LINE_);
	for (j=0; j < outputList.Size(); j++)
	{
		SLNet::OP_DELETE(outputList[j],_FILE_AND_LINE_);
	}
	localSlots.Clear(_FILE_AND_LINE_);

	DataStructures::List<LocalRPCFunction*> outputList2;
	localFunctions.GetAsList(outputList2,keyList,_FILE_AND_LINE_);
	for (j=0; j < outputList2.Size(); j++)
	{
		SLNet::OP_DELETE(outputList2[j],_FILE_AND_LINE_);
	}
	localFunctions.Clear(_FILE_AND_LINE_);
//	remoteFunctions.Clear();
//	remoteSlots.Clear();
	outgoingExtraData.Reset();
	incomingExtraData.Reset();
}

void RPC3::SendError(SystemAddress target, unsigned char errorCode, const char *functionName)
{
	SLNet::BitStream bs;
	bs.Write((MessageID)ID_RPC_REMOTE_ERROR);
	bs.Write(errorCode);
	bs.WriteAlignedBytes((const unsigned char*) functionName,(const unsigned int) strlen(functionName)+1);
	SendUnified(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, target, false);
}

// bool RPC3::GetRemoteFunctionIndex(const SystemAddress &systemAddress, RPC3::RPCIdentifier identifier, unsigned int *outerIndex, unsigned int *innerIndex, bool isCall)
// {
// 	bool objectExists=false;
// 	if (isCall)
// 	{
// 		if (remoteFunctions.Has(systemAddress))
// 		{
// 			*outerIndex = remoteFunctions.GetIndexAtKey(systemAddress);
// 			DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, RPC3::RemoteRPCFunctionComp> *theList = remoteFunctions[*outerIndex];
// 			*innerIndex = theList->GetIndexFromKey(identifier, &objectExists);
// 		}
// 	}
// 	else
// 	{
// 		if (remoteSlots.Has(systemAddress))
// 		{
// 			*outerIndex = remoteFunctions.GetIndexAtKey(systemAddress);
// 			DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, RPC3::RemoteRPCFunctionComp> *theList = remoteSlots[*outerIndex];
// 			*innerIndex = theList->GetIndexFromKey(identifier, &objectExists);
// 		}
// 	}
// 	return objectExists;
// }
DataStructures::HashIndex RPC3::GetLocalSlotIndex(const char *sharedIdentifier)
{
	return localSlots.GetIndexOf(sharedIdentifier);
}
DataStructures::HashIndex RPC3::GetLocalFunctionIndex(RPC3::RPCIdentifier identifier)
{
	return localFunctions.GetIndexOf(identifier.C_String());
}

