예제 #1
0
// send a call
int CScriptRMI::ProxyFunction( IFunctionHandler* pH, void *pBuffer, int nSize )
{
	if (!m_pThis->m_pParent)
	{
		pH->GetIScriptSystem()->RaiseError( "Trying to proxy a remote method invocation with no game started... failing" );
		return pH->EndFunction();
	}

	string funcInfo;
	string dispatchInfo;
	bool gatherDebugInfo = pLogRMIEvents && pLogRMIEvents->GetIVal() != 0;

	IScriptSystem * pSS = pH->GetIScriptSystem();
	const SFunctionInfo * pFunctionInfo = static_cast<const SFunctionInfo *>(pBuffer);

	SmartScriptTable proxyTable;
	if (!pH->GetParam( 1, proxyTable ))
		return pH->EndFunction( false );

	ScriptHandle flags;
	ScriptHandle id;
	if (!proxyTable->GetValue(FLAGS_FIELD, flags))
		return pH->EndFunction( false );
	if (!proxyTable->GetValue(ID_FIELD, id))
		return pH->EndFunction( false );

	int formatStart = 2;
	if (uint32 flagsMask = flags.n & (eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels))
	{
		if (flagsMask != (eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels))
			formatStart ++;
	}

	_smart_ptr<CScriptMessage> pMsg = new CScriptMessage( 
		pFunctionInfo->reliability,
		pFunctionInfo->attachment,
		(EntityId)id.n, 
		pFunctionInfo->funcID, 
		pFunctionInfo->format );
	if (!pMsg->SetFromFunction( pH, formatStart ))
	{
		return pH->EndFunction( false );
	}

	if (gatherDebugInfo)
		funcInfo = pMsg->DebugInfo();

	INetContext * pNetContext = m_pThis->m_pParent->GetNetContext();
	CCryAction * pFramework = m_pThis->m_pParent->GetFramework();

	if (flags.n & eDF_ToServer)
	{
		CGameClientNub * pClientNub = pFramework->GetGameClientNub();
		bool called = false;
		if (pClientNub)
		{
			CGameClientChannel * pChannel = pClientNub->GetGameClientChannel();
			if (pChannel)
			{
				DispatchRMI( pChannel->GetNetChannel(), pMsg, false );
				called = true;
			}
		}
		if (!called)
		{
			pSS->RaiseError( "RMI via client (to server) requested but we are not a client" );
		}

		if (gatherDebugInfo)
			dispatchInfo = "server";
	}
	else if (flags.n & (eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels))
	{
		CGameServerNub * pServerNub = pFramework->GetGameServerNub();
		if (pServerNub)
		{
			int myChannelId = 0;
			bool ok = true;
			if (flags.n != (eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels))
			{
				if (!pH->GetParam( 2, myChannelId ))
				{
					pSS->RaiseError( "RMI onClient or otherClients must have a valid channel id for its first argument" );
					ok = false;
				}
				else if (pServerNub->GetServerChannelMap()->find(myChannelId) == pServerNub->GetServerChannelMap()->end())
				{
					pSS->RaiseError( "RMI No such server channel %d", myChannelId );
					ok = false;
				}
			}
			if (ok)
			{
				TServerChannelMap * pChannelMap = pServerNub->GetServerChannelMap();
				for (TServerChannelMap::iterator iter = pChannelMap->begin(); iter != pChannelMap->end(); ++iter)
				{
					bool isOwn = iter->first == myChannelId;
					if (isOwn && !(flags.n & eDF_ToClientOnChannel))
						continue;
					if (!isOwn && !(flags.n & eDF_ToClientOnOtherChannels))
						continue;

					if (iter->second->GetNetChannel())
						DispatchRMI( iter->second->GetNetChannel(), pMsg, true );
				}
				if (gatherDebugInfo)
				{
					dispatchInfo = "client: ";
					bool appendChannel = false;
					switch (flags.n)
					{
					case eDF_ToClientOnChannel:
						dispatchInfo += "specificChannel";
						appendChannel = true;
						break;
					case eDF_ToClientOnOtherChannels:
						dispatchInfo += "otherChannels";
						appendChannel = true;
						break;
					case eDF_ToClientOnChannel | eDF_ToClientOnOtherChannels:
						dispatchInfo += "allChannels";
						break;
					default:
						dispatchInfo += "UNKNOWN(BUG)";
						appendChannel = true;
						CRY_ASSERT(false);
					}
					if (appendChannel)
					{
						string temp;
						temp.Format(" %d", myChannelId);
						dispatchInfo += temp;
					}
				}
			}
		}
		else
		{
			pSS->RaiseError( "RMI via server (to client) requested but we are not a server" );
		}
	}

	if (gatherDebugInfo)
	{
		IEntity * pEntity = gEnv->pEntitySystem->GetEntity((EntityId)id.n);
		const char * name = "<invalid>";
		if (pEntity) name = pEntity->GetName();
		CryLogAlways("[rmi] %s(%s) [%s] on entity[%d] %s", 
			pH->GetFuncName(), funcInfo.c_str(), dispatchInfo.c_str(), (int)id.n, name);
	}

	return pH->EndFunction( true );
}
예제 #2
0
// build a script dispatch table - this table is the metatable for all
// dispatch proxy tables used (onClient, allClients, otherClients)
bool CScriptRMI::BuildDispatchTable( 
	SmartScriptTable methods, 
	SmartScriptTable classMethodTable, 
	SmartScriptTable cls, 
	const char * name )
{
	IScriptSystem * pSS = methods->GetScriptSystem();
	SmartScriptTable dispatch( pSS );

	uint8 funcID = 0;

	IScriptTable::SUserFunctionDesc fd;
	SFunctionInfo info;

	IScriptTable::Iterator iter = methods->BeginIteration();
	while (methods->MoveNext(iter))
	{
		if (iter.sKey)
		{
			const char * function = iter.sKey;

			if (strlen(function)>=2 && function[0] == '_' && function[1] == '_')
			{
				methods->EndIteration(iter);
				pSS->RaiseError( "In Net.Expose: can't expose functions beginning with '__' (function was %s)",
					function );
				return false;
			}

			SmartScriptTable specTable;
			if (!methods->GetValue(function, specTable))
			{
				methods->EndIteration(iter);
				pSS->RaiseError( "In Net.Expose: function %s entry is not a table (in %s)", 
					function, name );
				return false;
			}

			// fetch format
			int count = specTable->Count();
			if (count < 1)
			{
				methods->EndIteration(iter);
				pSS->RaiseError( "In Net.Expose: function %s entry is an empty table (in %s)", 
					function, name );
				return false;
			}
			else if (count-1 > MaxRMIParameters)
			{
				methods->EndIteration(iter);
				pSS->RaiseError( "In Net.Expose: function %s has too many parameters (%d) (in %s)", 
					function, count-1, name );
				return false;
			}
			int tempReliability;
			if (!specTable->GetAt(1, tempReliability) || tempReliability < 0 || tempReliability >= eNRT_NumReliabilityTypes)
			{
				methods->EndIteration(iter);
				pSS->RaiseError( "In Net.Expose: function %s has invalid reliability type %d (in %s)", 
					function, tempReliability, name );
				return false;
			}
			ENetReliabilityType reliability = (ENetReliabilityType) tempReliability;
			if (!specTable->GetAt(2, tempReliability) || tempReliability < 0 || tempReliability >= eRAT_NumAttachmentTypes)
			{
				methods->EndIteration(iter);
				pSS->RaiseError( "In Net.Expose: function %s has invalid attachment type %d (in %s)", 
					function, tempReliability, name );
			}
			ERMIAttachmentType attachment = (ERMIAttachmentType) tempReliability;
			string format;
			format.reserve(count-1);
			for (int i=3; i<=count; i++)
			{
				int type = 666;
				if (!specTable->GetAt( i, type ) || type<-128 || type>127)
				{
					methods->EndIteration(iter);
					pSS->RaiseError( "In Net.Expose: function %s has invalid serialization policy %d at %d (in %s)", 
						function, type, i, name );
					return false;
				}
				format.push_back( (char) type );
			}

			CRY_ASSERT( format.length() <= MaxRMIParameters );
			strcpy( info.format, format.c_str() );
			info.funcID = funcID;
			info.reliability = reliability;
			info.attachment = attachment;

			fd.pUserDataFunc = ProxyFunction;
			fd.sFunctionName = function;
			fd.nDataSize = sizeof(SFunctionInfo);
			fd.pDataBuffer = &info;
			fd.sGlobalName = "<net-dispatch>";
			fd.sFunctionParams = "(...)";

			dispatch->AddFunction( fd );

			string lookupData = function;
			lookupData += ":";
			lookupData += format;
			dispatch->SetAt( funcID+1, lookupData.c_str() );


			funcID ++;
			if (funcID == 0)
			{
				funcID--;
				methods->EndIteration(iter);
				pSS->RaiseError( "Too many functions... max is %d", funcID );
				return false;
			}
		}
		else
		{
			GameWarning( "In Net.Expose: non-string key ignored" );
		}
	}
	methods->EndIteration(iter);

	dispatch->SetValue( VALIDATED_FIELD, false );
	cls->SetValue( name, dispatch );

	return true;
}
예제 #3
0
// setup the meta-table for synched variables
bool CScriptRMI::BuildSynchTable( SmartScriptTable vars, SmartScriptTable cls, const char * name )
{
	IScriptSystem * pSS = vars->GetScriptSystem();
	SmartScriptTable synched( pSS );
	SmartScriptTable defaultValues( pSS );

	// TODO: Improve
	IScriptTable::SUserFunctionDesc fd;
	fd.pFunctor = functor_ret( SynchedNewIndexFunction );
	fd.sFunctionName = "__newindex";
	fd.sGlobalName = "<net-dispatch>";
	fd.sFunctionParams = "(...)";
	synched->AddFunction( fd );

	std::vector<SSynchedPropertyInfo> properties;

	IScriptTable::Iterator iter = vars->BeginIteration();
	while (vars->MoveNext(iter))
	{
		if (iter.sKey)
		{
			int type;
			if (!vars->GetValue(iter.sKey, type))
			{
				vars->EndIteration(iter);
				pSS->RaiseError( "No type for %s", iter.sKey );
				return false;
			}
			size_t len = strlen(iter.sKey);
			if (len > MaxSynchedPropertyNameLength)
			{
				vars->EndIteration(iter);
				pSS->RaiseError( "Synched var name '%s' too long (max is %d)",
					iter.sKey, (int)MaxSynchedPropertyNameLength );
				return false;
			}
			SSynchedPropertyInfo info;
			strcpy( info.name, iter.sKey );
			info.type = (EScriptSerializeType) type;
			properties.push_back( info );

			if (info.type == eSST_String)
				defaultValues->SetValue( iter.sKey, "" );
			else
				defaultValues->SetValue( iter.sKey, 0 );
		}
	}
	vars->EndIteration( iter );

	if (properties.empty())
		return true;

	fd.pFunctor = NULL;
	fd.pUserDataFunc = SerializeFunction;
	fd.nDataSize = sizeof(SSynchedPropertyInfo) * properties.size();
	fd.pDataBuffer = &properties[0];
	fd.sFunctionName = SERIALIZE_FUNCTION;
	fd.sFunctionParams = "(...)";
	fd.sGlobalName = "<net-dispatch>";
	synched->AddFunction( fd );

	cls->SetValue( SERVER_SYNCHED_FIELD, synched );
	cls->SetValue( "synched", defaultValues );

	return true;
}
예제 #4
0
// implementation of Net.Expose() - exposes a class
int CScriptRMI::ExposeClass( IFunctionHandler * pFH )
{
	SmartScriptTable params, cls, clientMethods, serverMethods;
	SmartScriptTable clientTable, serverTable;
	SmartScriptTable serverProperties;

	IScriptSystem * pSS = pFH->GetIScriptSystem();

	if (pFH->GetParamCount() != 1)
	{
		pSS->RaiseError( "Net.Expose takes only one parameter - a table" );
		return pFH->EndFunction(false);
	}

	if (!pFH->GetParam( 1, params ))
	{
		pSS->RaiseError( "Net.Expose takes only one parameter - a table" );
		return pFH->EndFunction(false);
	}
	if (!params->GetValue( "Class", cls ))
	{
		pSS->RaiseError( "No 'Class' parameter to Net.Expose" );
		return pFH->EndFunction(false);
	}

	if (!params->GetValue( "ClientMethods", clientMethods ))
	{
		GameWarning( "No 'ClientMethods' parameter to Net.Expose" );
	}
	else if (!cls->GetValue("Client", clientTable))
	{
		pSS->RaiseError( "'ClientMethods' exposed, but no 'Client' table in class" );
		return pFH->EndFunction(false);
	}
	if (!params->GetValue( "ServerMethods", serverMethods ))
	{
		GameWarning( "No 'ServerMethods' parameter to Net.Expose" );
	}
	else if (!cls->GetValue("Server", serverTable))
	{
		pSS->RaiseError( "'ServerMethods' exposed, but no 'Server' table in class" );
		return pFH->EndFunction(false);
	}
	params->GetValue( "ServerProperties", serverProperties );

	if (clientMethods.GetPtr())
	{
		CRY_ASSERT( clientTable.GetPtr() );
		if (!BuildDispatchTable( clientMethods, clientTable, cls, CLIENT_DISPATCH_FIELD ))
			return pFH->EndFunction(false);
	}

	if (serverMethods.GetPtr())
	{
		CRY_ASSERT( serverTable.GetPtr() );
		if (!BuildDispatchTable( serverMethods, serverTable, cls, SERVER_DISPATCH_FIELD ))
			return pFH->EndFunction(false);
	}

	if (serverProperties.GetPtr())
	{
		if (!BuildSynchTable(serverProperties, cls, SERVER_SYNCHED_FIELD))
			return pFH->EndFunction(false);
	}

	return pFH->EndFunction(true);
}