From f1b9bf0571b39d00e95b35d607456e3d1a67dc55 Mon Sep 17 00:00:00 2001 From: Sardelka9515 Date: Wed, 1 Feb 2023 21:21:07 +0800 Subject: [PATCH] Some works for the new resource system Rewrite some parts of CustomEvent Expose some API as dll entry --- .../RageCoop.Client.Installer.csproj | 5 - Client/Scripts/Menus/Sub/DebugMenu.cs | 20 +- Client/Scripts/Networking/Networking.cs | 9 +- Client/Scripts/Networking/Receive.cs | 20 +- Client/Scripts/Properties/AssemblyInfo.cs | 12 +- Client/Scripts/RageCoop.Client.csproj | 6 - Client/Scripts/Scripting/API.Exports.cs | 97 +++++++++ Client/Scripts/Scripting/API.cs | 10 +- Client/Scripts/Sync/EntityPool.cs | 30 +-- Core/BitReader.cs | 59 ------ Core/Buffer.cs | 135 +++++++++++-- Core/Packets/CustomEvent.cs | 78 ++------ Core/Packets/Packets.cs | 3 + Core/Scripting/CustomEvents.cs | 187 ++++++++++++++++-- Core/XSpan.cs | 54 +++++ RageCoop.Client.Scripting/API.cs | 7 + RageCoop.Client.Scripting/ClientScript.cs | 12 ++ .../RageCoop.Client.Scripting.csproj | 13 ++ Server/Properties/AssemblyInfo.cs | 4 +- Server/Scripting/ServerObject.cs | 9 +- Tools/UnitTest/Program.cs | 2 +- Tools/UnitTest/UnitTest.csproj | 8 +- build.cmd | 1 + 23 files changed, 558 insertions(+), 223 deletions(-) create mode 100644 Client/Scripts/Scripting/API.Exports.cs delete mode 100644 Core/BitReader.cs create mode 100644 Core/XSpan.cs create mode 100644 RageCoop.Client.Scripting/API.cs create mode 100644 RageCoop.Client.Scripting/ClientScript.cs create mode 100644 RageCoop.Client.Scripting/RageCoop.Client.Scripting.csproj diff --git a/Client/Installer/RageCoop.Client.Installer.csproj b/Client/Installer/RageCoop.Client.Installer.csproj index a898170..4680e34 100644 --- a/Client/Installer/RageCoop.Client.Installer.csproj +++ b/Client/Installer/RageCoop.Client.Installer.csproj @@ -49,9 +49,4 @@ all - - - ..\..\libs\ScriptHookVDotNetCore.dll - - \ No newline at end of file diff --git a/Client/Scripts/Menus/Sub/DebugMenu.cs b/Client/Scripts/Menus/Sub/DebugMenu.cs index fbf5fdb..38db9e8 100644 --- a/Client/Scripts/Menus/Sub/DebugMenu.cs +++ b/Client/Scripts/Menus/Sub/DebugMenu.cs @@ -42,6 +42,16 @@ namespace RageCoop.Client DiagnosticMenu.Add( new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString())); }; + ShowNetworkInfoItem.CheckboxChanged += (s, e) => + { + Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked; + }; + ShowOwnerItem.CheckboxChanged += (s, e) => + { + Main.Settings.ShowEntityOwnerName = ShowOwnerItem.Checked; + Util.SaveSettings(); + }; +#if DEBUG SimulatedLatencyItem.Activated += (s, e) => { try @@ -55,16 +65,8 @@ namespace RageCoop.Client Main.Logger.Error(ex); } }; - ShowNetworkInfoItem.CheckboxChanged += (s, e) => - { - Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked; - }; - ShowOwnerItem.CheckboxChanged += (s, e) => - { - Main.Settings.ShowEntityOwnerName = ShowOwnerItem.Checked; - Util.SaveSettings(); - }; Menu.Add(SimulatedLatencyItem); +#endif Menu.Add(ShowNetworkInfoItem); Menu.Add(ShowOwnerItem); Menu.AddSubMenu(DiagnosticMenu); diff --git a/Client/Scripts/Networking/Networking.cs b/Client/Scripts/Networking/Networking.cs index 6bb3d0f..ce893bd 100644 --- a/Client/Scripts/Networking/Networking.cs +++ b/Client/Scripts/Networking/Networking.cs @@ -31,7 +31,6 @@ namespace RageCoop.Client static Networking() { Security = new Security(Main.Logger); - Packets.CustomEvent.ResolveHandle = _resolveHandle; } public static float Latency => ServerConnection.AverageRoundtripTime / 2; @@ -65,12 +64,14 @@ namespace RageCoop.Client var config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0") { AutoFlushSendQueue = false, - SimulatedMinimumLatency = SimulatedLatency, - SimulatedRandomLatency = 0, AcceptIncomingConnections = true, MaximumConnections = 32, PingInterval = 5 }; +#if DEBUG + config.SimulatedMinimumLatency = SimulatedLatency; + config.SimulatedRandomLatency = 0; +#endif config.EnableMessageType(NetIncomingMessageType.UnconnectedData); config.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess); @@ -153,7 +154,7 @@ namespace RageCoop.Client } IsConnecting = false; - },"Connect"); + }, "Connect"); } } diff --git a/Client/Scripts/Networking/Receive.cs b/Client/Scripts/Networking/Receive.cs index bf9c9bd..5cae2f0 100644 --- a/Client/Scripts/Networking/Receive.cs +++ b/Client/Scripts/Networking/Receive.cs @@ -12,25 +12,7 @@ namespace RageCoop.Client { internal static partial class Networking { - /// - /// Used to reslove entity handle in a - /// - private static readonly Func _resolveHandle = (t, reader) => - { - switch (t) - { - case 50: - return EntityPool.ServerProps[reader.ReadInt32()].MainProp?.Handle; - case 51: - return EntityPool.GetPedByID(reader.ReadInt32())?.MainPed?.Handle; - case 52: - return EntityPool.GetVehicleByID(reader.ReadInt32())?.MainVehicle?.Handle; - case 60: - return EntityPool.ServerBlips[reader.ReadInt32()].Handle; - default: - throw new ArgumentException("Cannot resolve server side argument: " + t); - } - }; + private static readonly AutoResetEvent _publicKeyReceived = new AutoResetEvent(false); diff --git a/Client/Scripts/Properties/AssemblyInfo.cs b/Client/Scripts/Properties/AssemblyInfo.cs index b12579d..9657657 100644 --- a/Client/Scripts/Properties/AssemblyInfo.cs +++ b/Client/Scripts/Properties/AssemblyInfo.cs @@ -1,4 +1,7 @@ -using System.Reflection; + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Resources; // General Information @@ -13,6 +16,7 @@ using System.Resources; // Version information -[assembly: AssemblyVersion("1.6.0.24")] -[assembly: AssemblyFileVersion("1.6.0.24")] -[assembly: NeutralResourcesLanguageAttribute("en-US")] \ No newline at end of file +[assembly: AssemblyVersion("1.6.0.27")] +[assembly: AssemblyFileVersion("1.6.0.27")] +[assembly: NeutralResourcesLanguageAttribute( "en-US" )] + diff --git a/Client/Scripts/RageCoop.Client.csproj b/Client/Scripts/RageCoop.Client.csproj index 88460a3..96cf2bb 100644 --- a/Client/Scripts/RageCoop.Client.csproj +++ b/Client/Scripts/RageCoop.Client.csproj @@ -33,12 +33,6 @@ ..\..\libs\LemonUI.SHVDNC.dll - - - .editorconfig - - - PreserveNewest diff --git a/Client/Scripts/Scripting/API.Exports.cs b/Client/Scripts/Scripting/API.Exports.cs new file mode 100644 index 0000000..3be860c --- /dev/null +++ b/Client/Scripts/Scripting/API.Exports.cs @@ -0,0 +1,97 @@ +using Lidgren.Network; +using RageCoop.Core.Scripting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using static RageCoop.Core.Scripting.CustomEvents; + +namespace RageCoop.Client.Scripting +{ + public static unsafe partial class API + { + [UnmanagedCallersOnly(EntryPoint = "Connect")] + public static void Connect(char* address) => Connect(new string(address)); + + [UnmanagedCallersOnly(EntryPoint = "GetLocalPlayerID")] + public static int GetLocalPlayerID() => LocalPlayerID; + + /// + /// Get configuration value + /// + /// The name of the config + /// Buffer to store retrived value + /// Buffer size + /// The string length of returned value, not including the null terminator + + [UnmanagedCallersOnly(EntryPoint = "GetConfigValue")] + public static int GetConfigValue(char* szName, char* buf, int bufSize) + { + var name = new string(szName); + var value = name switch + { + nameof(Config.EnableAutoRespawn) => Config.EnableAutoRespawn.ToString(), + nameof(Config.Username) => Config.Username.ToString(), + nameof(Config.BlipColor) => Config.BlipColor.ToString(), + nameof(Config.BlipScale) => Config.BlipScale.ToString(), + nameof(Config.BlipSprite) => Config.BlipSprite.ToString(), + _ => null + }; + + if (value == null) + return 0; + + fixed (char* p = value) + { + var cbRequired = (value.Length + 1) * sizeof(char); + Buffer.MemoryCopy(p, buf, bufSize, cbRequired); + return value.Length; + } + } + + [UnmanagedCallersOnly(EntryPoint = "SetConfigValue")] + public static void SetConfigValue(char* szName, char* szValue) + { + var name = new string(szName); + var value = new string(szValue); + switch (name) + { + case nameof(Config.EnableAutoRespawn): Config.EnableAutoRespawn = bool.Parse(value); break; + case nameof(Config.Username): Config.Username = value; break; + case nameof(Config.BlipColor): Config.BlipColor = Enum.Parse(value); break; + case nameof(Config.BlipScale): Config.BlipScale = float.Parse(value); break; + case nameof(Config.BlipSprite): Config.BlipSprite = Enum.Parse(value); break; + }; + } + + [UnmanagedCallersOnly(EntryPoint = "LocalChatMessage")] + public static void LocalChatMessage(char* from, char* msg) => LocalChatMessage(new string(from), new string(msg)); + + [UnmanagedCallersOnly(EntryPoint = "SendChatMessage")] + public static void SendChatMessage(char* msg) => SendChatMessage(new string(msg)); + + [UnmanagedCallersOnly(EntryPoint = "GetEventHash")] + public static CustomEventHash GetEventHash(char* name)=>new string(name); + + public static void SendCustomEvent(int hash, CustomEventFlags flags, byte* data, int cbData) + { + + } + + /// + /// Convert Entity ID to handle + /// + [UnmanagedCallersOnly(EntryPoint = "IdToHandle")] + public static int IdToHandle(byte type, int id) + { + return type switch + { + T_ID_PROP => EntityPool.GetPropByID(id)?.MainProp?.Handle ?? 0, + T_ID_PED => EntityPool.GetPedByID(id)?.MainPed?.Handle ?? 0, + T_ID_VEH => EntityPool.GetVehicleByID(id)?.MainVehicle?.Handle ?? 0, + T_ID_BLIP => EntityPool.GetBlipByID(id)?.Handle ?? 0, + _ => 0, + }; + } + } +} diff --git a/Client/Scripts/Scripting/API.cs b/Client/Scripts/Scripting/API.cs index 293f262..12072cf 100644 --- a/Client/Scripts/Scripting/API.cs +++ b/Client/Scripts/Scripting/API.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Net; +using System.Runtime.InteropServices; using System.Threading; using GTA; using Lidgren.Network; @@ -30,7 +31,7 @@ namespace RageCoop.Client.Scripting /// /// Provides vital functionality to interact with RAGECOOP /// - public static class API + public static unsafe partial class API { #region INTERNAL @@ -270,6 +271,7 @@ namespace RageCoop.Client.Scripting /// /// Disconnect from current server or cancel the connection attempt. /// + [UnmanagedCallersOnly(EntryPoint = "Disconnect")] public static void Disconnect() { if (Networking.IsOnServer || Networking.IsConnecting) Networking.ToggleConnection(null); @@ -369,9 +371,9 @@ namespace RageCoop.Client.Scripting }; DownloadManager.DownloadCompleted += handler; Networking.GetResponse(new Packets.FileTransferRequest - { - Name = name - }, + { + Name = name + }, p => { if (p.Response != FileResponse.Loaded) diff --git a/Client/Scripts/Sync/EntityPool.cs b/Client/Scripts/Sync/EntityPool.cs index 7069c87..e817a5e 100644 --- a/Client/Scripts/Sync/EntityPool.cs +++ b/Client/Scripts/Sync/EntityPool.cs @@ -84,14 +84,10 @@ namespace RageCoop.Client #region PEDS public static SyncedPed GetPedByID(int id) - { - return PedsByID.TryGetValue(id, out var p) ? p : null; - } + => PedsByID.TryGetValue(id, out var p) ? p : null; public static SyncedPed GetPedByHandle(int handle) - { - return PedsByHandle.TryGetValue(handle, out var p) ? p : null; - } + => PedsByHandle.TryGetValue(handle, out var p) ? p : null; public static List GetPedIDs() { @@ -179,14 +175,10 @@ namespace RageCoop.Client #region VEHICLES public static SyncedVehicle GetVehicleByID(int id) - { - return VehiclesByID.TryGetValue(id, out var v) ? v : null; - } + => VehiclesByID.TryGetValue(id, out var v) ? v : null; public static SyncedVehicle GetVehicleByHandle(int handle) - { - return VehiclesByHandle.TryGetValue(handle, out var v) ? v : null; - } + => VehiclesByHandle.TryGetValue(handle, out var v) ? v : null; public static List GetVehicleIDs() { @@ -233,9 +225,7 @@ namespace RageCoop.Client #region PROJECTILES public static SyncedProjectile GetProjectileByID(int id) - { - return ProjectilesByID.TryGetValue(id, out var p) ? p : null; - } + => ProjectilesByID.TryGetValue(id, out var p) ? p : null; public static void Add(SyncedProjectile p) { @@ -290,6 +280,16 @@ namespace RageCoop.Client #endregion + #region SERVER OBJECTS + + public static SyncedProp GetPropByID(int id) + => ServerProps.TryGetValue(id, out var p) ? p : null; + + public static Blip GetBlipByID(int id) + => ServerBlips.TryGetValue(id, out var p) ? p : null; + + #endregion + private static int vehStateIndex; private static int pedStateIndex; private static int vehStatesPerFrame; diff --git a/Core/BitReader.cs b/Core/BitReader.cs deleted file mode 100644 index 2f8a20f..0000000 --- a/Core/BitReader.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.IO; -using System.Text; -using GTA.Math; - -namespace RageCoop.Core -{ - internal class BitReader : BinaryReader - { - public BitReader(byte[] array) : base(new MemoryStream(array)) - { - } - - ~BitReader() - { - Close(); - Dispose(); - } - - public byte[] ReadByteArray() - { - return base.ReadBytes(ReadInt32()); - } - - public override string ReadString() - { - return Encoding.UTF8.GetString(ReadBytes(ReadInt32())); - } - - public Vector3 ReadVector3() - { - return new Vector3 - { - X = ReadSingle(), - Y = ReadSingle(), - Z = ReadSingle() - }; - } - - public Vector2 ReadVector2() - { - return new Vector2 - { - X = ReadSingle(), - Y = ReadSingle() - }; - } - - public Quaternion ReadQuaternion() - { - return new Quaternion - { - X = ReadSingle(), - Y = ReadSingle(), - Z = ReadSingle(), - W = ReadSingle() - }; - } - } -} \ No newline at end of file diff --git a/Core/Buffer.cs b/Core/Buffer.cs index 49a320a..267be62 100644 --- a/Core/Buffer.cs +++ b/Core/Buffer.cs @@ -5,10 +5,21 @@ using GTA.Math; namespace RageCoop.Core { - internal unsafe abstract class BufferBase + public unsafe abstract class BufferBase { + /// + /// Size of this buffer in memory + /// public int Size { get; protected set; } - public int CurrentIndex { get; protected set; } + + /// + /// The current read/write index + /// + public int Position { get; protected set; } + + /// + /// Pointer to the start of this buffer + /// public byte* Address { get; protected set; } /// @@ -20,9 +31,17 @@ namespace RageCoop.Core protected T* Alloc(int count = 1) where T : unmanaged => (T*)Alloc(count * sizeof(T)); + + /// + /// Reset position to the start of this buffer + /// + public void Reset() + { + Position = 0; + } } - internal unsafe sealed class WriteBuffer : BufferBase + public unsafe sealed class WriteBuffer : BufferBase { public WriteBuffer(int size) { @@ -49,12 +68,12 @@ namespace RageCoop.Core protected override byte* Alloc(int cbSize) { - var index = CurrentIndex; - CurrentIndex += cbSize; + var index = Position; + Position += cbSize; // Resize the buffer by at least 50% if there's no sufficient space - if (CurrentIndex > Size) - Resize(Math.Max(CurrentIndex + 1, (int)(Size * 1.5f))); + if (Position > Size) + Resize(Math.Max(Position + 1, (int)(Size * 1.5f))); return Address + index; } @@ -113,22 +132,83 @@ namespace RageCoop.Core faddr[2] = quat.Z; faddr[3] = quat.W; } + + public void Write(ReadOnlySpan source) where T : unmanaged + { + var len = source.Length; + fixed (T* pSource = source) + { + Buffer.MemoryCopy(pSource, Alloc(sizeof(T) * len), len, len); + } + } + + public void Write(Span source) where T : unmanaged => Write((ReadOnlySpan)source); + + /// + /// Write an array, prefix the data with its length so it can latter be read using + /// + public void WriteArray(T[] values) where T : unmanaged + { + var len = values.Length; + WriteVal(len); + fixed (T* pFrom = values) + { + Buffer.MemoryCopy(pFrom, Alloc(sizeof(T) * len), len, len); + } + } + + /// + /// Allocate a byte array on managed heap and copy the data of specified size to it + /// + /// + /// The newly created managed byte array + /// + public byte[] ToByteArray(int cbSize) + { + if (cbSize > Size) + throw new ArgumentOutOfRangeException(nameof(cbSize)); + + var result = new byte[cbSize]; + fixed (byte* pResult = result) + { + Buffer.MemoryCopy(Address, pResult, Size, Size); + } + return result; + } + + /// + /// Free the associated memory allocated on the unmanaged heap + /// + public void Free() => Marshal.FreeHGlobal((IntPtr)Address); } - internal unsafe sealed class ReadBuffer : BufferBase + public unsafe sealed class ReadBuffer : BufferBase { - public ReadBuffer(byte* address, int size) + /// + /// Initialize an empty instance, needs to call before reading data + /// + public ReadBuffer() + { + + } + public ReadBuffer(byte* address, int size) => Initialise(address, size); + + public void Initialise(byte* address, int size) { Address = address; Size = size; + Reset(); } protected override byte* Alloc(int cbSize) { - var index = CurrentIndex; - CurrentIndex += cbSize; + if (Address == null) + throw new NullReferenceException("Address is null"); - if (CurrentIndex > Size) + var index = Position; + Position += cbSize; + + if (Position > Size) throw new InvalidOperationException("Attempting to read beyond the existing buffer"); return Address + index; @@ -178,5 +258,36 @@ namespace RageCoop.Core W = faddr[3], }; } + + /// + /// Read a span of type from current position to + /// + /// + /// + public void Read(Span destination) where T : unmanaged + { + var len = destination.Length; + fixed (T* pTo = destination) + { + Buffer.MemoryCopy(Alloc(len * sizeof(T)), pTo, len, len); + } + } + + /// + /// Reads an array previously written using + /// + /// + /// + public T[] ReadArray() where T : unmanaged + { + var len = ReadVal(); + var from = Alloc(len); + var result = new T[len]; + fixed (T* pTo = result) + { + Buffer.MemoryCopy(from, pTo, len, len); + } + return result; + } } } diff --git a/Core/Packets/CustomEvent.cs b/Core/Packets/CustomEvent.cs index 0e8393d..d04f32b 100644 --- a/Core/Packets/CustomEvent.cs +++ b/Core/Packets/CustomEvent.cs @@ -9,7 +9,6 @@ namespace RageCoop.Core { internal class CustomEvent : Packet { - public static Func ResolveHandle = null; public CustomEventFlags Flags; public CustomEvent(CustomEventFlags flags = CustomEventFlags.None) @@ -19,77 +18,36 @@ namespace RageCoop.Core public override PacketType Type => PacketType.CustomEvent; public int Hash { get; set; } - public object[] Args { get; set; } + public byte[] Payload; + public object[] Args; protected override void Serialize(NetOutgoingMessage m) { - Args = Args ?? new object[] { }; m.Write((byte)Flags); m.Write(Hash); - m.Write(Args.Length); - foreach (var arg in Args) CoreUtils.GetBytesFromObject(arg, m); + if (Args != null) + { + lock (WriteBufferShared) + { + WriteBufferShared.Reset(); + CustomEvents.WriteObjects(WriteBufferShared, Args); + Payload = WriteBufferShared.ToByteArray(WriteBufferShared.Position); + } + } + m.Write(Payload); } - public override void Deserialize(NetIncomingMessage m) + public unsafe override void Deserialize(NetIncomingMessage m) { Flags = (CustomEventFlags)m.ReadByte(); Hash = m.ReadInt32(); - Args = new object[m.ReadInt32()]; - for (var i = 0; i < Args.Length; i++) + Payload = m.ReadBytes(m.LengthBytes - m.PositionInBytes); + fixed (byte* p = Payload) { - var type = m.ReadByte(); - switch (type) + lock (ReadBufferShared) { - case 0x01: - Args[i] = m.ReadByte(); - break; - case 0x02: - Args[i] = m.ReadInt32(); - break; - case 0x03: - Args[i] = m.ReadUInt16(); - break; - case 0x04: - Args[i] = m.ReadInt32(); - break; - case 0x05: - Args[i] = m.ReadUInt32(); - break; - case 0x06: - Args[i] = m.ReadInt64(); - break; - case 0x07: - Args[i] = m.ReadUInt64(); - break; - case 0x08: - Args[i] = m.ReadFloat(); - break; - case 0x09: - Args[i] = m.ReadBoolean(); - break; - case 0x10: - Args[i] = m.ReadString(); - break; - case 0x11: - Args[i] = m.ReadVector3(); - break; - case 0x12: - Args[i] = m.ReadQuaternion(); - break; - case 0x13: - Args[i] = (Model)m.ReadInt32(); - break; - case 0x14: - Args[i] = m.ReadVector2(); - break; - case 0x15: - Args[i] = m.ReadByteArray(); - break; - default: - if (ResolveHandle == null) throw new InvalidOperationException($"Unexpected type: {type}"); - - Args[i] = ResolveHandle(type, m); - break; + ReadBufferShared.Initialise(p,Payload.Length); + Args = CustomEvents.ReadObjects(ReadBufferShared); } } } diff --git a/Core/Packets/Packets.cs b/Core/Packets/Packets.cs index cdf9edf..fe05bbf 100644 --- a/Core/Packets/Packets.cs +++ b/Core/Packets/Packets.cs @@ -152,6 +152,9 @@ namespace RageCoop.Core internal abstract class Packet : IPacket { + public static WriteBuffer WriteBufferShared = new(1024); + public static ReadBuffer ReadBufferShared = new(); + public abstract PacketType Type { get; } public virtual void Deserialize(NetIncomingMessage m) diff --git a/Core/Scripting/CustomEvents.cs b/Core/Scripting/CustomEvents.cs index 3df7a08..82fdb2f 100644 --- a/Core/Scripting/CustomEvents.cs +++ b/Core/Scripting/CustomEvents.cs @@ -1,5 +1,8 @@ -using System; +using GTA; +using GTA.Math; +using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; @@ -94,7 +97,7 @@ namespace RageCoop.Core.Scripting /// /// - public static class CustomEvents + public static partial class CustomEvents { internal static readonly CustomEventHash OnPlayerDied = "RageCoop.OnPlayerDied"; internal static readonly CustomEventHash SetWeather = "RageCoop.SetWeather"; @@ -116,17 +119,177 @@ namespace RageCoop.Core.Scripting internal static readonly CustomEventHash WeatherTimeSync = "RageCoop.WeatherTimeSync"; internal static readonly CustomEventHash IsHost = "RageCoop.IsHost"; - /// - /// Get event hash from string. - /// - /// - /// - /// This method is obsoete, you should use implicit operator or - /// - [Obsolete] - public static int Hash(string s) + #region TYPE CONSTANTS + + public const byte T_BYTE = 1; + public const byte T_SHORT = 2; + public const byte T_USHORT = 3; + public const byte T_INT = 4; + public const byte T_UINT = 5; + public const byte T_LONG = 6; + public const byte T_ULONG = 7; + public const byte T_FLOAT = 8; + public const byte T_BOOL = 9; + public const byte T_STR = 10; + public const byte T_VEC3 = 11; + public const byte T_QUAT = 12; + public const byte T_MODEL = 13; + public const byte T_VEC2 = 14; + public const byte T_BYTEARR = 15; + public const byte T_ID_PROP = 50; + public const byte T_ID_PED = 51; + public const byte T_ID_VEH = 52; + public const byte T_ID_BLIP = 60; + + #endregion + + public static void WriteObjects(WriteBuffer b, params object[] objs) { - return CustomEventHash.FromString(s); + b.WriteVal(objs.Length); + foreach(var obj in objs) + { + switch (obj) + { + case byte value: + b.WriteVal(T_BYTE); + b.WriteVal(value); + break; + case short value: + b.WriteVal(T_SHORT); + b.WriteVal(value); + break; + case ushort value: + b.WriteVal(T_USHORT); + b.WriteVal(value); + break; + case int value: + b.WriteVal(T_INT); + b.WriteVal(value); + break; + case uint value: + b.WriteVal(T_UINT); + b.WriteVal(value); + break; + case long value: + b.WriteVal(T_LONG); + b.WriteVal(value); + break; + case ulong value: + b.WriteVal(T_ULONG); + b.WriteVal(value); + break; + case float value: + b.WriteVal(T_FLOAT); + b.WriteVal(value); + break; + case bool value: + b.WriteVal(T_BOOL); + b.WriteVal(value); + break; + case string value: + b.WriteVal(T_STR); + b.Write(value); + break; + case Vector2 value: + b.WriteVal(T_VEC2); + b.Write(ref value); + break; + case Vector3 value: + b.WriteVal(T_VEC3); + b.Write(ref value); + break; + case Quaternion value: + b.WriteVal(T_QUAT); + b.Write(ref value); + break; + case Model value: + b.WriteVal(T_MODEL); + b.WriteVal(value); + break; + case byte[] value: + b.WriteVal(T_BYTEARR); + b.WriteArray(value); + break; + case Tuple value: + b.WriteVal(value.Item1); + b.Write(new ReadOnlySpan(value.Item2)); + break; + default: + throw new Exception("Unsupported object type: " + obj.GetType()); + } + } } + public static object[] ReadObjects(ReadBuffer r) + { + var Args = new object[r.ReadVal()]; + for (var i = 0; i < Args.Length; i++) + { + var type = r.ReadVal(); + switch (type) + { + case T_BYTE: + Args[i] = r.ReadVal(); + break; + case T_SHORT: + Args[i] = r.ReadVal(); + break; + case T_USHORT: + Args[i] = r.ReadVal(); + break; + case T_INT: + Args[i] = r.ReadVal(); + break; + case T_UINT: + Args[i] = r.ReadVal(); + break; + case T_LONG: + Args[i] = r.ReadVal(); + break; + case T_ULONG: + Args[i] = r.ReadVal(); + break; + case T_FLOAT: + Args[i] = r.ReadVal(); + break; + case T_BOOL: + Args[i] = r.ReadVal(); + break; + case T_STR: + r.Read(out string str); + Args[i] = str; + break; + case T_VEC3: + r.Read(out Vector3 vec); + Args[i] = vec; + break; + case T_QUAT: + r.Read(out Quaternion quat); + Args[i] = quat; + break; + case T_MODEL: + Args[i] = r.ReadVal(); + break; + case T_VEC2: + r.Read(out Vector2 vec2); + Args[i] = vec2; + break; + case T_BYTEARR: + Args[i] = r.ReadArray(); + break; + case T_ID_BLIP: + case T_ID_PED: + case T_ID_PROP: + case T_ID_VEH: + Args[i] = IdToHandle(type,r.ReadVal()); + break; + default: + throw new InvalidOperationException($"Unexpected type: {type}"); + } + } + return Args; + } + + [LibraryImport("RageCoop.Client.dll")] + public static partial int IdToHandle(byte type,int id); } } \ No newline at end of file diff --git a/Core/XSpan.cs b/Core/XSpan.cs new file mode 100644 index 0000000..48dcb0e --- /dev/null +++ b/Core/XSpan.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RageCoop.Core +{ + /// + /// A light-weight and less restricted implementation of , gonna be used at some point, maybe? + /// + /// + public readonly unsafe struct XSpan where T : unmanaged + { + public XSpan(void* address, int len) + { + Address = (T*)address; + Length = len; + } + + public T this[int i] + { + get { return Address[i]; } + set { Address[i] = value; } + } + + public readonly T* Address; + public readonly int Length; + + public void CopyTo(XSpan dest, int destStart = 0) + { + for (int i = 0; i < Length; i++) + { + dest[destStart + i] = this[i]; + } + } + + public XSpan Slice(int start) => new(Address + start, Length - start); + public XSpan Slice(int start, int len) => new(Address + start, len); + + public static implicit operator Span(XSpan s) + { + return new Span(s.Address, s.Length); + } + + public static implicit operator XSpan(Span s) + { + fixed (T* ptr = s) + { + return new XSpan(ptr, s.Length); + } + } + } +} diff --git a/RageCoop.Client.Scripting/API.cs b/RageCoop.Client.Scripting/API.cs new file mode 100644 index 0000000..501a65f --- /dev/null +++ b/RageCoop.Client.Scripting/API.cs @@ -0,0 +1,7 @@ +namespace RageCoop.Client.Scripting +{ + public static class API + { + + } +} \ No newline at end of file diff --git a/RageCoop.Client.Scripting/ClientScript.cs b/RageCoop.Client.Scripting/ClientScript.cs new file mode 100644 index 0000000..bb371c8 --- /dev/null +++ b/RageCoop.Client.Scripting/ClientScript.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RageCoop.Client.Scripting +{ + internal class ClientScript + { + } +} diff --git a/RageCoop.Client.Scripting/RageCoop.Client.Scripting.csproj b/RageCoop.Client.Scripting/RageCoop.Client.Scripting.csproj new file mode 100644 index 0000000..fa30479 --- /dev/null +++ b/RageCoop.Client.Scripting/RageCoop.Client.Scripting.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + enable + enable + + + + + + + diff --git a/Server/Properties/AssemblyInfo.cs b/Server/Properties/AssemblyInfo.cs index f2f25e6..dee7d77 100644 --- a/Server/Properties/AssemblyInfo.cs +++ b/Server/Properties/AssemblyInfo.cs @@ -15,7 +15,7 @@ using System.Resources; [assembly: AssemblyCulture("")] // Version information -[assembly: AssemblyVersion("1.6.0.25")] -[assembly: AssemblyFileVersion("1.6.0.25")] +[assembly: AssemblyVersion("1.6.0.28")] +[assembly: AssemblyFileVersion("1.6.0.28")] [assembly: NeutralResourcesLanguageAttribute( "en-US" )] diff --git a/Server/Scripting/ServerObject.cs b/Server/Scripting/ServerObject.cs index d6c1f50..6a50f2a 100644 --- a/Server/Scripting/ServerObject.cs +++ b/Server/Scripting/ServerObject.cs @@ -6,6 +6,7 @@ using GTA.Math; using GTA.Native; using RageCoop.Core; using RageCoop.Core.Scripting; +using static RageCoop.Core.Scripting.CustomEvents; namespace RageCoop.Server.Scripting; @@ -78,11 +79,11 @@ public abstract class ServerObject switch (this) { case ServerProp _: - return 50; + return T_ID_PROP; case ServerPed _: - return 51; + return T_ID_PED; case ServerVehicle _: - return 52; + return T_ID_VEH; default: throw new NotImplementedException(); } @@ -280,7 +281,7 @@ public class ServerBlip /// /// Pass this as an argument in CustomEvent or NativeCall to convert this object to handle at client side. /// - public Tuple Handle => new(60, BitConverter.GetBytes(ID)); + public Tuple Handle => new(T_ID_BLIP, BitConverter.GetBytes(ID)); /// /// Network ID (not handle!) diff --git a/Tools/UnitTest/Program.cs b/Tools/UnitTest/Program.cs index 8ea0479..b12b616 100644 --- a/Tools/UnitTest/Program.cs +++ b/Tools/UnitTest/Program.cs @@ -39,7 +39,7 @@ namespace UnitTest buf.Write(e.str); } Console.WriteLine($"Buffer size: {buf.Size}"); - Console.WriteLine($"Current position: {buf.CurrentIndex}"); + Console.WriteLine($"Current position: {buf.Position}"); Console.WriteLine("Validating data"); var reader = new ReadBuffer(buf.Address, buf.Size); diff --git a/Tools/UnitTest/UnitTest.csproj b/Tools/UnitTest/UnitTest.csproj index 436cc68..723d28b 100644 --- a/Tools/UnitTest/UnitTest.csproj +++ b/Tools/UnitTest/UnitTest.csproj @@ -1,4 +1,4 @@ - + Exe @@ -11,10 +11,4 @@ - - - ..\..\libs\ScriptHookVDotNetCore.dll - - - diff --git a/build.cmd b/build.cmd index d03620a..c993528 100644 --- a/build.cmd +++ b/build.cmd @@ -1,3 +1,4 @@ +git submodule update --init if exist "bin" rmdir /s /q "bin" dotnet build -c Release dotnet build -c API Core\RageCoop.Core.csproj