Prepare for CoreCLR version

This commit is contained in:
Sardelka9515
2023-03-06 21:54:41 +08:00
parent 0e5271b322
commit 2451131e36
16 changed files with 155 additions and 125 deletions

View File

@ -1,9 +1,9 @@
using RageCoop.Core;
using RageCoop.Core.Scripting;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: DisableRuntimeMarshalling]
[assembly: InternalsVisibleTo("RageCoop.Client")] // For debugging
namespace RageCoop.Client.Scripting
@ -11,7 +11,28 @@ namespace RageCoop.Client.Scripting
public static unsafe partial class APIBridge
{
static readonly ThreadLocal<char[]> _resultBuf = new(() => new char[4096]);
static List<CustomEventHandler> _handlers = new();
static readonly List<CustomEventHandler> _handlers = new();
static APIBridge()
{
if (SHVDN.Core.GetPtr == null)
throw new InvalidOperationException("Game not running");
foreach(var fd in typeof(APIBridge).GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
{
var importAttri = fd.GetCustomAttribute<ApiImportAttribute>();
if (importAttri == null)
continue;
importAttri.EntryPoint ??= fd.Name;
var key = $"RageCoop.Client.Scripting.API.{importAttri.EntryPoint}";
var fptr = SHVDN.Core.GetPtr(key);
if (fptr == default)
throw new KeyNotFoundException($"Failed to find function pointer: {key}");
fd.SetValue(null,fptr);
}
}
/// <summary>
/// Copy content of string to a sequential block of memory
/// </summary>
@ -56,10 +77,13 @@ namespace RageCoop.Client.Scripting
var argv = StringArrayToMemory(args.Select(JsonSerialize).ToArray());
try
{
var resultLen = InvokeCommandAsJsonUnsafe(name, argc, argv);
if (resultLen == 0)
throw new Exception(GetLastResult());
return GetLastResult();
fixed(char* pName = name)
{
var resultLen = InvokeCommandAsJsonUnsafe(pName, argc, argv);
if (resultLen == 0)
throw new Exception(GetLastResult());
return GetLastResult();
}
}
finally
{
@ -77,7 +101,7 @@ namespace RageCoop.Client.Scripting
var cbBufSize = _resultBuf.Value.Length * sizeof(char);
fixed (char* pBuf = _resultBuf.Value)
{
if (GetLastResult(pBuf, cbBufSize) > 0)
if (GetLastResultUnsafe(pBuf, cbBufSize) > 0)
{
return new string(pBuf);
}
@ -91,7 +115,7 @@ namespace RageCoop.Client.Scripting
{
var writer = GetWriter();
CustomEvents.WriteObjects(writer, args);
SendCustomEvent(flags, hash, writer.Address, writer.Position);
SendCustomEventUnsafe(flags, hash, writer.Address, writer.Position);
}
public static void RegisterCustomEventHandler(CustomEventHash hash, Action<CustomEventReceivedArgs> handler)
@ -107,25 +131,31 @@ namespace RageCoop.Client.Scripting
internal static void SetConfig(string name, object val) => InvokeCommand("SetConfig", name, val);
[LibraryImport("RageCoop.Client.dll", StringMarshalling = StringMarshalling.Utf16)]
public static partial CustomEventHash GetEventHash(string name);
[ApiImport]
public static delegate* unmanaged<char*, CustomEventHash> GetEventHash;
[LibraryImport("RageCoop.Client.dll", StringMarshalling = StringMarshalling.Utf16)]
internal static partial void SetLastResult(string msg);
[ApiImport]
private static delegate* unmanaged<char*,void> SetLastResult;
[LibraryImport("RageCoop.Client.dll")]
private static partial int GetLastResult(char* buf, int cbBufSize);
[ApiImport(EntryPoint = "GetLastResult")]
private static delegate* unmanaged<char*, int, int> GetLastResultUnsafe;
[LibraryImport("RageCoop.Client.dll", EntryPoint = "InvokeCommand", StringMarshalling = StringMarshalling.Utf16)]
private static partial int InvokeCommandAsJsonUnsafe(string name, int argc, char** argv);
[ApiImport(EntryPoint = "InvokeCommand")]
private static delegate* unmanaged<char*, int, char**, int> InvokeCommandAsJsonUnsafe;
[LibraryImport("RageCoop.Client.dll")]
private static partial void SendCustomEvent(CustomEventFlags flags, int hash, byte* data, int cbData);
[ApiImport(EntryPoint = "SendCustomEvent")]
private static delegate* unmanaged<CustomEventFlags, int, byte*, int, void> SendCustomEventUnsafe;
[LibraryImport("RageCoop.Client.dll")]
private static partial int GetLastResultLenInChars();
[ApiImport]
private static delegate* unmanaged<int> GetLastResultLenInChars;
[LibraryImport("RageCoop.Client.dll", StringMarshalling = StringMarshalling.Utf16)]
public static partial void LogEnqueue(LogLevel level, string msg);
[ApiImport]
public static delegate* unmanaged<LogLevel, char*, void> LogEnqueue;
}
[AttributeUsage(AttributeTargets.Field)]
class ApiImportAttribute : Attribute
{
public string EntryPoint;
}
}

View File

@ -20,7 +20,8 @@ namespace RageCoop.Client.Scripting
static unsafe ClientScript()
{
char* buf = stackalloc char[260];
SHVDN.PInvoke.GetModuleFileNameW(SHVDN.Core.CurrentModule, buf, 260);
// TODO: needs some fix up here
// SHVDN.PInvoke.GetModuleFileNameW(SHVDN.Core.CurrentModule, buf, 260);
if (Marshal.GetLastWin32Error() != 0)
throw new Win32Exception("Failed to get path for current module");
FullPath = new(buf);
@ -37,12 +38,14 @@ namespace RageCoop.Client.Scripting
{
Logger.Warning("No file associated with curent script was found");
}
Tick += DoQueuedJobs;
}
protected void QueueAction(Func<bool> action) => _jobQueue.Enqueue(action);
protected void QueueAction(Action action) => QueueAction(() => { action(); return true; });
protected override void OnTick()
{
base.OnTick();
DoQueuedJobs();
}
private void DoQueuedJobs()
{
while (_reAdd.TryDequeue(out var toAdd))

View File

@ -12,13 +12,16 @@ namespace RageCoop.Client.Scripting
public static readonly ResourceLogger Default = new();
public ResourceLogger()
{
FlushImmediately= true;
FlushImmediately = true;
OnFlush += FlushToMainModule;
}
private void FlushToMainModule(LogLine line, string fomatted)
private unsafe void FlushToMainModule(LogLine line, string fomatted)
{
APIBridge.LogEnqueue(line.LogLevel, line.Message);
fixed (char* pMsg = line.Message)
{
APIBridge.LogEnqueue(line.LogLevel, pMsg);
}
}
}

View File

@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using GTA.UI;
namespace RageCoop.Client
{
internal enum TimeStamp
{
AddPeds,
PedTotal,
AddVehicles,
VehicleTotal,
SendPed,
SendPedState,
SendVehicle,
SendVehicleState,
UpdatePed,
UpdateVehicle,
CheckProjectiles,
GetAllEntities,
Receive,
ProjectilesTotal
}
internal static class Debug
{
public static Dictionary<TimeStamp, long> TimeStamps = new Dictionary<TimeStamp, long>();
private static int _lastNfHandle;
static Debug()
{
foreach (TimeStamp t in Enum.GetValues<TimeStamp>()) TimeStamps.Add(t, 0);
}
public static string Dump(this Dictionary<TimeStamp, long> d)
{
var s = "";
foreach (var kvp in d) s += kvp.Key + ":" + kvp.Value + "\n";
return s;
}
public static void ShowTimeStamps()
{
Notification.Hide(_lastNfHandle);
_lastNfHandle = Notification.Show(TimeStamps.Dump());
}
}
}

View File

@ -5,6 +5,7 @@ using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using GTA;
using GTA.Math;
@ -34,7 +35,7 @@ namespace RageCoop.Client
internal static Logger Log = null;
internal static ulong Ticked = 0;
internal static Vector3 PlayerPosition;
internal static Resources MainRes = null;
internal static Scripting.Resources MainRes = null;
public static Ped P;
public static float FPS;
@ -61,7 +62,8 @@ namespace RageCoop.Client
Log = new Logger()
{
Writers = new List<StreamWriter> { CoreUtils.OpenWriter(LogPath) },
FlushImmediately = true,
Writers = null,
#if DEBUG
LogLevel = 0,
#else
@ -70,26 +72,11 @@ namespace RageCoop.Client
};
Log.OnFlush += (line, formatted) =>
{
switch (line.LogLevel)
{
#if DEBUG
// case LogLevel.Trace:
case LogLevel.Debug:
Console.PrintInfo(line.Message);
break;
#endif
case LogLevel.Info:
Console.PrintInfo(line.Message);
break;
case LogLevel.Warning:
Console.PrintWarning(line.Message);
break;
case LogLevel.Error:
Console.PrintError(line.Message);
break;
}
SHVDN.Logger.Write(line.Message, (uint)line.LogLevel);
};
// Run static constructor to register all function pointers and remoting entries
RuntimeHelpers.RunClassConstructor(typeof(API).TypeHandle);
}
protected override void OnAborted(AbortedEventArgs e)
@ -120,7 +107,7 @@ namespace RageCoop.Client
throw new NotSupportedException("Please update your GTA5 to v1.0.1290 or newer!");
}
MainRes = new Resources();
MainRes = new();
@ -220,6 +207,22 @@ namespace RageCoop.Client
protected override void OnKeyUp(GTA.KeyEventArgs e)
{
base.OnKeyUp(e);
if (e.KeyCode == Keys.U)
{
foreach (var prop in typeof(APIBridge).GetProperties(BindingFlags.Public | BindingFlags.Static))
{
Console.PrintInfo($"{prop.Name}: {JsonSerialize(prop.GetValue(null))}");
}
foreach (var prop in typeof(APIBridge.Config).GetProperties(BindingFlags.Public | BindingFlags.Static))
{
Console.PrintInfo($"{prop.Name}: {JsonSerialize(prop.GetValue(null))}");
}
}
if (e.KeyCode == Keys.I)
{
APIBridge.SendChatMessage("test");
}
#if CEF
if (CefRunning)
{
@ -379,23 +382,23 @@ namespace RageCoop.Client
if (reason != "Abort")
{
Log.Info($">> Disconnected << reason: {reason}");
Log.Info($">> Disconnected << reason: {reason}");
Notification.Show("~r~Disconnected: " + reason);
}
if (MainChat.Focused)
if (MainChat?.Focused == true)
{
MainChat.Focused = false;
}
PlayerList.Cleanup();
MainChat.Clear();
MainChat?.Clear();
EntityPool.Cleanup();
WorldThread.Traffic(true);
Call(SET_ENABLE_VEHICLE_SLIPSTREAMING, false);
CoopMenu.DisconnectedMenuSetting();
LocalPlayerID = default;
MainRes.Unload();
MainRes?.Unload();
Memory.RestorePatches();
#if CEF
if (CefRunning)

View File

@ -38,9 +38,9 @@ namespace RageCoop.Client
{
DiagnosticMenu.Clear();
DiagnosticMenu.Add(new NativeItem("EntityPool", EntityPool.DumpDebug()));
foreach (var pair in Debug.TimeStamps)
DiagnosticMenu.Add(
new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString()));
// foreach (var pair in Debug.TimeStamps)
// DiagnosticMenu.Add(
// new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString()));
};
ShowNetworkInfoItem.CheckboxChanged += (s, e) =>
{

View File

@ -1,5 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<EnableDynamicLoading>true</EnableDynamicLoading>
<NoAotCompile>false</NoAotCompile>
<TargetFramework>net7.0</TargetFramework>
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
@ -63,9 +64,6 @@
<PackageReference Include="SharpZipLib" Version="1.4.0" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.3.0" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(SolutionDir)' != '*Undefined*' And '$(NoAotCompile)' != 'true'">
<Exec Command="dotnet publish &quot;$(ProjectPath)&quot; -o &quot;$(OutDir)\native&quot; -p:PublishAOT=true -r win-x64 " />
</Target>
<PropertyGroup Condition="'$(SolutionDir)' != '*Undefined*' AND '$(PublishAot)' != 'true'">
<PostBuildEvent Condition=" '$(DevEnvDir)' != '*Undefined*'">
"$(DevEnvDir)TextTransform.exe" -a !!BuildConfiguration!$(Configuration) "$(ProjectDir)Properties\AssemblyInfo.tt"

View File

@ -3,6 +3,8 @@ using RageCoop.Core;
using RageCoop.Core.Scripting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using static RageCoop.Core.Scripting.CustomEvents;
@ -11,6 +13,23 @@ namespace RageCoop.Client.Scripting
{
internal static unsafe partial class API
{
static API()
{
RegisterFunctionPointers();
}
static void RegisterFunctionPointers()
{
foreach (var method in typeof(API).GetMethods(BindingFlags.Public | BindingFlags.Static))
{
var attri = method.GetCustomAttribute<UnmanagedCallersOnlyAttribute>();
if (attri == null) continue;
Debug.Assert(attri.EntryPoint == method.Name);
SHVDN.Core.SetPtr($"{typeof(API).FullName}.{method.Name}", method.MethodHandle.GetFunctionPointer());
Log.Debug($"Registered function pointer for {method.DeclaringType}.{method.Name}");
}
}
[ThreadStatic]
static string _lastResult;

View File

@ -465,8 +465,8 @@ namespace RageCoop.Client.Scripting
[Remoting]
public static void RegisterCustomEventHandler(CustomEventHash hash, CustomEventHandler handler)
{
if (handler.Module == default)
throw new ArgumentException("Module not specified");
if (handler.Directory == default)
throw new ArgumentException("Script directory not specified");
if (handler.FunctionPtr == default)
throw new ArgumentException("Function pointer not specified");

View File

@ -65,6 +65,8 @@ namespace RageCoop.Client.Scripting
}
}
// TODO
/*
// Unregister associated handler
foreach (var handlers in API.CustomEventHandlers.Values)
{
@ -77,7 +79,7 @@ namespace RageCoop.Client.Scripting
}
}
}
*/
LoadedResources.Clear();
}

View File

@ -14,9 +14,13 @@ namespace RageCoop.Client
/// </summary>
internal static class ThreadManager
{
private static List<Thread> _threads = new();
private static Thread _watcher = new(() => _removeStopped());
private static void _removeStopped()
private static readonly List<Thread> _threads = new();
private static readonly Thread _watcher = new(RemoveStopped);
static ThreadManager()
{
_watcher.Start();
}
private static void RemoveStopped()
{
while (!IsUnloading)
{
@ -46,8 +50,10 @@ namespace RageCoop.Client
{
Log.Debug($"Thread stopped: " + Environment.CurrentManagedThreadId);
}
});
created.Name = name;
})
{
Name = name
};
Log.Debug($"Thread created: {name}, id: {created.ManagedThreadId}");
_threads.Add(created);
if (startNow) created.Start();

View File

@ -50,7 +50,7 @@ namespace RageCoop.Core
internal Logger()
{
Name = Process.GetCurrentProcess().Id.ToString();
Name = Environment.ProcessId.ToString();
if (!FlushImmediately)
{
LoggerThread = new Thread(() =>
@ -164,7 +164,7 @@ namespace RageCoop.Core
while (_queuedLines.TryDequeue(out var line))
{
var formatted = Format(line);
Writers.ForEach(x =>
Writers?.ForEach(x =>
{
try
{

View File

@ -38,7 +38,7 @@ namespace RageCoop.Core.Scripting
public CustomEventHandler(IntPtr func) : this()
{
FunctionPtr = (ulong)func;
Module = (ulong)SHVDN.Core.CurrentModule;
Directory = SHVDN.Core.CurrentDirectory;
}
[JsonIgnore]
@ -48,7 +48,7 @@ namespace RageCoop.Core.Scripting
public ulong FunctionPtr { get; private set; }
[JsonProperty]
public ulong Module { get; private set; }
public string Directory { get; private set; }
/// <summary>
///

View File

@ -2,6 +2,7 @@
using GTA.Math;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
@ -290,7 +291,20 @@ namespace RageCoop.Core.Scripting
return Args;
}
[LibraryImport("RageCoop.Client.dll")]
public static partial int IdToHandle(byte type, int id);
static unsafe delegate* unmanaged<byte, int, int> _idToHandlePtr;
public static unsafe int IdToHandle(byte type, int id)
{
if (_idToHandlePtr == default)
{
if (SHVDN.Core.GetPtr == default)
throw new InvalidOperationException("Not client");
_idToHandlePtr = (delegate* unmanaged<byte, int, int>)SHVDN.Core.GetPtr("RageCoop.Client.Scripting.API.IdToHandle");
if (_idToHandlePtr == default)
throw new KeyNotFoundException("IdToHandle function not found");
}
return _idToHandlePtr(type, id);
}
}
}

View File

@ -15,13 +15,13 @@ namespace RageCoop.Core
{
static Type JsonTypeCheck(Type type)
{
if (type.GetCustomAttribute<JsonDontSerialize>() != null)
if (type?.GetCustomAttribute<JsonDontSerialize>() != null)
throw new TypeAccessException($"The type {type} cannot be serialized");
return type;
}
static object JsonTypeCheck(object obj)
{
JsonTypeCheck(obj.GetType());
JsonTypeCheck(obj?.GetType());
return obj;
}
public static readonly JsonSerializerSettings JsonSettings = new();