diff --git a/.gitmodules b/.gitmodules index b95ace0aa4..6e808b3753 100644 --- a/.gitmodules +++ b/.gitmodules @@ -441,3 +441,7 @@ [submodule "vendor/boost-submodules/boost-tokenizer"] path = vendor/boost-submodules/boost-tokenizer url = https://github.com/boostorg/tokenizer +[submodule "vendor/msgpack-cs"] + path = vendor/msgpack-cs + url = https://github.com/citizenfx/msgpack-cs.git + branch = production diff --git a/code/client/clrcore-v2/Attributes.cs b/code/client/clrcore-v2/Attributes.cs index b282b20adf..3e19e6f54f 100644 --- a/code/client/clrcore-v2/Attributes.cs +++ b/code/client/clrcore-v2/Attributes.cs @@ -49,7 +49,7 @@ public class CommandAttribute : Attribute public bool Restricted { get; set; } /// - /// + /// public bool RemapParameters { get; set; } = false; public CommandAttribute(string command, bool restricted = false) { @@ -77,7 +77,7 @@ public class KeyMapAttribute : Attribute public string InputParameter { get; } /// - /// + /// public bool RemapParameters { get; set; } = false; /// diff --git a/code/client/clrcore-v2/BaseScript.cs b/code/client/clrcore-v2/BaseScript.cs index bd4ea15e38..fa90272cf3 100644 --- a/code/client/clrcore-v2/BaseScript.cs +++ b/code/client/clrcore-v2/BaseScript.cs @@ -1,6 +1,8 @@ +using CitizenFX.MsgPack; using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Reflection; using System.Security; @@ -33,9 +35,9 @@ protected event Func Tick remove => UnregisterTick(value); } - private readonly List> m_commands = new List>(); + private readonly List> m_commands = new List>(); - private readonly Dictionary m_nuiCallbacks = new Dictionary(); + private readonly Dictionary m_nuiCallbacks = new Dictionary(); #if REMOTE_FUNCTION_ENABLED private readonly List m_persistentFunctions = new List(); @@ -92,22 +94,30 @@ internal void Initialize() break; case EventHandlerAttribute eventHandler: - RegisterEventHandler(eventHandler.Event, Func.Create(this, method), eventHandler.Binding); + RegisterEventHandler(eventHandler.Event, MsgPackDeserializer.CreateDelegate(this, method), eventHandler.Binding); break; case CommandAttribute command: - RegisterCommand(command.Command, Func.CreateCommand(this, method, command.RemapParameters), command.Restricted); + { + // Automatically remap methods with [Source] parameters + bool remap = command.RemapParameters || method.GetParameters().Any(p => Attribute.GetCustomAttribute(p, typeof(SourceAttribute)) != null); + RegisterCommand(command.Command, MsgPackDeserializer.CreateCommandDelegate(this, method, remap), command.Restricted); + } break; #if !IS_FXSERVER case KeyMapAttribute keyMap: - RegisterKeyMap(keyMap.Command, keyMap.Description, keyMap.InputMapper, keyMap.InputParameter, Func.CreateCommand(this, method, keyMap.RemapParameters)); + { + // Automatically remap methods with [Source] parameters + bool remap = keyMap.RemapParameters || method.GetParameters().Any(p => Attribute.GetCustomAttribute(p, typeof(SourceAttribute)) != null); + RegisterKeyMap(keyMap.Command, keyMap.Description, keyMap.InputMapper, keyMap.InputParameter, MsgPackDeserializer.CreateCommandDelegate(this, method, remap)); + } break; case NuiCallbackAttribute nuiCallback: RegisterNuiCallback(nuiCallback.CallbackName, Func.Create(this, method)); break; #endif case ExportAttribute export: - Exports.Add(export.Export, Func.Create(this, method), export.Binding); + Exports.Add(export.Export, MsgPackDeserializer.CreateDelegate(this, method), export.Binding); break; } } @@ -179,7 +189,7 @@ public void Disable() // commands for (int i = 0; i < m_commands.Count; ++i) { - ReferenceFunctionManager.SetDelegate(m_commands[i].Key, (_0, _1) => null); + ReferenceFunctionManager.SetDelegate(m_commands[i].Key, delegate(Remote _0, ref MsgPackDeserializer _1) { return null; }); } foreach (var nuiCallback in m_nuiCallbacks) @@ -280,13 +290,13 @@ public void UnregisterTick(Func tick) #endregion #region Events & Command registration - internal void RegisterEventHandler(string eventName, DynFunc deleg, Binding binding = Binding.Local) => EventHandlers[eventName].Add(deleg, binding); - internal void UnregisterEventHandler(string eventName, DynFunc deleg) => EventHandlers[eventName].Remove(deleg); + internal void RegisterEventHandler(string eventName, MsgPackFunc deleg, Binding binding = Binding.Local) => EventHandlers[eventName].Add(deleg, binding); + internal void UnregisterEventHandler(string eventName, MsgPackFunc deleg) => EventHandlers[eventName].Remove(deleg); - internal void RegisterCommand(string command, DynFunc dynFunc, bool isRestricted = true) - => m_commands.Add(new KeyValuePair(ReferenceFunctionManager.CreateCommand(command, dynFunc, isRestricted), dynFunc)); + internal void RegisterCommand(string command, MsgPackFunc dynFunc, bool isRestricted = true) + => m_commands.Add(new KeyValuePair(ReferenceFunctionManager.CreateCommand(command, dynFunc, isRestricted), dynFunc)); - internal void RegisterKeyMap(string command, string description, string inputMapper, string inputParameter, DynFunc dynFunc) + internal void RegisterKeyMap(string command, string description, string inputMapper, string inputParameter, MsgPackFunc dynFunc) { #if !GTA_FIVE throw new NotImplementedException(); @@ -295,7 +305,7 @@ internal void RegisterKeyMap(string command, string description, string inputMap { Native.CoreNatives.RegisterKeyMapping(command, description, inputMapper, inputParameter); } - m_commands.Add(new KeyValuePair(ReferenceFunctionManager.CreateCommand(command, dynFunc, false), dynFunc)); + m_commands.Add(new KeyValuePair(ReferenceFunctionManager.CreateCommand(command, dynFunc, false), dynFunc)); #endif } @@ -303,7 +313,7 @@ internal void RegisterKeyMap(string command, string description, string inputMap #region NUI Callback registration - internal void RegisterNuiCallback(string callbackName, DynFunc dynFunc) + internal void RegisterNuiCallback(string callbackName, MsgPackFunc dynFunc) { #if IS_FXSERVER throw new NotImplementedException(); @@ -326,7 +336,7 @@ public void RegisterNuiCallback(string callbackName, Delegate delegateFn) #if IS_FXSERVER throw new NotImplementedException(); #endif - DynFunc dynFunc = Func.Create(delegateFn); + MsgPackFunc dynFunc = MsgPackDeserializer.CreateDelegate(delegateFn); m_nuiCallbacks.Add(callbackName, dynFunc); Native.CoreNatives.RegisterNuiCallback(callbackName, dynFunc); } diff --git a/code/client/clrcore-v2/DynFunc.cs b/code/client/clrcore-v2/DynFunc.cs index 330d1f79f3..71b3e31cfd 100644 --- a/code/client/clrcore-v2/DynFunc.cs +++ b/code/client/clrcore-v2/DynFunc.cs @@ -102,10 +102,6 @@ public static DynFunc Create(object target, MethodInfo method) // no need to recreate it public static DynFunc Create(DynFunc deleg) => deleg; - [SecurityCritical] - internal static DynFunc CreateCommand(object target, MethodInfo method, bool remap) - => remap ? ConstructCommandRemapped(target, method) : Construct(target, method); - [SecurityCritical] private static DynFunc Construct(object target, MethodInfo method) { @@ -216,140 +212,6 @@ private static DynFunc Construct(object target, MethodInfo method) } } -#if DYN_FUNC_CALLI - g.Emit(OpCodes.Ldc_I8, (long)method.MethodHandle.GetFunctionPointer()); - g.EmitCalli(OpCodes.Calli, method.CallingConvention, method.ReturnType, parameterTypes, null); -#else - g.EmitCall(OpCodes.Call, method, null); -#endif - - if (method.ReturnType == typeof(void)) - g.Emit(OpCodes.Ldnull); - else - g.Emit(OpCodes.Box, method.ReturnType); - - g.Emit(OpCodes.Ret); - - Delegate dynFunc = lambda.CreateDelegate(typeof(DynFunc), target); - - s_wrappedMethods.Add(method, dynFunc.Method); - s_dynfuncMethods.Add(dynFunc.Method, method); - - return (DynFunc)dynFunc; - } - - /// - /// If enabled creates a that remaps input ( source, [] arguments, raw) to known types:
- /// source: , , , , , or any type constructable from including Player types.
- /// arguments: [] or [].
- /// raw: - ///
- /// Method's associated instance - /// Method to wrap - /// Dynamic invocable with remapping and conversion support. - /// When is used on a non supported type. - /// When any requested parameter isn't supported. - [SecurityCritical] - private static DynFunc ConstructCommandRemapped(object target, MethodInfo method) - { - if (s_wrappedMethods.TryGetValue(method, out var existingMethod)) - { - return (DynFunc)existingMethod.CreateDelegate(typeof(DynFunc), target); - } - - ParameterInfo[] parameters = method.GetParameters(); -#if DYN_FUNC_CALLI - Type[] parameterTypes = new Type[parameters.Length]; -#endif - bool hasThis = (method.CallingConvention & CallingConventions.HasThis) != 0; - - var lambda = new DynamicMethod($"{method.DeclaringType.FullName}.{method.Name}", typeof(object), - hasThis ? new[] { typeof(object), typeof(Remote), typeof(object[]) } : new[] { typeof(Remote), typeof(object[]) }); - - ILGenerator g = lambda.GetILGenerator(); - - OpCode ldarg_args; - if (hasThis) - { - g.Emit(OpCodes.Ldarg_0); - ldarg_args = OpCodes.Ldarg_2; - } - else - { - target = null; - ldarg_args = OpCodes.Ldarg_1; - } - - for (int i = 0; i < parameters.Length; ++i) - { - var parameter = parameters[i]; - var t = parameter.ParameterType; - -#if DYN_FUNC_CALLI - parameterTypes[i] = t; -#endif - if (Attribute.IsDefined(parameter, typeof(SourceAttribute), true)) // source - { - g.Emit(ldarg_args); - g.Emit(OpCodes.Ldc_I4_0); - g.Emit(OpCodes.Ldelem_Ref); - g.Emit(OpCodes.Call, GetMethodInfo(Convert.ToUInt16)); - - if (t.IsPrimitive) - { - if (t == typeof(int) || t == typeof(uint) || t == typeof(ushort)) - { - // 16 bit integers are pushed onto the evaluation stack as 32 bit integers - continue; - } - else if (t == typeof(bool)) - { - g.Emit(OpCodes.Ldc_I4_0); - g.Emit(OpCodes.Cgt_Un); - continue; - } - } - else if (t == typeof(Remote)) - { - g.Emit(OpCodes.Call, ((Func)Remote.Create).Method); - continue; - } - else - { - var constructor = t.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { typeof(Remote) }, null); - if (constructor != null) - { - g.Emit(OpCodes.Call, ((Func)Remote.Create).Method); - g.Emit(OpCodes.Newobj, constructor); - continue; - } - } - - throw new ArgumentException($"{nameof(SourceAttribute)} used on type {t}, this type can't be constructed with parameter Remote."); - } - else if (t == typeof(object[])) // arguments; simply pass it on - { - g.Emit(ldarg_args); - g.Emit(OpCodes.Ldc_I4_S, 1); - g.Emit(OpCodes.Ldelem_Ref); - } - else if (t == typeof(string[])) // arguments; convert to string[] - { - g.Emit(ldarg_args); - g.Emit(OpCodes.Ldc_I4_S, 1); - g.Emit(OpCodes.Ldelem_Ref); - g.EmitCall(OpCodes.Call, ((Func)ConvertToStringArray).Method, null); - } - else if (t == typeof(string)) // raw data; simply pass it on - { - g.Emit(ldarg_args); - g.Emit(OpCodes.Ldc_I4_S, 2); - g.Emit(OpCodes.Ldelem_Ref); - } - else - throw new TargetParameterCountException($"Command can't be registered with requested remapping, type {t} is not supported."); - } - #if DYN_FUNC_CALLI g.Emit(OpCodes.Ldc_I8, (long)method.MethodHandle.GetFunctionPointer()); g.EmitCalli(OpCodes.Calli, method.CallingConvention, method.ReturnType, parameterTypes, null); @@ -445,20 +307,5 @@ public static MethodInfo GetWrappedMethod(this DynFunc dynFunc) { return s_dynfuncMethods.TryGetValue(dynFunc.Method, out var existingMethod) ? existingMethod : dynFunc.Method; } - - #region Helper functions - - internal static string[] ConvertToStringArray(object[] objects) - { - string[] result = new string[objects.Length]; - for (int i = 0; i < objects.Length; ++i) - { - result[i] = objects[i]?.ToString(); - } - - return result; - } - - #endregion } } diff --git a/code/client/clrcore-v2/Interop/Events.cs b/code/client/clrcore-v2/Interop/Events.cs index 2f96f853c2..1831b6a0a6 100644 --- a/code/client/clrcore-v2/Interop/Events.cs +++ b/code/client/clrcore-v2/Interop/Events.cs @@ -1,4 +1,5 @@ using CitizenFX.Core.Native; +using CitizenFX.MsgPack; using System; using System.ComponentModel; @@ -17,7 +18,7 @@ public static class Events /// name to listen for /// delegate to call once triggered /// limit calls to certain sources, e.g.: server only, client only - public static void RegisterEventHandler(string eventName, DynFunc handler, Binding binding = Binding.Local) + public static void RegisterEventHandler(string eventName, MsgPackFunc handler, Binding binding = Binding.Local) { if (handler.Target is BaseScript script) { @@ -35,7 +36,7 @@ public static void RegisterEventHandler(string eventName, DynFunc handler, Bindi /// /// name to remove event for /// delegate to remove - public static void UnregisterEventHandler(string eventName, DynFunc handler) + public static void UnregisterEventHandler(string eventName, MsgPackFunc handler) { if (handler.Target is BaseScript script) { diff --git a/code/client/clrcore-v2/Interop/EventsManager.cs b/code/client/clrcore-v2/Interop/EventsManager.cs index 58ce9878f6..cb82ec155e 100644 --- a/code/client/clrcore-v2/Interop/EventsManager.cs +++ b/code/client/clrcore-v2/Interop/EventsManager.cs @@ -1,3 +1,4 @@ +using CitizenFX.MsgPack; using System; using System.Collections.Generic; using System.Linq; @@ -9,55 +10,55 @@ namespace CitizenFX.Core { internal static class EventsManager { - private static Dictionary>> s_eventHandlers = new Dictionary>>(); + private static Dictionary>> s_eventHandlers = new Dictionary>>(); [SecuritySafeCritical] - internal static unsafe void IncomingEvent(string eventName, string sourceString, Binding origin, byte* argsSerialized, int serializedSize, object[] args) + internal static unsafe void IncomingEvent(string eventName, string sourceString, Binding origin, byte* argsSerialized, int serializedSize) { if (s_eventHandlers.TryGetValue(eventName, out var delegateList)) { - if (args is null) - args = MsgPackDeserializer.DeserializeArray(argsSerialized, serializedSize, origin == Binding.Remote ? sourceString : null); + Remote remote = new Remote(origin, sourceString); - if (args != null) + CitizenFX.MsgPack.MsgPackDeserializer deserializer = new MsgPackDeserializer(argsSerialized, (ulong)serializedSize, origin == Binding.Remote ? sourceString : null); + var restorePoint = deserializer.CreateRestorePoint(); + + for (int i = 0; i < delegateList.Count; ++i) { - Remote remote = new Remote(origin, sourceString); + var ev = delegateList[i]; - for (int i = 0; i < delegateList.Count; ++i) + try { - var ev = delegateList[i]; - - try - { - if ((ev.Item2 & origin) != 0) - { - var result = ev.Item1(remote, args); - if (result != null) - return; - } - } - catch (Exception ex) + if ((ev.Item2 & origin) != 0) { - Debug.WriteException(ex, ev.Item1, args, "event handler"); + var result = ev.Item1(remote, ref deserializer); + if (result is bool b && !b) + return; + + deserializer.Restore(restorePoint); } } + catch (Exception ex) + { + //Debug.WriteException(ex, ev.Item1, args, "event handler"); + Debug.WriteLine(ex); + } } } } #region Registration - internal static void AddEventHandler(string eventName, DynFunc del, Binding binding = Binding.Local) + internal static void AddEventHandler(string eventName, MsgPackFunc del, Binding binding = Binding.Local) { if (!s_eventHandlers.TryGetValue(eventName, out var delegateList)) { - delegateList = new List>(); + delegateList = new List>(); s_eventHandlers.Add(eventName, delegateList); CoreNatives.RegisterResourceAsEventHandler(eventName); } - delegateList.Add(new Tuple(del, binding)); + delegateList.Add(new Tuple(del, binding)); } internal static void RemoveEventHandler(string eventName, Delegate del) @@ -93,7 +94,7 @@ public class EventHandler : Dictionary set { /* ignore, this will enable += syntax */ } } - public void Add(string key, DynFunc value) => this[key].Add(value); + public void Add(string key, MsgPackFunc value) => this[key].Add(value); /// /// Should only be called by or any other code that guarantees that it is only called once @@ -118,7 +119,7 @@ internal void Disable() public class EventHandlerSet { private readonly string m_eventName; - private readonly List m_handlers = new List(); + private readonly List m_handlers = new List(); public EventHandlerSet(string eventName) { @@ -135,7 +136,7 @@ public EventHandlerSet(string eventName) /// /// delegate to call once triggered /// limit calls to certain sources, e.g.: server only, client only - public EventHandlerSet Add(DynFunc deleg, Binding binding = Binding.Local) + public EventHandlerSet Add(MsgPackFunc deleg, Binding binding = Binding.Local) { m_handlers.Add(deleg); EventsManager.AddEventHandler(m_eventName, deleg, binding); @@ -160,11 +161,11 @@ public EventHandlerSet Remove(Delegate deleg) /// /// Register an event handler /// - /// Will add it as , use to explicitly set the binding. + /// Will add it as , use to explicitly set the binding. /// this event handler set /// delegate to register /// itself - public static EventHandlerSet operator +(EventHandlerSet entry, DynFunc deleg) => entry.Add(deleg); + public static EventHandlerSet operator +(EventHandlerSet entry, MsgPackFunc deleg) => entry.Add(deleg); /// /// Unregister an event handler @@ -172,16 +173,16 @@ public EventHandlerSet Remove(Delegate deleg) /// this event handler set /// delegate to register /// itself - public static EventHandlerSet operator -(EventHandlerSet entry, DynFunc deleg) => entry.Remove(deleg); + public static EventHandlerSet operator -(EventHandlerSet entry, MsgPackFunc deleg) => entry.Remove(deleg); /// /// Register an event handler /// - /// Will add it as , use to explicitly set the binding. + /// Will add it as , use to explicitly set the binding. /// this event handler set /// delegate to register /// itself - public static EventHandlerSet operator +(EventHandlerSet entry, Delegate deleg) => entry.Add(Func.Create(deleg)); + public static EventHandlerSet operator +(EventHandlerSet entry, Delegate deleg) => entry.Add(MsgPackDeserializer.CreateDelegate(deleg)); /// /// Unregister an event handler diff --git a/code/client/clrcore-v2/Interop/Exports.cs b/code/client/clrcore-v2/Interop/Exports.cs index 3ea871b18c..5051dc273a 100644 --- a/code/client/clrcore-v2/Interop/Exports.cs +++ b/code/client/clrcore-v2/Interop/Exports.cs @@ -1,3 +1,4 @@ +using CitizenFX.MsgPack; using System; using System.ComponentModel; using System.Collections.Generic; @@ -6,7 +7,7 @@ namespace CitizenFX.Core { public class Exports { - private Dictionary m_exports => new Dictionary(); + private Dictionary m_exports => new Dictionary(); public static LocalExports Local { get; } = new LocalExports(); @@ -58,7 +59,7 @@ internal void Disable() } } - public DynFunc this[string export] + public MsgPackFunc this[string export] { set => Add(export, value); get => m_exports[export]; @@ -71,7 +72,7 @@ public DynFunc this[string export] } #endif - public void Add(string name, DynFunc method, Binding binding = Binding.Local) + public void Add(string name, MsgPackFunc method, Binding binding = Binding.Local) { if (ExportsManager.AddExportHandler(name, method, binding)) m_exports.Add(name, method); diff --git a/code/client/clrcore-v2/Interop/ExportsManager.cs b/code/client/clrcore-v2/Interop/ExportsManager.cs index ebd33a0930..1c22f5462e 100644 --- a/code/client/clrcore-v2/Interop/ExportsManager.cs +++ b/code/client/clrcore-v2/Interop/ExportsManager.cs @@ -3,6 +3,7 @@ using System.Security; using System.Runtime.CompilerServices; using CitizenFX.Core.Native; +using CitizenFX.MsgPack; namespace CitizenFX.Core { @@ -12,7 +13,7 @@ internal static class ExportsManager { internal static string ExportPrefix { get; private set; } - private static Dictionary> s_exports = new Dictionary>(); + private static Dictionary> s_exports = new Dictionary>(); internal static void Initialize(string resourceName) { @@ -20,12 +21,11 @@ internal static void Initialize(string resourceName) } [SecuritySafeCritical] - internal static unsafe bool IncomingRequest(string eventName, string sourceString, Binding origin, byte* argsSerialized, int serializedSize, ref object[] args) + internal static unsafe bool IncomingRequest(string eventName, string sourceString, Binding origin, byte* argsSerialized, int serializedSize) { if (s_exports.TryGetValue(eventName, out var export) && (export.Item2 & origin) != 0) { - if (args == null) - args = MsgPackDeserializer.DeserializeArray(argsSerialized, serializedSize, origin == Binding.Remote ? sourceString : null); + object[] args = MsgPackDeserializer.DeserializeAsObjectArray(argsSerialized, serializedSize, origin == Binding.Remote ? sourceString : null); if (origin == Binding.Local) { @@ -33,6 +33,7 @@ internal static unsafe bool IncomingRequest(string eventName, string sourceStrin if (args[0] is Callback cb) cb.Invoke(export.Item1); } +#if REMOTE_FUNCTION_ENABLED else // REMOTE export request { if (args.Length > 3 @@ -90,6 +91,7 @@ internal static unsafe bool IncomingRequest(string eventName, string sourceStrin } } } +#endif return true; } @@ -144,7 +146,7 @@ internal static Coroutine LocalInvoke(CString fullExportName, params obj } [SecuritySafeCritical] - internal static bool AddExportHandler(string name, DynFunc method, Binding ports = Binding.Local) + internal static bool AddExportHandler(string name, MsgPackFunc method, Binding ports = Binding.Local) { #if !REMOTE_FUNCTION_ENABLED if (ports == Binding.Remote) @@ -157,7 +159,7 @@ internal static bool AddExportHandler(string name, DynFunc method, Binding ports string eventName = CreateCurrentResourceFullExportName(name); if (!s_exports.ContainsKey(eventName)) { - s_exports.Add(eventName, new Tuple(method, ports)); + s_exports.Add(eventName, new Tuple(method, ports)); CoreNatives.RegisterResourceAsEventHandler(eventName); return true; diff --git a/code/client/clrcore-v2/Interop/ExternalsManager.cs b/code/client/clrcore-v2/Interop/ExternalsManager.cs index 458fa3bb19..26f860b2fe 100644 --- a/code/client/clrcore-v2/Interop/ExternalsManager.cs +++ b/code/client/clrcore-v2/Interop/ExternalsManager.cs @@ -1,5 +1,6 @@ #if REMOTE_FUNCTION_ENABLED using CitizenFX.Core.Native; +using CitizenFX.MsgPack; using System; using System.Collections.Generic; using System.Linq; @@ -29,14 +30,14 @@ internal static void Initialize(string resourceName, int instanceId) #endif } - internal static unsafe bool IncomingRequest(string eventName, string sourceString, Binding origin, byte* argsSerialized, int serializedSize, ref object[] args) + internal static unsafe bool IncomingRequest(string eventName, string sourceString, Binding origin, byte* argsSerialized, int serializedSize) { // only accepted for remote, might change in the future if (origin == Binding.REMOTE) { if (eventName == RpcRequestName) { - args = MsgPackDeserializer.DeserializeArguments(argsSerialized, serializedSize, origin == Binding.Remote ? sourceString : null); + var args = MsgPackDeserializer.DeserializeArguments(argsSerialized, serializedSize, origin == Binding.Remote ? sourceString : null); if (args.Length > 3 && args[2] is string requestId) { ulong rid = Convert.ToUInt64(requestId); diff --git a/code/client/clrcore-v2/Interop/MsgPackDeserializer.cs b/code/client/clrcore-v2/Interop/MsgPackDeserializer.cs deleted file mode 100644 index bbd4861fc5..0000000000 --- a/code/client/clrcore-v2/Interop/MsgPackDeserializer.cs +++ /dev/null @@ -1,338 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security; - -namespace CitizenFX.Core -{ - public delegate Coroutine Callback(params object[] args); - - // Can be a struct as it's merely used for for temporary storage - [SecuritySafeCritical] - internal struct MsgPackDeserializer - { - private unsafe byte* m_ptr; - private readonly unsafe byte* m_end; - private readonly string m_netSource; - - private unsafe MsgPackDeserializer(byte* data, ulong size, string netSource) - { - m_ptr = data; - m_end = data + size; - m_netSource = netSource; - } - - internal static unsafe object Deserialize(byte[] data, string netSource = null) - { - if (data?.Length > 0) - { - fixed (byte* dataPtr = data) - return Deserialize(dataPtr, data.Length, netSource); - } - - return null; - } - - internal static unsafe object Deserialize(byte* data, long size, string netSource = null) - { - if (data != null && size > 0) - { - var deserializer = new MsgPackDeserializer(data, (ulong)size, netSource); - return deserializer.Deserialize(); - } - - return null; - } - - /// - /// Starts deserialization from an array type - /// - /// ptr to byte data - /// size of byte data - /// from whom came this? - /// arguments that can be passed into dynamic delegates - public static unsafe object[] DeserializeArray(byte* data, long size, string netSource = null) - { - if (data != null && size > 0) - { - var deserializer = new MsgPackDeserializer(data, (ulong)size, netSource); - return deserializer.DeserializeArray(); - } - - return new object[0]; - } - - private unsafe object[] DeserializeArray() - { - int length; - var type = ReadByte(); - - // should start with an array - if (type >= 0x90 && type < 0xA0) - length = type % 16; - else if (type == 0xDC) - length = ReadUInt16(); - else if (type == 0xDD) - length = ReadInt32(); - else - return new object[0]; - - object[] array = new object[length]; - for (var i = 0; i < length; ++i) - { - array[i] = Deserialize(); - } - - return array; - } - - private object Deserialize() - { - var type = ReadByte(); - - if (type < 0xC0) - { - if (type < 0x80) - { - return type; - } - else if (type < 0x90) - { - return ReadMap(type % 16u); - } - else if (type < 0xA0) - { - return ReadObjectArray(type % 16u); - } - - return ReadString(type % 32u); - } - else if (type > 0xDF) - { - return type - 256; // fix negative number - } - - switch (type) - { - case 0xC0: return null; - - case 0xC2: return false; - case 0xC3: return true; - - case 0xC4: return ReadBytes(ReadUInt8()); - case 0xC5: return ReadBytes(ReadUInt16()); - case 0xC6: return ReadBytes(ReadUInt32()); - - case 0xC7: return ReadExtraType(ReadUInt8()); - case 0xC8: return ReadExtraType(ReadUInt16()); - case 0xC9: return ReadExtraType(ReadUInt32()); - - case 0xCA: return ReadSingle(); - case 0xCB: return ReadDouble(); - - case 0xCC: return ReadUInt8(); - case 0xCD: return ReadUInt16(); - case 0xCE: return ReadUInt32(); - case 0xCF: return ReadUInt64(); - - case 0xD0: return ReadInt8(); - case 0xD1: return ReadInt16(); - case 0xD2: return ReadInt32(); - case 0xD3: return ReadInt64(); - - case 0xD4: return ReadExtraType(1); - case 0xD5: return ReadExtraType(2); - case 0xD6: return ReadExtraType(4); - case 0xD7: return ReadExtraType(8); - case 0xD8: return ReadExtraType(16); - - case 0xD9: return ReadString(ReadUInt8()); - case 0xDA: return ReadString(ReadUInt16()); - case 0xDB: return ReadString(ReadUInt32()); - - case 0xDC: return ReadObjectArray(ReadUInt16()); - case 0xDD: return ReadObjectArray(ReadUInt32()); - - case 0xDE: return ReadMap(ReadUInt16()); - case 0xDF: return ReadMap(ReadUInt32()); - } - - throw new InvalidOperationException($"Tried to decode invalid MsgPack type {type}"); - } - - private IDictionary ReadMap(uint length) - { - var retobject = new ExpandoObject() as IDictionary; - - for (var i = 0; i < length; i++) - { - var key = Deserialize().ToString(); - var value = Deserialize(); - - retobject.Add(key, value); - } - - return retobject; - } - - private unsafe byte[] ReadBytes(uint length) - { - var ptr = (IntPtr)AdvancePointer(length); - - byte[] retobject = new byte[length]; - Marshal.Copy(ptr, retobject, 0, (int)length); - - return retobject; - } - - private object[] ReadObjectArray(uint length) - { - object[] retobject = new object[length]; - - for (var i = 0; i < length; i++) - { - retobject[i] = Deserialize(); - } - - return retobject; - } - - private unsafe float ReadSingle() - { - var v = ReadUInt32(); - return *(float*)&v; - } - - /// - /// Read a stored as little endian, used for custom vectors - /// - private unsafe float ReadSingleLE() - { - uint v = *(uint*)AdvancePointer(4); - - if (!BitConverter.IsLittleEndian) - { - v = (v >> 16) | (v << 16); // swap adjacent 16-bit blocks - v = ((v & 0xFF00FF00u) >> 8) | ((v & 0x00FF00FFu) << 8); // swap adjacent 8-bit blocks - } - - return *(float*)&v; - } - - private unsafe double ReadDouble() - { - var v = ReadUInt64(); - return *(double*)&v; - } - - private unsafe byte ReadByte() - { - byte v = *AdvancePointer(1); - return v; - } - - private unsafe byte ReadUInt8() => ReadByte(); - - private unsafe ushort ReadUInt16() - { - uint v = *(ushort*)AdvancePointer(2); - - if (BitConverter.IsLittleEndian) - v = (ushort)((v >> 8) | (v << 8)); // swap adjacent 8-bit blocks - - return (ushort)v; - } - - private unsafe uint ReadUInt32() - { - uint v = *(uint*)AdvancePointer(4); - - if (BitConverter.IsLittleEndian) - { - v = (v >> 16) | (v << 16); // swap adjacent 16-bit blocks - v = ((v & 0xFF00FF00u) >> 8) | ((v & 0x00FF00FFu) << 8); // swap adjacent 8-bit blocks - } - - return v; - } - - private unsafe ulong ReadUInt64() - { - ulong v = *(ulong*)AdvancePointer(8); - - if (BitConverter.IsLittleEndian) - { - v = (v >> 32) | (v << 32); // swap adjacent 32-bit blocks - v = ((v & 0xFFFF0000FFFF0000u) >> 16) | ((v & 0x0000FFFF0000FFFFu) << 16); // swap adjacent 16-bit blocks - v = ((v & 0xFF00FF00FF00FF00u) >> 8) | ((v & 0x00FF00FF00FF00FFu) << 8); // swap adjacent 8-bit blocks - } - - return v; - } - - private sbyte ReadInt8() => unchecked((sbyte)ReadUInt8()); - - private short ReadInt16() => unchecked((short)ReadUInt16()); - - private int ReadInt32() => unchecked((int)ReadUInt32()); - - private long ReadInt64() => unchecked((long)ReadUInt64()); - - private unsafe string ReadString(uint length) - { - sbyte* v = (sbyte*)AdvancePointer(length); - return new string(v, 0, (int)length); - } - - [SecuritySafeCritical] - private unsafe CString ReadCString(uint length) - { - byte* v = AdvancePointer(length); - return CString.Create(v, length); - } - - private object ReadExtraType(uint length) - { - var extType = ReadByte(); - switch (extType) - { - case 10: // remote funcref - case 11: // local funcref - var refFunc = ReadString(length); - return m_netSource is null - ? _LocalFunction.Create(refFunc) -#if REMOTE_FUNCTION_ENABLED - : _RemoteFunction.Create(refFunc, m_netSource); -#else - : null; -#endif - case 20: // vector2 - return new Vector2(ReadSingleLE(), ReadSingleLE()); - case 21: // vector3 - return new Vector3(ReadSingleLE(), ReadSingleLE(), ReadSingleLE()); - case 22: // vector4 - return new Vector4(ReadSingleLE(), ReadSingleLE(), ReadSingleLE(), ReadSingleLE()); - case 23: // quaternion - return new Quaternion(ReadSingleLE(), ReadSingleLE(), ReadSingleLE(), ReadSingleLE()); - default: // shouldn't ever happen due to the check above - throw new InvalidOperationException($"Extension type {extType} not supported."); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe byte* AdvancePointer(uint amount) - { - byte* curPtr = m_ptr; - m_ptr += amount; - if (m_ptr > m_end) - { - m_ptr -= amount; // reverse damage - throw new ArgumentException($"MsgPackDeserializer tried to retrieve {amount} bytes while only {m_end - m_ptr} bytes remain"); - } - - return curPtr; - } - } -} diff --git a/code/client/clrcore-v2/Interop/MsgPackSerializer.cs b/code/client/clrcore-v2/Interop/MsgPackSerializer.cs deleted file mode 100644 index 1b3c5c65cf..0000000000 --- a/code/client/clrcore-v2/Interop/MsgPackSerializer.cs +++ /dev/null @@ -1,421 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using MsgPack; -using MsgPack.Serialization; -using System.Security; -using System.Runtime.InteropServices; - -namespace CitizenFX.Core -{ - [SecuritySafeCritical] - internal static class MsgPackSerializer - { - internal readonly static ByteArray nullResult; - - [ThreadStatic] - private static bool ts_remote; - - [SecuritySafeCritical] - static unsafe MsgPackSerializer() - { - byte* nullData = (byte*)Marshal.AllocCoTaskMem(1); - *nullData = 0xC0; - nullResult = new ByteArray(nullData, 1); - } - - internal static byte[] Serialize(object obj, bool remote = false) - { - if (obj is object) // not null - { - ts_remote = remote; - - var stream = new MemoryStream(256); - using (var packer = Packer.Create(stream, PackerCompatibilityOptions.None)) - { - Serialize(obj, packer); - return stream.ToArray(); // other runtimes read the full length we give them, so shrink the buffer (copy) - //return stream.GetBuffer(); - } - } - - return new byte[] { 0xC0 }; // null - } - - /// - /// Experimental, uses the CoTask memory instead of a managed byte[] - /// - /// - /// - /// - internal static ByteArray SerializeCoTaskMemory(object obj, bool remote = false) - { - if (obj is object) // not null - { - ts_remote = remote; - - var stream = new CoTaskMemoryStream(256); - using (var packer = Packer.Create(stream, PackerCompatibilityOptions.None)) - { - Serialize(obj, packer); - //return stream.ToArray(); - return stream.TakeBuffer(); - } - } - - return nullResult; - } - - [SecuritySafeCritical] - private static void Serialize(object obj, Packer packer) - { - // This msgpack packer is heaven if you love checking all arguments *multiple* times and throwing exceptions all over the place (hence the checking it multiple times), lol - // SerializationContext.Default.GetSerializer().PackTo(packer, obj) == packer.Pack(obj) without all the hoops and checks - void Pack(Packer p, T o) => SerializationContext.Default.GetSerializer().PackTo(p, o); - - if (obj is null) - { - packer.PackNull(); - return; - } - - var type = obj.GetType(); - - if (type.IsPrimitive - || type == typeof(string) || type == typeof(decimal) - || type == typeof(DateTime) || type == typeof(Guid)) - { - Pack(packer, obj); - return; - } - else if (type.IsEnum) - { - // enums in C# may only be of an integer types - Pack(packer, Convert.ToUInt64(obj)); - return; - } - else if (type.IsValueType) // structs in C# - { - switch (obj) - { - case Vector2 vec2: - unsafe - { - byte[] data = new byte[2 * sizeof(float)]; - fixed (byte* ptr = data) - { - *((float*)ptr) = vec2.X; - *((float*)ptr + 1) = vec2.Y; - } - packer.PackExtendedTypeValue(20, data); - } - return; - - case Vector3 vec3: - unsafe - { - byte[] data = new byte[3 * sizeof(float)]; - fixed (byte* ptr = data) - { - *((float*)ptr) = vec3.X; - *((float*)ptr + 1) = vec3.Y; - *((float*)ptr + 2) = vec3.Z; - } - packer.PackExtendedTypeValue(21, data); - } - return; - - case Vector4 vec4: - unsafe - { - byte[] data = new byte[4 * sizeof(float)]; - fixed (byte* ptr = data) - { - *((float*)ptr) = vec4.X; - *((float*)ptr + 1) = vec4.Y; - *((float*)ptr + 2) = vec4.Z; - *((float*)ptr + 3) = vec4.W; - } - packer.PackExtendedTypeValue(22, data); - } - return; - - case Quaternion quat: - unsafe - { - byte[] data = new byte[4 * sizeof(float)]; - fixed (byte* ptr = data) - { - *((float*)ptr) = quat.X; - *((float*)ptr + 1) = quat.Y; - *((float*)ptr + 2) = quat.Z; - *((float*)ptr + 3) = quat.W; - } - packer.PackExtendedTypeValue(23, data); - } - return; - } - } - else if(obj is IEnumerable enumerable) // this includes any container like, arrays and generic ones (ICollection) - { - switch (enumerable) - { - case byte[] bytes: - Pack(packer, bytes); - return; - - case IDictionary dictStringObject: // more common than below, also faster iteration - { - packer.PackMapHeader(dictStringObject.Count); - foreach (var kvp in dictStringObject) - { - Serialize(kvp.Key, packer); - Serialize(kvp.Value, packer); - } - } - return; - - case IDictionary dict: // less common than above, will handle all types of dictionaries. 1.3 (30%) times slower than above - { - packer.PackMapHeader(dict.Count); - foreach (DictionaryEntry kvp in dict) - { - Serialize(kvp.Key, packer); - Serialize(kvp.Value, packer); - } - } - return; - - case IList array: // any array like, including T[] and List - { - packer.PackArrayHeader(array.Count); - for (int i = 0; i < array.Count; ++i) - { - Serialize(array[i], packer); - } - } - return; - - default: // unknown/undefined size, so go through them - { - var list = new List(); - foreach (var item in enumerable) - { - list.Add(item); - } - - packer.PackArrayHeader(list.Count); - for (int i = 0; i < list.Count; ++i) - { - Serialize(list[i], packer); - } - } - return; - } - } - - switch (obj) - { - case IPackable packable: - packable.PackToMessage(packer, null); - return; - - case Delegate deleg: - PackDelegate(packer, deleg); - return; - } - - // no suitable conversion found, serialize all public properties into a dictionary - // TODO: check if this is really what we want - var dictionary = new Dictionary(); - - var properties = type.GetProperties(); - for (int i = 0; i < properties.Length; ++i) - { - var property = properties[i]; - dictionary[property.Name] = property.GetValue(obj, null); - } - - /* - // fields as well? - var fields = type.GetFields(); - for (int i = 0; i < fields.Length; ++i) - { - var field = fields[i]; - dictionary[field.Name] = field.GetValue(obj); - }*/ - - Serialize(dictionary, packer); - } - - [SecuritySafeCritical] - private static void PackDelegate(Packer packer, in Delegate deleg) - { - /*if (deleg is Callback) - { - var funcRef = deleg.Method.DeclaringType?.GetFields( - BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static - ).FirstOrDefault(a => a.FieldType == typeof(RemoteFunction)); - - if (funcRef != null) - { - var remoteFunction = (RemoteFunction)funcRef.GetValue(deleg.Target); - - DevDebug.WriteLine($"MsgPackSerializer:WriteDelegate({deleg}) {deleg.Method} {deleg.GetHashCode()} as ?"); - packer.PackExtendedTypeValue(10, remoteFunction.Duplicate()); - } - else - { - throw new ArgumentException("The CallbackDelegate does not contain a RemoteFunction capture."); - } - - return; - }*/ - - var dynFunc = deleg as DynFunc ?? Func.Create(deleg); - - if (!ts_remote) - { - var refType = ReferenceFunctionManager.Create(dynFunc); - packer.PackExtendedTypeValue(11, refType.Value); - } - else - { -#if REMOTE_FUNCTION_ENABLED - ulong callbackId; - - if (deleg.Target is _RemoteHandler _pf) - { - callbackId = _pf.m_id; - } - else - { - callbackId = ExternalsManager.RegisterRemoteFunction(deleg.Method.ReturnType, new DynFunc(args => - args.Length == 1 || args[1] == null ? dynFunc(args[0]) : null)); - } - - packer.PackExtendedTypeValue(10, Encoding.UTF8.GetBytes(callbackId.ToString())); -#endif - packer.PackNull(); - } - } - } - - /// - /// Experimental memory stream - /// - [SecuritySafeCritical] - internal class CoTaskMemoryStream : Stream - { - private unsafe IntPtr buffer; - private int capacity; - private int position; - private int length; - - private bool canRead; - private bool canWrite; - - public override bool CanRead => canRead; - - public override bool CanSeek => false; - - public override bool CanWrite => canWrite; - - public override long Length => length; - - public override long Position - { - get => position; - set - { - if (value < Length) - position = (int)value & int.MaxValue; // makes sure it's always >= 0 - else - throw new NotSupportedException(); - } - } - - public unsafe CoTaskMemoryStream(byte* ptr, int size) - { - buffer = (IntPtr)ptr; - capacity = length = size; - position = 0; - canRead = true; - canWrite = false; - } - - public unsafe CoTaskMemoryStream(int initialSize = 64) - { - buffer = Marshal.AllocCoTaskMem(initialSize); - capacity = initialSize; - position = length = 0; - canRead = false; - canWrite = true; - } - - [SecurityCritical] - public unsafe ByteArray TakeBuffer() - { - var result = new ByteArray((byte*)buffer, (ulong)length); - - buffer = IntPtr.Zero; - canRead = canWrite = false; - capacity = length = position = 0; - - return result; - } - - [SecuritySafeCritical] - public unsafe override void SetLength(long value) - { - int size = (int)value; - - if (size < capacity) - length = size; - else if (value > capacity) - EnsureCapacity(size); - } - - private void EnsureCapacity(int newCapacity) - { - // same setup as MemoryStream - int num = newCapacity; - if (num < 256) - num = 256; - - if (num < capacity * 2) - num = capacity * 2; - - if ((uint)(capacity * 2) > 2147483591u) - num = ((newCapacity > 2147483591) ? newCapacity : 2147483591); - - buffer = Marshal.ReAllocCoTaskMem(buffer, num); - capacity = newCapacity; - } - - [SecuritySafeCritical] - public unsafe override void Write(byte[] buffer, int offset, int count) - { - if (position + count > capacity) - EnsureCapacity(position + count); - - Marshal.Copy(buffer, offset, this.buffer + position, count); - } - - public override void Flush() { } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - [SecuritySafeCritical] - public override int Read(byte[] buffer, int offset, int count) - { - int length = Math.Min(count, this.length - position); - Marshal.Copy(this.buffer + position, buffer, offset, length); - return length; - } - } -} diff --git a/code/client/clrcore-v2/Interop/ReferenceFunctionManager.cs b/code/client/clrcore-v2/Interop/ReferenceFunctionManager.cs index 0ad406c287..22ebb9a3b7 100644 --- a/code/client/clrcore-v2/Interop/ReferenceFunctionManager.cs +++ b/code/client/clrcore-v2/Interop/ReferenceFunctionManager.cs @@ -1,3 +1,4 @@ +using CitizenFX.MsgPack; using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -10,11 +11,11 @@ internal static class ReferenceFunctionManager { internal class Function { - public DynFunc m_method; + public MsgPackFunc m_method; public readonly byte[] m_refId; public int m_refCount; - public Function(DynFunc method, byte[] id) + public Function(MsgPackFunc method, byte[] id) { m_method = method; m_refId = id; @@ -34,7 +35,7 @@ public Function(DynFunc method, byte[] id) /// ( internalReferenceId, externalReferenceId ) /// Don't alter the returned value [SecuritySafeCritical] - internal static KeyValuePair Create(DynFunc method) + internal static KeyValuePair Create(MsgPackFunc method) { // TODO: change return type to `ValueTuple` once clients support those @@ -101,7 +102,7 @@ internal static void RemoveAllWithTarget(object target) /// Reference id of the reference to remove /// New delegate/method to set the reference function to /// if found and changed, otherwise - internal static bool SetDelegate(int referenceId, DynFunc newFunc) + internal static bool SetDelegate(int referenceId, MsgPackFunc newFunc) { if (s_references.TryGetValue(referenceId, out var refFunc)) { @@ -112,7 +113,7 @@ internal static bool SetDelegate(int referenceId, DynFunc newFunc) return false; } - internal static int CreateCommand(string command, DynFunc method, bool isRestricted) + internal static int CreateCommand(string command, MsgPackFunc method, bool isRestricted) { var registration = Create(method); Native.CoreNatives.RegisterCommand(command, new Native.InFunc(registration.Value), isRestricted); @@ -164,18 +165,19 @@ internal unsafe static byte[] Invoke(int reference, byte* arguments, uint argsSi { if (s_references.TryGetValue(reference, out var funcRef)) { - var args = MsgPackDeserializer.DeserializeArray(arguments, argsSize); + var deserializer = new MsgPackDeserializer(arguments, argsSize, null); object result = null; try { // there's no remote invocation support through here - result = funcRef.m_method(default, args); + result = funcRef.m_method(default, ref deserializer); } catch (Exception ex) { - Debug.WriteException(ex, funcRef.m_method, args, "reference function"); + //Debug.WriteException(ex, funcRef.m_method, args, "reference function"); + Debug.WriteLine(ex); } if (result is Coroutine coroutine) @@ -187,7 +189,7 @@ internal unsafe static byte[] Invoke(int reference, byte* arguments, uint argsSi Debug.Write(coroutine.Exception); } - return MsgPackSerializer.Serialize(new[] { coroutine.GetResultNonThrowing(), coroutine.Exception?.ToString() }); + return MsgPackSerializer.SerializeToByteArray(new[] { coroutine.GetResultNonThrowing(), coroutine.Exception?.ToString() }); } else { @@ -206,11 +208,11 @@ internal unsafe static byte[] Invoke(int reference, byte* arguments, uint argsSi } }; - return MsgPackSerializer.Serialize(new object[] { returnDictionary }); + return MsgPackSerializer.SerializeToByteArray(new object[] { returnDictionary }); } } - return MsgPackSerializer.Serialize(new[] { result }); + return MsgPackSerializer.SerializeToByteArray(new[] { result }); } else { diff --git a/code/client/clrcore-v2/Interop/Types/CString.cs b/code/client/clrcore-v2/Interop/Types/CString.cs index 4874cefa41..7c910b832d 100644 --- a/code/client/clrcore-v2/Interop/Types/CString.cs +++ b/code/client/clrcore-v2/Interop/Types/CString.cs @@ -3,6 +3,7 @@ using System.Security; using System.Runtime.InteropServices; using System.Text; +using System.Runtime.CompilerServices; namespace CitizenFX.Core { @@ -402,6 +403,32 @@ public static unsafe bool CompareASCII(CString left, string right) } } + /// + /// Case insensitive variant of + [SecuritySafeCritical] + public static unsafe bool CompareASCIICaseInsensitive(CString left, string right) + { + if (left == null && right == null) + return true; + else + { + fixed (byte* lPin = left.value) + fixed (char* rPin = right) + { + byte* l = lPin, lEnd = lPin + left.value.Length - 1; + char* r = rPin, rEnd = rPin + right.Length; + while (l < lEnd && r < rEnd) + { + char c1 = *r++; + if (c1 > 0x7F || !ToLowerEquals((byte)c1, *l++)) + return false; + } + } + + return true; + } + } + /// /// Converts a C# string to a CString only accepting ASCII characters (7 bits) /// any non 7 bit character will be become the given value @@ -429,6 +456,21 @@ public static unsafe CString ToASCII(string str, byte invalid = (byte)'?') return null; } + [SecuritySafeCritical, MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool ToLowerEquals(byte l, byte r) + { + const uint offset = 'A'; + const byte toLower = 'a' - 'A'; + + if (l - offset < 26) + l += toLower; + + if (r - offset < 26) + r += toLower; + + return l == r; + } + #endregion #region Encoding algorithms @@ -449,7 +491,7 @@ public override unsafe int GetHashCode() } [SecurityCritical] - private static unsafe int UTF8EncodeLength(char* src, int length) + internal static unsafe int UTF8EncodeLength(char* src, int length) { char* c = src, end = src + length; length = 0; @@ -476,7 +518,7 @@ private static unsafe int UTF8EncodeLength(char* src, int length) } [SecurityCritical] - private static unsafe int UTF8Encode(byte* dst, char* src, int length) + internal static unsafe int UTF8Encode(byte* dst, char* src, int length) { byte* s = dst; char* c = src, end = src + length; diff --git a/code/client/clrcore-v2/Interop/Types/Callback.cs b/code/client/clrcore-v2/Interop/Types/Callback.cs new file mode 100644 index 0000000000..5cae912492 --- /dev/null +++ b/code/client/clrcore-v2/Interop/Types/Callback.cs @@ -0,0 +1,4 @@ +namespace CitizenFX.Core +{ + public delegate Coroutine Callback(params object[] args); +} \ No newline at end of file diff --git a/code/client/clrcore-v2/Native/Native.cs b/code/client/clrcore-v2/Native/Native.cs index 69f4b2d54c..239625e893 100644 --- a/code/client/clrcore-v2/Native/Native.cs +++ b/code/client/clrcore-v2/Native/Native.cs @@ -1,4 +1,5 @@ using CitizenFX.Core.Native.Input; +using CitizenFX.MsgPack; using System; using System.Security; @@ -130,7 +131,7 @@ internal static unsafe object[] InvokeFunctionReference(CString referenceIdentit ulong* __data = stackalloc ulong[] { (ulong)p_referenceIdentity, (ulong)p_argsSerialized, unchecked((ulong)argsSerialized.value?.LongLength), (ulong)&retLength }; ScriptContext.InvokeNative(ref s_0xe3551879, 0xe3551879, __data, 4); // INVOKE_FUNCTION_REFERENCE - return MsgPackDeserializer.DeserializeArray(*(byte**)__data, (long)retLength); + return MsgPackDeserializer.DeserializeAsObjectArray(*(byte**)__data, (long)retLength); } } diff --git a/code/client/clrcore-v2/Native/ScriptContext.cs b/code/client/clrcore-v2/Native/ScriptContext.cs index 7a8ebc4f66..0b43574a0a 100644 --- a/code/client/clrcore-v2/Native/ScriptContext.cs +++ b/code/client/clrcore-v2/Native/ScriptContext.cs @@ -1,3 +1,4 @@ +using CitizenFX.MsgPack; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -53,7 +54,7 @@ internal static void CleanUp() [SecurityCritical] internal static unsafe ByteArray SerializeObject(object v) { - var argsSerialized = MsgPackSerializer.Serialize(v); + var argsSerialized = MsgPackSerializer.SerializeToByteArray(v); var handle = GCHandle.Alloc(argsSerialized, GCHandleType.Pinned); s_gcHandles.Enqueue(handle); @@ -142,7 +143,7 @@ internal unsafe static object GetResult(Type type, ulong* ptr, ulong hash) else if (type == typeof(object)) { if ((retSafetyInfo & (byte)PASFlags.OBJECT) != 0) - return MsgPackDeserializer.Deserialize((byte*)ptr[0], *(long*)&ptr[1]); + return MsgPackDeserializer.DeserializeAsObject((byte*)ptr[0], *(long*)&ptr[1]); } else if (type == typeof(Callback)) { diff --git a/code/client/clrcore-v2/Native/Types/NativeTypes.cs b/code/client/clrcore-v2/Native/Types/NativeTypes.cs index 0885b28b8a..8ab05f4fd0 100644 --- a/code/client/clrcore-v2/Native/Types/NativeTypes.cs +++ b/code/client/clrcore-v2/Native/Types/NativeTypes.cs @@ -1,3 +1,4 @@ +using CitizenFX.MsgPack; using System; using System.Runtime.InteropServices; using System.Security; @@ -39,7 +40,7 @@ public struct N64 [SecurityCritical] internal static unsafe ulong To_ulong(ulong* v) => *v; [SecurityCritical] internal static unsafe string ToString(ulong* v) => Marshal.PtrToStringAnsi((IntPtr)(byte*)v[0]); - [SecurityCritical] internal static unsafe object ToObject(ulong* v) => MsgPackDeserializer.Deserialize((byte*)v[0], unchecked((long)v[1])); + [SecurityCritical] internal static unsafe object ToObject(ulong* v) => MsgPackDeserializer.DeserializeAsObject((byte*)v[0], unchecked((long)v[1])); [SecurityCritical] internal static unsafe Vector3 To_Vector3(ulong* v) => (Vector3)(*(NativeVector3*)v); } @@ -66,17 +67,17 @@ public readonly ref struct InFunc internal readonly byte[] value; internal InFunc(byte[] funcRef) => value = funcRef; - public InFunc(DynFunc del) => value = ReferenceFunctionManager.Create(del).Value; - public InFunc(Delegate del) : this(Func.Create(del)) { } + public InFunc(MsgPackFunc del) => value = ReferenceFunctionManager.Create(del).Value; + public InFunc(Delegate del) : this(MsgPackDeserializer.CreateDelegate(del)) { } public static implicit operator InFunc(Delegate func) => new InFunc(func); - public static implicit operator InFunc(DynFunc func) => new InFunc(func); + public static implicit operator InFunc(MsgPackFunc func) => new InFunc(func); } public readonly ref struct InPacket { internal readonly byte[] value; - public InPacket(object obj) => value = MsgPackSerializer.Serialize(obj); + public InPacket(object obj) => value = MsgPackSerializer.SerializeToByteArray(obj); public static InPacket Serialize(object obj) => new InPacket(obj); public static implicit operator InPacket(object[] obj) => Serialize(obj); } @@ -89,7 +90,7 @@ public readonly ref struct OutPacket private unsafe readonly ulong size; #pragma warning restore 0649 - public unsafe object Deserialize() => MsgPackDeserializer.Deserialize(data, (long)size); - internal unsafe object[] DeserializeArray() => MsgPackDeserializer.DeserializeArray(data, (long)size); + public unsafe object Deserialize() => MsgPackDeserializer.DeserializeAsObject(data, (long)size); + internal unsafe object[] DeserializeArray() => MsgPackDeserializer.DeserializeAsObjectArray(data, (long)size); } } diff --git a/code/client/clrcore-v2/ScriptInterface.cs b/code/client/clrcore-v2/ScriptInterface.cs index 07e6d9535e..97c99bbd1b 100644 --- a/code/client/clrcore-v2/ScriptInterface.cs +++ b/code/client/clrcore-v2/ScriptInterface.cs @@ -144,15 +144,14 @@ internal static unsafe void TriggerEvent(string eventName, byte* argsSerialized, sourceString = origin == Binding.Remote ? sourceString : null; #endif - object[] args = null; // will make sure we only deserialize it once #if REMOTE_FUNCTION_ENABLED - if (!ExternalsManager.IncomingRequest(eventName, sourceString, origin, argsSerialized, serializedSize, ref args)) + if (!ExternalsManager.IncomingRequest(eventName, sourceString, origin, argsSerialized, serializedSize)) #endif { - if (!ExportsManager.IncomingRequest(eventName, sourceString, origin, argsSerialized, serializedSize, ref args)) + if (!ExportsManager.IncomingRequest(eventName, sourceString, origin, argsSerialized, serializedSize)) { // if a remote function or export has consumed this event then it surely wasn't meant for event handlers - EventsManager.IncomingEvent(eventName, sourceString, origin, argsSerialized, serializedSize, args); + EventsManager.IncomingEvent(eventName, sourceString, origin, argsSerialized, serializedSize); } } } diff --git a/code/client/clrcore-v2/Shared/Player.cs b/code/client/clrcore-v2/Shared/Player.cs index 796d36eb36..3c9e5ac8e9 100644 --- a/code/client/clrcore-v2/Shared/Player.cs +++ b/code/client/clrcore-v2/Shared/Player.cs @@ -11,7 +11,12 @@ public abstract class Player internal Player() { } - + + public Player(ulong handle) + { + m_handle = handle.ToString(); + } + /// /// Gets the handle of this player /// @@ -19,7 +24,7 @@ internal Player() #else public abstract class Player : Core.Native.Input.Primitive { - internal Player(ulong handle) : base(handle) + public Player(ulong handle) : base(handle) { } @@ -28,6 +33,8 @@ internal Player(ulong handle) : base(handle) /// public int Handle => (int)m_nativeValue; #endif + public override string ToString() => $"{nameof(Player)}({Handle})"; + /// /// Gets the name of this player /// diff --git a/code/client/clrcore/External/Player.cs b/code/client/clrcore/External/Player.cs index ba58d19e9d..e61ff6a0f0 100644 --- a/code/client/clrcore/External/Player.cs +++ b/code/client/clrcore/External/Player.cs @@ -2,7 +2,8 @@ #if MONO_V2 using CitizenFX.Core; -using INativeValue = CitizenFX.Shared.Player; +using CitizenFX.MsgPack; + using API = CitizenFX.FiveM.Native.Natives; using TaskBool = CitizenFX.Core.Coroutine; @@ -32,16 +33,17 @@ public enum ParachuteTint HighAltitude, Airbone, Sunrise - } - - public sealed class Player : INativeValue, IEquatable + } + +#if MONO_V2 + public sealed class Player : Shared.Player, IEquatable { - #region Fields -#if !MONO_V2 +#else + public sealed class Player : INativeValue, IEquatable + { private int _handle; #endif - Ped _ped; - #endregion + Ped _ped; private static Player m_player = new Player(0); public static Player Local @@ -62,7 +64,7 @@ public static Player Local #if MONO_V2 public Player(int handle) : base((ulong)handle) { - } + } #else public Player(int handle) { @@ -786,6 +788,16 @@ public int ServerId { return API.GetPlayerServerId(Handle); } - } + } + +#if MONO_V2 + #region Serializers + + public static void Serialize(MsgPackSerializer serializer, Player player) => serializer.Serialize(player.Handle); + + public static Player Deserialize(ref MsgPackDeserializer serializer) => new Player(serializer.DeserializeAsInt32()); + + #endregion +#endif } } diff --git a/code/client/clrcore/Server/ServerWrappers.cs b/code/client/clrcore/Server/ServerWrappers.cs index 0cf76541d3..3921c835dd 100644 --- a/code/client/clrcore/Server/ServerWrappers.cs +++ b/code/client/clrcore/Server/ServerWrappers.cs @@ -7,6 +7,7 @@ #if MONO_V2 using CitizenFX.Core; +using CitizenFX.MsgPack; using static CitizenFX.Server.Native.Natives; namespace CitizenFX.Server @@ -29,8 +30,12 @@ public class Player public string Handle => m_handle; #endif + public Player(int source) + { + m_handle = source.ToString(); + } - internal Player(string sourceString) + public Player(string sourceString) { if (sourceString.StartsWith("net:")) { @@ -137,6 +142,16 @@ public override bool Equals(object obj) public static bool operator ==(Player left, Player right) => Equals(left, right); public static bool operator !=(Player left, Player right) => !Equals(left, right); + +#if MONO_V2 + #region Serializers + + public static void Serialize(MsgPackSerializer serializer, Player player) => serializer.Serialize(player.Handle); + + public static Player Deserialize(ref MsgPackDeserializer serializer) => new Player(serializer.DeserializeAsInt32()); + + #endregion +#endif } public class IdentifierCollection : IEnumerable diff --git a/code/premake5.lua b/code/premake5.lua index 670553cbd6..87d4e9335e 100644 --- a/code/premake5.lua +++ b/code/premake5.lua @@ -633,6 +633,14 @@ if _OPTIONS['game'] ~= 'launcher' then 'client/clrcore-v2/Math/Vector4.cs', } + -- Add MsgPack source files directly, otherwise we'd get a cyclic dependency + files { '../vendor/msgpack-cs/MsgPack/**.cs' } + removefiles { + '../vendor/msgpack-cs/MsgPack/AssemblyInfo.cs', + '../vendor/msgpack-cs/MsgPack/PlatformTypes/**', + '../vendor/msgpack-cs/MsgPack/obj/**', -- allows working in the submodule + } + defines { 'MONO_V2' } do -- prev. disabled on certain games diff --git a/vendor/msgpack-cs b/vendor/msgpack-cs new file mode 160000 index 0000000000..1ddf4e8426 --- /dev/null +++ b/vendor/msgpack-cs @@ -0,0 +1 @@ +Subproject commit 1ddf4e84262b95b250606fa8101a1bde60352350