Fix invoking Client RPC on Host client when it's not included in targetIds list

This commit is contained in:
Wojtek Figat
2023-06-28 14:52:58 +02:00
parent fb6bc75e3a
commit b16a6199d0
6 changed files with 41 additions and 44 deletions

View File

@@ -114,3 +114,14 @@ inline Span<T> ToSpan(const T* ptr, int32 length)
{ {
return Span<T>(ptr, length); return Span<T>(ptr, length);
} }
template<typename T>
inline bool SpanContains(const Span<T> span, const T& value)
{
for (int32 i = 0; i < span.Length(); i++)
{
if (span.Get()[i] == value)
return true;
}
return false;
}

View File

@@ -700,9 +700,9 @@ void NetworkReplicator::AddRPC(const ScriptingTypeHandle& typeHandle, const Stri
NetworkRpcInfo::RPCsTable[rpcName] = rpcInfo; NetworkRpcInfo::RPCsTable[rpcName] = rpcInfo;
} }
void NetworkReplicator::CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds) bool NetworkReplicator::CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds)
{ {
EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream, MUtils::ToSpan<uint32>(targetIds)); return EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream, MUtils::ToSpan<uint32>(targetIds));
} }
StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name) StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name)
@@ -1113,12 +1113,12 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC()
return CachedWriteStream; return CachedWriteStream;
} }
void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds) bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds)
{ {
Scripting::ObjectsLookupIdMapping.Set(nullptr); Scripting::ObjectsLookupIdMapping.Set(nullptr);
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name)); const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
if (!info || !obj || NetworkManager::IsOffline()) if (!info || !obj || NetworkManager::IsOffline())
return; return false;
ObjectsLock.Lock(); ObjectsLock.Lock();
auto& rpc = RpcQueue.AddOne(); auto& rpc = RpcQueue.AddOne();
rpc.Object = obj; rpc.Object = obj;
@@ -1135,6 +1135,16 @@ void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
} }
#endif #endif
ObjectsLock.Unlock(); ObjectsLock.Unlock();
// Check if skip local execution (eg. server rpc called from client or client rpc with specific targets)
const NetworkManagerMode networkMode = NetworkManager::Mode;
if (info->Server && networkMode == NetworkManagerMode::Client)
return true;
if (info->Client && networkMode == NetworkManagerMode::Server)
return true;
if (info->Client && networkMode == NetworkManagerMode::Host && targetIds.IsValid() && !SpanContains(targetIds, NetworkManager::LocalClientId))
return true;
return false;
} }
void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client) void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)

View File

@@ -120,10 +120,11 @@ namespace FlaxEngine.Networking
/// <param name="name">The RPC name.</param> /// <param name="name">The RPC name.</param>
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param> /// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param> /// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
/// <returns>True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids).</returns>
[Unmanaged] [Unmanaged]
public static void EndInvokeRPC(Object obj, Type type, string name, NetworkStream argsStream, uint[] targetIds = null) public static bool EndInvokeRPC(Object obj, Type type, string name, NetworkStream argsStream, uint[] targetIds = null)
{ {
Internal_CSharpEndInvokeRPC(FlaxEngine.Object.GetUnmanagedPtr(obj), type, name, FlaxEngine.Object.GetUnmanagedPtr(argsStream), targetIds); return Internal_CSharpEndInvokeRPC(FlaxEngine.Object.GetUnmanagedPtr(obj), type, name, FlaxEngine.Object.GetUnmanagedPtr(argsStream), targetIds);
} }
/// <summary> /// <summary>

View File

@@ -195,13 +195,14 @@ public:
/// <param name="name">The RPC name.</param> /// <param name="name">The RPC name.</param>
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param> /// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param> /// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
static void EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds = Span<uint32>()); /// <returns>True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids).</returns>
static bool EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds = Span<uint32>());
private: private:
#if !COMPILE_WITHOUT_CSHARP #if !COMPILE_WITHOUT_CSHARP
API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function<void(void*, void*)>& serialize, const Function<void(void*, void*)>& deserialize); API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function<void(void*, void*)>& serialize, const Function<void(void*, void*)>& deserialize);
API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function<void(void*, void*)>& execute, bool isServer, bool isClient, NetworkChannelType channel); API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function<void(void*, void*)>& execute, bool isServer, bool isClient, NetworkChannelType channel);
API_FUNCTION(NoProxy) static void CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds); API_FUNCTION(NoProxy) static bool CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds);
static StringAnsiView GetCSharpCachedName(const StringAnsiView& name); static StringAnsiView GetCSharpCachedName(const StringAnsiView& name);
#endif #endif
}; };

View File

@@ -41,7 +41,7 @@ struct FLAXENGINE_API NetworkRpcInfo
uint8 Client : 1; uint8 Client : 1;
uint8 Channel : 4; uint8 Channel : 4;
void (*Execute)(ScriptingObject* obj, NetworkStream* stream, void* tag); void (*Execute)(ScriptingObject* obj, NetworkStream* stream, void* tag);
void (*Invoke)(ScriptingObject* obj, void** args); bool (*Invoke)(ScriptingObject* obj, void** args);
void* Tag; void* Tag;
/// <summary> /// <summary>
@@ -83,10 +83,7 @@ FORCE_INLINE void NetworkRpcInitArg(Array<void*, FixedAllocation<16>>& args, con
{ \ { \
Array<void*, FixedAllocation<16>> args; \ Array<void*, FixedAllocation<16>> args; \
NetworkRpcInitArg(args, __VA_ARGS__); \ NetworkRpcInitArg(args, __VA_ARGS__); \
rpcInfo.Invoke(this, args.Get()); \ if (rpcInfo.Invoke(this, args.Get())) \
if (rpcInfo.Server && networkMode == NetworkManagerMode::Client) \
return; \
if (rpcInfo.Client && networkMode == NetworkManagerMode::Server) \
return; \ return; \
} \ } \
} }

View File

@@ -254,7 +254,7 @@ namespace Flax.Build.Plugins
// Generated method thunk to invoke RPC to network // Generated method thunk to invoke RPC to network
{ {
contents.Append(" static void ").Append(functionInfo.Name).AppendLine("_Invoke(ScriptingObject* obj, void** args)"); contents.Append(" static bool ").Append(functionInfo.Name).AppendLine("_Invoke(ScriptingObject* obj, void** args)");
contents.AppendLine(" {"); contents.AppendLine(" {");
contents.AppendLine(" NetworkStream* stream = NetworkReplicator::BeginInvokeRPC();"); contents.AppendLine(" NetworkStream* stream = NetworkReplicator::BeginInvokeRPC();");
contents.AppendLine(" Span<uint32> targetIds;"); contents.AppendLine(" Span<uint32> targetIds;");
@@ -274,7 +274,7 @@ namespace Flax.Build.Plugins
} }
// Invoke RPC // Invoke RPC
contents.AppendLine($" NetworkReplicator::EndInvokeRPC(obj, {typeInfo.NativeName}::TypeInitializer, StringAnsiView(\"{functionInfo.Name}\", {functionInfo.Name.Length}), stream, targetIds);"); contents.AppendLine($" return NetworkReplicator::EndInvokeRPC(obj, {typeInfo.NativeName}::TypeInitializer, StringAnsiView(\"{functionInfo.Name}\", {functionInfo.Name.Length}), stream, targetIds);");
contents.AppendLine(" }"); contents.AppendLine(" }");
} }
contents.AppendLine(); contents.AppendLine();
@@ -1753,10 +1753,10 @@ namespace Flax.Build.Plugins
if (jumpBodyEnd == null) if (jumpBodyEnd == null)
throw new Exception("Missing IL Return op code in method " + method.Name); throw new Exception("Missing IL Return op code in method " + method.Name);
il.Emit(OpCodes.Ldloc, varsStart + 0); il.Emit(OpCodes.Ldloc, varsStart + 0);
il.Emit(OpCodes.Brfalse_S, jumpIf2Start); il.Emit(OpCodes.Brfalse, jumpIf2Start);
il.Emit(OpCodes.Ldloc, varsStart + 2); il.Emit(OpCodes.Ldloc, varsStart + 2);
il.Emit(OpCodes.Ldc_I4_2); il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Beq_S, jumpIfBodyStart); il.Emit(OpCodes.Beq, jumpIfBodyStart);
// || // ||
il.Emit(jumpIf2Start); il.Emit(jumpIf2Start);
il.Emit(OpCodes.Ldloc, varsStart + 1); il.Emit(OpCodes.Ldloc, varsStart + 1);
@@ -1812,35 +1812,12 @@ namespace Flax.Build.Plugins
else else
il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldnull);
var endInvokeRPC = networkReplicatorType.Resolve().GetMethod("EndInvokeRPC", 5); var endInvokeRPC = networkReplicatorType.Resolve().GetMethod("EndInvokeRPC", 5);
if (endInvokeRPC.ReturnType.FullName != boolType.FullName)
throw new Exception("Invalid EndInvokeRPC return type. Remove any 'Binaries' folders to force project recompile.");
il.Emit(OpCodes.Call, module.ImportReference(endInvokeRPC)); il.Emit(OpCodes.Call, module.ImportReference(endInvokeRPC));
// if (server && networkMode == NetworkManagerMode.Client) return; // if (EndInvokeRPC) return
if (methodRPC.IsServer) il.Emit(OpCodes.Brtrue, jumpBodyEnd);
{
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldloc, varsStart + 2);
il.Emit(OpCodes.Ldc_I4_2);
var tmp = ilp.Create(OpCodes.Nop);
il.Emit(OpCodes.Beq_S, tmp);
il.Emit(OpCodes.Br, jumpBodyStart);
il.Emit(tmp);
//il.Emit(OpCodes.Ret);
il.Emit(OpCodes.Br, jumpBodyEnd);
}
// if (client && networkMode == NetworkManagerMode.Server) return;
if (methodRPC.IsClient)
{
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldloc, varsStart + 2);
il.Emit(OpCodes.Ldc_I4_1);
var tmp = ilp.Create(OpCodes.Nop);
il.Emit(OpCodes.Beq_S, tmp);
il.Emit(OpCodes.Br, jumpBodyStart);
il.Emit(tmp);
//il.Emit(OpCodes.Ret);
il.Emit(OpCodes.Br, jumpBodyEnd);
}
// Continue to original method body // Continue to original method body
il.Emit(jumpBodyStart); il.Emit(jumpBodyStart);