Introduce CustomEventHandler for more flexible invocation

This commit is contained in:
Sardelka9515
2023-02-15 11:38:02 +08:00
parent e4f432b593
commit e5f426690f
14 changed files with 182 additions and 73 deletions

View File

@ -12,10 +12,6 @@ namespace RageCoop.Client.Scripting
{ {
static readonly ThreadLocal<char[]> _resultBuf = new(() => new char[4096]); static readonly ThreadLocal<char[]> _resultBuf = new(() => new char[4096]);
static readonly ThreadLocal<BufferWriter> _bufWriter = new(() => new(4096));
static readonly ThreadLocal<BufferReader> _bufReader = new(() => new());
/// <summary> /// <summary>
/// Copy content of string to a sequential block of memory /// Copy content of string to a sequential block of memory
/// </summary> /// </summary>
@ -91,8 +87,7 @@ namespace RageCoop.Client.Scripting
public static void SendCustomEvent(CustomEventFlags flags, CustomEventHash hash, params object[] args) public static void SendCustomEvent(CustomEventFlags flags, CustomEventHash hash, params object[] args)
{ {
var writer = _bufWriter.Value; var writer = GetWriter();
writer.Reset();
CustomEvents.WriteObjects(writer, args); CustomEvents.WriteObjects(writer, args);
SendCustomEvent(flags, hash, writer.Address, writer.Position); SendCustomEvent(flags, hash, writer.Address, writer.Position);
} }

View File

@ -237,11 +237,6 @@ namespace RageCoop.Client
return; return;
} }
if (e.KeyCode == Keys.I)
{
APIBridge.SendChatMessage("hello there");
}
#if CEF #if CEF
if (CefRunning) if (CefRunning)
{ {

View File

@ -15,20 +15,6 @@ using System.Threading;
namespace RageCoop.Client.Scripting namespace RageCoop.Client.Scripting
{ {
/// <summary>
/// </summary>
public class CustomEventReceivedArgs : EventArgs
{
/// <summary>
/// The event hash
/// </summary>
public int Hash { get; set; }
/// <summary>
/// Supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string, Vector3, Quaternion
/// </summary>
public object[] Args { get; set; }
}
/// <summary> /// <summary>
/// Provides vital functionality to interact with RAGECOOP /// Provides vital functionality to interact with RAGECOOP
@ -37,8 +23,8 @@ namespace RageCoop.Client.Scripting
{ {
#region INTERNAL #region INTERNAL
internal static Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = internal static Dictionary<int, List<CustomEventHandler>> CustomEventHandlers =
new Dictionary<int, List<Action<CustomEventReceivedArgs>>>(); new();
#endregion #endregion
@ -166,12 +152,26 @@ namespace RageCoop.Client.Scripting
internal static void InvokeCustomEventReceived(Packets.CustomEvent p) internal static void InvokeCustomEventReceived(Packets.CustomEvent p)
{ {
var args = new CustomEventReceivedArgs { Hash = p.Hash, Args = p.Args };
// Log.Debug($"CustomEvent:\n"+args.Args.DumpWithType()); // Log.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
if (CustomEventHandlers.TryGetValue(p.Hash, out var handlers)) if (CustomEventHandlers.TryGetValue(p.Hash, out var handlers))
handlers.ForEach(x => { x.Invoke(args); }); {
fixed (byte* pData = p.Payload)
{
foreach (var handler in handlers)
{
try
{
handler.Invoke(p.Hash, pData, p.Payload.Length);
}
catch (Exception ex)
{
Log.Error("InvokeCustomEvent", ex);
}
}
}
}
} }
#endregion #endregion
@ -290,9 +290,12 @@ namespace RageCoop.Client.Scripting
/// </param> /// </param>
public static void SendCustomEvent(CustomEventFlags flags, CustomEventHash eventHash, params object[] args) public static void SendCustomEvent(CustomEventFlags flags, CustomEventHash eventHash, params object[] args)
{ {
var writer = GetWriter();
CustomEvents.WriteObjects(writer, args);
Networking.Peer.SendTo(new Packets.CustomEvent(flags) Networking.Peer.SendTo(new Packets.CustomEvent(flags)
{ {
Args = args,
Payload = writer.ToByteArray(writer.Position),
Hash = eventHash Hash = eventHash
}, Networking.ServerConnection, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered); }, Networking.ServerConnection, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
} }
@ -310,7 +313,7 @@ namespace RageCoop.Client.Scripting
lock (CustomEventHandlers) lock (CustomEventHandlers)
{ {
if (!CustomEventHandlers.TryGetValue(hash, out var handlers)) if (!CustomEventHandlers.TryGetValue(hash, out var handlers))
CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>()); CustomEventHandlers.Add(hash, handlers = new());
handlers.Add(handler); handlers.Add(handler);
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading;
using GTA.Math; using GTA.Math;
namespace RageCoop.Core namespace RageCoop.Core
@ -43,6 +44,11 @@ namespace RageCoop.Core
public unsafe sealed class BufferWriter : Buffer public unsafe sealed class BufferWriter : Buffer
{ {
/// <summary>
/// Gets a thread local instance of this writer
/// </summary>
public static readonly ThreadLocal<BufferWriter> ThreadLocal = new(() => new(4096));
public BufferWriter(int size) public BufferWriter(int size)
{ {
Resize(size); Resize(size);
@ -184,6 +190,11 @@ namespace RageCoop.Core
public unsafe sealed class BufferReader : Buffer public unsafe sealed class BufferReader : Buffer
{ {
/// <summary>
/// Gets a thread local instance of this reader
/// </summary>
public static readonly ThreadLocal<BufferReader> ThreadLocal = new(() => new());
/// <summary> /// <summary>
/// Initialize an empty instance, needs to call <see cref="Initialise(byte*, int)"/> before reading data /// Initialize an empty instance, needs to call <see cref="Initialise(byte*, int)"/> before reading data
/// </summary> /// </summary>

View File

@ -19,21 +19,11 @@ namespace RageCoop.Core
public override PacketType Type => PacketType.CustomEvent; public override PacketType Type => PacketType.CustomEvent;
public int Hash { get; set; } public int Hash { get; set; }
public byte[] Payload; public byte[] Payload;
public object[] Args;
protected override void Serialize(NetOutgoingMessage m) protected override void Serialize(NetOutgoingMessage m)
{ {
m.Write((byte)Flags); m.Write((byte)Flags);
m.Write(Hash); m.Write(Hash);
if (Args != null)
{
lock (SharedWriter)
{
SharedWriter.Reset();
CustomEvents.WriteObjects(SharedWriter, Args);
Payload = SharedWriter.ToByteArray(SharedWriter.Position);
}
}
m.Write(Payload); m.Write(Payload);
} }
@ -42,14 +32,6 @@ namespace RageCoop.Core
Flags = (CustomEventFlags)m.ReadByte(); Flags = (CustomEventFlags)m.ReadByte();
Hash = m.ReadInt32(); Hash = m.ReadInt32();
Payload = m.ReadBytes(m.LengthBytes - m.PositionInBytes); Payload = m.ReadBytes(m.LengthBytes - m.PositionInBytes);
fixed (byte* p = Payload)
{
lock (SharedReader)
{
SharedReader.Initialise(p, Payload.Length);
Args = CustomEvents.ReadObjects(SharedReader);
}
}
} }
} }
} }

View File

@ -152,8 +152,6 @@ namespace RageCoop.Core
internal abstract class Packet : IPacket internal abstract class Packet : IPacket
{ {
public static BufferWriter SharedWriter = new(1024);
public static BufferReader SharedReader = new();
public abstract PacketType Type { get; } public abstract PacketType Type { get; }

View File

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<ImplicitUsings>true</ImplicitUsings>
<NoWarn>1701;1702;CS1591</NoWarn> <NoWarn>1701;1702;CS1591</NoWarn>
<TargetFrameworks>net7.0</TargetFrameworks> <TargetFrameworks>net7.0</TargetFrameworks>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>

View File

@ -0,0 +1,72 @@
using System.Runtime.InteropServices;
namespace RageCoop.Core.Scripting
{
public unsafe delegate void CustomEventHandlerDelegate(int hash, byte* data, int cbData);
/// <summary>
/// </summary>
public class CustomEventReceivedArgs : EventArgs
{
/// <summary>
/// The event hash
/// </summary>
public int Hash { get; set; }
/// <summary>
/// Supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string, Vector3, Quaternion
/// </summary>
public object[] Args { get; set; }
internal object Tag { get; set; }
}
public unsafe class CustomEventHandler
{
[ThreadStatic]
static object _tag;
public CustomEventHandler(IntPtr func)
{
FunctionPtr = func;
if (Path.GetFileName(Environment.ProcessPath).ToLower() == "gtav.exe")
{
Module = SHVDN.Core.CurrentModule;
}
}
private CustomEventHandlerDelegate _managedHandler; // Used to keep GC reference
public IntPtr FunctionPtr { get; }
public IntPtr Module { get; }
/// <summary>
///
/// </summary>
/// <param name="hash"></param>
/// <param name="data"></param>
/// <param name="cbData"></param>
/// <param name="tag">Only works when using <see cref="CustomEventReceivedArgs"/></param>
public void Invoke(int hash, byte* data, int cbData, object tag = null)
{
_tag = tag;
((delegate* unmanaged<int, byte*, int, void>)FunctionPtr)(hash, data, cbData);
_tag = null;
}
public static implicit operator CustomEventHandler(CustomEventHandlerDelegate handler)
=> new(Marshal.GetFunctionPointerForDelegate(handler)) { _managedHandler = handler };
public static implicit operator CustomEventHandler(Action<CustomEventReceivedArgs> handler)
{
return new CustomEventHandlerDelegate((hash, data, cbData) =>
{
var reader = GetReader(data, cbData);
var arg = new CustomEventReceivedArgs
{
Hash = hash,
Args = CustomEvents.ReadObjects(reader),
Tag = _tag
};
handler(arg);
});
}
}
}

View File

@ -22,5 +22,30 @@ namespace RageCoop.Core
public static T JsonDeserialize<T>(string text) => (T)JsonDeserialize(text, typeof(T)); public static T JsonDeserialize<T>(string text) => (T)JsonDeserialize(text, typeof(T));
public static string JsonSerialize(object obj) => JsonConvert.SerializeObject(obj, JsonSettings); public static string JsonSerialize(object obj) => JsonConvert.SerializeObject(obj, JsonSettings);
/// <summary>
/// Shortcut to <see cref="BufferReader.ThreadLocal"/>
/// </summary>
/// <returns></returns>
public static unsafe BufferReader GetReader(byte* data=null,int cbData=0)
{
var reader = BufferReader.ThreadLocal.Value;
reader.Initialise(data,cbData);
return reader;
}
/// <summary>
/// Shortcut to <see cref="BufferWriter.ThreadLocal"/>
/// </summary>
/// <returns></returns>
public static BufferWriter GetWriter(bool reset=true) {
var writer=BufferWriter.ThreadLocal.Value;
if (reset)
{
writer.Reset();
}
return writer;
}
} }
} }

View File

@ -198,10 +198,13 @@ public class Client
try try
{ {
var outgoingMessage = Server.MainNetServer.CreateMessage(); var outgoingMessage = Server.MainNetServer.CreateMessage();
var writer = GetWriter();
CustomEvents.WriteObjects(writer, args);;
new Packets.CustomEvent(flags) new Packets.CustomEvent(flags)
{ {
Hash = hash, Hash = hash,
Args = args Payload = writer.ToByteArray(writer.Position)
}.Pack(outgoingMessage); }.Pack(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered,
(byte)ConnectionChannel.Event); (byte)ConnectionChannel.Event);

View File

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

View File

@ -22,6 +22,7 @@
<GenerateDocumentationFile>True</GenerateDocumentationFile> <GenerateDocumentationFile>True</GenerateDocumentationFile>
<Configurations>Debug;Release;API</Configurations> <Configurations>Debug;Release;API</Configurations>
<OutDir>..\bin\$(Configuration)\Server</OutDir> <OutDir>..\bin\$(Configuration)\Server</OutDir>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'API'"> <PropertyGroup Condition="'$(Configuration)' == 'API'">

View File

@ -19,7 +19,7 @@ public class ServerEvents
#region INTERNAL #region INTERNAL
internal Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new(); internal Dictionary<int, List<CustomEventHandler>> CustomEventHandlers = new();
#endregion #endregion
@ -138,10 +138,25 @@ public class ServerEvents
OnPlayerDisconnected?.Invoke(this, client); OnPlayerDisconnected?.Invoke(this, client);
} }
internal void InvokeCustomEventReceived(Packets.CustomEvent p, Client sender) internal unsafe void InvokeCustomEventReceived(Packets.CustomEvent p, Client sender)
{ {
var args = new CustomEventReceivedArgs { Hash = p.Hash, Args = p.Args, Client = sender }; if (CustomEventHandlers.TryGetValue(p.Hash, out var handlers))
if (CustomEventHandlers.TryGetValue(p.Hash, out var handlers)) handlers.ForEach(x => { x.Invoke(args); }); {
fixed (byte* pData = p.Payload)
{
foreach (var handler in handlers)
{
try
{
handler.Invoke(p.Hash, pData, p.Payload.Length, sender);
}
catch (Exception ex)
{
Server.Logger?.Error("InvokeCustomEvent", ex);
}
}
}
}
} }
internal void InvokePlayerUpdate(Client client) internal void InvokePlayerUpdate(Client client)
@ -351,9 +366,11 @@ public class API
public void SendCustomEvent(CustomEventFlags flags, List<Client> targets, CustomEventHash eventHash, public void SendCustomEvent(CustomEventFlags flags, List<Client> targets, CustomEventHash eventHash,
params object[] args) params object[] args)
{ {
var writer = GetWriter();
CustomEvents.WriteObjects(writer, args);
var p = new Packets.CustomEvent(flags) var p = new Packets.CustomEvent(flags)
{ {
Args = args, Payload = writer.ToByteArray(writer.Position),
Hash = eventHash Hash = eventHash
}; };
if (targets == null) if (targets == null)
@ -383,8 +400,11 @@ public class API
lock (Events.CustomEventHandlers) lock (Events.CustomEventHandlers)
{ {
if (!Events.CustomEventHandlers.TryGetValue(hash, out var handlers)) if (!Events.CustomEventHandlers.TryGetValue(hash, out var handlers))
Events.CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>()); Events.CustomEventHandlers.Add(hash, handlers = new());
handlers.Add(handler); handlers.Add(new Action<Core.Scripting.CustomEventReceivedArgs>((e) =>
{
handler.Invoke(CustomEventReceivedArgs.From(e));
}));
} }
} }

View File

@ -25,23 +25,26 @@ public class ChatEventArgs : EventArgs
/// <summary> /// <summary>
/// </summary> /// </summary>
public class CustomEventReceivedArgs : EventArgs public class CustomEventReceivedArgs : Core.Scripting.CustomEventReceivedArgs
{ {
/// <summary> /// <summary>
/// The <see cref="RageCoop.Server.Client" /> that triggered this event /// The <see cref="RageCoop.Server.Client" /> that triggered this event
/// </summary> /// </summary>
public Client Client { get; set; } public Client Client
{
get => Tag as Client;
set => Tag = value;
}
/// <summary> public static CustomEventReceivedArgs From(Core.Scripting.CustomEventReceivedArgs e)
/// The event hash {
/// </summary> return new CustomEventReceivedArgs
public int Hash { get; set; } {
Args = e.Args,
/// <summary> Tag = e.Tag,
/// Supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string, Vector3, Quaternion, Vector2 Hash = e.Hash,
/// <see cref="ServerObject.Handle" /> };
/// </summary> }
public object[] Args { get; set; }
} }
/// <summary> /// <summary>