diff --git a/Source/Engine/Networking/NetworkReplicator.cs b/Source/Engine/Networking/NetworkReplicator.cs index dc2bc77c8..54e0e893e 100644 --- a/Source/Engine/Networking/NetworkReplicator.cs +++ b/Source/Engine/Networking/NetworkReplicator.cs @@ -9,14 +9,23 @@ namespace FlaxEngine.Networking partial class NetworkReplicator { private static Dictionary> _managedSerializers; + private static List _managedExecuteRpcFuncs; #if FLAX_EDITOR - private static void OnScriptsReloadBegin() + private static void ClearManagedReferences() { // Clear refs to managed types that will be hot-reloaded - _managedSerializers.Clear(); - _managedSerializers = null; - FlaxEditor.ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; + if (_managedSerializers != null) + { + _managedSerializers.Clear(); + _managedSerializers = null; + } + if (_managedExecuteRpcFuncs != null) + { + _managedExecuteRpcFuncs.Clear(); + _managedExecuteRpcFuncs = null; + } + FlaxEditor.ScriptsBuilder.ScriptsReloadBegin -= ClearManagedReferences; } #endif @@ -55,8 +64,9 @@ namespace FlaxEngine.Networking if (_managedSerializers == null) { _managedSerializers = new Dictionary>(); + _managedExecuteRpcFuncs = new List(); #if FLAX_EDITOR - FlaxEditor.ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; + FlaxEditor.ScriptsBuilder.ScriptsReloadBegin += ClearManagedReferences; #endif } _managedSerializers[type] = new KeyValuePair(serialize, deserialize); @@ -125,10 +135,22 @@ namespace FlaxEngine.Networking /// Client RPC. /// Network channel to use for RPC transport. [Unmanaged] - public static void AddRPC(Type type, string name, ExecuteRPCFunc execute, bool isServer = true, bool isClient = false, NetworkChannelType channel = NetworkChannelType.ReliableOrdered) + public static unsafe void AddRPC(Type type, string name, ExecuteRPCFunc execute, bool isServer = true, bool isClient = false, NetworkChannelType channel = NetworkChannelType.ReliableOrdered) { if (!typeof(FlaxEngine.Object).IsAssignableFrom(type)) throw new ArgumentException("Not supported type for RPC. Only FlaxEngine.Object types are valid."); + + if (_managedExecuteRpcFuncs == null) + { + _managedSerializers = new Dictionary>(); + _managedExecuteRpcFuncs = new List(); +#if FLAX_EDITOR + FlaxEditor.ScriptsBuilder.ScriptsReloadBegin += ClearManagedReferences; +#endif + } + // Store the reference to prevent garbage collection + _managedExecuteRpcFuncs.Add(execute); + Internal_AddRPC(type, name, Marshal.GetFunctionPointerForDelegate(execute), isServer, isClient, channel); } } diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 9f7d7222a..c60f13064 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -983,7 +983,15 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly) { if (method->GetParametersCount() == 0) { - method->Invoke(nullptr, nullptr, nullptr); + MObject* exception = nullptr; + method->Invoke(nullptr, nullptr, &exception); + if (exception) + { + MException ex(exception); + String methodName = String(method->GetName()); + ex.Log(LogType::Error, methodName.Get()); + LOG(Error, "Failed to call module initializer for class {0} from assembly {1}.", String(mclass->GetFullName()), assembly->ToString()); + } } } } diff --git a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs index fcc1ca83b..83918e102 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs @@ -13,7 +13,7 @@ using Mono.Cecil.Cil; namespace Flax.Build.Plugins { /// - /// Flax.Build plugin for Networking extenrions support. Generates required bindings glue code for automatic types replication and RPCs invoking. + /// Flax.Build plugin for Networking extensions support. Generates required bindings glue code for automatic types replication and RPCs invoking. /// /// internal sealed class NetworkingPlugin : Plugin @@ -476,14 +476,14 @@ namespace Flax.Build.Plugins if (!binaryModule.Any(module => module.Tags.ContainsKey(Network))) return; - // Generate netoworking code inside assembly after it's being compiled + // Generate networking code inside assembly after it's being compiled var assemblyPath = buildTask.ProducedFiles[0]; var task = graph.Add(); task.ProducedFiles.Add(assemblyPath); task.WorkingDirectory = buildTask.WorkingDirectory; task.Command = () => OnPatchDotNetAssembly(buildData, buildOptions, buildTask, assemblyPath); task.CommandPath = null; - task.InfoMessage = $"Generating netowrking code for {Path.GetFileName(assemblyPath)}..."; + task.InfoMessage = $"Generating networking code for {Path.GetFileName(assemblyPath)}..."; task.Cost = 50; task.DisableCache = true; task.DependentTasks = new HashSet(); @@ -580,7 +580,7 @@ namespace Flax.Build.Plugins } else if (isNetworkReplicated) { - // Generate serializization methods + // Generate serialization methods GenerateSerializer(type, true, ref failed, Thunk1, voidType, networkStreamType); GenerateSerializer(type, false, ref failed, Thunk2, voidType, networkStreamType); modified = true; @@ -588,7 +588,7 @@ namespace Flax.Build.Plugins } else if (!isINetworkSerializable && isNetworkReplicated) { - // Generate serializization methods + // Generate serialization methods var addSerializer = new TypeSerializer(); addSerializer.Type = type; addSerializer.Serialize = GenerateNativeSerializer(type, true, ref failed, Thunk1, voidType, networkStreamType); @@ -1274,7 +1274,7 @@ namespace Flax.Build.Plugins // Generate static method to execute RPC locally { - var m = new MethodDefinition(method.Name + "_Execute", MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig, voidType); + var m = new MethodDefinition(method.Name + "_Execute", MethodAttributes.Static | MethodAttributes.Assembly | MethodAttributes.HideBySig, voidType); m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtrType)); m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, module.ImportReference(intPtrType))); ILProcessor il = m.Body.GetILProcessor();