Working client resource system

This commit is contained in:
Sardelka9515
2023-03-07 20:17:53 +08:00
parent 2451131e36
commit 83f7cbc963
14 changed files with 50 additions and 210 deletions

View File

@ -36,10 +36,12 @@ namespace RageCoop.Client.Scripting
public static void LocalChatMessage(System.String from, System.String message) => InvokeCommand("LocalChatMessage", from, message);
public static void SendChatMessage(System.String message) => InvokeCommand("SendChatMessage", message);
public static RageCoop.Client.Scripting.ClientResource GetResource(System.String name) => InvokeCommand<RageCoop.Client.Scripting.ClientResource>("GetResource", name);
public static RageCoop.Client.Scripting.ClientResource GetResouceFromFilePath(System.String filePath) => InvokeCommand<RageCoop.Client.Scripting.ClientResource>("GetResouceFromFilePath", filePath);
public static RageCoop.Client.Scripting.ClientResource GetResourceFromPath(System.String path) => InvokeCommand<RageCoop.Client.Scripting.ClientResource>("GetResourceFromPath", path);
public static System.Object GetConfig(System.String name) => InvokeCommand<System.Object>("GetConfig", name);
public static void SetConfig(System.String name, System.String jsonVal) => InvokeCommand("SetConfig", name, jsonVal);
public static void RegisterCustomEventHandler(RageCoop.Core.Scripting.CustomEventHash hash, RageCoop.Core.Scripting.CustomEventHandler handler) => InvokeCommand("RegisterCustomEventHandler", hash, handler);
#endregion
}
}

View File

@ -38,12 +38,6 @@ namespace RageCoop.Client.Scripting
[JsonProperty]
public Dictionary<string, ClientFile> Files { get; internal set; } = new Dictionary<string, ClientFile>();
/// <summary>
/// List of the path of loaded modules, don't modify
/// </summary>
[JsonProperty]
public List<string> Modules = new();
/// <summary>
/// A <see cref="Core.Logger" /> instance that can be used to debug your resource.
/// </summary>

View File

@ -11,29 +11,16 @@ namespace RageCoop.Client.Scripting
[ScriptAttributes(NoDefaultInstance = true)]
public abstract class ClientScript : Script
{
ConcurrentQueue<Func<bool>> _jobQueue = new();
Queue<Func<bool>> _reAdd = new();
/// <summary>
/// Fully qualified path to the module that the current script runs in.
/// </summary>
public static readonly string FullPath;
static unsafe ClientScript()
{
char* buf = stackalloc char[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);
}
readonly ConcurrentQueue<Func<bool>> _jobQueue = new();
readonly Queue<Func<bool>> _reAdd = new();
public ClientScript()
{
CurrentResource = APIBridge.GetResouceFromFilePath(FullPath);
var dir = SHVDN.Core.CurrentDirectory;
CurrentResource = APIBridge.GetResourceFromPath(dir);
if (CurrentResource == null)
throw new Exception("No resource associated with this script is found");
CurrentFile = CurrentResource.Files.Values.FirstOrDefault(x => x?.FullPath?.ToLower() == FullPath.ToLower());
CurrentFile = CurrentResource.Files.Values.FirstOrDefault(x => x?.FullPath?.ToLower() == FilePath?.ToLower());
if (CurrentFile == null)
{
Logger.Warning("No file associated with curent script was found");

View File

@ -1,79 +0,0 @@
using System;
using System.Runtime.InteropServices;
using GTA;
using RageCoop.Client;
using SHVDN;
/// <summary>
/// Template for generating AOT entrypoints
/// </summary>
public static class EntryPoint
{
private static void ModuleSetup()
{
Script script = new Main();
Core.RegisterScript(script);
script = new WorldThread();
Core.RegisterScript(script);
script = new DevTool();
Core.RegisterScript(script);
}
[UnmanagedCallersOnly(EntryPoint = "OnInit")]
public unsafe static void OnInit(IntPtr module)
{
try
{
Core.OnInit(module);
ModuleSetup();
}
catch (Exception ex)
{
PInvoke.MessageBoxA((IntPtr)0, ex.ToString(), "Module initialization error", 0u);
throw;
}
}
/// <summary>
/// Called prior to module unload
/// </summary>
/// <param name="module"></param>
[UnmanagedCallersOnly(EntryPoint = "OnUnload")]
public static void OnUnload(IntPtr module)
{
try
{
Core.OnUnload(module);
}
catch (Exception ex)
{
Logger.Error((ReadOnlySpan<char>)("Module unload error: " + ex.ToString()));
}
}
[UnmanagedCallersOnly(EntryPoint = "OnKeyboard")]
public unsafe static void OnKeyboard(uint key, ushort repeats, bool scanCode, bool isExtended, bool isWithAlt, bool wasDownBefore, bool isUpNow)
{
try
{
Core.DoKeyEvent(key, !isUpNow, (PInvoke.GetAsyncKeyState(17) & 0x8000) != 0, (PInvoke.GetAsyncKeyState(16) & 0x8000) != 0, isWithAlt);
}
catch (Exception ex)
{
Logger.Error((ReadOnlySpan<char>)("Keyboard event error: " + ex.ToString()));
}
}
[UnmanagedCallersOnly(EntryPoint = "OnTick")]
public static void OnTick(IntPtr currentFiber)
{
try
{
Core.DoTick(currentFiber);
}
catch (Exception ex)
{
Logger.Error((ReadOnlySpan<char>)("Tick error: " + ex.ToString()));
}
}
}

View File

@ -35,7 +35,7 @@ namespace RageCoop.Client
internal static Logger Log = null;
internal static ulong Ticked = 0;
internal static Vector3 PlayerPosition;
internal static Scripting.Resources MainRes = null;
internal static Resources MainRes = null;
public static Ped P;
public static float FPS;
@ -207,22 +207,6 @@ 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)
{
@ -386,19 +370,19 @@ namespace RageCoop.Client
Notification.Show("~r~Disconnected: " + reason);
}
if (MainChat?.Focused == true)
if (MainChat.Focused)
{
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

@ -22,10 +22,10 @@ namespace RageCoop.Client.Scripting
{
foreach (var method in typeof(API).GetMethods(BindingFlags.Public | BindingFlags.Static))
{
var attri = method.GetCustomAttribute<UnmanagedCallersOnlyAttribute>();
var attri = method.GetCustomAttribute<ApiExportAttribute>();
if (attri == null) continue;
Debug.Assert(attri.EntryPoint == method.Name);
SHVDN.Core.SetPtr($"{typeof(API).FullName}.{method.Name}", method.MethodHandle.GetFunctionPointer());
attri.EntryPoint ??= method.Name;
SHVDN.Core.SetPtr($"{typeof(API).FullName}.{attri.EntryPoint}", method.MethodHandle.GetFunctionPointer());
Log.Debug($"Registered function pointer for {method.DeclaringType}.{method.Name}");
}
}
@ -33,7 +33,7 @@ namespace RageCoop.Client.Scripting
[ThreadStatic]
static string _lastResult;
[UnmanagedCallersOnly(EntryPoint = nameof(GetLastResult))]
[ApiExportAttribute(EntryPoint = nameof(GetLastResult))]
public static int GetLastResult(char* buf, int cbBufSize)
{
if (_lastResult == null)
@ -52,7 +52,7 @@ namespace RageCoop.Client.Scripting
}
public static void SetLastResult(string msg) => _lastResult = msg;
[UnmanagedCallersOnly(EntryPoint = nameof(SetLastResult))]
[ApiExportAttribute(EntryPoint = nameof(SetLastResult))]
public static void SetLastResult(char* msg)
{
try
@ -65,10 +65,10 @@ namespace RageCoop.Client.Scripting
}
}
[UnmanagedCallersOnly(EntryPoint = nameof(GetEventHash))]
[ApiExportAttribute(EntryPoint = nameof(GetEventHash))]
public static CustomEventHash GetEventHash(char* name) => new string(name);
[UnmanagedCallersOnly(EntryPoint = nameof(SendCustomEvent))]
[ApiExportAttribute(EntryPoint = nameof(SendCustomEvent))]
public static void SendCustomEvent(CustomEventFlags flags, int hash, byte* data, int cbData)
{
var payload = new byte[cbData];
@ -81,7 +81,7 @@ namespace RageCoop.Client.Scripting
}, Networking.ServerConnection, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
}
[UnmanagedCallersOnly(EntryPoint = nameof(InvokeCommand))]
[ApiExportAttribute(EntryPoint = nameof(InvokeCommand))]
public static int InvokeCommand(char* name, int argc, char** argv)
{
try
@ -102,13 +102,13 @@ namespace RageCoop.Client.Scripting
}
}
[UnmanagedCallersOnly(EntryPoint = nameof(GetLastResultLenInChars))]
[ApiExportAttribute(EntryPoint = nameof(GetLastResultLenInChars))]
public static int GetLastResultLenInChars() => _lastResult?.Length ?? 0;
/// <summary>
/// Convert Entity ID to handle
/// </summary>
[UnmanagedCallersOnly(EntryPoint = nameof(IdToHandle))]
[ApiExportAttribute(EntryPoint = nameof(IdToHandle))]
public static int IdToHandle(byte type, int id)
{
return type switch
@ -126,10 +126,15 @@ namespace RageCoop.Client.Scripting
/// </summary>
/// <param name="level"></param>
/// <param name="msg"></param>
[UnmanagedCallersOnly(EntryPoint = nameof(LogEnqueue))]
[ApiExportAttribute(EntryPoint = nameof(LogEnqueue))]
public static void LogEnqueue(LogLevel level, char* msg)
{
Log.Enqueue((int)level, new(msg));
}
class ApiExportAttribute : Attribute
{
public string EntryPoint;
}
}
}

View File

@ -7,6 +7,7 @@ using RageCoop.Core.Scripting;
using SHVDN;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
@ -411,16 +412,17 @@ namespace RageCoop.Client.Scripting
}
/// <summary>
/// Get <see cref="ClientResource"/> that contains the specified file
/// Get <see cref="ClientResource"/> that contains the specified file or directory
/// </summary>
/// <returns></returns>
[Remoting]
public static ClientResource GetResouceFromFilePath(string filePath)
public static ClientResource GetResourceFromPath(string path)
{
foreach (var res in MainRes.LoadedResources)
path = Path.GetFullPath(path).ToLower();
foreach (var res in MainRes.LoadedResources.Values)
{
if (res.Value.Files.Any(file => file.Value.FullPath.ToLower() == filePath.ToLower()))
return res.Value;
if (res.ScriptsDirectory.ToLower() == path || res.Files.Any(file => file.Value.FullPath.ToLower() == path))
return res;
}
return null;
}

View File

@ -8,18 +8,18 @@ using System.Reflection;
using ICSharpCode.SharpZipLib.Zip;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using SHVDN;
namespace RageCoop.Client.Scripting
{
internal class Resources
{
public static string TempPath;
public static string TempPath = Path.Combine(Path.GetTempPath(), "RageCoop");
internal readonly ConcurrentDictionary<string, ClientResource> LoadedResources = new();
static Resources()
{
TempPath = Path.Combine(Path.GetTempPath(), "RageCoop");
if (Directory.Exists(TempPath))
try
{
@ -47,39 +47,24 @@ namespace RageCoop.Client.Scripting
public unsafe void Unload()
{
HashSet<IntPtr> modules = new();
foreach (var res in LoadedResources.Values)
var dirs = LoadedResources.Values.Select(x => x.ScriptsDirectory);
foreach (var dir in dirs)
{
foreach (var module in res.Modules)
{
fixed (char* pModulePath = module)
{
Log.Debug($"Unloading module: {module}");
SHVDN.Core.ScheduleUnload(pModulePath);
var hModule = Util.GetModuleHandleW(module);
if (hModule == IntPtr.Zero)
Log.Warning("Failed to get module handler for " + Path.GetFileName(module));
else
modules.Add(hModule);
}
}
SHVDN.Core.RuntimeController.RequestUnload(dir);
}
// TODO
/*
// Unregister associated handler
foreach (var handlers in API.CustomEventHandlers.Values)
{
foreach (var handler in handlers.ToArray())
{
if (modules.Contains((IntPtr)handler.Module))
if (dirs.Contains(handler.Directory, StringComparer.OrdinalIgnoreCase))
{
Log.Debug($"Unregister handler from module {handler.Module}");
handlers.Remove(handler);
Log.Debug($"Unregistered handler from script directory {handler.Directory}");
}
}
}
*/
LoadedResources.Clear();
}
@ -110,21 +95,6 @@ namespace RageCoop.Client.Scripting
});
foreach (var file in Directory.GetFiles(scriptsDir, "*", SearchOption.AllDirectories))
{
if (Path.GetFileName(file).CanBeIgnored())
{
try
{
File.Delete(file);
}
catch (Exception ex)
{
Log.Warning(
$"Failed to delete API assembly: {file}. This may or may cause some unexpected behaviours.\n{ex}");
}
continue;
}
var relativeName = file.Substring(scriptsDir.Length + 1).Replace('\\', '/');
var rfile = new ClientFile
{
@ -133,16 +103,8 @@ namespace RageCoop.Client.Scripting
FullPath = file
};
r.Files.Add(relativeName, rfile);
if (file.EndsWith(".dll"))
{
fixed (char* pModulePath = file)
{
SHVDN.Core.ScheduleLoad(pModulePath);
r.Modules.Add(file);
}
}
}
SHVDN.Core.RuntimeController.RequestLoad(scriptsDir);
LoadedResources.TryAdd(r.Name, r);
return r;
}

View File

@ -425,6 +425,8 @@ namespace RageCoop.Client
if (!VehiclesByHandle.ContainsKey(veh))
{
var cveh = (Vehicle)Entity.FromHandle(veh);
if (cveh == null)
continue;
if (allVehicles.Length > Settings.WorldVehicleSoftLimit)
{
var type = cveh.PopulationType;

View File

@ -26,15 +26,10 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libs", "libs", "{1AF84C35-B86B-46BB-9FDB-ACB7787E582A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptHookVDotNetCore", "libs\ScriptHookVDotNetCore\src\ScriptHookVDotNetCore\ScriptHookVDotNetCore.csproj", "{B15EDABB-30AF-475A-823D-ACB9F75CFE13}"
ProjectSection(ProjectDependencies) = postProject
{2C08A5AF-C189-4350-B54F-3D23379E9E1D} = {2C08A5AF-C189-4350-B54F-3D23379E9E1D}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lidgren.Network", "libs\Lidgren.Network\Lidgren.Network\Lidgren.Network.csproj", "{02616B5A-2A68-42AA-A91E-311EF95FCF44}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptHookVDotNetCore.Generator", "libs\ScriptHookVDotNetCore\src\ScriptHookVDotNetCore.Generator\ScriptHookVDotNetCore.Generator.csproj", "{2C08A5AF-C189-4350-B54F-3D23379E9E1D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGen", "Tools\CodeGen\CodeGen.csproj", "{C4CF8A98-7393-42BD-97A1-2E850D12890A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeGen", "Tools\CodeGen\CodeGen.csproj", "{C4CF8A98-7393-42BD-97A1-2E850D12890A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -154,18 +149,6 @@ Global
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.Release|Any CPU.Build.0 = Release|Any CPU
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.Release|x64.ActiveCfg = Release|Any CPU
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.Release|x64.Build.0 = Release|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.API|Any CPU.ActiveCfg = Debug|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.API|Any CPU.Build.0 = Debug|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.API|x64.ActiveCfg = Debug|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.API|x64.Build.0 = Debug|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.Debug|x64.ActiveCfg = Debug|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.Debug|x64.Build.0 = Debug|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.Release|Any CPU.Build.0 = Release|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.Release|x64.ActiveCfg = Release|Any CPU
{2C08A5AF-C189-4350-B54F-3D23379E9E1D}.Release|x64.Build.0 = Release|Any CPU
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.API|Any CPU.ActiveCfg = Debug|Any CPU
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.API|Any CPU.Build.0 = Debug|Any CPU
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.API|x64.ActiveCfg = Debug|Any CPU
@ -190,7 +173,6 @@ Global
{4FE96671-3DC5-4394-B2E3-584399E57310} = {70A1F09D-648D-4C8B-8947-E920B1A587A3}
{B15EDABB-30AF-475A-823D-ACB9F75CFE13} = {1AF84C35-B86B-46BB-9FDB-ACB7787E582A}
{02616B5A-2A68-42AA-A91E-311EF95FCF44} = {1AF84C35-B86B-46BB-9FDB-ACB7787E582A}
{2C08A5AF-C189-4350-B54F-3D23379E9E1D} = {1AF84C35-B86B-46BB-9FDB-ACB7787E582A}
{C4CF8A98-7393-42BD-97A1-2E850D12890A} = {70A1F09D-648D-4C8B-8947-E920B1A587A3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution

View File

@ -15,7 +15,7 @@ using System.Resources;
[assembly: AssemblyCulture("")]
// Version information
[assembly: AssemblyVersion("1.6.0.45")]
[assembly: AssemblyFileVersion("1.6.0.45")]
[assembly: AssemblyVersion("1.6.0.54")]
[assembly: AssemblyFileVersion("1.6.0.54")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]

View File

@ -359,7 +359,7 @@ public class API
/// <param name="flags"></param>
/// <param name="eventHash">An unique identifier of the event/> to get it from a string</param>
/// <param name="args">
/// The objects conataing your data, see <see cref="Scripting.CustomEventReceivedArgs.Args" /> for
/// The objects conataing your data, see <see cref="Core.Scripting.CustomEventReceivedArgs.Args" /> for
/// supported types.
/// </param>
/// <param name="targets">The target clients to send. Leave it null to send to all clients</param>

View File

@ -103,7 +103,6 @@ internal class Resources
zip.AddDirectory(dir[(resourceFolder.Length + 1)..]);
foreach (var file in Directory.GetFiles(resourceFolder, "*", SearchOption.AllDirectories))
{
if (Path.GetFileName(file).CanBeIgnored()) continue;
zip.Add(file, file[(resourceFolder.Length + 1)..]);
}