// Copyright (c) Wojciech Figat. All rights reserved. using System; using System.Runtime.InteropServices; using System.Collections.Generic; namespace FlaxEngine.Networking { partial class NetworkReplicator { private static Dictionary> _managedSerializers; private static List _managedExecuteRpcFuncs; #if FLAX_EDITOR private static void ClearManagedReferences() { // Clear refs to managed types that will be hot-reloaded if (_managedSerializers != null) { _managedSerializers.Clear(); _managedSerializers = null; } if (_managedExecuteRpcFuncs != null) { _managedExecuteRpcFuncs.Clear(); _managedExecuteRpcFuncs = null; } FlaxEditor.ScriptsBuilder.ScriptsReloadBegin -= ClearManagedReferences; } #endif /// /// Network object replication serialization/deserialization delegate. /// /// /// Use Object.FromUnmanagedPtr(instancePtr/streamPtr) to get object or NetworkStream from raw native pointers. /// /// var instance = Object.FromUnmanagedPtr(instancePtr) /// var stream = (NetworkStream)Object.FromUnmanagedPtr(streamPtr) public delegate void SerializeFunc(IntPtr instancePtr, IntPtr streamPtr); /// /// Network RPC executing delegate. /// /// /// Use Object.FromUnmanagedPtr(objPtr/streamPtr) to get object or NetworkStream from raw native pointers. /// /// var instance = Object.FromUnmanagedPtr(instancePtr) /// var stream = (NetworkStream)Object.FromUnmanagedPtr(streamPtr) public delegate void ExecuteRPCFunc(IntPtr instancePtr, IntPtr streamPtr); /// /// Registers a new serialization methods for a given C# type. /// /// /// Use Object.FromUnmanagedPtr(instancePtr/streamPtr) to get object or NetworkStream from raw native pointers. /// /// The C# type (class or structure). /// Function to call for value serialization. /// Function to call for value deserialization. [Unmanaged] public static void AddSerializer(Type type, SerializeFunc serialize, SerializeFunc deserialize) { if (_managedSerializers == null) { _managedSerializers = new Dictionary>(); _managedExecuteRpcFuncs = new List(); #if FLAX_EDITOR FlaxEditor.ScriptsBuilder.ScriptsReloadBegin += ClearManagedReferences; #endif } _managedSerializers[type] = new KeyValuePair(serialize, deserialize); // C#-only types (eg. custom C# structures) cannot use native serializers due to missing ScriptingType if (typeof(FlaxEngine.Object).IsAssignableFrom(type)) { Internal_AddSerializer(type, Marshal.GetFunctionPointerForDelegate(serialize), Marshal.GetFunctionPointerForDelegate(deserialize)); } } /// /// Invokes the network replication serializer for a given type. /// /// The scripting type to serialize. /// The value instance to serialize. /// The input/output stream to use for serialization. /// True if serialize, otherwise deserialize mode. /// True if failed, otherwise false. [Unmanaged] public static bool InvokeSerializer(Type type, FlaxEngine.Object instance, NetworkStream stream, bool serialize) { return Internal_InvokeSerializer(type, FlaxEngine.Object.GetUnmanagedPtr(instance), FlaxEngine.Object.GetUnmanagedPtr(stream), serialize); } /// /// Invokes the network replication serializer for a given type. /// /// The scripting type to serialize. /// The value instance to serialize. /// The input/output stream to use for serialization. /// True if serialize, otherwise deserialize mode. /// True if failed, otherwise false. [Unmanaged] public static bool InvokeSerializer(System.Type type, IntPtr instance, NetworkStream stream, bool serialize) { if (_managedSerializers != null && _managedSerializers.TryGetValue(type, out var e)) { var serializer = serialize ? e.Key : e.Value; serializer(instance, FlaxEngine.Object.GetUnmanagedPtr(stream)); return false; } return Internal_InvokeSerializer(type, instance, FlaxEngine.Object.GetUnmanagedPtr(stream), serialize); } /// /// Ends invoking the RPC. /// /// The target object to invoke RPC. /// The RPC type. /// 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 bool EndInvokeRPC(Object obj, Type type, string name, NetworkStream argsStream, uint[] targetIds = null) { return Internal_CSharpEndInvokeRPC(FlaxEngine.Object.GetUnmanagedPtr(obj), type, name, FlaxEngine.Object.GetUnmanagedPtr(argsStream), targetIds); } /// /// Registers a RPC method for a given C# method. /// /// The C# type (FlaxEngine.Object). /// The RPC method name (from that type). /// Function to call for RPC execution. /// Server RPC. /// Client RPC. /// Network channel to use for RPC transport. [Unmanaged] 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); } } }