Introduce CustomEventHandler for more flexible invocation
This commit is contained in:
@ -12,10 +12,6 @@ namespace RageCoop.Client.Scripting
|
||||
{
|
||||
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>
|
||||
/// Copy content of string to a sequential block of memory
|
||||
/// </summary>
|
||||
@ -91,8 +87,7 @@ namespace RageCoop.Client.Scripting
|
||||
|
||||
public static void SendCustomEvent(CustomEventFlags flags, CustomEventHash hash, params object[] args)
|
||||
{
|
||||
var writer = _bufWriter.Value;
|
||||
writer.Reset();
|
||||
var writer = GetWriter();
|
||||
CustomEvents.WriteObjects(writer, args);
|
||||
SendCustomEvent(flags, hash, writer.Address, writer.Position);
|
||||
}
|
||||
|
@ -237,11 +237,6 @@ namespace RageCoop.Client
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (e.KeyCode == Keys.I)
|
||||
{
|
||||
APIBridge.SendChatMessage("hello there");
|
||||
}
|
||||
#if CEF
|
||||
if (CefRunning)
|
||||
{
|
||||
|
@ -15,20 +15,6 @@ using System.Threading;
|
||||
|
||||
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>
|
||||
/// Provides vital functionality to interact with RAGECOOP
|
||||
@ -37,8 +23,8 @@ namespace RageCoop.Client.Scripting
|
||||
{
|
||||
#region INTERNAL
|
||||
|
||||
internal static Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers =
|
||||
new Dictionary<int, List<Action<CustomEventReceivedArgs>>>();
|
||||
internal static Dictionary<int, List<CustomEventHandler>> CustomEventHandlers =
|
||||
new();
|
||||
|
||||
#endregion
|
||||
|
||||
@ -166,12 +152,26 @@ namespace RageCoop.Client.Scripting
|
||||
|
||||
internal static void InvokeCustomEventReceived(Packets.CustomEvent p)
|
||||
{
|
||||
var args = new CustomEventReceivedArgs { Hash = p.Hash, Args = p.Args };
|
||||
|
||||
// Log.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
|
||||
|
||||
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
|
||||
@ -290,9 +290,12 @@ namespace RageCoop.Client.Scripting
|
||||
/// </param>
|
||||
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)
|
||||
{
|
||||
Args = args,
|
||||
|
||||
Payload = writer.ToByteArray(writer.Position),
|
||||
Hash = eventHash
|
||||
}, Networking.ServerConnection, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
@ -310,7 +313,7 @@ namespace RageCoop.Client.Scripting
|
||||
lock (CustomEventHandlers)
|
||||
{
|
||||
if (!CustomEventHandlers.TryGetValue(hash, out var handlers))
|
||||
CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>());
|
||||
CustomEventHandlers.Add(hash, handlers = new());
|
||||
handlers.Add(handler);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using GTA.Math;
|
||||
|
||||
namespace RageCoop.Core
|
||||
@ -43,6 +44,11 @@ namespace RageCoop.Core
|
||||
|
||||
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)
|
||||
{
|
||||
Resize(size);
|
||||
@ -184,6 +190,11 @@ namespace RageCoop.Core
|
||||
|
||||
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>
|
||||
/// Initialize an empty instance, needs to call <see cref="Initialise(byte*, int)"/> before reading data
|
||||
/// </summary>
|
||||
|
@ -19,21 +19,11 @@ namespace RageCoop.Core
|
||||
public override PacketType Type => PacketType.CustomEvent;
|
||||
public int Hash { get; set; }
|
||||
public byte[] Payload;
|
||||
public object[] Args;
|
||||
|
||||
protected override void Serialize(NetOutgoingMessage m)
|
||||
{
|
||||
m.Write((byte)Flags);
|
||||
m.Write(Hash);
|
||||
if (Args != null)
|
||||
{
|
||||
lock (SharedWriter)
|
||||
{
|
||||
SharedWriter.Reset();
|
||||
CustomEvents.WriteObjects(SharedWriter, Args);
|
||||
Payload = SharedWriter.ToByteArray(SharedWriter.Position);
|
||||
}
|
||||
}
|
||||
m.Write(Payload);
|
||||
}
|
||||
|
||||
@ -42,14 +32,6 @@ namespace RageCoop.Core
|
||||
Flags = (CustomEventFlags)m.ReadByte();
|
||||
Hash = m.ReadInt32();
|
||||
Payload = m.ReadBytes(m.LengthBytes - m.PositionInBytes);
|
||||
fixed (byte* p = Payload)
|
||||
{
|
||||
lock (SharedReader)
|
||||
{
|
||||
SharedReader.Initialise(p, Payload.Length);
|
||||
Args = CustomEvents.ReadObjects(SharedReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,8 +152,6 @@ namespace RageCoop.Core
|
||||
|
||||
internal abstract class Packet : IPacket
|
||||
{
|
||||
public static BufferWriter SharedWriter = new(1024);
|
||||
public static BufferReader SharedReader = new();
|
||||
|
||||
public abstract PacketType Type { get; }
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>true</ImplicitUsings>
|
||||
<NoWarn>1701;1702;CS1591</NoWarn>
|
||||
<TargetFrameworks>net7.0</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
|
72
Core/Scripting/CustomEventHandler.cs
Normal file
72
Core/Scripting/CustomEventHandler.cs
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -22,5 +22,30 @@ namespace RageCoop.Core
|
||||
public static T JsonDeserialize<T>(string text) => (T)JsonDeserialize(text, typeof(T));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -198,10 +198,13 @@ public class Client
|
||||
try
|
||||
{
|
||||
var outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
|
||||
var writer = GetWriter();
|
||||
CustomEvents.WriteObjects(writer, args);;
|
||||
new Packets.CustomEvent(flags)
|
||||
{
|
||||
Hash = hash,
|
||||
Args = args
|
||||
Payload = writer.ToByteArray(writer.Position)
|
||||
}.Pack(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered,
|
||||
(byte)ConnectionChannel.Event);
|
||||
|
@ -15,7 +15,7 @@ using System.Resources;
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information
|
||||
[assembly: AssemblyVersion("1.6.0.30")]
|
||||
[assembly: AssemblyFileVersion("1.6.0.30")]
|
||||
[assembly: AssemblyVersion("1.6.0.32")]
|
||||
[assembly: AssemblyFileVersion("1.6.0.32")]
|
||||
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<Configurations>Debug;Release;API</Configurations>
|
||||
<OutDir>..\bin\$(Configuration)\Server</OutDir>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'API'">
|
||||
|
@ -19,7 +19,7 @@ public class ServerEvents
|
||||
|
||||
#region INTERNAL
|
||||
|
||||
internal Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new();
|
||||
internal Dictionary<int, List<CustomEventHandler>> CustomEventHandlers = new();
|
||||
|
||||
#endregion
|
||||
|
||||
@ -138,10 +138,25 @@ public class ServerEvents
|
||||
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)) handlers.ForEach(x => { x.Invoke(args); });
|
||||
if (CustomEventHandlers.TryGetValue(p.Hash, out var handlers))
|
||||
{
|
||||
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)
|
||||
@ -351,9 +366,11 @@ public class API
|
||||
public void SendCustomEvent(CustomEventFlags flags, List<Client> targets, CustomEventHash eventHash,
|
||||
params object[] args)
|
||||
{
|
||||
var writer = GetWriter();
|
||||
CustomEvents.WriteObjects(writer, args);
|
||||
var p = new Packets.CustomEvent(flags)
|
||||
{
|
||||
Args = args,
|
||||
Payload = writer.ToByteArray(writer.Position),
|
||||
Hash = eventHash
|
||||
};
|
||||
if (targets == null)
|
||||
@ -383,8 +400,11 @@ public class API
|
||||
lock (Events.CustomEventHandlers)
|
||||
{
|
||||
if (!Events.CustomEventHandlers.TryGetValue(hash, out var handlers))
|
||||
Events.CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>());
|
||||
handlers.Add(handler);
|
||||
Events.CustomEventHandlers.Add(hash, handlers = new());
|
||||
handlers.Add(new Action<Core.Scripting.CustomEventReceivedArgs>((e) =>
|
||||
{
|
||||
handler.Invoke(CustomEventReceivedArgs.From(e));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,23 +25,26 @@ public class ChatEventArgs : EventArgs
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class CustomEventReceivedArgs : EventArgs
|
||||
public class CustomEventReceivedArgs : Core.Scripting.CustomEventReceivedArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="RageCoop.Server.Client" /> that triggered this event
|
||||
/// </summary>
|
||||
public Client Client { get; set; }
|
||||
public Client Client
|
||||
{
|
||||
get => Tag as Client;
|
||||
set => Tag = value;
|
||||
}
|
||||
|
||||
/// <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, Vector2
|
||||
/// <see cref="ServerObject.Handle" />
|
||||
/// </summary>
|
||||
public object[] Args { get; set; }
|
||||
public static CustomEventReceivedArgs From(Core.Scripting.CustomEventReceivedArgs e)
|
||||
{
|
||||
return new CustomEventReceivedArgs
|
||||
{
|
||||
Args = e.Args,
|
||||
Tag = e.Tag,
|
||||
Hash = e.Hash,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
Reference in New Issue
Block a user