Skip to content

Commit

Permalink
Merge remote-tracking branch 'thorium-cfx/mono_rt2_several_fixes'
Browse files Browse the repository at this point in the history
  • Loading branch information
blattersturm committed Jun 11, 2023
2 parents 0b13b2a + 17e0b15 commit bb87c52
Show file tree
Hide file tree
Showing 15 changed files with 428 additions and 61 deletions.
2 changes: 2 additions & 0 deletions code/client/clrcore-v2/Coroutine/Coroutine.cs
Expand Up @@ -202,6 +202,8 @@ public object GetResult()
return GetResultInternal();
}

internal object GetResultNonThrowing() => GetResultInternal();

protected virtual object GetResultInternal() => null;

internal void Complete() => CompleteInternal(State.Completed);
Expand Down
11 changes: 7 additions & 4 deletions code/client/clrcore-v2/Interop/LocalFunction.cs
Expand Up @@ -53,12 +53,15 @@ internal unsafe Coroutine<object> Invoke(object[] args)
&& requestAsyncCallback is Callback callbackRequested)
{
var c = new Coroutine<object>();
callbackRequested(new Action<object, object>((a, e) =>
callbackRequested(new Action<object, object>((results, exception) =>
{
if (e != null)
c.SetException(new Exception(e.ToString()));
if (exception != null)
c.Fail(null, new Exception(exception.ToString()));
else if(results is object[] array && array.Length > 0)
c.Complete(array[0]);
else
c.Complete(a);
c.Complete(null);
}));

return c;
Expand Down
4 changes: 2 additions & 2 deletions code/client/clrcore-v2/Interop/MsgPackSerializer.cs
Expand Up @@ -35,8 +35,8 @@ internal static byte[] Serialize(object obj, bool remote = false)
using (var packer = Packer.Create(stream, PackerCompatibilityOptions.None))
{
Serialize(obj, packer);
//return stream.ToArray();
return stream.GetBuffer();
return stream.ToArray(); // other runtimes read the full length we give them, so shrink the buffer (copy)
//return stream.GetBuffer();
}
}

Expand Down
34 changes: 22 additions & 12 deletions code/client/clrcore-v2/Interop/ReferenceFunctionManager.cs
Expand Up @@ -182,21 +182,31 @@ internal unsafe static byte[] Invoke(int reference, byte* arguments, uint argsSi
{
if (coroutine.IsCompleted)
{
if (coroutine.GetResult() is Callback callback)
coroutine.ContinueWith(() => callback(coroutine.GetResult(), coroutine.Exception));
else
return MsgPackSerializer.Serialize(new[] { coroutine.GetResult() });
if (coroutine.Exception != null)
{
Debug.Write(coroutine.Exception);
}

return MsgPackSerializer.Serialize(new[] { coroutine.GetResultNonThrowing(), coroutine.Exception?.ToString() });
}
else
{
var returnDictionary = new Dictionary<string, object>(1);
returnDictionary.Add("__cfx_async_retval", new Action<Callback>(asyncResult =>
{
coroutine.ContinueWith(new Action(() => asyncResult(coroutine.GetResult(), coroutine.Exception)));
})
);

return MsgPackSerializer.Serialize(new[] { returnDictionary });
var returnDictionary = new Dictionary<string, object>()
{
{ "__cfx_async_retval", new Action<Callback>(asyncResult =>
coroutine.ContinueWith(() =>
{
if (coroutine.Exception != null)
{
Debug.Write(coroutine.Exception);
}
asyncResult(coroutine.GetResultNonThrowing(), coroutine.Exception?.ToString());
}))
}
};

return MsgPackSerializer.Serialize(new object[] { returnDictionary });
}
}

Expand Down
159 changes: 120 additions & 39 deletions code/client/clrcore-v2/Native/MemoryAccess.cs
@@ -1,59 +1,140 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Security;

namespace CitizenFX.Core.Native
{
[SecuritySafeCritical]
[SecurityCritical]
internal static class MemoryAccess
{
public static int[] GetPickupObjectHandles() => new int[0];
public static int[] GetPedHandles() => new int[0];
public static int[] GetEntityHandles() => new int[0];
public static int[] GetPropHandles() => new int[0];
public static int[] GetVehicleHandles() => new int[0];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe IntPtr Deref(IntPtr ptr, int offset)
{
return *(IntPtr*)(ptr + offset);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe IntPtr DerefIfNotNull(IntPtr ptr, int offset)
{
return ptr != IntPtr.Zero
? *(IntPtr*)(ptr + offset)
: IntPtr.Zero;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe IntPtr Deref(IntPtr ptr, int offset, int postDerefOffset)
{
return *(IntPtr*)(ptr + offset) + postDerefOffset;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe IntPtr DerefIfNotNull(IntPtr ptr, int offset, int postDerefOffset)
{
return ptr != IntPtr.Zero
? *(IntPtr*)(ptr + offset) + postDerefOffset
: IntPtr.Zero;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe T Read<T>(IntPtr ptr, int offset) where T : unmanaged
{
return *(T*)(ptr + offset);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe T ReadIfNotNull<T>(IntPtr ptr, int offset, T orDefault) where T : unmanaged
{
return ptr != IntPtr.Zero
? *(T*)(ptr + offset)
: orDefault;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe T ReadIfNotNull<T>(IntPtr ptr, int offset, ref T setOrDefault) where T : unmanaged
{
return ptr != IntPtr.Zero
? setOrDefault = *(T*)(ptr + offset)
: setOrDefault;
}

public static int[][] VehicleModels => null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Write<T>(IntPtr ptr, int offset, T value) where T : unmanaged
{
*(T*)(ptr + offset) = value;
}

public static float ReadWorldGravity()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void WriteIfNotNull<T>(IntPtr ptr, int offset, T value) where T : unmanaged
{
return 0.0f;
if (ptr != IntPtr.Zero)
{
*(T*)(ptr + offset) = value;
}
}

public static void WriteWorldGravity(float f)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool IsBitSetIfNotNull(IntPtr ptr, int offset, int bit, bool orDefault)
{
return ptr != IntPtr.Zero
? (*(int*)(ptr + offset) & (1 << bit)) != 0
: orDefault;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void WriteBitIfNotNull(IntPtr ptr, int offset, int bit, bool value)
{
if (ptr != IntPtr.Zero)
{
if (value)
{
*(int*)(ptr + offset) |= 1 << bit;
}
else
{
*(int*)(ptr + offset) &= ~(1 << bit);
}
}
}

[SecurityCritical]
public static unsafe T GetValueOrDefault<T>(IntPtr ptr, int offset, T def) where T : unmanaged => ptr != IntPtr.Zero ? *(T*)(ptr + offset) : def;
[SecurityCritical]
public static unsafe void SetValueIfNotNull<T>(IntPtr ptr, int offset, T val) where T : unmanaged
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void SetBitIfNotNull(IntPtr ptr, int offset, int bit)
{
if (ptr != IntPtr.Zero)
*(T*)(ptr + offset) = val;
}

[SecuritySafeCritical] public static byte ReadByte(IntPtr pointer) => Marshal.ReadByte(pointer);
[SecuritySafeCritical] public static short ReadShort(IntPtr pointer) => Marshal.ReadInt16(pointer);
[SecuritySafeCritical] public static int ReadInt(IntPtr pointer) => Marshal.ReadInt32(pointer);
[SecuritySafeCritical] public static IntPtr ReadPtr(IntPtr pointer) => Marshal.ReadIntPtr(pointer);
[SecuritySafeCritical] public static float ReadFloat(IntPtr pointer) => BitConverter.ToSingle(BitConverter.GetBytes(Marshal.ReadInt32(pointer)), 0);
[SecuritySafeCritical] public static Matrix ReadMatrix(IntPtr pointer) => Marshal.PtrToStructure<Matrix>(pointer);
[SecuritySafeCritical] public static Vector3 ReadVector3(IntPtr pointer) => Marshal.PtrToStructure<Vector3>(pointer);

[SecuritySafeCritical] public static void WriteByte(IntPtr pointer, byte value) => Marshal.WriteByte(pointer, value);
[SecuritySafeCritical] public static void WriteShort(IntPtr pointer, short value) => Marshal.WriteInt16(pointer, value);
[SecuritySafeCritical] public static void WriteInt(IntPtr pointer, int value) => Marshal.WriteInt32(pointer, value);
[SecuritySafeCritical] public static void WriteFloat(IntPtr pointer, float value) => Marshal.WriteInt32(pointer, BitConverter.ToInt32(BitConverter.GetBytes(value), 0));
[SecuritySafeCritical] public static void WriteVector3(IntPtr pointer, Vector3 value) => Marshal.StructureToPtr(value, pointer, false);

[SecuritySafeCritical] public static unsafe bool IsBitSet(IntPtr pointer, int bit) => (*(int*)pointer & (1 << bit)) != 0;
[SecuritySafeCritical] public static unsafe void ClearBit(IntPtr pointer, int bit) => *(int*)pointer &= ~(1 << bit);
[SecuritySafeCritical] public static unsafe void SetBit(IntPtr pointer, int bit) => *(int*)pointer |= 1 << bit;

public static IntPtr StringPtr { get; } = Marshal.StringToHGlobalAnsi("STRING");
public static IntPtr NullString { get; } = Marshal.StringToHGlobalAnsi("");
public static IntPtr CellEmailBcon { get; } = Marshal.StringToHGlobalAnsi("CELL_EMAIL_BCON");
{
*(int*)(ptr + offset) |= 1 << bit;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void ClearBitIfNotNull(IntPtr ptr, int offset, int bit)
{
if (ptr != IntPtr.Zero)
{
*(int*)(ptr + offset) &= ~(1 << bit);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool IsBitSet(IntPtr pointer, int offset, int bit) => (*(int*)(pointer + offset) & (1 << bit)) != 0;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void ClearBit(IntPtr pointer, int offset, int bit) => *(int*)(pointer + offset) &= ~(1 << bit);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void SetBit(IntPtr pointer, int offset, int bit) => *(int*)(pointer + offset) |= 1 << bit;

/// <remarks>
/// Purely used to circumvent <see cref="IntPtr"/> operator+'s <see langword="internal"/> accessibility in game libraries
/// TODO: make mscorlib IntPtr operator+ public and [SecurityCritical]
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe IntPtr Offset(IntPtr ptr, int offset)
{
return ptr + offset;
}

// Backward compatibility
public static float ReadWorldGravity() => throw new NotImplementedException();
public static void WriteWorldGravity(float value) => throw new NotImplementedException();
}
}
2 changes: 1 addition & 1 deletion code/client/clrcore-v2/Native/Native.cs
Expand Up @@ -93,7 +93,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.DeserializeArray(*(byte**)__data, (long)retLength);
}
}

Expand Down
4 changes: 2 additions & 2 deletions code/client/clrcore-v2/StateBag.cs
Expand Up @@ -6,9 +6,9 @@ namespace CitizenFX.Core
[SecuritySafeCritical]
public class StateBag
{
private CString m_bagName;
private readonly CString m_bagName;

static StateBag Global => new StateBag("global");
public static StateBag Global { get; } = new StateBag("global");

internal StateBag(string bagName)
{
Expand Down
22 changes: 22 additions & 0 deletions code/client/clrcore/External/Camera.cs
@@ -1,5 +1,6 @@
using System;
using CitizenFX.Core.Native;
using System.Security;

#if MONO_V2
using CitizenFX.Core;
Expand Down Expand Up @@ -62,14 +63,19 @@ public IntPtr MemoryAddress

private IntPtr MatrixAddress
{
[SecuritySafeCritical]
get
{
IntPtr address = MemoryAddress;
if (address == IntPtr.Zero)
{
return IntPtr.Zero;
}
#if MONO_V2
return MemoryAccess.Offset(address, MemoryAccess.IsBitSet(address, 0x220, 0) ? 0x110 : 0x30);
#else
return (MemoryAccess.ReadByte(address + 0x220) & 1) == 0 ? address + 0x30 : address + 0x110;
#endif
}
}

Expand Down Expand Up @@ -526,29 +532,45 @@ public static Vector3 Direction
/// </summary>
public static Vector3 UpVector
{
#if MONO_V2
[SecuritySafeCritical] get => MemoryAccess.ReadIfNotNull(MemoryAddress, 0x210, Vector3.Up);
#else
get { return MemoryAccess.ReadVector3(MemoryAddress + 0x210); }
#endif
}
/// <summary>
/// Gets the forward vector of the <see cref="GameplayCamera"/>, see also <seealso cref="Direction"/>.
/// </summary>
public static Vector3 ForwardVector
{
#if MONO_V2
[SecuritySafeCritical] get => MemoryAccess.ReadIfNotNull(MemoryAddress, 0x200, Vector3.ForwardLH);
#else
get { return MemoryAccess.ReadVector3(MemoryAddress + 0x200); }
#endif
}
/// <summary>
/// Gets the right vector of the <see cref="GameplayCamera"/>.
/// </summary>
public static Vector3 RightVector
{
#if MONO_V2
[SecuritySafeCritical] get => MemoryAccess.ReadIfNotNull(MemoryAddress, 0x1F0, Vector3.Right);
#else
get { return MemoryAccess.ReadVector3(MemoryAddress + 0x1F0); }
#endif
}

/// <summary>
/// Gets the matrix of the <see cref="GameplayCamera"/>.
/// </summary>
public static Matrix Matrix
{
#if MONO_V2
[SecuritySafeCritical] get => MemoryAccess.ReadIfNotNull(MemoryAddress, 0x1F0, default(Matrix));
#else
get { return MemoryAccess.ReadMatrix(MemoryAddress + 0x1F0); }
#endif
}

/// <summary>
Expand Down

0 comments on commit bb87c52

Please sign in to comment.