diff --git a/Source/Engine/Core/Types/Span.h b/Source/Engine/Core/Types/Span.h index 678550e85..e7bea5fbd 100644 --- a/Source/Engine/Core/Types/Span.h +++ b/Source/Engine/Core/Types/Span.h @@ -114,3 +114,14 @@ inline Span ToSpan(const T* ptr, int32 length) { return Span(ptr, length); } + +template +inline bool SpanContains(const Span span, const T& value) +{ + for (int32 i = 0; i < span.Length(); i++) + { + if (span.Get()[i] == value) + return true; + } + return false; +} diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index c41e104ef..ae7549a50 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -700,9 +700,9 @@ void NetworkReplicator::AddRPC(const ScriptingTypeHandle& typeHandle, const Stri 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(targetIds)); + return EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream, MUtils::ToSpan(targetIds)); } StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name) @@ -1113,12 +1113,12 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC() return CachedWriteStream; } -void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span targetIds) +bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span targetIds) { Scripting::ObjectsLookupIdMapping.Set(nullptr); const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name)); if (!info || !obj || NetworkManager::IsOffline()) - return; + return false; ObjectsLock.Lock(); auto& rpc = RpcQueue.AddOne(); rpc.Object = obj; @@ -1135,6 +1135,16 @@ void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa } #endif 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) diff --git a/Source/Engine/Networking/NetworkReplicator.cs b/Source/Engine/Networking/NetworkReplicator.cs index 1ca251f75..bc437b82e 100644 --- a/Source/Engine/Networking/NetworkReplicator.cs +++ b/Source/Engine/Networking/NetworkReplicator.cs @@ -120,10 +120,11 @@ namespace FlaxEngine.Networking /// The RPC name. /// The RPC serialized arguments stream returned from BeginInvokeRPC. /// Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs. + /// True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids). [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); } /// diff --git a/Source/Engine/Networking/NetworkReplicator.h b/Source/Engine/Networking/NetworkReplicator.h index 156e7eeea..ecccc52cd 100644 --- a/Source/Engine/Networking/NetworkReplicator.h +++ b/Source/Engine/Networking/NetworkReplicator.h @@ -195,13 +195,14 @@ public: /// The RPC name. /// The RPC serialized arguments stream returned from BeginInvokeRPC. /// Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs. - static void EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span targetIds = Span()); + /// True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids). + static bool EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span targetIds = Span()); private: #if !COMPILE_WITHOUT_CSHARP API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function& serialize, const Function& deserialize); API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function& 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); #endif }; diff --git a/Source/Engine/Networking/NetworkRpc.h b/Source/Engine/Networking/NetworkRpc.h index c0c8a558e..6e8d26752 100644 --- a/Source/Engine/Networking/NetworkRpc.h +++ b/Source/Engine/Networking/NetworkRpc.h @@ -41,7 +41,7 @@ struct FLAXENGINE_API NetworkRpcInfo uint8 Client : 1; uint8 Channel : 4; void (*Execute)(ScriptingObject* obj, NetworkStream* stream, void* tag); - void (*Invoke)(ScriptingObject* obj, void** args); + bool (*Invoke)(ScriptingObject* obj, void** args); void* Tag; /// @@ -83,10 +83,7 @@ FORCE_INLINE void NetworkRpcInitArg(Array>& args, con { \ Array> args; \ NetworkRpcInitArg(args, __VA_ARGS__); \ - rpcInfo.Invoke(this, args.Get()); \ - if (rpcInfo.Server && networkMode == NetworkManagerMode::Client) \ - return; \ - if (rpcInfo.Client && networkMode == NetworkManagerMode::Server) \ + if (rpcInfo.Invoke(this, args.Get())) \ return; \ } \ } diff --git a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs index 21464ab55..b24b887b3 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs @@ -254,7 +254,7 @@ namespace Flax.Build.Plugins // 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(" NetworkStream* stream = NetworkReplicator::BeginInvokeRPC();"); contents.AppendLine(" Span targetIds;"); @@ -274,7 +274,7 @@ namespace Flax.Build.Plugins } // 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(); @@ -1753,10 +1753,10 @@ namespace Flax.Build.Plugins if (jumpBodyEnd == null) throw new Exception("Missing IL Return op code in method " + method.Name); 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.Ldc_I4_2); - il.Emit(OpCodes.Beq_S, jumpIfBodyStart); + il.Emit(OpCodes.Beq, jumpIfBodyStart); // || il.Emit(jumpIf2Start); il.Emit(OpCodes.Ldloc, varsStart + 1); @@ -1812,35 +1812,12 @@ namespace Flax.Build.Plugins else il.Emit(OpCodes.Ldnull); 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)); - // if (server && networkMode == NetworkManagerMode.Client) return; - if (methodRPC.IsServer) - { - 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); - } + // if (EndInvokeRPC) return + il.Emit(OpCodes.Brtrue, jumpBodyEnd); // Continue to original method body il.Emit(jumpBodyStart);