Merge pull request #44 from RAGECOOP/net7-shvdnc
Use SHVDNC as new runtime
This commit is contained in:
3
.github/workflows/build-test.yaml
vendored
3
.github/workflows/build-test.yaml
vendored
@ -24,6 +24,9 @@ jobs:
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: ${{ matrix.dotnet-version }}
|
||||
|
||||
- name: Initialise submodules
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
3
.github/workflows/nightly-build.yaml
vendored
3
.github/workflows/nightly-build.yaml
vendored
@ -18,6 +18,9 @@ jobs:
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: ${{ matrix.dotnet-version }}
|
||||
|
||||
- name: Initialise submodules
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
[submodule "libs/Lidgren.Network"]
|
||||
path = libs/Lidgren.Network
|
||||
url = https://github.com/RAGECOOP/lidgren-network-gen3
|
||||
[submodule "libs/ScriptHookVDotNetCore"]
|
||||
path = libs/ScriptHookVDotNetCore
|
||||
url = https://github.com/Sardelka9515/scripthookvdotnetcore
|
@ -6,7 +6,6 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using RageCoop.Core;
|
||||
using static RageCoop.Client.Shared;
|
||||
@ -116,14 +115,14 @@ namespace RageCoop.Client.Installer
|
||||
UpdateStatus("Checking conflicts");
|
||||
var menyooConfig = Path.Combine(root, @"menyooStuff\menyooConfig.ini");
|
||||
var settingsPath = Path.Combine(root, SettingsPath);
|
||||
Settings settings = null;
|
||||
ClientSettings settings = null;
|
||||
try
|
||||
{
|
||||
settings = Util.ReadSettings(settingsPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
settings = new Settings();
|
||||
settings = new();
|
||||
}
|
||||
|
||||
if (File.Exists(menyooConfig))
|
||||
@ -141,7 +140,7 @@ namespace RageCoop.Client.Installer
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
KeyDown += (s, e) =>
|
||||
{
|
||||
settings.MenuKey = (Keys)KeyInterop.VirtualKeyFromKey(e.Key);
|
||||
settings.MenuKey = (GTA.Keys)KeyInterop.VirtualKeyFromKey(e.Key);
|
||||
ae.Set();
|
||||
}));
|
||||
ae.WaitOne();
|
||||
|
@ -1,57 +1,52 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AllowedReferenceRelatedFileExtensions>
|
||||
<PropertyGroup>
|
||||
<NoAotCompile>true</NoAotCompile>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AllowedReferenceRelatedFileExtensions>
|
||||
-
|
||||
</AllowedReferenceRelatedFileExtensions>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<OutDir>..\..\bin\Debug\Client</OutDir>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefineConstants>DEBUG</DefineConstants>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<OutDir>..\..\bin\Release\Client</OutDir>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Loader\RageCoop.Client.Loader.csproj" />
|
||||
<ProjectReference Include="..\Scripts\RageCoop.Client.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="bg.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resource.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resource.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resource.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Costura.Fody" Version="5.7.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<OutDir>..\..\bin\Debug\Client</OutDir>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefineConstants>DEBUG</DefineConstants>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<OutDir>..\..\bin\Release\Client</OutDir>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Scripts\RageCoop.Client.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="bg.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Resource.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resource.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resource.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Costura.Fody" Version="5.7.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>compile; runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.355802">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
47
Client/Scripting/APIBridge.Generated.cs
Normal file
47
Client/Scripting/APIBridge.Generated.cs
Normal file
@ -0,0 +1,47 @@
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
public static unsafe partial class APIBridge
|
||||
{
|
||||
|
||||
public static class Config
|
||||
{
|
||||
public static System.String Username => GetConfig<System.String>("Username");
|
||||
public static System.Boolean EnableAutoRespawn => GetConfig<System.Boolean>("EnableAutoRespawn");
|
||||
public static GTA.BlipColor BlipColor => GetConfig<GTA.BlipColor>("BlipColor");
|
||||
public static GTA.BlipSprite BlipSprite => GetConfig<GTA.BlipSprite>("BlipSprite");
|
||||
public static System.Single BlipScale => GetConfig<System.Single>("BlipScale");
|
||||
public static System.Boolean ShowPlayerNameTag => GetConfig<System.Boolean>("ShowPlayerNameTag");
|
||||
|
||||
}
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
public static System.Int32 LocalPlayerID => GetProperty<System.Int32>("LocalPlayerID");
|
||||
public static System.Boolean IsOnServer => GetProperty<System.Boolean>("IsOnServer");
|
||||
public static System.Net.IPEndPoint ServerEndPoint => GetProperty<System.Net.IPEndPoint>("ServerEndPoint");
|
||||
public static System.Boolean IsMenuVisible => GetProperty<System.Boolean>("IsMenuVisible");
|
||||
public static System.Boolean IsChatFocused => GetProperty<System.Boolean>("IsChatFocused");
|
||||
public static System.Boolean IsPlayerListVisible => GetProperty<System.Boolean>("IsPlayerListVisible");
|
||||
public static System.Version CurrentVersion => GetProperty<System.Version>("CurrentVersion");
|
||||
public static System.Collections.Generic.Dictionary<System.Int32, RageCoop.Client.Scripting.PlayerInfo> Players => GetProperty<System.Collections.Generic.Dictionary<System.Int32, RageCoop.Client.Scripting.PlayerInfo>>("Players");
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region FUNCTIONS
|
||||
|
||||
public static void Connect(System.String address) => InvokeCommand("Connect", address);
|
||||
public static void Disconnect() => InvokeCommand("Disconnect");
|
||||
public static System.Collections.Generic.List<RageCoop.Core.ServerInfo> ListServers() => InvokeCommand<System.Collections.Generic.List<RageCoop.Core.ServerInfo>>("ListServers");
|
||||
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 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
|
||||
}
|
||||
}
|
161
Client/Scripting/APIBridge.cs
Normal file
161
Client/Scripting/APIBridge.cs
Normal file
@ -0,0 +1,161 @@
|
||||
using RageCoop.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("RageCoop.Client")] // For debugging
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
public static unsafe partial class APIBridge
|
||||
{
|
||||
static readonly ThreadLocal<char[]> _resultBuf = new(() => new char[4096]);
|
||||
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>
|
||||
/// <param name="strs"></param>
|
||||
/// <returns>Pointer to the start of the block, can be used as argv</returns>
|
||||
/// <remarks>Call <see cref="Marshal.FreeHGlobal(nint)"/> with the returned pointer when finished using</remarks>
|
||||
internal static char** StringArrayToMemory(string[] strs)
|
||||
{
|
||||
var argc = strs.Length;
|
||||
var cbSize = sizeof(IntPtr) * argc + strs.Sum(s => (s.Length + 1) * sizeof(char));
|
||||
var result = (char**)Marshal.AllocHGlobal(cbSize);
|
||||
var pCurStr = (char*)(result + argc);
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
result[i] = pCurStr;
|
||||
var len = strs[i].Length;
|
||||
var cbStrSize = (len + 1) * sizeof(char); // null terminator
|
||||
fixed (char* pStr = strs[i])
|
||||
{
|
||||
System.Buffer.MemoryCopy(pStr, pCurStr, cbStrSize, cbStrSize);
|
||||
}
|
||||
pCurStr += len + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static void InvokeCommand(string name, params object[] args)
|
||||
=> InvokeCommandAsJson(name, args);
|
||||
internal static T InvokeCommand<T>(string name, params object[] args)
|
||||
=> JsonDeserialize<T>(InvokeCommandAsJson(name, args));
|
||||
|
||||
/// <summary>
|
||||
/// Invoke command and get the return value as json
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>The json representation of returned object</returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
internal static string InvokeCommandAsJson(string name, params object[] args)
|
||||
{
|
||||
var argc = args.Length;
|
||||
var argv = StringArrayToMemory(args.Select(JsonSerialize).ToArray());
|
||||
try
|
||||
{
|
||||
fixed(char* pName = name)
|
||||
{
|
||||
var resultLen = InvokeCommandAsJsonUnsafe(pName, argc, argv);
|
||||
if (resultLen == 0)
|
||||
throw new Exception(GetLastResult());
|
||||
return GetLastResult();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal((IntPtr)argv);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetLastResult()
|
||||
{
|
||||
var countCharsRequired = GetLastResultLenInChars() + 1;
|
||||
if (countCharsRequired > _resultBuf.Value.Length)
|
||||
{
|
||||
_resultBuf.Value = new char[countCharsRequired];
|
||||
}
|
||||
var cbBufSize = _resultBuf.Value.Length * sizeof(char);
|
||||
fixed (char* pBuf = _resultBuf.Value)
|
||||
{
|
||||
if (GetLastResultUnsafe(pBuf, cbBufSize) > 0)
|
||||
{
|
||||
return new string(pBuf);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SendCustomEvent(CustomEventHash hash, params object[] args)
|
||||
=> SendCustomEvent(CustomEventFlags.None, hash, args);
|
||||
public static void SendCustomEvent(CustomEventFlags flags, CustomEventHash hash, params object[] args)
|
||||
{
|
||||
var writer = GetWriter();
|
||||
CustomEvents.WriteObjects(writer, args);
|
||||
SendCustomEventUnsafe(flags, hash, writer.Address, writer.Position);
|
||||
}
|
||||
|
||||
public static void RegisterCustomEventHandler(CustomEventHash hash, Action<CustomEventReceivedArgs> handler)
|
||||
=> RegisterCustomEventHandler(hash, (CustomEventHandler)handler);
|
||||
|
||||
internal static string GetPropertyAsJson(string name) => InvokeCommandAsJson("GetProperty", name);
|
||||
internal static string GetConfigAsJson(string name) => InvokeCommandAsJson("GetConfig", name);
|
||||
|
||||
internal static T GetProperty<T>(string name) => JsonDeserialize<T>(GetPropertyAsJson(name));
|
||||
internal static T GetConfig<T>(string name) => JsonDeserialize<T>(GetConfigAsJson(name));
|
||||
|
||||
internal static void SetProperty(string name, object val) => InvokeCommand("SetProperty", name, val);
|
||||
internal static void SetConfig(string name, object val) => InvokeCommand("SetConfig", name, val);
|
||||
|
||||
|
||||
[ApiImport]
|
||||
public static delegate* unmanaged<char*, CustomEventHash> GetEventHash;
|
||||
|
||||
[ApiImport]
|
||||
private static delegate* unmanaged<char*,void> SetLastResult;
|
||||
|
||||
[ApiImport(EntryPoint = "GetLastResult")]
|
||||
private static delegate* unmanaged<char*, int, int> GetLastResultUnsafe;
|
||||
|
||||
[ApiImport(EntryPoint = "InvokeCommand")]
|
||||
private static delegate* unmanaged<char*, int, char**, int> InvokeCommandAsJsonUnsafe;
|
||||
|
||||
[ApiImport(EntryPoint = "SendCustomEvent")]
|
||||
private static delegate* unmanaged<CustomEventFlags, int, byte*, int, void> SendCustomEventUnsafe;
|
||||
|
||||
[ApiImport]
|
||||
private static delegate* unmanaged<int> GetLastResultLenInChars;
|
||||
|
||||
[ApiImport]
|
||||
public static delegate* unmanaged<LogLevel, char*, void> LogEnqueue;
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
class ApiImportAttribute : Attribute
|
||||
{
|
||||
public string EntryPoint;
|
||||
}
|
||||
}
|
28
Client/Scripting/ClientFile.cs
Normal file
28
Client/Scripting/ClientFile.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using Newtonsoft.Json;
|
||||
using RageCoop.Core.Scripting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
public class ClientFile : ResourceFile
|
||||
{
|
||||
public ClientFile() {
|
||||
GetStream = GetStreamMethod;
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
public string FullPath { get; internal set; }
|
||||
Stream GetStreamMethod()
|
||||
{
|
||||
if (IsDirectory)
|
||||
{
|
||||
return File.Open(FullPath, FileMode.Open);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot open directory as file");
|
||||
}
|
||||
}
|
||||
}
|
49
Client/Scripting/ClientResource.cs
Normal file
49
Client/Scripting/ClientResource.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using Newtonsoft.Json;
|
||||
using RageCoop.Core.Scripting;
|
||||
using SHVDN;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class ClientResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the resource
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string Name { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Directory where the scripts is loaded from
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string ScriptsDirectory { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// A resource-specific folder that can be used to store your files.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string DataFolder { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="ClientFile" /> where this script is loaded from.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public Dictionary<string, ClientFile> Files { get; internal set; } = new Dictionary<string, ClientFile>();
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="Core.Logger" /> instance that can be used to debug your resource.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public ResourceLogger Logger => ResourceLogger.Default;
|
||||
|
||||
}
|
||||
|
||||
}
|
62
Client/Scripting/ClientScript.cs
Normal file
62
Client/Scripting/ClientScript.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using GTA;
|
||||
using RageCoop.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
[JsonDontSerialize]
|
||||
[ScriptAttributes(NoDefaultInstance = true)]
|
||||
public abstract class ClientScript : Script
|
||||
{
|
||||
readonly ConcurrentQueue<Func<bool>> _jobQueue = new();
|
||||
readonly Queue<Func<bool>> _reAdd = new();
|
||||
public ClientScript()
|
||||
{
|
||||
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() == FilePath?.ToLower());
|
||||
if (CurrentFile == null)
|
||||
{
|
||||
Logger.Warning("No file associated with curent script was found");
|
||||
}
|
||||
}
|
||||
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))
|
||||
_jobQueue.Enqueue(toAdd);
|
||||
while (_jobQueue.TryDequeue(out var job))
|
||||
{
|
||||
if (!job())
|
||||
_reAdd.Enqueue(job);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="ClientFile" /> instance where this script is loaded from.
|
||||
/// </summary>
|
||||
public ClientFile CurrentFile { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="ClientResource" /> that this script belongs to.
|
||||
/// </summary>
|
||||
public ClientResource CurrentResource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Eqivalent of <see cref="ClientResource.Logger" /> in <see cref="CurrentResource" />
|
||||
/// </summary>
|
||||
public ResourceLogger Logger => CurrentResource.Logger;
|
||||
}
|
||||
}
|
24
Client/Scripting/PlayerInfo.cs
Normal file
24
Client/Scripting/PlayerInfo.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
public class PlayerInfo
|
||||
{
|
||||
public byte HolePunchStatus { get; internal set; }
|
||||
public bool IsHost { get; internal set; }
|
||||
public string Username { get; internal set; }
|
||||
public int ID { get; internal set; }
|
||||
public int EntityHandle { get; internal set; }
|
||||
public IPEndPoint InternalEndPoint { get; internal set; }
|
||||
public IPEndPoint ExternalEndPoint { get; internal set; }
|
||||
public float Ping { get; internal set; }
|
||||
public float PacketTravelTime { get; internal set; }
|
||||
public bool DisplayNameTag { get; internal set; }
|
||||
public bool HasDirectConnection { get; internal set; }
|
||||
}
|
||||
}
|
17
Client/Scripting/RageCoop.Client.Scripting.csproj
Normal file
17
Client/Scripting/RageCoop.Client.Scripting.csproj
Normal file
@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<OutDir>..\..\bin\API</OutDir>
|
||||
<DocumentationFile>..\..\bin\API\RageCoop.Client.Scripting.xml</DocumentationFile>
|
||||
<NoWarn>CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\RageCoop.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
28
Client/Scripting/ResourceLogger.cs
Normal file
28
Client/Scripting/ResourceLogger.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
public class ResourceLogger : Core.Logger
|
||||
{
|
||||
public static readonly ResourceLogger Default = new();
|
||||
public ResourceLogger()
|
||||
{
|
||||
FlushImmediately = true;
|
||||
OnFlush += FlushToMainModule;
|
||||
}
|
||||
|
||||
private unsafe void FlushToMainModule(LogLine line, string fomatted)
|
||||
{
|
||||
fixed (char* pMsg = line.Message)
|
||||
{
|
||||
APIBridge.LogEnqueue(line.LogLevel, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
13
Client/Scripting/Shared.cs
Normal file
13
Client/Scripting/Shared.cs
Normal file
@ -0,0 +1,13 @@
|
||||
global using static RageCoop.Core.Shared;
|
||||
global using static RageCoop.Client.Scripting.Shared;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
internal class Shared
|
||||
{
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
#undef DEBUG
|
||||
using System.Windows.Forms;
|
||||
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class Settings
|
||||
public class ClientSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// LogLevel for RageCoop.
|
||||
@ -47,7 +46,7 @@ namespace RageCoop.Client
|
||||
/// <summary>
|
||||
/// The key to open menu
|
||||
/// </summary>
|
||||
public Keys MenuKey { get; set; } = Keys.F9;
|
||||
public Keys MenuKey { get; set; } = Keys.F7;
|
||||
|
||||
/// <summary>
|
||||
/// The key to enter a vehicle as passenger.
|
@ -1,27 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace GTA
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper that provides access to SHVDN's in-game console
|
||||
/// </summary>
|
||||
public class Console
|
||||
{
|
||||
private static SHVDN.Console console => (SHVDN.Console)AppDomain.CurrentDomain.GetData("Console");
|
||||
|
||||
public static void Warning(object format, params object[] objects)
|
||||
{
|
||||
console.PrintInfo("[~o~WARNING~w~] ", format.ToString(), objects);
|
||||
}
|
||||
|
||||
public static void Error(object format, params object[] objects)
|
||||
{
|
||||
console.PrintError("[~r~ERROR~w~] ", format.ToString(), objects);
|
||||
}
|
||||
|
||||
public static void Info(object format, params object[] objects)
|
||||
{
|
||||
console.PrintWarning("[~b~INFO~w~] ", format.ToString(), objects);
|
||||
}
|
||||
}
|
||||
}
|
@ -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(typeof(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());
|
||||
}
|
||||
}
|
||||
}
|
@ -5,8 +5,7 @@ using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
[ScriptAttributes(Author = "RageCoop", NoDefaultInstance = false,
|
||||
SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V")]
|
||||
[ScriptAttributes(Author = "RageCoop", SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V")]
|
||||
internal class DevTool : Script
|
||||
{
|
||||
public static Vehicle ToMark;
|
||||
@ -14,14 +13,14 @@ namespace RageCoop.Client
|
||||
|
||||
public DevTool()
|
||||
{
|
||||
Util.StartUpCheck();
|
||||
Instance = this;
|
||||
Tick += OnTick;
|
||||
Pause();
|
||||
}
|
||||
|
||||
private void OnTick(object sender, EventArgs e)
|
||||
protected override void OnTick()
|
||||
{
|
||||
base.OnTick();
|
||||
|
||||
foreach (var p in World.GetAllPeds()) DrawWeaponBone(p);
|
||||
if (ToMark == null) return;
|
||||
|
||||
|
@ -3,7 +3,6 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using DXHook.Hook.Common;
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
using RageCoop.Client.CefHost;
|
||||
|
Binary file not shown.
@ -4,7 +4,9 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
@ -12,53 +14,42 @@ using GTA.UI;
|
||||
using LemonUI.Elements;
|
||||
using LemonUI.Menus;
|
||||
using Lidgren.Network;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using RageCoop.Client.GUI;
|
||||
using RageCoop.Client.Menus;
|
||||
using RageCoop.Client.Scripting;
|
||||
using RageCoop.Core;
|
||||
using static RageCoop.Client.Shared;
|
||||
using Console = GTA.Console;
|
||||
using Control = GTA.Control;
|
||||
using Screen = System.Windows.Forms.Screen;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
[ScriptAttributes(Author = "RageCoop", NoScriptThread = true,
|
||||
SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V")]
|
||||
[ScriptAttributes(Author = "RageCoop", SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V", NoScriptThread = true)]
|
||||
internal class Main : Script
|
||||
{
|
||||
private static bool _gameLoaded = false;
|
||||
internal static Version Version = typeof(Main).Assembly.GetName().Version;
|
||||
internal static Version ModVersion = typeof(Main).Assembly.GetName().Version;
|
||||
|
||||
internal static int LocalPlayerID = 0;
|
||||
|
||||
internal static RelationshipGroup SyncedPedsGroup;
|
||||
|
||||
internal static new Settings Settings = null;
|
||||
internal static ClientSettings Settings = null;
|
||||
internal static Chat MainChat = null;
|
||||
internal static Stopwatch Counter = new Stopwatch();
|
||||
internal static Logger Logger = null;
|
||||
internal static Stopwatch Counter = new();
|
||||
internal static Logger Log = null;
|
||||
internal static ulong Ticked = 0;
|
||||
internal static Vector3 PlayerPosition;
|
||||
internal static Resources Resources = null;
|
||||
private static readonly ConcurrentQueue<Action> TaskQueue = new ConcurrentQueue<Action>();
|
||||
internal static Resources MainRes = null;
|
||||
|
||||
public static Ped P;
|
||||
public static float FPS;
|
||||
private static bool _lastDead;
|
||||
public static bool CefRunning;
|
||||
public static bool IsUnloading { get; private set; }
|
||||
public static Script Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public Main()
|
||||
{
|
||||
Util.StartUpCheck();
|
||||
|
||||
Instance = this;
|
||||
Directory.CreateDirectory(DataPath);
|
||||
try
|
||||
{
|
||||
@ -67,167 +58,100 @@ namespace RageCoop.Client
|
||||
catch
|
||||
{
|
||||
Notification.Show("Malformed configuration, overwriting with default values...");
|
||||
Settings = new Settings();
|
||||
Settings = new();
|
||||
Util.SaveSettings();
|
||||
}
|
||||
|
||||
Logger = new Logger()
|
||||
Log = new Logger()
|
||||
{
|
||||
Writers = new List<StreamWriter> { CoreUtils.OpenWriter(LogPath) },
|
||||
FlushImmediately = true,
|
||||
Writers = null,
|
||||
#if DEBUG
|
||||
LogLevel = 0,
|
||||
#else
|
||||
LogLevel = Settings.LogLevel,
|
||||
#endif
|
||||
};
|
||||
Logger.OnFlush += (line, formatted) =>
|
||||
Log.OnFlush += (line, formatted) =>
|
||||
{
|
||||
switch (line.LogLevel)
|
||||
{
|
||||
#if DEBUG
|
||||
// case LogLevel.Trace:
|
||||
case LogLevel.Debug:
|
||||
Console.Info(line.Message);
|
||||
break;
|
||||
#endif
|
||||
case LogLevel.Info:
|
||||
Console.Info(line.Message);
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
Console.Warning(line.Message);
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
Console.Error(line.Message);
|
||||
break;
|
||||
}
|
||||
SHVDN.Logger.Write($"[RageCoop] {line.Message}", (uint)line.LogLevel);
|
||||
};
|
||||
Resources = new Resources();
|
||||
|
||||
// Run static constructor to register all function pointers and remoting entries
|
||||
RuntimeHelpers.RunClassConstructor(typeof(API).TypeHandle);
|
||||
}
|
||||
|
||||
protected override void OnAborted(AbortedEventArgs e)
|
||||
{
|
||||
base.OnAborted(e);
|
||||
try
|
||||
{
|
||||
IsUnloading = e.IsUnloading;
|
||||
CleanUp("Abort");
|
||||
WorldThread.DoQueuedActions();
|
||||
if (IsUnloading)
|
||||
{
|
||||
ThreadManager.OnUnload();
|
||||
Log.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
}
|
||||
}
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
|
||||
if (Game.Version < GameVersion.v1_0_1290_1_Steam)
|
||||
{
|
||||
Tick += (object sender, EventArgs e) =>
|
||||
{
|
||||
if (Game.IsLoading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_gameLoaded)
|
||||
{
|
||||
Notification.Show("~r~Please update your GTA5 to v1.0.1290 or newer!", true);
|
||||
_gameLoaded = true;
|
||||
}
|
||||
};
|
||||
return;
|
||||
throw new NotSupportedException("Please update your GTA5 to v1.0.1290 or newer!");
|
||||
}
|
||||
|
||||
Logger.Info(
|
||||
$"Starting {typeof(Main).FullName}, domain: {AppDomain.CurrentDomain.Id} {AppDomain.CurrentDomain.FriendlyName}");
|
||||
MainRes = new();
|
||||
|
||||
|
||||
|
||||
Log.Info(
|
||||
$"Main script initialized");
|
||||
|
||||
BaseScript.OnStart();
|
||||
SyncedPedsGroup = World.AddRelationshipGroup("SYNCPED");
|
||||
Game.Player.Character.RelationshipGroup.SetRelationshipBetweenGroups(SyncedPedsGroup, Relationship.Neutral,
|
||||
true);
|
||||
#if !NON_INTERACTIVE
|
||||
#endif
|
||||
MainChat = new Chat();
|
||||
Aborted += OnAborted;
|
||||
Tick += OnTick;
|
||||
KeyDown += OnKeyDown;
|
||||
KeyUp += OnKeyUp;
|
||||
|
||||
Util.NativeMemory();
|
||||
Counter.Restart();
|
||||
}
|
||||
|
||||
private static void OnAborted(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
WorldThread.Instance?.Abort();
|
||||
DevTool.Instance?.Abort();
|
||||
CleanUp("Abort");
|
||||
WorldThread.DoQueuedActions();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue an action to main thread and wait for execution to complete, must be called from script thread.
|
||||
/// </summary>
|
||||
/// <param name="task"></param>
|
||||
internal static void QueueToMainThreadAndWait(Action task)
|
||||
{
|
||||
Exception e = null;
|
||||
TaskQueue.Enqueue(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
task();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
e = ex;
|
||||
}
|
||||
});
|
||||
Yield();
|
||||
if (e != null)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnTick(object sender, EventArgs e)
|
||||
protected override void OnTick()
|
||||
{
|
||||
base.OnTick();
|
||||
P = Game.Player.Character;
|
||||
PlayerPosition = P.ReadPosition();
|
||||
FPS = Game.FPS;
|
||||
if (Game.IsLoading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_gameLoaded && (_gameLoaded = true))
|
||||
{
|
||||
#if !NON_INTERACTIVE
|
||||
Notification.Show(NotificationIcon.AllPlayersConf, "RAGECOOP", "Welcome!",
|
||||
$"Press ~g~{Settings.MenuKey}~s~ to open the menu.");
|
||||
#endif
|
||||
}
|
||||
|
||||
while (TaskQueue.TryDequeue(out var task))
|
||||
{
|
||||
try
|
||||
{
|
||||
task.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#if CEF
|
||||
if (CefRunning)
|
||||
{
|
||||
CefManager.Tick();
|
||||
}
|
||||
|
||||
#endif
|
||||
if (!Networking.IsOnServer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
EntityPool.DoSync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
Log.Error(ex);
|
||||
}
|
||||
|
||||
|
||||
if (Game.TimeScale != 1.0f)
|
||||
{
|
||||
Game.TimeScale = 1;
|
||||
@ -235,42 +159,42 @@ namespace RageCoop.Client
|
||||
|
||||
if (Networking.ShowNetworkInfo)
|
||||
{
|
||||
new ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 0),
|
||||
new ScaledText(new PointF(200, 0),
|
||||
$"L: {Networking.Latency * 1000:N0}ms", 0.5f)
|
||||
{ Alignment = Alignment.Center }.Draw();
|
||||
new ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 30),
|
||||
{ Alignment = Alignment.Center }.Draw();
|
||||
new ScaledText(new PointF(200, 30),
|
||||
$"R: {NetUtility.ToHumanReadable(Statistics.BytesDownPerSecond)}/s", 0.5f)
|
||||
{ Alignment = Alignment.Center }.Draw();
|
||||
new ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 60),
|
||||
{ Alignment = Alignment.Center }.Draw();
|
||||
new ScaledText(new PointF(200, 60),
|
||||
$"S: {NetUtility.ToHumanReadable(Statistics.BytesUpPerSecond)}/s", 0.5f)
|
||||
{ Alignment = Alignment.Center }.Draw();
|
||||
{ Alignment = Alignment.Center }.Draw();
|
||||
}
|
||||
|
||||
MainChat.Tick();
|
||||
PlayerList.Tick();
|
||||
if (!API.Config.EnableAutoRespawn)
|
||||
{
|
||||
Function.Call(Hash.PAUSE_DEATH_ARREST_RESTART, true);
|
||||
Function.Call(Hash.IGNORE_NEXT_RESTART, true);
|
||||
Function.Call(Hash.FORCE_GAME_STATE_PLAYING);
|
||||
Function.Call(Hash.TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, "respawn_controller");
|
||||
Call(PAUSE_DEATH_ARREST_RESTART, true);
|
||||
Call(IGNORE_NEXT_RESTART, true);
|
||||
Call(FORCE_GAME_STATE_PLAYING);
|
||||
Call(TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, "respawn_controller");
|
||||
if (P.IsDead)
|
||||
{
|
||||
Function.Call(Hash.SET_FADE_OUT_AFTER_DEATH, false);
|
||||
Call(SET_FADE_OUT_AFTER_DEATH, false);
|
||||
|
||||
if (P.Health != 1)
|
||||
{
|
||||
P.Health = 1;
|
||||
Game.Player.WantedLevel = 0;
|
||||
Logger.Debug("Player died.");
|
||||
Log.Debug("Player died.");
|
||||
API.Events.InvokePlayerDied();
|
||||
}
|
||||
|
||||
GTA.UI.Screen.StopEffects();
|
||||
Screen.StopEffects();
|
||||
}
|
||||
else
|
||||
{
|
||||
Function.Call(Hash.DISPLAY_HUD, true);
|
||||
Call(DISPLAY_HUD, true);
|
||||
}
|
||||
}
|
||||
else if (P.IsDead && !_lastDead)
|
||||
@ -282,27 +206,31 @@ namespace RageCoop.Client
|
||||
Ticked++;
|
||||
}
|
||||
|
||||
private void OnKeyUp(object sender, KeyEventArgs e)
|
||||
protected override void OnKeyUp(GTA.KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyUp(e);
|
||||
#if CEF
|
||||
if (CefRunning)
|
||||
{
|
||||
CefManager.KeyUp(e.KeyCode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void OnKeyDown(object sender, KeyEventArgs e)
|
||||
protected unsafe override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
if (MainChat.Focused)
|
||||
{
|
||||
MainChat.OnKeyDown(e.KeyCode);
|
||||
return;
|
||||
}
|
||||
|
||||
#if CEF
|
||||
if (CefRunning)
|
||||
{
|
||||
CefManager.KeyDown(e.KeyCode);
|
||||
}
|
||||
|
||||
#endif
|
||||
if (Networking.IsOnServer)
|
||||
{
|
||||
if (Voice.WasInitialized())
|
||||
@ -322,15 +250,15 @@ namespace RageCoop.Client
|
||||
|
||||
if (Game.IsControlPressed(Control.FrontendPause))
|
||||
{
|
||||
Function.Call(Hash.ACTIVATE_FRONTEND_MENU,
|
||||
Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0);
|
||||
Call(ACTIVATE_FRONTEND_MENU,
|
||||
SHVDN.NativeMemory.GetHashKey("FE_MENU_VERSION_SP_PAUSE"), false, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Game.IsControlPressed(Control.FrontendPauseAlternate) && Settings.DisableAlternatePause)
|
||||
{
|
||||
Function.Call(Hash.ACTIVATE_FRONTEND_MENU,
|
||||
Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0);
|
||||
Call(ACTIVATE_FRONTEND_MENU,
|
||||
SHVDN.NativeMemory.GetHashKey("FE_MENU_VERSION_SP_PAUSE"), false, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -423,25 +351,27 @@ namespace RageCoop.Client
|
||||
API.QueueAction(() =>
|
||||
{
|
||||
WorldThread.Traffic(!Settings.DisableTraffic);
|
||||
Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, true);
|
||||
Call(SET_ENABLE_VEHICLE_SLIPSTREAMING, true);
|
||||
CoopMenu.ConnectedMenuSetting();
|
||||
MainChat.Init();
|
||||
Notification.Show("~g~Connected!");
|
||||
});
|
||||
|
||||
Logger.Info(">> Connected <<");
|
||||
Log.Info(">> Connected <<");
|
||||
}
|
||||
|
||||
private static readonly object _cleanupLock = new();
|
||||
public static void CleanUp(string reason)
|
||||
{
|
||||
if (reason != "Abort")
|
||||
lock (_cleanupLock)
|
||||
{
|
||||
Logger.Info($">> Disconnected << reason: {reason}");
|
||||
API.QueueAction(() => { Notification.Show("~r~Disconnected: " + reason); });
|
||||
}
|
||||
|
||||
API.QueueAction(() =>
|
||||
{
|
||||
if (reason != "Abort")
|
||||
{
|
||||
Log.Info($">> Disconnected << reason: {reason}");
|
||||
Notification.Show("~r~Disconnected: " + reason);
|
||||
}
|
||||
|
||||
if (MainChat.Focused)
|
||||
{
|
||||
MainChat.Focused = false;
|
||||
@ -451,23 +381,24 @@ namespace RageCoop.Client
|
||||
MainChat.Clear();
|
||||
EntityPool.Cleanup();
|
||||
WorldThread.Traffic(true);
|
||||
Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, false);
|
||||
Call(SET_ENABLE_VEHICLE_SLIPSTREAMING, false);
|
||||
CoopMenu.DisconnectedMenuSetting();
|
||||
LocalPlayerID = default;
|
||||
Resources.Unload();
|
||||
});
|
||||
Memory.RestorePatches();
|
||||
MainRes.Unload();
|
||||
Memory.RestorePatches();
|
||||
#if CEF
|
||||
if (CefRunning)
|
||||
{
|
||||
CefManager.CleanUp();
|
||||
}
|
||||
#endif
|
||||
|
||||
HookManager.CleanUp();
|
||||
DownloadManager.Cleanup();
|
||||
Voice.ClearAll();
|
||||
DownloadManager.Cleanup();
|
||||
Voice.ClearAll();
|
||||
Networking.Peer?.Dispose();
|
||||
Networking.Peer = null;
|
||||
}
|
||||
}
|
||||
|
||||
#if !NON_INTERACTIVE
|
||||
#endif
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ namespace RageCoop.Client.Menus
|
||||
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "MAIN")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Main.Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
public static PopUp PopUp = new PopUp
|
||||
@ -46,7 +46,7 @@ namespace RageCoop.Client.Menus
|
||||
ServerIpItem.Activated += ServerIpActivated;
|
||||
_serverConnectItem.Activated += (sender, item) =>
|
||||
{
|
||||
Networking.ToggleConnection(Main.Settings.LastServerAddress);
|
||||
Networking.ToggleConnection(Settings.LastServerAddress);
|
||||
};
|
||||
|
||||
|
||||
@ -95,9 +95,9 @@ namespace RageCoop.Client.Menus
|
||||
scaleform.CallFunction("CREATE_CONTAINER");
|
||||
|
||||
scaleform.CallFunction("SET_DATA_SLOT", 0,
|
||||
Function.Call<string>((Hash)0x0499D7B09FC9B407, 2, (int)Control.FrontendAccept, 0), "Continue");
|
||||
Call<string>((Hash)0x0499D7B09FC9B407, 2, (int)Control.FrontendAccept, 0), "Continue");
|
||||
scaleform.CallFunction("SET_DATA_SLOT", 1,
|
||||
Function.Call<string>((Hash)0x0499D7B09FC9B407, 2, (int)Control.FrontendCancel, 0), "Cancel");
|
||||
Call<string>((Hash)0x0499D7B09FC9B407, 2, (int)Control.FrontendCancel, 0), "Cancel");
|
||||
scaleform.CallFunction("DRAW_INSTRUCTIONAL_BUTTONS", -1);
|
||||
scaleform.Render2D();
|
||||
if (Game.IsControlJustPressed(Control.FrontendAccept))
|
||||
@ -122,7 +122,7 @@ namespace RageCoop.Client.Menus
|
||||
var newUsername = Game.GetUserInput(WindowTitle.EnterMessage20, _usernameItem.AltTitle, 20);
|
||||
if (!string.IsNullOrWhiteSpace(newUsername))
|
||||
{
|
||||
Main.Settings.Username = newUsername;
|
||||
Settings.Username = newUsername;
|
||||
Util.SaveSettings();
|
||||
|
||||
_usernameItem.AltTitle = newUsername;
|
||||
@ -132,7 +132,7 @@ namespace RageCoop.Client.Menus
|
||||
private static void _passwordActivated(object sender, EventArgs e)
|
||||
{
|
||||
var newPass = Game.GetUserInput(WindowTitle.EnterMessage20, "", 20);
|
||||
Main.Settings.Password = newPass;
|
||||
Settings.Password = newPass;
|
||||
Util.SaveSettings();
|
||||
_passwordItem.AltTitle = new string('*', newPass.Length);
|
||||
}
|
||||
@ -142,7 +142,7 @@ namespace RageCoop.Client.Menus
|
||||
var newServerIp = Game.GetUserInput(WindowTitle.EnterMessage60, ServerIpItem.AltTitle, 60);
|
||||
if (!string.IsNullOrWhiteSpace(newServerIp) && newServerIp.Contains(":"))
|
||||
{
|
||||
Main.Settings.LastServerAddress = newServerIp;
|
||||
Settings.LastServerAddress = newServerIp;
|
||||
Util.SaveSettings();
|
||||
|
||||
ServerIpItem.AltTitle = newServerIp;
|
||||
@ -174,20 +174,20 @@ namespace RageCoop.Client.Menus
|
||||
#region ITEMS
|
||||
|
||||
private static readonly NativeItem _usernameItem = new NativeItem("Username")
|
||||
{ AltTitle = Main.Settings.Username };
|
||||
{ AltTitle = Settings.Username };
|
||||
|
||||
private static readonly NativeItem _passwordItem = new NativeItem("Password")
|
||||
{ AltTitle = new string('*', Main.Settings.Password.Length) };
|
||||
{ AltTitle = new string('*', Settings.Password.Length) };
|
||||
|
||||
public static readonly NativeItem ServerIpItem = new NativeItem("Server IP")
|
||||
{ AltTitle = Main.Settings.LastServerAddress };
|
||||
{ AltTitle = Settings.LastServerAddress };
|
||||
|
||||
internal static readonly NativeItem _serverConnectItem = new NativeItem("Connect");
|
||||
|
||||
private static readonly NativeItem _aboutItem = new NativeItem("About", "~y~SOURCE~s~~n~" +
|
||||
"https://github.com/RAGECOOP~n~" +
|
||||
"~y~VERSION~s~~n~" +
|
||||
Main.Version)
|
||||
Main.ModVersion)
|
||||
{ LeftBadge = new ScaledTexture("commonmenu", "shop_new_star") };
|
||||
|
||||
#endregion
|
||||
|
@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using GTA;
|
||||
using GTA.UI;
|
||||
using LemonUI.Menus;
|
||||
using RageCoop.Client.GUI;
|
||||
using RageCoop.Client.Loader;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
@ -13,17 +10,15 @@ namespace RageCoop.Client
|
||||
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Debug", "Debug settings")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Main.Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
public static NativeMenu DiagnosticMenu = new NativeMenu("RAGECOOP", "Diagnostic", "Performence and Diagnostic")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Main.Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
public static NativeItem ReloadItem = new NativeItem("Reload", "Reload RAGECOOP and associated scripts");
|
||||
|
||||
public static NativeItem SimulatedLatencyItem =
|
||||
new NativeItem("Simulated network latency", "Simulated network latency in ms (one way)", "0");
|
||||
|
||||
@ -33,14 +28,6 @@ namespace RageCoop.Client
|
||||
private static readonly NativeCheckboxItem ShowNetworkInfoItem =
|
||||
new NativeCheckboxItem("Show Network Info", Networking.ShowNetworkInfo);
|
||||
|
||||
private static readonly NativeCheckboxItem DxHookTest =
|
||||
new NativeCheckboxItem("Enable D3D11 hook", false);
|
||||
|
||||
private static readonly NativeCheckboxItem CefTest =
|
||||
new NativeCheckboxItem("Test CEF overlay", false);
|
||||
|
||||
private static CefClient _testCef;
|
||||
|
||||
static DebugMenu()
|
||||
{
|
||||
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
|
||||
@ -51,10 +38,20 @@ 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) =>
|
||||
{
|
||||
Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked;
|
||||
};
|
||||
ShowOwnerItem.CheckboxChanged += (s, e) =>
|
||||
{
|
||||
Settings.ShowEntityOwnerName = ShowOwnerItem.Checked;
|
||||
Util.SaveSettings();
|
||||
};
|
||||
#if DEBUG
|
||||
SimulatedLatencyItem.Activated += (s, e) =>
|
||||
{
|
||||
try
|
||||
@ -65,61 +62,14 @@ namespace RageCoop.Client
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error(ex);
|
||||
Log.Error(ex);
|
||||
}
|
||||
};
|
||||
ShowNetworkInfoItem.CheckboxChanged += (s, e) =>
|
||||
{
|
||||
Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked;
|
||||
};
|
||||
ShowOwnerItem.CheckboxChanged += (s, e) =>
|
||||
{
|
||||
Main.Settings.ShowEntityOwnerName = ShowOwnerItem.Checked;
|
||||
Util.SaveSettings();
|
||||
};
|
||||
DxHookTest.CheckboxChanged += Hook;
|
||||
CefTest.CheckboxChanged += CefTestChange;
|
||||
;
|
||||
ReloadItem.Activated += ReloadDomain;
|
||||
Menu.Add(SimulatedLatencyItem);
|
||||
#endif
|
||||
Menu.Add(ShowNetworkInfoItem);
|
||||
Menu.Add(ShowOwnerItem);
|
||||
Menu.Add(ReloadItem);
|
||||
Menu.AddSubMenu(DiagnosticMenu);
|
||||
Menu.Add(DxHookTest);
|
||||
Menu.Add(CefTest);
|
||||
}
|
||||
|
||||
private static void CefTestChange(object sender, EventArgs e)
|
||||
{
|
||||
if (CefTest.Checked)
|
||||
{
|
||||
_testCef = CefManager.CreateClient(new Size(640, 480));
|
||||
_testCef.Scale = 0.8f;
|
||||
_testCef.Opacity = 128;
|
||||
Script.Wait(2000);
|
||||
_testCef.Controller.LoadUrl("https://ragecoop.online/");
|
||||
CefManager.ActiveClient = _testCef;
|
||||
}
|
||||
else
|
||||
{
|
||||
CefManager.DestroyClient(_testCef);
|
||||
}
|
||||
|
||||
DxHookTest.Checked = HookManager.Hooked;
|
||||
}
|
||||
|
||||
private static void Hook(object sender, EventArgs e)
|
||||
{
|
||||
if (DxHookTest.Checked)
|
||||
HookManager.Initialize();
|
||||
else
|
||||
HookManager.CleanUp();
|
||||
}
|
||||
|
||||
private static void ReloadDomain(object sender, EventArgs e)
|
||||
{
|
||||
LoaderContext.RequestUnload();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,8 +7,8 @@ using GTA.UI;
|
||||
using LemonUI.Menus;
|
||||
using Newtonsoft.Json;
|
||||
using RageCoop.Core;
|
||||
using static RageCoop.Client.Shared;
|
||||
using Console = GTA.Console;
|
||||
|
||||
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
@ -17,7 +17,7 @@ namespace RageCoop.Client
|
||||
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "DevTool", "Internal testing tools")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Main.Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
private static readonly NativeCheckboxItem enableItem = new NativeCheckboxItem("Show weapon bones");
|
||||
@ -37,12 +37,12 @@ namespace RageCoop.Client
|
||||
{
|
||||
if (File.Exists(AnimationsDataPath))
|
||||
{
|
||||
var anims = JsonConvert.DeserializeObject<AnimDic[]>(File.ReadAllText(AnimationsDataPath));
|
||||
var anims = JsonDeserialize<AnimDic[]>(File.ReadAllText(AnimationsDataPath));
|
||||
foreach (var anim in anims)
|
||||
foreach (var a in anim.Animations)
|
||||
if (Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, Main.P, anim.DictionaryName, a, 3))
|
||||
if (Call<bool>(IS_ENTITY_PLAYING_ANIM, P, anim.DictionaryName, a, 3))
|
||||
{
|
||||
Console.Info(anim.DictionaryName + " : " + a);
|
||||
Console.PrintInfo(anim.DictionaryName + " : " + a);
|
||||
Notification.Show(anim.DictionaryName + " : " + a);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using GTA.UI;
|
||||
using LemonUI.Menus;
|
||||
@ -22,7 +23,7 @@ namespace RageCoop.Client.Menus
|
||||
internal static NativeMenu Menu = new NativeMenu("RAGECOOP", "Servers", "Go to the server list")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Main.Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
internal static NativeItem ResultItem = null;
|
||||
@ -41,8 +42,7 @@ namespace RageCoop.Client.Menus
|
||||
Menu.Add(ResultItem = new NativeItem("Loading..."));
|
||||
|
||||
// Prevent freezing
|
||||
GetServersThread = new Thread(() => GetAllServers());
|
||||
GetServersThread.Start();
|
||||
GetServersThread = ThreadManager.CreateThread(() => GetAllServers(),"GetServers");
|
||||
};
|
||||
Menu.Closing += (object sender, CancelEventArgs e) => { CleanUpList(); };
|
||||
}
|
||||
@ -56,8 +56,10 @@ namespace RageCoop.Client.Menus
|
||||
private static void GetAllServers()
|
||||
{
|
||||
List<ServerInfo> serverList = null;
|
||||
var realUrl = Main.Settings.MasterServer;
|
||||
serverList = JsonConvert.DeserializeObject<List<ServerInfo>>(DownloadString(realUrl));
|
||||
var realUrl = Settings.MasterServer;
|
||||
serverList = null;
|
||||
try { serverList = JsonDeserialize<List<ServerInfo>>(DownloadString(realUrl)); }
|
||||
catch (Exception ex) { Log.Error(ex); }
|
||||
|
||||
// Need to be processed in main thread
|
||||
API.QueueAction(() =>
|
||||
@ -81,7 +83,7 @@ namespace RageCoop.Client.Menus
|
||||
NativeItem tmpItem =
|
||||
new NativeItem($"[{server.country}] {server.name}",
|
||||
$"~b~{address}~s~~n~~g~Version {server.version}.x~s~")
|
||||
{ AltTitle = $"[{server.players}/{server.maxPlayers}]" };
|
||||
{ AltTitle = $"[{server.players}/{server.maxPlayers}]" };
|
||||
tmpItem.Activated += (object sender, EventArgs e) =>
|
||||
{
|
||||
try
|
||||
@ -103,7 +105,7 @@ namespace RageCoop.Client.Menus
|
||||
|
||||
CoopMenu.Menu.Visible = true;
|
||||
#endif
|
||||
Main.Settings.LastServerAddress = address;
|
||||
Settings.LastServerAddress = address;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -130,8 +132,8 @@ namespace RageCoop.Client.Menus
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
|
||||
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
|
||||
|
||||
WebClient client = new WebClient();
|
||||
return client.DownloadString(url);
|
||||
var client = new HttpClient();
|
||||
return client.GetStringAsync(url).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using GTA;
|
||||
using GTA.UI;
|
||||
using LemonUI.Menus;
|
||||
using RageCoop.Client.Scripting;
|
||||
|
||||
namespace RageCoop.Client.Menus
|
||||
{
|
||||
@ -12,40 +13,40 @@ namespace RageCoop.Client.Menus
|
||||
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Settings", "Go to the settings")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Main.Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
private static readonly NativeCheckboxItem _disableTrafficItem =
|
||||
new NativeCheckboxItem("Disable Traffic (NPCs/Vehicles)", "Local traffic only",
|
||||
Main.Settings.DisableTraffic);
|
||||
Settings.DisableTraffic);
|
||||
|
||||
private static readonly NativeCheckboxItem _flipMenuItem =
|
||||
new NativeCheckboxItem("Flip menu", Main.Settings.FlipMenu);
|
||||
new NativeCheckboxItem("Flip menu", Settings.FlipMenu);
|
||||
|
||||
private static readonly NativeCheckboxItem _disablePauseAlt = new NativeCheckboxItem("Disable Alternate Pause",
|
||||
"Don't freeze game time when Esc pressed", Main.Settings.DisableAlternatePause);
|
||||
"Don't freeze game time when Esc pressed", Settings.DisableAlternatePause);
|
||||
|
||||
private static readonly NativeCheckboxItem _disableVoice = new NativeCheckboxItem("Enable voice",
|
||||
"Check your GTA:V settings to find the right key on your keyboard for PushToTalk and talk to your friends",
|
||||
Main.Settings.Voice);
|
||||
Settings.Voice);
|
||||
|
||||
private static readonly NativeCheckboxItem _showBlip = new NativeCheckboxItem("Show player blip",
|
||||
"Show other player's nametag on your screen, only effective if server didn't disable nametag display",
|
||||
Main.Settings.ShowPlayerBlip);
|
||||
Settings.ShowPlayerBlip);
|
||||
|
||||
private static readonly NativeCheckboxItem _showNametag = new NativeCheckboxItem("Show player nametag",
|
||||
"Show other player's blip on map, can be overridden by server resource ",
|
||||
Main.Settings.ShowPlayerNameTag);
|
||||
Settings.ShowPlayerNameTag);
|
||||
|
||||
private static readonly NativeItem _menuKey =
|
||||
new NativeItem("Menu Key", "The key to open menu", Main.Settings.MenuKey.ToString());
|
||||
new NativeItem("Menu Key", "The key to open menu", Settings.MenuKey.ToString());
|
||||
|
||||
private static readonly NativeItem _passengerKey = new NativeItem("Passenger Key",
|
||||
"The key to enter a vehicle as passenger", Main.Settings.PassengerKey.ToString());
|
||||
"The key to enter a vehicle as passenger", Settings.PassengerKey.ToString());
|
||||
|
||||
private static readonly NativeItem _vehicleSoftLimit = new NativeItem("Vehicle limit (soft)",
|
||||
"The game won't spawn more NPC traffic if the limit is exceeded. \n-1 for unlimited (not recommended).",
|
||||
Main.Settings.WorldVehicleSoftLimit.ToString());
|
||||
Settings.WorldVehicleSoftLimit.ToString());
|
||||
|
||||
static SettingsMenu()
|
||||
{
|
||||
@ -61,13 +62,12 @@ namespace RageCoop.Client.Menus
|
||||
_vehicleSoftLimit.Activated += VehicleSoftLimitActivated;
|
||||
_showBlip.Activated += (s, e) =>
|
||||
{
|
||||
Main.Settings.ShowPlayerBlip = _showBlip.Checked;
|
||||
Settings.ShowPlayerBlip = _showBlip.Checked;
|
||||
Util.SaveSettings();
|
||||
};
|
||||
_showNametag.Activated += (s, e) =>
|
||||
{
|
||||
Main.Settings.ShowPlayerNameTag = _showNametag.Checked;
|
||||
Util.SaveSettings();
|
||||
API.Config.ShowPlayerNameTag = _showNametag.Checked;
|
||||
};
|
||||
|
||||
Menu.Add(_disableTrafficItem);
|
||||
@ -92,13 +92,13 @@ namespace RageCoop.Client.Menus
|
||||
Voice.ClearAll();
|
||||
}
|
||||
|
||||
Main.Settings.Voice = _disableVoice.Checked;
|
||||
Settings.Voice = _disableVoice.Checked;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
|
||||
private static void DisablePauseAltCheckboxChanged(object sender, EventArgs e)
|
||||
{
|
||||
Main.Settings.DisableAlternatePause = _disablePauseAlt.Checked;
|
||||
Settings.DisableAlternatePause = _disablePauseAlt.Checked;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
|
||||
@ -106,10 +106,10 @@ namespace RageCoop.Client.Menus
|
||||
{
|
||||
try
|
||||
{
|
||||
Main.Settings.WorldVehicleSoftLimit = int.Parse(
|
||||
Settings.WorldVehicleSoftLimit = int.Parse(
|
||||
Game.GetUserInput(WindowTitle.EnterMessage20,
|
||||
Main.Settings.WorldVehicleSoftLimit.ToString(), 20));
|
||||
_menuKey.AltTitle = Main.Settings.WorldVehicleSoftLimit.ToString();
|
||||
Settings.WorldVehicleSoftLimit.ToString(), 20));
|
||||
_menuKey.AltTitle = Settings.WorldVehicleSoftLimit.ToString();
|
||||
Util.SaveSettings();
|
||||
}
|
||||
catch
|
||||
@ -121,11 +121,11 @@ namespace RageCoop.Client.Menus
|
||||
{
|
||||
try
|
||||
{
|
||||
Main.Settings.MenuKey = (Keys)Enum.Parse(
|
||||
Settings.MenuKey = (Keys)Enum.Parse(
|
||||
typeof(Keys),
|
||||
Game.GetUserInput(WindowTitle.EnterMessage20,
|
||||
Main.Settings.MenuKey.ToString(), 20));
|
||||
_menuKey.AltTitle = Main.Settings.MenuKey.ToString();
|
||||
Settings.MenuKey.ToString(), 20));
|
||||
_menuKey.AltTitle = Settings.MenuKey.ToString();
|
||||
Util.SaveSettings();
|
||||
}
|
||||
catch
|
||||
@ -137,11 +137,11 @@ namespace RageCoop.Client.Menus
|
||||
{
|
||||
try
|
||||
{
|
||||
Main.Settings.PassengerKey = (Keys)Enum.Parse(
|
||||
Settings.PassengerKey = (Keys)Enum.Parse(
|
||||
typeof(Keys),
|
||||
Game.GetUserInput(WindowTitle.EnterMessage20,
|
||||
Main.Settings.PassengerKey.ToString(), 20));
|
||||
_passengerKey.AltTitle = Main.Settings.PassengerKey.ToString();
|
||||
Settings.PassengerKey.ToString(), 20));
|
||||
_passengerKey.AltTitle = Settings.PassengerKey.ToString();
|
||||
Util.SaveSettings();
|
||||
}
|
||||
catch
|
||||
@ -152,7 +152,7 @@ namespace RageCoop.Client.Menus
|
||||
public static void DisableTrafficCheckboxChanged(object a, EventArgs b)
|
||||
{
|
||||
WorldThread.Traffic(!_disableTrafficItem.Checked);
|
||||
Main.Settings.DisableTraffic = _disableTrafficItem.Checked;
|
||||
Settings.DisableTraffic = _disableTrafficItem.Checked;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
|
||||
@ -161,7 +161,7 @@ namespace RageCoop.Client.Menus
|
||||
CoopMenu.Menu.Alignment = _flipMenuItem.Checked ? Alignment.Right : Alignment.Left;
|
||||
|
||||
Menu.Alignment = _flipMenuItem.Checked ? Alignment.Right : Alignment.Left;
|
||||
Main.Settings.FlipMenu = _flipMenuItem.Checked;
|
||||
Settings.FlipMenu = _flipMenuItem.Checked;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
|
||||
@ -74,7 +73,7 @@ namespace RageCoop.Client
|
||||
|
||||
if (!CurrentFocused) return;
|
||||
|
||||
Function.Call(Hash.DISABLE_ALL_CONTROL_ACTIONS, 0);
|
||||
Call(DISABLE_ALL_CONTROL_ACTIONS, 0);
|
||||
}
|
||||
|
||||
public void AddMessage(string sender, string msg)
|
||||
@ -132,6 +131,9 @@ namespace RageCoop.Client
|
||||
StringBuilder receivingBuffer,
|
||||
int bufferSize, uint flags, IntPtr kblayout);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern IntPtr GetKeyboardLayout(uint idThread);
|
||||
|
||||
public static string GetCharFromKey(Keys key, bool shift, bool altGr)
|
||||
{
|
||||
var buf = new StringBuilder(256);
|
||||
@ -145,7 +147,7 @@ namespace RageCoop.Client
|
||||
keyboardState[(int)Keys.Menu] = 0xff;
|
||||
}
|
||||
|
||||
ToUnicodeEx((uint)key, 0, keyboardState, buf, 256, 0, InputLanguage.CurrentInputLanguage.Handle);
|
||||
ToUnicodeEx((uint)key, 0, keyboardState, buf, 256, 0, GetKeyboardLayout(0));
|
||||
return buf.ToString();
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ namespace RageCoop.Client
|
||||
var packet = new Packets.FileTransferComplete();
|
||||
packet.Deserialize(data);
|
||||
|
||||
Main.Logger.Debug($"Finalizing download:{packet.ID}");
|
||||
Log.Debug($"Finalizing download:{packet.ID}");
|
||||
Complete(packet.ID);
|
||||
|
||||
// Inform the server that the download is completed
|
||||
@ -48,13 +48,13 @@ namespace RageCoop.Client
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(ResourceFolder);
|
||||
Main.Resources.Load(ResourceFolder, _resources.ToArray());
|
||||
MainRes.Load(ResourceFolder, _resources.ToArray());
|
||||
return new Packets.FileTransferResponse { ID = 0, Response = FileResponse.Loaded };
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error("Error occurred when loading server resource");
|
||||
Main.Logger.Error(ex);
|
||||
Log.Error("Error occurred when loading server resource");
|
||||
Log.Error(ex);
|
||||
return new Packets.FileTransferResponse { ID = 0, Response = FileResponse.LoadFailed };
|
||||
}
|
||||
});
|
||||
@ -68,13 +68,13 @@ namespace RageCoop.Client
|
||||
public static bool AddFile(int id, string name, long length)
|
||||
{
|
||||
var path = $"{ResourceFolder}\\{name}";
|
||||
Main.Logger.Debug($"Downloading file to {path} , id:{id}");
|
||||
Log.Debug($"Downloading file to {path} , id:{id}");
|
||||
if (!Directory.Exists(Directory.GetParent(path).FullName))
|
||||
Directory.CreateDirectory(Directory.GetParent(path).FullName);
|
||||
|
||||
if (FileAlreadyExists(ResourceFolder, name, length))
|
||||
{
|
||||
Main.Logger.Debug($"File already exists! canceling download:{name}");
|
||||
Log.Debug($"File already exists! canceling download:{name}");
|
||||
DownloadCompleted?.Invoke(null, Path.Combine(ResourceFolder, name));
|
||||
return false;
|
||||
}
|
||||
@ -82,7 +82,7 @@ namespace RageCoop.Client
|
||||
/*
|
||||
if (!name.EndsWith(".zip"))
|
||||
{
|
||||
Main.Logger.Error($"File download blocked! [{name}]");
|
||||
Log.Error($"File download blocked! [{name}]");
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
@ -114,7 +114,6 @@ namespace RageCoop.Client
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
if (new FileInfo(filePath).Length == length) return true;
|
||||
|
||||
// Delete the file because the length is wrong (maybe the file was updated)
|
||||
File.Delete(filePath);
|
||||
}
|
||||
@ -129,7 +128,7 @@ namespace RageCoop.Client
|
||||
if (InProgressDownloads.TryGetValue(id, out var file))
|
||||
file.Stream.Write(chunk, 0, chunk.Length);
|
||||
else
|
||||
Main.Logger.Trace($"Received unhandled file chunk:{id}");
|
||||
Log.Trace($"Received unhandled file chunk:{id}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,12 +138,12 @@ namespace RageCoop.Client
|
||||
{
|
||||
InProgressDownloads.Remove(id);
|
||||
f.Dispose();
|
||||
Main.Logger.Info($"Download finished:{f.FileName}");
|
||||
Log.Info($"Download finished:{f.FileName}");
|
||||
DownloadCompleted?.Invoke(null, Path.Combine(ResourceFolder, f.FileName));
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Logger.Error($"Download not found! {id}");
|
||||
Log.Error($"Download not found! {id}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,12 +27,12 @@ namespace RageCoop.Client
|
||||
if (p.InternalEndPoint != null && p.ExternalEndPoint != null && (p.Connection == null ||
|
||||
p.Connection.Status == NetConnectionStatus.Disconnected))
|
||||
{
|
||||
Main.Logger.Trace(
|
||||
Log.Trace(
|
||||
$"Sending HolePunch message to {p.InternalEndPoint},{p.ExternalEndPoint}. {p.Username}:{p.ID}");
|
||||
var msg = Networking.Peer.CreateMessage();
|
||||
new Packets.HolePunch
|
||||
{
|
||||
Puncher = Main.LocalPlayerID,
|
||||
Puncher = LocalPlayerID,
|
||||
Status = p.HolePunchStatus
|
||||
}.Pack(msg);
|
||||
Networking.Peer.SendUnconnectedMessage(msg,
|
||||
@ -41,7 +41,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error(ex);
|
||||
Log.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,33 +49,33 @@ namespace RageCoop.Client
|
||||
{
|
||||
if (PlayerList.Players.TryGetValue(p.TargetID, out var player))
|
||||
{
|
||||
Main.Logger.Debug($"{p.TargetID},{player.Username} added to HolePunch target");
|
||||
Log.Debug($"{p.TargetID},{player.Username} added to HolePunch target");
|
||||
player.InternalEndPoint = CoreUtils.StringToEndPoint(p.TargetInternal);
|
||||
player.ExternalEndPoint = CoreUtils.StringToEndPoint(p.TargetExternal);
|
||||
player.ConnectWhenPunched = p.Connect;
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Logger.Warning("No player with specified TargetID found for hole punching:" + p.TargetID);
|
||||
Log.Warning("No player with specified TargetID found for hole punching:" + p.TargetID);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Punched(Packets.HolePunch p, IPEndPoint from)
|
||||
{
|
||||
Main.Logger.Debug($"HolePunch message received from:{from}, status:{p.Status}");
|
||||
Log.Debug($"HolePunch message received from:{from}, status:{p.Status}");
|
||||
if (PlayerList.Players.TryGetValue(p.Puncher, out var puncher))
|
||||
{
|
||||
Main.Logger.Debug("Puncher identified as: " + puncher.Username);
|
||||
Log.Debug("Puncher identified as: " + puncher.Username);
|
||||
puncher.HolePunchStatus = (byte)(p.Status + 1);
|
||||
if (p.Status >= 3)
|
||||
{
|
||||
Main.Logger.Debug("HolePunch sucess: " + from + ", " + puncher.ID);
|
||||
Log.Debug("HolePunch sucess: " + from + ", " + puncher.ID);
|
||||
if (puncher.ConnectWhenPunched && (puncher.Connection == null ||
|
||||
puncher.Connection.Status == NetConnectionStatus.Disconnected))
|
||||
{
|
||||
Main.Logger.Debug("Connecting to peer: " + from);
|
||||
Log.Debug("Connecting to peer: " + from);
|
||||
var msg = Networking.Peer.CreateMessage();
|
||||
new Packets.P2PConnect { ID = Main.LocalPlayerID }.Pack(msg);
|
||||
new Packets.P2PConnect { ID = LocalPlayerID }.Pack(msg);
|
||||
puncher.Connection = Networking.Peer.Connect(from, msg);
|
||||
Networking.Peer.FlushSendQueue();
|
||||
}
|
||||
|
@ -14,25 +14,18 @@ namespace RageCoop.Client
|
||||
{
|
||||
internal static partial class Networking
|
||||
{
|
||||
public static CoopPeer Peer;
|
||||
public static bool ShowNetworkInfo = false;
|
||||
public static Security Security;
|
||||
public static NetConnection ServerConnection;
|
||||
|
||||
private static readonly Dictionary<int, Action<PacketType, NetIncomingMessage>> PendingResponses =
|
||||
new Dictionary<int, Action<PacketType, NetIncomingMessage>>();
|
||||
|
||||
internal static readonly Dictionary<PacketType, Func<NetIncomingMessage, Packet>> RequestHandlers =
|
||||
new Dictionary<PacketType, Func<NetIncomingMessage, Packet>>();
|
||||
|
||||
internal static float SimulatedLatency = 0;
|
||||
public static IPEndPoint _targetServerEP;
|
||||
|
||||
static Networking()
|
||||
{
|
||||
Security = new Security(Main.Logger);
|
||||
Packets.CustomEvent.ResolveHandle = _resolveHandle;
|
||||
}
|
||||
public static CoopPeer Peer;
|
||||
public static bool ShowNetworkInfo = false;
|
||||
public static Security Security = new();
|
||||
public static NetConnection ServerConnection;
|
||||
|
||||
private static readonly Dictionary<int, Action<PacketType, NetIncomingMessage>> PendingResponses = new();
|
||||
|
||||
internal static readonly Dictionary<PacketType, Func<NetIncomingMessage, Packet>> RequestHandlers = new();
|
||||
|
||||
internal static float SimulatedLatency = 0;
|
||||
|
||||
public static float Latency => ServerConnection.AverageRoundtripTime / 2;
|
||||
public static bool IsConnecting { get; private set; }
|
||||
@ -42,35 +35,38 @@ namespace RageCoop.Client
|
||||
PublicKey publicKey = null)
|
||||
{
|
||||
CoopMenu.Menu.Visible = false;
|
||||
Peer?.Shutdown("Bye");
|
||||
if (IsOnServer)
|
||||
{
|
||||
// ?
|
||||
}
|
||||
else if (IsConnecting)
|
||||
|
||||
if (IsConnecting)
|
||||
{
|
||||
_publicKeyReceived.Set();
|
||||
IsConnecting = false;
|
||||
Notification.Show("Connection has been canceled");
|
||||
}
|
||||
else
|
||||
{
|
||||
Peer?.Dispose();
|
||||
API.QueueAction(() =>
|
||||
Notification.Show("Connection has been canceled"));
|
||||
|
||||
Peer.Shutdown("bye");
|
||||
}
|
||||
else if (IsOnServer)
|
||||
{
|
||||
Peer.Shutdown("bye");
|
||||
}
|
||||
else
|
||||
{
|
||||
IsConnecting = true;
|
||||
password = password ?? Main.Settings.Password;
|
||||
username = username ?? Main.Settings.Username;
|
||||
password ??= Settings.Password;
|
||||
username ??= Settings.Username;
|
||||
|
||||
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
|
||||
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);
|
||||
@ -95,7 +91,7 @@ namespace RageCoop.Client
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(() =>
|
||||
ThreadManager.CreateThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -103,7 +99,7 @@ namespace RageCoop.Client
|
||||
|
||||
// Ensure static constructor invocation
|
||||
DownloadManager.Cleanup();
|
||||
Peer = new CoopPeer(config);
|
||||
Peer = new CoopPeer(config,Log);
|
||||
Peer.OnMessageReceived += (s, m) =>
|
||||
{
|
||||
try
|
||||
@ -112,7 +108,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error(ex);
|
||||
Log.Error(ex);
|
||||
}
|
||||
};
|
||||
API.QueueAction(() => { Notification.Show("~y~Trying to connect..."); });
|
||||
@ -135,9 +131,9 @@ namespace RageCoop.Client
|
||||
var outgoingMessage = Peer.CreateMessage();
|
||||
var handshake = new Packets.Handshake
|
||||
{
|
||||
PedID = Main.LocalPlayerID,
|
||||
PedID = LocalPlayerID,
|
||||
Username = username,
|
||||
ModVersion = Main.Version.ToString(),
|
||||
ModVersion = Main.ModVersion.ToString(),
|
||||
PasswordEncrypted = Security.Encrypt(password.GetBytes()),
|
||||
InternalEndPoint = new IPEndPoint(CoreUtils.GetLocalAddress(ip[0]), Peer.Port)
|
||||
};
|
||||
@ -148,12 +144,12 @@ namespace RageCoop.Client
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error("Cannot connect to server: ", ex);
|
||||
API.QueueAction(() => Notification.Show("Cannot connect to server: " + ex.Message));
|
||||
Log.Error("Cannot connect to server: ", ex);
|
||||
API.QueueAction(() => Notification.Show("~r~Cannot connect to server: " + ex.Message));
|
||||
}
|
||||
|
||||
IsConnecting = false;
|
||||
});
|
||||
}, "Connect");
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,7 +180,7 @@ namespace RageCoop.Client
|
||||
};
|
||||
PlayerList.SetPlayer(packet.PedID, packet.Username);
|
||||
|
||||
Main.Logger.Debug($"player connected:{p.Username}");
|
||||
Log.Debug($"player connected:{p.Username}");
|
||||
API.QueueAction(() =>
|
||||
Notification.Show($"~h~{p.Username}~h~ connected."));
|
||||
}
|
||||
|
@ -12,25 +12,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
internal static partial class Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to reslove entity handle in a <see cref="Packets.CustomEvent" />
|
||||
/// </summary>
|
||||
private static readonly Func<byte, NetIncomingMessage, object> _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);
|
||||
|
||||
@ -57,7 +39,7 @@ namespace RageCoop.Client
|
||||
var p = new Packets.HandshakeSuccess();
|
||||
p.Deserialize(response);
|
||||
foreach (var player in p.Players) PlayerList.SetPlayer(player.ID, player.Username);
|
||||
Main.Connected();
|
||||
Connected();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -68,11 +50,11 @@ namespace RageCoop.Client
|
||||
if (PlayerList.Players.TryGetValue(p.ID, out var player))
|
||||
{
|
||||
player.Connection = message.SenderConnection;
|
||||
Main.Logger.Debug($"Direct connection to {player.Username} established");
|
||||
Log.Debug($"Direct connection to {player.Username} established");
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Logger.Info(
|
||||
Log.Info(
|
||||
$"Unidentified peer connection from {message.SenderEndPoint} was rejected.");
|
||||
message.SenderConnection.Disconnect("eat poop");
|
||||
}
|
||||
@ -80,7 +62,7 @@ namespace RageCoop.Client
|
||||
|
||||
break;
|
||||
case NetConnectionStatus.Disconnected:
|
||||
if (message.SenderConnection == ServerConnection) Main.CleanUp(reason);
|
||||
if (message.SenderConnection == ServerConnection) API.QueueAction(() => CleanUp(reason));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -122,7 +104,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Logger.Debug("Did not find a request handler of type: " + realType);
|
||||
Log.Debug("Did not find a request handler of type: " + realType);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -141,8 +123,8 @@ namespace RageCoop.Client
|
||||
Notification.Show($"~r~~h~Packet Error {ex.Message}");
|
||||
return true;
|
||||
});
|
||||
Main.Logger.Error($"[{packetType}] {ex.Message}");
|
||||
Main.Logger.Error(ex);
|
||||
Log.Error($"[{packetType}] {ex.Message}");
|
||||
Log.Error(ex);
|
||||
Peer.Shutdown($"Packet Error [{packetType}]");
|
||||
}
|
||||
|
||||
@ -174,7 +156,7 @@ namespace RageCoop.Client
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
Main.Logger.Trace(message.ReadString());
|
||||
Log.Trace(message.ReadString());
|
||||
break;
|
||||
}
|
||||
|
||||
@ -223,7 +205,7 @@ namespace RageCoop.Client
|
||||
|
||||
API.QueueAction(() =>
|
||||
{
|
||||
Main.MainChat.AddMessage(packet.Username, packet.Message);
|
||||
MainChat.AddMessage(packet.Username, packet.Message);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -231,7 +213,7 @@ namespace RageCoop.Client
|
||||
|
||||
case PacketType.Voice:
|
||||
{
|
||||
if (Main.Settings.Voice)
|
||||
if (Settings.Voice)
|
||||
{
|
||||
var packet = new Packets.Voice();
|
||||
packet.Deserialize(msg);
|
||||
@ -239,7 +221,7 @@ namespace RageCoop.Client
|
||||
|
||||
var player = EntityPool.GetPedByID(packet.ID);
|
||||
player.IsSpeaking = true;
|
||||
player.LastSpeakingTime = Main.Ticked;
|
||||
player.LastSpeakingTime = Ticked;
|
||||
|
||||
Voice.AddVoiceData(packet.Buffer, packet.Recorded);
|
||||
}
|
||||
@ -295,7 +277,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
var c = EntityPool.GetPedByID(packet.ID);
|
||||
if (c == null)
|
||||
// Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}");
|
||||
// Log.Debug($"Creating character for incoming sync:{packet.ID}");
|
||||
EntityPool.ThreadSafe.Add(c = new SyncedPed(packet.ID));
|
||||
var flags = packet.Flags;
|
||||
c.ID = packet.ID;
|
||||
@ -378,7 +360,7 @@ namespace RageCoop.Client
|
||||
if (p == null)
|
||||
{
|
||||
if (packet.Flags.HasProjDataFlag(ProjectileDataFlags.Exploded)) return;
|
||||
// Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}");
|
||||
// Log.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}");
|
||||
EntityPool.ThreadSafe.Add(p = new SyncedProjectile(packet.ID));
|
||||
}
|
||||
|
||||
|
@ -47,15 +47,15 @@ namespace RageCoop.Client
|
||||
var veh = ped.CurrentVehicle?.GetSyncEntity() ??
|
||||
ped.VehicleTryingToEnter?.GetSyncEntity() ?? ped.LastVehicle?.GetSyncEntity();
|
||||
p.VehicleID = veh?.ID ?? 0;
|
||||
if (p.VehicleID == 0) Main.Logger.Error("Invalid vehicle");
|
||||
if (p.VehicleID == 0) Log.Error("Invalid vehicle");
|
||||
if (p.Speed == 5)
|
||||
p.Seat = ped.GetSeatTryingToEnter();
|
||||
else
|
||||
p.Seat = ped.SeatIndex;
|
||||
if (!veh.IsLocal && p.Speed == 4 && p.Seat == VehicleSeat.Driver)
|
||||
{
|
||||
veh.OwnerID = Main.LocalPlayerID;
|
||||
SyncEvents.TriggerChangeOwner(veh.ID, Main.LocalPlayerID);
|
||||
veh.OwnerID = LocalPlayerID;
|
||||
SyncEvents.TriggerChangeOwner(veh.ID, LocalPlayerID);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ namespace RageCoop.Client
|
||||
p.Clothes = ped.GetPedClothes();
|
||||
p.ModelHash = ped.Model.Hash;
|
||||
p.WeaponComponents = ped.Weapons.Current.GetWeaponComponents();
|
||||
p.WeaponTint = (byte)Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, ped, ped.Weapons.Current.Hash);
|
||||
p.WeaponTint = (byte)Call<int>(GET_PED_WEAPON_TINT_INDEX, ped, ped.Weapons.Current.Hash);
|
||||
|
||||
Blip b;
|
||||
if (sp.IsPlayer)
|
||||
@ -110,7 +110,7 @@ namespace RageCoop.Client
|
||||
packet.Position = veh.ReadPosition();
|
||||
packet.Velocity = veh.Velocity;
|
||||
packet.Quaternion = veh.ReadQuaternion();
|
||||
packet.RotationVelocity = veh.RotationVelocity;
|
||||
packet.RotationVelocity = veh.WorldRotationVelocity;
|
||||
packet.ThrottlePower = veh.ThrottlePower;
|
||||
packet.BrakePower = veh.BrakePower;
|
||||
v.LastSentStopWatch.Restart();
|
||||
@ -122,7 +122,7 @@ namespace RageCoop.Client
|
||||
byte secondaryColor = 0;
|
||||
unsafe
|
||||
{
|
||||
Function.Call<byte>(Hash.GET_VEHICLE_COLOURS, veh, &primaryColor, &secondaryColor);
|
||||
Call<byte>(GET_VEHICLE_COLOURS, veh, &primaryColor, &secondaryColor);
|
||||
}
|
||||
|
||||
packet.Flags |= VehicleDataFlags.IsFullSync;
|
||||
@ -134,8 +134,8 @@ namespace RageCoop.Client
|
||||
packet.ModelHash = veh.Model.Hash;
|
||||
packet.EngineHealth = veh.EngineHealth;
|
||||
packet.LockStatus = veh.LockStatus;
|
||||
packet.LicensePlate = Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh);
|
||||
packet.Livery = Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh);
|
||||
packet.LicensePlate = Call<string>(GET_VEHICLE_NUMBER_PLATE_TEXT, veh);
|
||||
packet.Livery = Call<int>(GET_VEHICLE_LIVERY, veh);
|
||||
if (v.MainVehicle == Game.Player.LastVehicle) packet.RadioStation = Util.GetPlayerRadioIndex();
|
||||
if (packet.EngineHealth > v.LastEngineHealth) packet.Flags |= VehicleDataFlags.Repaired;
|
||||
v.LastEngineHealth = packet.EngineHealth;
|
||||
@ -154,14 +154,14 @@ namespace RageCoop.Client
|
||||
public static void SendChatMessage(string message)
|
||||
{
|
||||
Peer.SendTo(new Packets.ChatMessage(s => Security.Encrypt(s.GetBytes()))
|
||||
{ Username = Main.Settings.Username, Message = message }, ServerConnection, ConnectionChannel.Chat,
|
||||
{ Username = Settings.Username, Message = message }, ServerConnection, ConnectionChannel.Chat,
|
||||
NetDeliveryMethod.ReliableOrdered);
|
||||
Peer.FlushSendQueue();
|
||||
}
|
||||
|
||||
public static void SendVoiceMessage(byte[] buffer, int recorded)
|
||||
{
|
||||
SendSync(new Packets.Voice { ID = Main.LocalPlayerID, Buffer = buffer, Recorded = recorded },
|
||||
SendSync(new Packets.Voice { ID = LocalPlayerID, Buffer = buffer, Recorded = recorded },
|
||||
ConnectionChannel.Voice, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,9 @@ namespace RageCoop.Client
|
||||
{
|
||||
static Statistics()
|
||||
{
|
||||
Task.Run(() =>
|
||||
ThreadManager.CreateThread(() =>
|
||||
{
|
||||
while (true)
|
||||
while (!IsUnloading)
|
||||
{
|
||||
var bu = Networking.Peer.Statistics.SentBytes;
|
||||
var bd = Networking.Peer.Statistics.ReceivedBytes;
|
||||
@ -17,7 +17,7 @@ namespace RageCoop.Client
|
||||
BytesUpPerSecond = Networking.Peer.Statistics.SentBytes - bu;
|
||||
BytesDownPerSecond = Networking.Peer.Statistics.ReceivedBytes - bd;
|
||||
}
|
||||
});
|
||||
},"Statistics");
|
||||
}
|
||||
|
||||
public static int BytesDownPerSecond { get; private set; }
|
||||
|
@ -5,6 +5,7 @@ using GTA;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
using Lidgren.Network;
|
||||
using Newtonsoft.Json;
|
||||
using RageCoop.Client.Menus;
|
||||
using RageCoop.Client.Scripting;
|
||||
using RageCoop.Core;
|
||||
@ -28,12 +29,12 @@ namespace RageCoop.Client
|
||||
|
||||
if (Util.GetTickCount64() - _lastUpdate >= 1000) Update();
|
||||
|
||||
if (Util.GetTickCount64() - Pressed < 5000 && !Main.MainChat.Focused
|
||||
if (Util.GetTickCount64() - Pressed < 5000 && !MainChat.Focused
|
||||
#if !NON_INTERACTIVE
|
||||
&& !CoopMenu.MenuPool.AreAnyVisible
|
||||
#endif
|
||||
)
|
||||
Function.Call(Hash.DRAW_SCALEFORM_MOVIE, _mainScaleform.Handle,
|
||||
Call(DRAW_SCALEFORM_MOVIE, _mainScaleform.Handle,
|
||||
LeftAlign ? LEFT_POSITION : RIGHT_POSITION, 0.3f,
|
||||
0.28f, 0.6f,
|
||||
255, 255, 255, 255, 0);
|
||||
@ -57,7 +58,7 @@ namespace RageCoop.Client
|
||||
|
||||
public static void SetPlayer(int id, string username, float latency = 0)
|
||||
{
|
||||
Main.Logger.Debug($"{id},{username},{latency}");
|
||||
Log.Debug($"{id},{username},{latency}");
|
||||
if (Players.TryGetValue(id, out var p))
|
||||
{
|
||||
p.Username = username;
|
||||
@ -127,9 +128,21 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
public class Player
|
||||
internal class Player
|
||||
{
|
||||
internal float _latencyToServer;
|
||||
internal bool ConnectWhenPunched { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Blip FakeBlip { get; internal set; }
|
||||
[JsonIgnore]
|
||||
public Vector3 Position { get; internal set; }
|
||||
[JsonIgnore]
|
||||
public SyncedPed Character { get; internal set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public NetConnection Connection { get; internal set; }
|
||||
|
||||
public byte HolePunchStatus { get; internal set; } = 1;
|
||||
public bool IsHost { get; internal set; }
|
||||
public string Username { get; internal set; }
|
||||
@ -139,17 +152,15 @@ namespace RageCoop.Client
|
||||
/// </summary>
|
||||
public int ID { get; internal set; }
|
||||
|
||||
public int EntityHandle => Character?.MainPed?.Handle ?? 0;
|
||||
|
||||
public IPEndPoint InternalEndPoint { get; internal set; }
|
||||
public IPEndPoint ExternalEndPoint { get; internal set; }
|
||||
internal bool ConnectWhenPunched { get; set; }
|
||||
public Blip FakeBlip { get; internal set; }
|
||||
public Vector3 Position { get; internal set; }
|
||||
public SyncedPed Character { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Player round-trip time in seconds, will be the rtt to server if not using P2P connection.
|
||||
/// </summary>
|
||||
public float Ping => Main.LocalPlayerID == ID ? Networking.Latency * 2 :
|
||||
public float Ping => LocalPlayerID == ID ? Networking.Latency * 2 :
|
||||
HasDirectConnection ? Connection.AverageRoundtripTime : _latencyToServer * 2;
|
||||
|
||||
public float PacketTravelTime => HasDirectConnection
|
||||
@ -157,7 +168,6 @@ namespace RageCoop.Client
|
||||
: Networking.Latency + _latencyToServer;
|
||||
|
||||
public bool DisplayNameTag { get; set; } = true;
|
||||
public NetConnection Connection { get; internal set; }
|
||||
public bool HasDirectConnection => Connection?.Status == NetConnectionStatus.Connected;
|
||||
}
|
||||
}
|
@ -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")]
|
||||
[assembly: AssemblyVersion("1.6.0.28")]
|
||||
[assembly: AssemblyFileVersion("1.6.0.28")]
|
||||
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]
|
||||
|
||||
|
@ -1,207 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>RageCoop.Client</RootNamespace>
|
||||
<AssemblyName>RageCoop.Client</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<OutPutPath>..\..\bin\$(Configuration)\Client\Scripts</OutPutPath>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
|
||||
<AllowedReferenceRelatedFileExtensions>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<NoAotCompile>false</NoAotCompile>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<OutDir>..\..\bin\$(Configuration)\Client\Scripts</OutDir>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<AllowedReferenceRelatedFileExtensions>
|
||||
.dll
|
||||
.pdb
|
||||
</AllowedReferenceRelatedFileExtensions>
|
||||
<DocumentationFile>..\..\bin\API\RageCoop.Client.xml</DocumentationFile>
|
||||
<DebugType>full</DebugType>
|
||||
<NoWarn>CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<DefineConstants>DEBUG</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Console.cs" />
|
||||
<Compile Include="Debug.cs" />
|
||||
<Compile Include="DevTools\DevTool.cs" />
|
||||
<Compile Include="GUI\CefClient.cs" />
|
||||
<Compile Include="GUI\CefManager.cs" />
|
||||
<Compile Include="GUI\HookManager.cs" />
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Menus\CoopMenu.cs" />
|
||||
<Compile Include="Menus\Sub\DebugMenu.cs" />
|
||||
<Compile Include="Menus\Sub\DevToolMenu.cs" />
|
||||
<Compile Include="Menus\Sub\ServersMenu.cs" />
|
||||
<Compile Include="Menus\Sub\SettingsMenu.cs" />
|
||||
<Compile Include="Networking\Chat.cs" />
|
||||
<Compile Include="Networking\DownloadManager.cs" />
|
||||
<Compile Include="Networking\HolePunch.cs" />
|
||||
<Compile Include="Networking\Networking.cs" />
|
||||
<Compile Include="Networking\Receive.cs" />
|
||||
<Compile Include="Networking\Send.cs" />
|
||||
<Compile Include="Networking\Statistics.cs" />
|
||||
<Compile Include="PlayerList.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>AssemblyInfo.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Scripting\API.cs" />
|
||||
<Compile Include="Scripting\BaseScript.cs" />
|
||||
<Compile Include="Scripting\ClientScript.cs" />
|
||||
<Compile Include="Scripting\Resources.cs" />
|
||||
<Compile Include="Security.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="Sync\Entities\Ped\SyncedPed.Variables.cs" />
|
||||
<Compile Include="Sync\Entities\Ped\SyncedPed.Animations.cs" />
|
||||
<Compile Include="Sync\Entities\SyncedEntity.cs" />
|
||||
<Compile Include="Sync\Entities\Ped\SyncedPed.cs" />
|
||||
<Compile Include="Sync\Entities\SyncedProjectile.cs" />
|
||||
<Compile Include="Sync\Entities\SyncedProp.cs" />
|
||||
<Compile Include="Sync\Entities\Vehicle\SyncedVehicle.cs" />
|
||||
<Compile Include="Sync\Entities\Vehicle\SyncedVehicle.Variables.cs" />
|
||||
<Compile Include="Sync\EntityPool.cs" />
|
||||
<Compile Include="Sync\SyncEvents.cs" />
|
||||
<Compile Include="Sync\Voice.cs" />
|
||||
<Compile Include="Util\Memory.cs" />
|
||||
<Compile Include="Util\NativeCaller.cs" />
|
||||
<Compile Include="Util\PedConfigFlags.cs" />
|
||||
<Compile Include="Util\PedExtensions.cs" />
|
||||
<Compile Include="Shared\Shared.cs" />
|
||||
<Compile Include="Util\TaskType.cs" />
|
||||
<Compile Include="Util\Util.cs" />
|
||||
<Compile Include="Util\VehicleExtensions.cs" />
|
||||
<Compile Include="Util\WeaponUtil.cs" />
|
||||
<Compile Include="Util\Win32.cs" />
|
||||
<Compile Include="WorldThread.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="BitmapUtil, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\libs\BitmapUtil.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="DXHook">
|
||||
<HintPath>..\..\libs\DXHook\DXHook.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=1.4.0.12, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\SharpZipLib.1.4.0\lib\netstandard2.0\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="LemonUI.SHVDN3">
|
||||
<HintPath>..\..\libs\LemonUI.SHVDN3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Lidgren.Network">
|
||||
<HintPath>..\..\libs\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="NativeInvoker">
|
||||
<HintPath>..\..\libs\NativeInvoker.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NAudio.2.1.0\lib\net472\NAudio.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio.Asio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NAudio.Asio.2.1.0\lib\netstandard2.0\NAudio.Asio.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NAudio.Core.2.1.0\lib\netstandard2.0\NAudio.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio.Midi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NAudio.Midi.2.1.0\lib\netstandard2.0\NAudio.Midi.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio.Wasapi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NAudio.Wasapi.2.1.0\lib\netstandard2.0\NAudio.Wasapi.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio.WinForms, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NAudio.WinForms.2.1.0\lib\net472\NAudio.WinForms.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio.WinMM, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NAudio.WinMM.2.1.0\lib\netstandard2.0\NAudio.WinMM.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RageCoop.Client.CefHost, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\bin\$(Configuration)\Client\SubProcess\ref\RageCoop.Client.CefHost.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet">
|
||||
<HintPath>..\..\libs\ScriptHookVDotNet.asi</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3, Version=3.5.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\libs\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Drawing.Common, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Drawing.Common.4.5.0\lib\net461\System.Drawing.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\.editorconfig">
|
||||
<Link>.editorconfig</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="icon.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Properties\AssemblyInfo.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
|
||||
</Content>
|
||||
<Content Include="sharpdx_direct3d11_1_effects_x64.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\RageCoop.Core.csproj">
|
||||
<Project>{cc2e8102-e568-4524-aa1f-f8e0f1cfe58a}</Project>
|
||||
<Name>RageCoop.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Loader\RageCoop.Client.Loader.csproj">
|
||||
<Project>{fc8cbdbb-6dc3-43af-b34d-092e476410a5}</Project>
|
||||
<Name>RageCoop.Client.Loader</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition=" '$(DevEnvDir)' != '*Undefined*'">
|
||||
"$(DevEnvDir)TextTransform.exe" -a !!BuildConfiguration!$(Configuration) "$(ProjectDir)Properties\AssemblyInfo.tt"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition="'$(Configuration)' != 'API' AND '$(SolutionDir)' != '*Undefined*'">
|
||||
xcopy "$(SolutionDir)Client\Data" "..\Data" /i /s
|
||||
</PostBuildEvent>
|
||||
<PreBuildEvent Condition=" '$(SolutionDir)' != '*Undefined*'">
|
||||
dotnet build -c $(Configuration) "$(SolutionDir)Client\CefHost\RageCoop.Client.CefHost.csproj"
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<NoWarn>CS1591</NoWarn>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateDocumentationFile>False</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<TrimmerRootDescriptor Include="Trimming.xml" />
|
||||
<Compile Remove="GUI\**" />
|
||||
<EmbeddedResource Remove="GUI\**" />
|
||||
<None Remove="GUI\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Scripting\ClientScript.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="LemonUI.SHVDNC">
|
||||
<HintPath>..\..\libs\LemonUI.SHVDNC.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\RageCoop.Core.csproj" />
|
||||
<ProjectReference Include="..\Scripting\RageCoop.Client.Scripting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NAudio" Version="2.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.0" />
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Properties\AssemblyInfo.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\AssemblyInfo.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>AssemblyInfo.tt</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(DevEnvDir)' != '*Undefined*'">
|
||||
<Exec Command=""$(DevEnvDir)TextTransform.exe" -a !!BuildConfiguration!$(Configuration) "$(ProjectDir)Properties\AssemblyInfo.tt"" />
|
||||
</Target>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(SolutionDir)' != '*Undefined*' and '$(Configuration)' != 'API'">
|
||||
<Exec Command="xcopy "$(SolutionDir)Client\Data" "$(OutDir)..\Data" /i /s /y" />
|
||||
</Target>
|
||||
</Project>
|
140
Client/Scripts/Scripting/API.Exports.cs
Normal file
140
Client/Scripts/Scripting/API.Exports.cs
Normal file
@ -0,0 +1,140 @@
|
||||
using Lidgren.Network;
|
||||
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;
|
||||
|
||||
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<ApiExportAttribute>();
|
||||
if (attri == null) continue;
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
static string _lastResult;
|
||||
|
||||
[ApiExportAttribute(EntryPoint = nameof(GetLastResult))]
|
||||
public static int GetLastResult(char* buf, int cbBufSize)
|
||||
{
|
||||
if (_lastResult == null)
|
||||
return 0;
|
||||
|
||||
fixed (char* pErr = _lastResult)
|
||||
{
|
||||
var cbToCopy = sizeof(char) * (_lastResult.Length + 1);
|
||||
System.Buffer.MemoryCopy(pErr, buf, cbToCopy, Math.Min(cbToCopy, cbBufSize));
|
||||
if (cbToCopy > cbBufSize && cbBufSize > 0)
|
||||
{
|
||||
buf[cbBufSize / sizeof(char) - 1] = '\0'; // Always add null terminator
|
||||
}
|
||||
return _lastResult.Length;
|
||||
}
|
||||
}
|
||||
public static void SetLastResult(string msg) => _lastResult = msg;
|
||||
|
||||
[ApiExportAttribute(EntryPoint = nameof(SetLastResult))]
|
||||
public static void SetLastResult(char* msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetLastResult(msg == null ? null : new string(msg));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SHVDN.PInvoke.MessageBoxA(default, ex.ToString(), "error", default);
|
||||
}
|
||||
}
|
||||
|
||||
[ApiExportAttribute(EntryPoint = nameof(GetEventHash))]
|
||||
public static CustomEventHash GetEventHash(char* name) => new string(name);
|
||||
|
||||
[ApiExportAttribute(EntryPoint = nameof(SendCustomEvent))]
|
||||
public static void SendCustomEvent(CustomEventFlags flags, int hash, byte* data, int cbData)
|
||||
{
|
||||
var payload = new byte[cbData];
|
||||
Marshal.Copy((IntPtr)data, payload, 0, cbData);
|
||||
Networking.Peer.SendTo(new Packets.CustomEvent()
|
||||
{
|
||||
Flags = flags,
|
||||
Payload = payload,
|
||||
Hash = hash
|
||||
}, Networking.ServerConnection, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
[ApiExportAttribute(EntryPoint = nameof(InvokeCommand))]
|
||||
public static int InvokeCommand(char* name, int argc, char** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
var args = new string[argc];
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
args[i] = new(argv[i]);
|
||||
}
|
||||
_lastResult = _invokeCommand(new string(name), args);
|
||||
return _lastResult.Length;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
SetLastResult(ex.ToString());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
[ApiExportAttribute(EntryPoint = nameof(GetLastResultLenInChars))]
|
||||
public static int GetLastResultLenInChars() => _lastResult?.Length ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Convert Entity ID to handle
|
||||
/// </summary>
|
||||
[ApiExportAttribute(EntryPoint = nameof(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,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue a message to the main logger
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="msg"></param>
|
||||
[ApiExportAttribute(EntryPoint = nameof(LogEnqueue))]
|
||||
public static void LogEnqueue(LogLevel level, char* msg)
|
||||
{
|
||||
Log.Enqueue((int)level, new(msg));
|
||||
}
|
||||
|
||||
class ApiExportAttribute : Attribute
|
||||
{
|
||||
public string EntryPoint;
|
||||
}
|
||||
}
|
||||
}
|
55
Client/Scripts/Scripting/API.Remoting.cs
Normal file
55
Client/Scripts/Scripting/API.Remoting.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
internal class RemotingAttribute : Attribute
|
||||
{
|
||||
public bool GenBridge = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some local remoting implementation with json-based serialization, somewhar slow, but convenient
|
||||
/// </summary>
|
||||
internal static unsafe partial class API
|
||||
{
|
||||
static readonly MethodInfo[] _apiEntries =
|
||||
typeof(API).GetMethods(BindingFlags.Static | BindingFlags.Public);
|
||||
|
||||
static readonly Dictionary<string, MethodInfo> _commands =
|
||||
new(typeof(API).GetMethods().
|
||||
Where(md => md.CustomAttributes.
|
||||
Any(attri => attri.AttributeType == typeof(RemotingAttribute))).
|
||||
Select(x => new KeyValuePair<string, MethodInfo>(x.Name, x)));
|
||||
|
||||
static string _invokeCommand(string name, string[] argsJson)
|
||||
{
|
||||
if (_commands.TryGetValue(name, out var method))
|
||||
{
|
||||
var ps = method.GetParameters();
|
||||
|
||||
if (argsJson.Length != ps.Length)
|
||||
throw new ArgumentException($"Parameter count mismatch, expecting {ps.Length} parameters, got {argsJson.Length}", nameof(argsJson));
|
||||
|
||||
object[] args = new object[ps.Length];
|
||||
for (int i = 0; i < ps.Length; i++)
|
||||
{
|
||||
args[i] = JsonDeserialize(argsJson[i], ps[i].ParameterType);
|
||||
}
|
||||
var result = method.Invoke(null, args);
|
||||
if (method.ReturnType == typeof(void))
|
||||
{
|
||||
return "void";
|
||||
}
|
||||
return JsonSerialize(result);
|
||||
}
|
||||
throw new KeyNotFoundException($"Command {name} was not found");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +1,33 @@
|
||||
#undef DEBUG
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using GTA;
|
||||
using Lidgren.Network;
|
||||
using Newtonsoft.Json;
|
||||
using RageCoop.Client.Menus;
|
||||
using RageCoop.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
using SHVDN;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
[assembly: InternalsVisibleTo("CodeGen")] // For generating api bridge
|
||||
|
||||
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
|
||||
/// </summary>
|
||||
public static class API
|
||||
internal static unsafe partial class API
|
||||
{
|
||||
#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
|
||||
|
||||
@ -50,11 +42,11 @@ namespace RageCoop.Client.Scripting
|
||||
/// </summary>
|
||||
public static string Username
|
||||
{
|
||||
get => Main.Settings.Username;
|
||||
get => Settings.Username;
|
||||
set
|
||||
{
|
||||
if (Networking.IsOnServer || string.IsNullOrEmpty(value)) return;
|
||||
Main.Settings.Username = value;
|
||||
Settings.Username = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,6 +69,17 @@ namespace RageCoop.Client.Scripting
|
||||
/// Get or set scale of player's blip
|
||||
/// </summary>
|
||||
public static float BlipScale { get; set; } = 1;
|
||||
|
||||
public static bool ShowPlayerNameTag
|
||||
{
|
||||
get => Settings.ShowPlayerNameTag;
|
||||
set
|
||||
{
|
||||
if (value == ShowPlayerNameTag) return;
|
||||
Settings.ShowPlayerNameTag = value;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -152,12 +155,26 @@ namespace RageCoop.Client.Scripting
|
||||
|
||||
internal static void InvokeCustomEventReceived(Packets.CustomEvent p)
|
||||
{
|
||||
var args = new CustomEventReceivedArgs { Hash = p.Hash, Args = p.Args };
|
||||
|
||||
// Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
|
||||
// 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
|
||||
@ -191,7 +208,7 @@ namespace RageCoop.Client.Scripting
|
||||
/// <summary>
|
||||
/// Check if the RAGECOOP chat is visible
|
||||
/// </summary>
|
||||
public static bool IsChatFocused => Main.MainChat.Focused;
|
||||
public static bool IsChatFocused => MainChat.Focused;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the RAGECOOP list of players is visible
|
||||
@ -201,18 +218,12 @@ namespace RageCoop.Client.Scripting
|
||||
/// <summary>
|
||||
/// Get the version of RAGECOOP
|
||||
/// </summary>
|
||||
public static Version CurrentVersion => Main.Version;
|
||||
|
||||
/// <summary>
|
||||
/// Get a <see cref="Core.Logger" /> that RAGECOOP is currently using.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Logger Logger => Main.Logger;
|
||||
public static Version CurrentVersion => Main.ModVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Get all players indexed by their ID
|
||||
/// </summary>
|
||||
public static Dictionary<int, Player> Players => new Dictionary<int, Player>(PlayerList.Players);
|
||||
public static Dictionary<int, Player> Players => new(PlayerList.Players);
|
||||
|
||||
#endregion
|
||||
|
||||
@ -260,56 +271,6 @@ namespace RageCoop.Client.Scripting
|
||||
WorldThread.QueueAction(a);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Connect to a server
|
||||
/// </summary>
|
||||
/// <param name="address">Address of the server, e.g. 127.0.0.1:4499</param>
|
||||
/// <exception cref="InvalidOperationException">When a connection is active or being established</exception>
|
||||
public static void Connect(string address)
|
||||
{
|
||||
if (Networking.IsOnServer || Networking.IsConnecting)
|
||||
throw new InvalidOperationException("Cannot connect to server when another connection is active");
|
||||
Networking.ToggleConnection(address);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from current server or cancel the connection attempt.
|
||||
/// </summary>
|
||||
public static void Disconnect()
|
||||
{
|
||||
if (Networking.IsOnServer || Networking.IsConnecting) Networking.ToggleConnection(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all servers from master server address
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static List<ServerInfo> ListServers()
|
||||
{
|
||||
return JsonConvert.DeserializeObject<List<ServerInfo>>(
|
||||
HttpHelper.DownloadString(Main.Settings.MasterServer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a local chat message to this player
|
||||
/// </summary>
|
||||
/// <param name="from">Name of the sender</param>
|
||||
/// <param name="message">The player's message</param>
|
||||
public static void LocalChatMessage(string from, string message)
|
||||
{
|
||||
Main.MainChat.AddMessage(from, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a chat message or command to server/other players
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void SendChatMessage(string message)
|
||||
{
|
||||
Networking.SendChatMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send an event and data to the server.
|
||||
/// </summary>
|
||||
@ -319,13 +280,7 @@ namespace RageCoop.Client.Scripting
|
||||
/// types
|
||||
/// </param>
|
||||
public static void SendCustomEvent(CustomEventHash eventHash, params object[] args)
|
||||
{
|
||||
Networking.Peer.SendTo(new Packets.CustomEvent
|
||||
{
|
||||
Args = args,
|
||||
Hash = eventHash
|
||||
}, Networking.ServerConnection, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
=> SendCustomEvent(CustomEventFlags.None, eventHash, args);
|
||||
|
||||
/// <summary>
|
||||
/// Send an event and data to the server
|
||||
@ -338,9 +293,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);
|
||||
}
|
||||
@ -350,19 +308,12 @@ namespace RageCoop.Client.Scripting
|
||||
/// backgound thread, use <see cref="QueueAction(Action)" /> in the handler to dispatch code to script thread.
|
||||
/// </summary>
|
||||
/// <param name="hash">
|
||||
/// An unique identifier of the event, you can hash your event name with
|
||||
/// <see cref="Core.Scripting.CustomEvents.Hash(string)" />
|
||||
/// An unique identifier of the event
|
||||
/// </param>
|
||||
/// <param name="handler">An handler to be invoked when the event is received from the server. </param>
|
||||
public static void RegisterCustomEventHandler(CustomEventHash hash, Action<CustomEventReceivedArgs> handler)
|
||||
{
|
||||
lock (CustomEventHandlers)
|
||||
{
|
||||
if (!CustomEventHandlers.TryGetValue(hash, out var handlers))
|
||||
CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>());
|
||||
handlers.Add(handler);
|
||||
}
|
||||
}
|
||||
=> RegisterCustomEventHandler(hash, (CustomEventHandler)handler);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
@ -375,9 +326,9 @@ namespace RageCoop.Client.Scripting
|
||||
};
|
||||
DownloadManager.DownloadCompleted += handler;
|
||||
Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest
|
||||
{
|
||||
Name = name
|
||||
},
|
||||
{
|
||||
Name = name
|
||||
},
|
||||
p =>
|
||||
{
|
||||
if (p.Response != FileResponse.Loaded)
|
||||
@ -388,6 +339,150 @@ namespace RageCoop.Client.Scripting
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Connect to a server
|
||||
/// </summary>
|
||||
/// <param name="address">Address of the server, e.g. 127.0.0.1:4499</param>
|
||||
/// <exception cref="InvalidOperationException">When a connection is active or being established</exception>
|
||||
[Remoting]
|
||||
public static void Connect(string address)
|
||||
{
|
||||
if (Networking.IsOnServer || Networking.IsConnecting)
|
||||
throw new InvalidOperationException("Cannot connect to server when another connection is active");
|
||||
Networking.ToggleConnection(address);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from current server or cancel the connection attempt.
|
||||
/// </summary>
|
||||
[Remoting]
|
||||
public static void Disconnect()
|
||||
{
|
||||
if (Networking.IsOnServer || Networking.IsConnecting) Networking.ToggleConnection(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all servers from master server address
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Remoting]
|
||||
public static List<ServerInfo> ListServers()
|
||||
{
|
||||
return JsonDeserialize<List<ServerInfo>>(
|
||||
HttpHelper.DownloadString(Settings.MasterServer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a local chat message to this player
|
||||
/// </summary>
|
||||
/// <param name="from">Name of the sender</param>
|
||||
/// <param name="message">The player's message</param>
|
||||
[Remoting]
|
||||
public static void LocalChatMessage(string from, string message)
|
||||
{
|
||||
MainChat.AddMessage(from, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a chat message or command to server/other players
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
[Remoting]
|
||||
public static void SendChatMessage(string message)
|
||||
{
|
||||
if (!IsOnServer)
|
||||
throw new InvalidOperationException("Not on server");
|
||||
Networking.SendChatMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="ClientResource"/> with this name
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
[Remoting]
|
||||
public static ClientResource GetResource(string name)
|
||||
{
|
||||
if (MainRes.LoadedResources.TryGetValue(name, out var resource))
|
||||
return resource;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get <see cref="ClientResource"/> that contains the specified file or directory
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Remoting]
|
||||
public static ClientResource GetResourceFromPath(string path)
|
||||
{
|
||||
path = Path.GetFullPath(path).ToLower();
|
||||
foreach (var res in MainRes.LoadedResources.Values)
|
||||
{
|
||||
if (res.ScriptsDirectory.ToLower() == path || res.Files.Any(file => file.Value.FullPath.ToLower() == path))
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
[Remoting(GenBridge = false)]
|
||||
public static object GetProperty(string name)
|
||||
=> typeof(API).GetProperty(name, BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
|
||||
|
||||
[Remoting(GenBridge = false)]
|
||||
public static void SetProperty(string name, string jsonVal)
|
||||
{
|
||||
var prop = typeof(API).GetProperty(name, BindingFlags.Static | BindingFlags.Public); ;
|
||||
if (prop == null)
|
||||
throw new KeyNotFoundException($"Property {name} was not found");
|
||||
prop.SetValue(null, JsonDeserialize(jsonVal, prop.PropertyType));
|
||||
}
|
||||
|
||||
[Remoting]
|
||||
public static object GetConfig(string name)
|
||||
=> typeof(Config).GetProperty(name, BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
|
||||
|
||||
[Remoting]
|
||||
public static void SetConfig(string name, string jsonVal)
|
||||
{
|
||||
var prop = typeof(Config).GetProperty(name, BindingFlags.Static | BindingFlags.Public);
|
||||
if (prop == null)
|
||||
throw new KeyNotFoundException($"Property {name} was not found");
|
||||
prop.SetValue(null, JsonDeserialize(jsonVal, prop.PropertyType));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Register an handler to the specifed event hash, one event can have multiple handlers. This will be invoked from
|
||||
/// backgound thread, use <see cref="QueueAction(Action)" /> in the handler to dispatch code to script thread.
|
||||
/// </summary>
|
||||
/// <param name="hash">
|
||||
/// An unique identifier of the event
|
||||
/// </param>
|
||||
/// <param name="handler">An handler to be invoked when the event is received from the server. </param>
|
||||
[Remoting]
|
||||
public static void RegisterCustomEventHandler(CustomEventHash hash, CustomEventHandler handler)
|
||||
{
|
||||
if (handler.Directory == default)
|
||||
throw new ArgumentException("Script directory not specified");
|
||||
|
||||
if (handler.FunctionPtr == default)
|
||||
throw new ArgumentException("Function pointer not specified");
|
||||
|
||||
lock (CustomEventHandlers)
|
||||
{
|
||||
if (!CustomEventHandlers.TryGetValue(hash, out var handlers))
|
||||
CustomEventHandlers.Add(hash, handlers = new());
|
||||
handlers.Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -35,34 +35,34 @@ namespace RageCoop.Client.Scripting
|
||||
API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, WeatherTimeSync);
|
||||
API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied,
|
||||
e => { Notification.Show($"~h~{e.Args[0]}~h~ died."); });
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_isHost)
|
||||
API.QueueAction(() =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var time = World.CurrentTimeOfDay;
|
||||
var weather1 = default(int);
|
||||
var weather2 = default(int);
|
||||
var percent2 = default(float);
|
||||
Function.Call(Hash.GET_CURR_WEATHER_STATE, &weather1, &weather2, &percent2);
|
||||
API.SendCustomEvent(CustomEvents.WeatherTimeSync, time.Hours, time.Minutes,
|
||||
time.Seconds, weather1, weather2, percent2);
|
||||
}
|
||||
});
|
||||
ThreadManager.CreateThread(() =>
|
||||
{
|
||||
while (!IsUnloading)
|
||||
{
|
||||
if (Networking.IsOnServer && _isHost)
|
||||
API.QueueAction(() =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var time = World.CurrentTimeOfDay;
|
||||
var weather1 = default(int);
|
||||
var weather2 = default(int);
|
||||
var percent2 = default(float);
|
||||
Call(GET_CURR_WEATHER_STATE, &weather1, &weather2, &percent2);
|
||||
API.SendCustomEvent(CustomEvents.WeatherTimeSync, time.Hours, time.Minutes,
|
||||
time.Seconds, weather1, weather2, percent2);
|
||||
}
|
||||
});
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
});
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}, "BaseScript");
|
||||
}
|
||||
|
||||
private static void WeatherTimeSync(CustomEventReceivedArgs e)
|
||||
{
|
||||
World.CurrentTimeOfDay = new TimeSpan((int)e.Args[0], (int)e.Args[1], (int)e.Args[2]);
|
||||
Function.Call(Hash.SET_CURR_WEATHER_STATE, (int)e.Args[3], (int)e.Args[4], (float)e.Args[5]);
|
||||
Call(SET_CURR_WEATHER_STATE, (int)e.Args[3], (int)e.Args[4], (float)e.Args[5]);
|
||||
}
|
||||
|
||||
private static void SetDisplayNameTag(CustomEventReceivedArgs e)
|
||||
@ -103,7 +103,7 @@ namespace RageCoop.Client.Scripting
|
||||
{
|
||||
ID = (int)e.Args[0],
|
||||
MainVehicle = veh,
|
||||
OwnerID = Main.LocalPlayerID
|
||||
OwnerID = LocalPlayerID
|
||||
};
|
||||
EntityPool.Add(v);
|
||||
}
|
||||
@ -173,7 +173,7 @@ namespace RageCoop.Client.Scripting
|
||||
EntityPool.ServerProps.Add(id, prop = new SyncedProp(id));
|
||||
}
|
||||
|
||||
prop.LastSynced = Main.Ticked + 1;
|
||||
prop.LastSynced = Ticked + 1;
|
||||
prop.Model = (Model)e.Args[1];
|
||||
prop.Position = (Vector3)e.Args[2];
|
||||
prop.Rotation = (Vector3)e.Args[3];
|
||||
@ -193,11 +193,10 @@ namespace RageCoop.Client.Scripting
|
||||
|
||||
if (returnType == TypeCode.Empty)
|
||||
{
|
||||
Function.Call(hash, arguments.ToArray());
|
||||
Call(hash, arguments.ToArray());
|
||||
return;
|
||||
}
|
||||
|
||||
var t = returnType;
|
||||
var id = (int)e.Args[1];
|
||||
|
||||
|
||||
@ -205,59 +204,59 @@ namespace RageCoop.Client.Scripting
|
||||
{
|
||||
case TypeCode.Boolean:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<bool>(hash, arguments.ToArray()));
|
||||
Call<bool>(hash, arguments.ToArray()));
|
||||
break;
|
||||
case TypeCode.Byte:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<byte>(hash, arguments.ToArray()));
|
||||
Call<byte>(hash, arguments.ToArray()));
|
||||
break;
|
||||
case TypeCode.Char:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<char>(hash, arguments.ToArray()));
|
||||
Call<char>(hash, arguments.ToArray()));
|
||||
break;
|
||||
|
||||
case TypeCode.Single:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<float>(hash, arguments.ToArray()));
|
||||
Call<float>(hash, arguments.ToArray()));
|
||||
break;
|
||||
|
||||
case TypeCode.Double:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<double>(hash, arguments.ToArray()));
|
||||
Call<double>(hash, arguments.ToArray()));
|
||||
break;
|
||||
|
||||
case TypeCode.Int16:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<short>(hash, arguments.ToArray()));
|
||||
Call<short>(hash, arguments.ToArray()));
|
||||
break;
|
||||
|
||||
case TypeCode.Int32:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id, Function.Call<int>(hash, arguments.ToArray()));
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id, Call<int>(hash, arguments.ToArray()));
|
||||
break;
|
||||
|
||||
case TypeCode.Int64:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<long>(hash, arguments.ToArray()));
|
||||
Call<long>(hash, arguments.ToArray()));
|
||||
break;
|
||||
|
||||
case TypeCode.String:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<string>(hash, arguments.ToArray()));
|
||||
Call<string>(hash, arguments.ToArray()));
|
||||
break;
|
||||
|
||||
case TypeCode.UInt16:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<ushort>(hash, arguments.ToArray()));
|
||||
Call<ushort>(hash, arguments.ToArray()));
|
||||
break;
|
||||
|
||||
case TypeCode.UInt32:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<uint>(hash, arguments.ToArray()));
|
||||
Call<uint>(hash, arguments.ToArray()));
|
||||
break;
|
||||
|
||||
case TypeCode.UInt64:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse, id,
|
||||
Function.Call<ulong>(hash, arguments.ToArray()));
|
||||
Call<ulong>(hash, arguments.ToArray()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -288,7 +287,7 @@ namespace RageCoop.Client.Scripting
|
||||
case string stuff:
|
||||
return stuff;
|
||||
default:
|
||||
return null;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,6 @@ using RageCoop.Core.Scripting;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Inherit from this class, constructor will be called automatically, but other scripts might have yet been loaded,
|
||||
/// you should use <see cref="OnStart" />. to initiate your script.
|
||||
/// </summary>
|
||||
public abstract class ClientScript : Script
|
||||
{
|
||||
/// <summary>
|
||||
@ -24,16 +20,5 @@ namespace RageCoop.Client.Scripting
|
||||
/// Eqivalent of <see cref="ClientResource.Logger" /> in <see cref="CurrentResource" />
|
||||
/// </summary>
|
||||
public Logger Logger => CurrentResource.Logger;
|
||||
|
||||
/// <summary>
|
||||
/// This method would be called from main thread, right after all script constructors are invoked.
|
||||
/// </summary>
|
||||
public abstract void OnStart();
|
||||
|
||||
/// <summary>
|
||||
/// This method would be called from main thread right before the whole <see cref="System.AppDomain" /> is unloded but
|
||||
/// prior to <see cref="GTA.Script.Aborted" />.
|
||||
/// </summary>
|
||||
public abstract void OnStop();
|
||||
}
|
||||
}
|
@ -1,62 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using RageCoop.Client.Loader;
|
||||
using RageCoop.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
using SHVDN;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class ClientResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the resource
|
||||
/// </summary>
|
||||
public string Name { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Directory where the scripts is loaded from
|
||||
/// </summary>
|
||||
public string ScriptsDirectory { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// A resource-specific folder that can be used to store your files.
|
||||
/// </summary>
|
||||
public string DataFolder { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get all <see cref="ClientScript" /> instance in this resource.
|
||||
/// </summary>
|
||||
public List<ClientScript> Scripts { get; internal set; } = new List<ClientScript>();
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="ResourceFile" /> where this script is loaded from.
|
||||
/// </summary>
|
||||
public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>();
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="Core.Logger" /> instance that can be used to debug your resource.
|
||||
/// </summary>
|
||||
public Logger Logger { get; internal set; }
|
||||
}
|
||||
|
||||
internal class Resources
|
||||
{
|
||||
public static string TempPath;
|
||||
public static string TempPath = Path.Combine(Path.GetTempPath(), "RageCoop");
|
||||
|
||||
internal readonly ConcurrentDictionary<string, ClientResource> LoadedResources =
|
||||
new ConcurrentDictionary<string, ClientResource>();
|
||||
internal readonly ConcurrentDictionary<string, ClientResource> LoadedResources = new();
|
||||
|
||||
static Resources()
|
||||
{
|
||||
TempPath = Path.Combine(Path.GetTempPath(), "RageCoop");
|
||||
if (Directory.Exists(TempPath))
|
||||
try
|
||||
{
|
||||
@ -70,52 +33,45 @@ namespace RageCoop.Client.Scripting
|
||||
Directory.CreateDirectory(TempPath);
|
||||
}
|
||||
|
||||
public Resources()
|
||||
{
|
||||
Logger = Main.Logger;
|
||||
}
|
||||
|
||||
private Logger Logger { get; }
|
||||
|
||||
public void Load(string path, string[] zips)
|
||||
public unsafe void Load(string path, string[] zips)
|
||||
{
|
||||
LoadedResources.Clear();
|
||||
foreach (var zip in zips)
|
||||
{
|
||||
var zipPath = Path.Combine(path, zip);
|
||||
Logger?.Info($"Loading resource: {Path.GetFileNameWithoutExtension(zip)}");
|
||||
Log?.Info($"Loading resource: {Path.GetFileNameWithoutExtension(zip)}");
|
||||
Unpack(zipPath, Path.Combine(path, "Data"));
|
||||
}
|
||||
|
||||
Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories).Where(x => x.CanBeIgnored())
|
||||
.ForEach(x => File.Delete(x));
|
||||
|
||||
// Load it in main thread
|
||||
API.QueueActionAndWait(() =>
|
||||
{
|
||||
Main.QueueToMainThreadAndWait(() =>
|
||||
{
|
||||
foreach (var res in LoadedResources)
|
||||
Directory.GetFiles(res.Value.ScriptsDirectory, "*.dll", SearchOption.AllDirectories)
|
||||
.ForEach(x => ScriptDomain.CurrentDomain.StartScripts(x));
|
||||
SetupScripts();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
public unsafe void Unload()
|
||||
{
|
||||
StopScripts();
|
||||
var dirs = LoadedResources.Values.Select(x => x.ScriptsDirectory);
|
||||
foreach (var dir in dirs)
|
||||
{
|
||||
SHVDN.Core.RuntimeController.RequestUnload(dir);
|
||||
}
|
||||
|
||||
// Unregister associated handler
|
||||
foreach (var handlers in API.CustomEventHandlers.Values)
|
||||
{
|
||||
foreach (var handler in handlers.ToArray())
|
||||
{
|
||||
if (dirs.Contains(handler.Directory, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
handlers.Remove(handler);
|
||||
Log.Debug($"Unregistered handler from script directory {handler.Directory}");
|
||||
}
|
||||
}
|
||||
}
|
||||
LoadedResources.Clear();
|
||||
LoaderContext.RequestUnload();
|
||||
}
|
||||
|
||||
private ClientResource Unpack(string zipPath, string dataFolderRoot)
|
||||
private unsafe ClientResource Unpack(string zipPath, string dataFolderRoot)
|
||||
{
|
||||
var r = new ClientResource
|
||||
{
|
||||
Logger = API.Logger,
|
||||
Scripts = new List<ClientScript>(),
|
||||
Name = Path.GetFileNameWithoutExtension(zipPath),
|
||||
DataFolder = Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(zipPath)),
|
||||
ScriptsDirectory = Path.Combine(TempPath, Path.GetFileNameWithoutExtension(zipPath))
|
||||
@ -131,89 +87,26 @@ namespace RageCoop.Client.Scripting
|
||||
|
||||
|
||||
foreach (var dir in Directory.GetDirectories(scriptsDir, "*", SearchOption.AllDirectories))
|
||||
r.Files.Add(dir, new ResourceFile
|
||||
r.Files.Add(dir, new ClientFile
|
||||
{
|
||||
IsDirectory = true,
|
||||
Name = dir.Substring(scriptsDir.Length + 1).Replace('\\', '/')
|
||||
Name = dir.Substring(scriptsDir.Length + 1).Replace('\\', '/'),
|
||||
FullPath = dir
|
||||
});
|
||||
var assemblies = new Dictionary<ResourceFile, Assembly>();
|
||||
foreach (var file in Directory.GetFiles(scriptsDir, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (Path.GetFileName(file).CanBeIgnored())
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
API.Logger.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 ResourceFile
|
||||
var rfile = new ClientFile
|
||||
{
|
||||
GetStream = () => { return new FileStream(file, FileMode.Open, FileAccess.Read); },
|
||||
IsDirectory = false,
|
||||
Name = relativeName
|
||||
Name = relativeName,
|
||||
FullPath = file
|
||||
};
|
||||
r.Files.Add(relativeName, rfile);
|
||||
}
|
||||
|
||||
SHVDN.Core.RuntimeController.RequestLoad(scriptsDir);
|
||||
LoadedResources.TryAdd(r.Name, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
private void SetupScripts()
|
||||
{
|
||||
foreach (var s in GetClientScripts())
|
||||
{
|
||||
try
|
||||
{
|
||||
API.Logger.Debug("Starting script: " + s.GetType().FullName);
|
||||
var script = (ClientScript)s;
|
||||
if (LoadedResources.TryGetValue(Directory.GetParent(script.Filename).Name, out var r))
|
||||
script.CurrentResource = r;
|
||||
else
|
||||
API.Logger.Warning("Failed to locate resource for script: " + script.Filename);
|
||||
var res = script.CurrentResource;
|
||||
script.CurrentFile = res?.Files.Values.Where(x =>
|
||||
x.Name.ToLower() == script.Filename.Substring(res.ScriptsDirectory.Length + 1)
|
||||
.Replace('\\', '/')).FirstOrDefault();
|
||||
res?.Scripts.Add(script);
|
||||
script.OnStart();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
API.Logger.Error($"Failed to start {s.GetType().FullName}", ex);
|
||||
}
|
||||
|
||||
API.Logger.Debug("Started script: " + s.GetType().FullName);
|
||||
}
|
||||
}
|
||||
|
||||
private void StopScripts()
|
||||
{
|
||||
foreach (var s in GetClientScripts())
|
||||
try
|
||||
{
|
||||
API.Logger.Debug("Stopping script: " + s.GetType().FullName);
|
||||
((ClientScript)s).OnStop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
API.Logger.Error($"Failed to stop {s.GetType().FullName}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static object[] GetClientScripts()
|
||||
{
|
||||
return ScriptDomain.CurrentDomain.RunningScripts.Where(x =>
|
||||
x.ScriptInstance.GetType().IsScript(typeof(ClientScript))).Select(x => x.ScriptInstance).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,11 +6,9 @@ namespace RageCoop.Client
|
||||
{
|
||||
internal class Security
|
||||
{
|
||||
private readonly Logger Logger;
|
||||
|
||||
public Security(Logger logger)
|
||||
public Security()
|
||||
{
|
||||
Logger = logger;
|
||||
ClientAes.GenerateKey();
|
||||
ClientAes.GenerateIV();
|
||||
}
|
||||
|
48
Client/Scripts/Shared.cs
Normal file
48
Client/Scripts/Shared.cs
Normal file
@ -0,0 +1,48 @@
|
||||
global using GTA;
|
||||
global using GTA.Native;
|
||||
global using static GTA.Native.Function;
|
||||
global using static GTA.Native.Hash;
|
||||
global using static RageCoop.Client.Shared;
|
||||
global using static RageCoop.Client.Main;
|
||||
global using Console = GTA.Console;
|
||||
global using static RageCoop.Core.Shared;
|
||||
using System.IO;
|
||||
using System;
|
||||
using SHVDN;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class Shared
|
||||
{
|
||||
private static string GetBasePath()
|
||||
{
|
||||
FileInfo info;
|
||||
string realScriptDir = Directory.GetParent(Instance.FilePath).FullName;
|
||||
nextTarget:
|
||||
info = new(realScriptDir);
|
||||
if (info.LinkTarget != null)
|
||||
{
|
||||
realScriptDir = info.LinkTarget;
|
||||
goto nextTarget;
|
||||
}
|
||||
if (Path.GetFileName(realScriptDir).ToLower() != "scripts")
|
||||
throw new FileNotFoundException($"Unexpected link target {realScriptDir}");
|
||||
|
||||
var baseDir = Directory.GetParent(realScriptDir).FullName;
|
||||
Logger.Debug($"Base directory is {baseDir}");
|
||||
return baseDir;
|
||||
}
|
||||
|
||||
public static string BasePath = GetBasePath();
|
||||
public static string DataPath = Path.Combine(BasePath, "Data");
|
||||
public static string SettingsPath = Path.Combine(DataPath, "Setting.json");
|
||||
|
||||
public static string VehicleWeaponDataPath = Path.Combine(DataPath, "VehicleWeapons.json");
|
||||
public static string WeaponFixDataPath = Path.Combine(DataPath, "WeaponFixes.json");
|
||||
public static string WeaponInfoDataPath = Path.Combine(DataPath, "Weapons.json");
|
||||
public static string AnimationsDataPath = Path.Combine(DataPath, "Animations.json");
|
||||
|
||||
public static string CefSubProcessPath = Path.Combine(BasePath, "SubProcess", "RageCoop.Client.CefHost.exe");
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class Shared
|
||||
{
|
||||
public static string BasePath = "RageCoop";
|
||||
public static string DataPath = Path.Combine(BasePath, "Data");
|
||||
public static string LogPath = Path.Combine(DataPath, "RageCoop.Client.log");
|
||||
public static string SettingsPath = Path.Combine(DataPath, "Setting.json");
|
||||
|
||||
public static string VehicleWeaponDataPath = Path.Combine(DataPath, "VehicleWeapons.json");
|
||||
public static string WeaponFixDataPath = Path.Combine(DataPath, "WeaponFixes.json");
|
||||
public static string WeaponInfoDataPath = Path.Combine(DataPath, "Weapons.json");
|
||||
public static string AnimationsDataPath = Path.Combine(DataPath, "Animations.json");
|
||||
|
||||
public static string CefSubProcessPath = Path.Combine(BasePath, "SubProcess", "RageCoop.Client.CefHost.exe");
|
||||
}
|
||||
}
|
@ -12,22 +12,22 @@ namespace RageCoop.Client
|
||||
|
||||
if (speaking)
|
||||
{
|
||||
Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mic_chatter", "mp_facial");
|
||||
Call(PLAY_FACIAL_ANIM, MainPed.Handle, "mic_chatter", "mp_facial");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (MainPed.Gender)
|
||||
{
|
||||
case Gender.Male:
|
||||
Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1",
|
||||
Call(PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1",
|
||||
"facials@gen_male@variations@normal");
|
||||
break;
|
||||
case Gender.Female:
|
||||
Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1",
|
||||
Call(PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1",
|
||||
"facials@gen_female@variations@normal");
|
||||
break;
|
||||
default:
|
||||
Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1",
|
||||
Call(PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1",
|
||||
"facials@mime@variations@normal");
|
||||
break;
|
||||
}
|
||||
@ -41,12 +41,12 @@ namespace RageCoop.Client
|
||||
if (ourAnim != null && animDict != null)
|
||||
{
|
||||
var flag = AnimationFlags.Loop;
|
||||
if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed, animDict, ourAnim, 3))
|
||||
if (!Call<bool>(IS_ENTITY_PLAYING_ANIM, MainPed, animDict, ourAnim, 3))
|
||||
{
|
||||
if (LoadAnim(animDict) == null) return;
|
||||
|
||||
MainPed.Task.ClearAll();
|
||||
Function.Call(Hash.TASK_PLAY_ANIM, MainPed, animDict, ourAnim, 8f, 10f, -1, flag, -8f, 1, 1, 1);
|
||||
Call(TASK_PLAY_ANIM, MainPed, animDict, ourAnim, 8f, 10f, -1, flag, -8f, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,9 +149,9 @@ namespace RageCoop.Client
|
||||
|
||||
private string LoadAnim(string anim)
|
||||
{
|
||||
if (!Function.Call<bool>(Hash.HAS_ANIM_DICT_LOADED, anim))
|
||||
if (!Call<bool>(HAS_ANIM_DICT_LOADED, anim))
|
||||
{
|
||||
Function.Call(Hash.REQUEST_ANIM_DICT, anim);
|
||||
Call(REQUEST_ANIM_DICT, anim);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace RageCoop.Client
|
||||
p.CanWrithe = false;
|
||||
p.IsOnlyDamagedByPlayer = false;
|
||||
MainPed = p;
|
||||
OwnerID = Main.LocalPlayerID;
|
||||
OwnerID = LocalPlayerID;
|
||||
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
|
||||
}
|
||||
@ -38,7 +38,7 @@ namespace RageCoop.Client
|
||||
internal SyncedPed(int id)
|
||||
{
|
||||
ID = id;
|
||||
LastSynced = Main.Ticked;
|
||||
LastSynced = Ticked;
|
||||
}
|
||||
|
||||
internal override void Update()
|
||||
@ -67,7 +67,7 @@ namespace RageCoop.Client
|
||||
if (!CreateCharacter())
|
||||
return;
|
||||
|
||||
if (!Main.Settings.ShowPlayerBlip && (byte)BlipColor != 255) BlipColor = (BlipColor)255;
|
||||
if (!Settings.ShowPlayerBlip && (byte)BlipColor != 255) BlipColor = (BlipColor)255;
|
||||
if ((byte)BlipColor == 255 && PedBlip != null)
|
||||
{
|
||||
PedBlip.Delete();
|
||||
@ -134,7 +134,7 @@ namespace RageCoop.Client
|
||||
|
||||
if (IsSpeaking)
|
||||
{
|
||||
if (Main.Ticked - LastSpeakingTime < 10)
|
||||
if (Ticked - LastSpeakingTime < 10)
|
||||
{
|
||||
DisplaySpeaking(true);
|
||||
}
|
||||
@ -147,13 +147,13 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
LastUpdated = Main.Ticked;
|
||||
LastUpdated = Ticked;
|
||||
}
|
||||
|
||||
private void RenderNameTag()
|
||||
{
|
||||
if (!Owner.DisplayNameTag || !Main.Settings.ShowPlayerNameTag || MainPed == null || !MainPed.IsVisible ||
|
||||
!MainPed.IsInRange(Main.PlayerPosition, 40f)) return;
|
||||
if (!Owner.DisplayNameTag || !Settings.ShowPlayerNameTag || MainPed == null || !MainPed.IsVisible ||
|
||||
!MainPed.IsInRange(PlayerPosition, 40f)) return;
|
||||
|
||||
var targetPos = MainPed.Bones[Bone.IKHead].Position + Vector3.WorldUp * 0.5f;
|
||||
Point toDraw = default;
|
||||
@ -175,7 +175,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
if (MainPed.Exists())
|
||||
{
|
||||
// Main.Logger.Debug($"Removing ped {ID}. Reason:CreateCharacter");
|
||||
// Log.Debug($"Removing ped {ID}. Reason:CreateCharacter");
|
||||
MainPed.Kill();
|
||||
MainPed.MarkAsNoLongerNeeded();
|
||||
MainPed.Delete();
|
||||
@ -204,17 +204,17 @@ namespace RageCoop.Client
|
||||
MainPed.CanWrithe = false;
|
||||
MainPed.CanBeDraggedOutOfVehicle = true;
|
||||
MainPed.IsOnlyDamagedByPlayer = false;
|
||||
MainPed.RelationshipGroup = Main.SyncedPedsGroup;
|
||||
MainPed.RelationshipGroup = SyncedPedsGroup;
|
||||
MainPed.IsFireProof = false;
|
||||
MainPed.IsExplosionProof = false;
|
||||
|
||||
Function.Call(Hash.SET_PED_DROPS_WEAPONS_WHEN_DEAD, MainPed.Handle, false);
|
||||
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED, MainPed.Handle, true);
|
||||
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, true);
|
||||
Function.Call(Hash.SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false);
|
||||
Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true);
|
||||
// Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false);
|
||||
Function.Call(Hash.SET_PED_CAN_EVASIVE_DIVE, MainPed.Handle, false);
|
||||
Call(SET_PED_DROPS_WEAPONS_WHEN_DEAD, MainPed.Handle, false);
|
||||
Call(SET_PED_CAN_BE_TARGETTED, MainPed.Handle, true);
|
||||
Call(SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, true);
|
||||
Call(SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false);
|
||||
Call(SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true);
|
||||
// Call(_SET_PED_CAN_PLAY_INJURED_ANIMS, false);
|
||||
Call(SET_PED_CAN_EVASIVE_DIVE, MainPed.Handle, false);
|
||||
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DrownsInWater, false);
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
|
||||
@ -244,7 +244,7 @@ namespace RageCoop.Client
|
||||
private void SetClothes()
|
||||
{
|
||||
for (byte i = 0; i < 12; i++)
|
||||
Function.Call(Hash.SET_PED_COMPONENT_VARIATION, MainPed.Handle, i, (int)Clothes[i],
|
||||
Call(SET_PED_COMPONENT_VARIATION, MainPed.Handle, i, (int)Clothes[i],
|
||||
(int)Clothes[i + 12], (int)Clothes[i + 24]);
|
||||
_lastClothes = Clothes;
|
||||
}
|
||||
@ -257,12 +257,12 @@ namespace RageCoop.Client
|
||||
MainPed.PositionNoOffset = Vector3.Lerp(MainPed.ReadPosition(), Position + Velocity, 0.5f);
|
||||
MainPed.Quaternion = Rotation.ToQuaternion();
|
||||
|
||||
if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "skydive@base", "free_idle", 3))
|
||||
if (!Call<bool>(IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "skydive@base", "free_idle", 3))
|
||||
{
|
||||
// Skip update if animation is not loaded
|
||||
var dict = LoadAnim("skydive@base");
|
||||
if (dict == null) return;
|
||||
Function.Call(Hash.TASK_PLAY_ANIM, MainPed.Handle, dict, "free_idle", 8f, 10f, -1, 0, -8f, 1, 1, 1);
|
||||
Call(TASK_PLAY_ANIM, MainPed.Handle, dict, "free_idle", 8f, 10f, -1, 0, -8f, 1, 1, 1);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -293,12 +293,12 @@ namespace RageCoop.Client
|
||||
MainPed.PositionNoOffset = Vector3.Lerp(MainPed.ReadPosition(), Position + Velocity, 0.5f);
|
||||
MainPed.Quaternion = Rotation.ToQuaternion();
|
||||
|
||||
if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "skydive@parachute@first_person",
|
||||
if (!Call<bool>(IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "skydive@parachute@first_person",
|
||||
"chute_idle_right", 3))
|
||||
{
|
||||
var dict = LoadAnim("skydive@parachute@first_person");
|
||||
if (dict == null) return;
|
||||
Function.Call(Hash.TASK_PLAY_ANIM, MainPed, dict, "chute_idle_right", 8f, 10f, -1, 0, -8f, 1, 1, 1);
|
||||
Call(TASK_PLAY_ANIM, MainPed, dict, "chute_idle_right", 8f, 10f, -1, 0, -8f, 1, 1, 1);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -321,7 +321,7 @@ namespace RageCoop.Client
|
||||
_currentAnimation[1] = anim;
|
||||
}
|
||||
|
||||
if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "laddersbase", anim, 3))
|
||||
if (!Call<bool>(IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "laddersbase", anim, 3))
|
||||
MainPed.Task.PlayAnimation("laddersbase", anim, 8f, -1, AnimationFlags.Loop);
|
||||
}
|
||||
else
|
||||
@ -334,7 +334,7 @@ namespace RageCoop.Client
|
||||
_currentAnimation[1] = "base_left_hand_up";
|
||||
}
|
||||
|
||||
if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "laddersbase",
|
||||
if (!Call<bool>(IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "laddersbase",
|
||||
"base_left_hand_up", 3))
|
||||
MainPed.Task.PlayAnimation("laddersbase", "base_left_hand_up", 8f, -1, AnimationFlags.Loop);
|
||||
}
|
||||
@ -346,7 +346,7 @@ namespace RageCoop.Client
|
||||
_currentAnimation[1] = "climb_up";
|
||||
}
|
||||
|
||||
if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "laddersbase", "climb_up",
|
||||
if (!Call<bool>(IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "laddersbase", "climb_up",
|
||||
3)) MainPed.Task.PlayAnimation("laddersbase", "climb_up", 8f, -1, AnimationFlags.Loop);
|
||||
}
|
||||
}
|
||||
@ -373,8 +373,8 @@ namespace RageCoop.Client
|
||||
if (!IsVaulting && MainPed.IsVaulting) MainPed.Task.ClearAllImmediately();
|
||||
|
||||
if (IsOnFire && !MainPed.IsOnFire)
|
||||
Function.Call(Hash.START_ENTITY_FIRE, MainPed);
|
||||
else if (!IsOnFire && MainPed.IsOnFire) Function.Call(Hash.STOP_ENTITY_FIRE, MainPed);
|
||||
Call(START_ENTITY_FIRE, MainPed);
|
||||
else if (!IsOnFire && MainPed.IsOnFire) Call(STOP_ENTITY_FIRE, MainPed);
|
||||
|
||||
if (IsJumping)
|
||||
{
|
||||
@ -397,7 +397,7 @@ namespace RageCoop.Client
|
||||
if (!_lastRagdoll)
|
||||
{
|
||||
_lastRagdoll = true;
|
||||
_lastRagdollTime = Main.Ticked;
|
||||
_lastRagdollTime = Ticked;
|
||||
}
|
||||
|
||||
return;
|
||||
@ -432,7 +432,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
else if (IsInCover)
|
||||
{
|
||||
if (!_lastInCover) Function.Call(Hash.TASK_STAY_IN_COVER, MainPed.Handle);
|
||||
if (!_lastInCover) Call(TASK_STAY_IN_COVER, MainPed.Handle);
|
||||
|
||||
_lastInCover = true;
|
||||
if (IsAiming)
|
||||
@ -476,16 +476,16 @@ namespace RageCoop.Client
|
||||
}
|
||||
else
|
||||
{
|
||||
var model = Function.Call<uint>(Hash.GET_WEAPONTYPE_MODEL, CurrentWeapon);
|
||||
if (!Function.Call<bool>(Hash.HAS_MODEL_LOADED, model))
|
||||
var model = Call<uint>(GET_WEAPONTYPE_MODEL, CurrentWeapon);
|
||||
if (!Call<bool>(HAS_MODEL_LOADED, model))
|
||||
{
|
||||
Function.Call(Hash.REQUEST_MODEL, model);
|
||||
Call(REQUEST_MODEL, model);
|
||||
return;
|
||||
}
|
||||
if (WeaponObj?.Exists() == true)
|
||||
WeaponObj.Delete();
|
||||
MainPed.Weapons.RemoveAll();
|
||||
WeaponObj = Entity.FromHandle(Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeapon, -1, Position.X, Position.Y, Position.Z, true, 0, 0));
|
||||
WeaponObj = Entity.FromHandle(Call<int>(CREATE_WEAPON_OBJECT, CurrentWeapon, -1, Position.X, Position.Y, Position.Z, true, 0, 0));
|
||||
}
|
||||
|
||||
if (compChanged)
|
||||
@ -494,17 +494,17 @@ namespace RageCoop.Client
|
||||
{
|
||||
if (comp.Value)
|
||||
{
|
||||
Function.Call(Hash.GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, WeaponObj.Handle, comp.Key);
|
||||
Call(GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, WeaponObj.Handle, comp.Key);
|
||||
}
|
||||
}
|
||||
_lastWeaponComponents = WeaponComponents;
|
||||
}
|
||||
Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, WeaponObj.Handle, MainPed.Handle);
|
||||
Call(GIVE_WEAPON_OBJECT_TO_PED, WeaponObj.Handle, MainPed.Handle);
|
||||
_lastWeaponHash = CurrentWeapon;
|
||||
}
|
||||
|
||||
if (Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeapon) != WeaponTint)
|
||||
Function.Call<int>(Hash.SET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeapon, WeaponTint);
|
||||
if (Call<int>(GET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeapon) != WeaponTint)
|
||||
Call<int>(SET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeapon, WeaponTint);
|
||||
}
|
||||
|
||||
private void DisplayAiming()
|
||||
@ -512,7 +512,7 @@ namespace RageCoop.Client
|
||||
if (Velocity == default)
|
||||
MainPed.Task.AimAt(AimCoords, 1000);
|
||||
else
|
||||
Function.Call(Hash.TASK_GO_TO_COORD_WHILE_AIMING_AT_COORD, MainPed.Handle,
|
||||
Call(TASK_GO_TO_COORD_WHILE_AIMING_AT_COORD, MainPed.Handle,
|
||||
Position.X + Velocity.X, Position.Y + Velocity.Y, Position.Z + Velocity.Z,
|
||||
AimCoords.X, AimCoords.Y, AimCoords.Z, 3f, false, 0x3F000000, 0x40800000, false, 512, false, 0);
|
||||
SmoothTransition();
|
||||
@ -520,7 +520,7 @@ namespace RageCoop.Client
|
||||
|
||||
private void WalkTo()
|
||||
{
|
||||
Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, MainPed, IsInStealthMode, 0);
|
||||
Call(SET_PED_STEALTH_MOVEMENT, MainPed, IsInStealthMode, 0);
|
||||
var predictPosition = Predict(Position) + Velocity;
|
||||
var range = predictPosition.DistanceToSquared(MainPed.ReadPosition());
|
||||
|
||||
@ -533,7 +533,7 @@ namespace RageCoop.Client
|
||||
if (nrange > 1.0f) nrange = 1.0f;
|
||||
|
||||
MainPed.Task.GoStraightTo(predictPosition);
|
||||
Function.Call(Hash.SET_PED_DESIRED_MOVE_BLEND_RATIO, MainPed.Handle, nrange);
|
||||
Call(SET_PED_DESIRED_MOVE_BLEND_RATIO, MainPed.Handle, nrange);
|
||||
}
|
||||
|
||||
_lastMoving = true;
|
||||
@ -542,7 +542,7 @@ namespace RageCoop.Client
|
||||
if (!MainPed.IsRunning || range > 0.50f)
|
||||
{
|
||||
MainPed.Task.RunTo(predictPosition, true);
|
||||
Function.Call(Hash.SET_PED_DESIRED_MOVE_BLEND_RATIO, MainPed.Handle, 1.0f);
|
||||
Call(SET_PED_DESIRED_MOVE_BLEND_RATIO, MainPed.Handle, 1.0f);
|
||||
}
|
||||
|
||||
_lastMoving = true;
|
||||
@ -550,10 +550,10 @@ namespace RageCoop.Client
|
||||
case 3:
|
||||
if (!MainPed.IsSprinting || range > 0.75f)
|
||||
{
|
||||
Function.Call(Hash.TASK_GO_STRAIGHT_TO_COORD, MainPed.Handle, predictPosition.X,
|
||||
Call(TASK_GO_STRAIGHT_TO_COORD, MainPed.Handle, predictPosition.X,
|
||||
predictPosition.Y, predictPosition.Z, 3.0f, -1, 0.0f, 0.0f);
|
||||
Function.Call(Hash.SET_RUN_SPRINT_MULTIPLIER_FOR_PLAYER, MainPed.Handle, 1.49f);
|
||||
Function.Call(Hash.SET_PED_DESIRED_MOVE_BLEND_RATIO, MainPed.Handle, 1.0f);
|
||||
Call(SET_RUN_SPRINT_MULTIPLIER_FOR_PLAYER, MainPed.Handle, 1.49f);
|
||||
Call(SET_PED_DESIRED_MOVE_BLEND_RATIO, MainPed.Handle, 1.0f);
|
||||
}
|
||||
|
||||
_lastMoving = true;
|
||||
@ -599,7 +599,7 @@ namespace RageCoop.Client
|
||||
|
||||
MainPed.Velocity = Velocity + 5 * dist * (predicted - MainPed.ReadPosition());
|
||||
}
|
||||
else if (Main.Ticked - _lastRagdollTime < 10)
|
||||
else if (Ticked - _lastRagdollTime < 10)
|
||||
{
|
||||
}
|
||||
else if (IsRagdoll)
|
||||
@ -640,7 +640,7 @@ namespace RageCoop.Client
|
||||
// localRagdoll
|
||||
var force = Velocity - MainPed.Velocity + 5 * dist * (predicted - MainPed.ReadPosition());
|
||||
if (force.Length() > 20) force = force.Normalized * 20;
|
||||
MainPed.ApplyForce(force);
|
||||
MainPed.ApplyWorldForceCenterOfMass(force, ForceType.InternalImpulse, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,7 +655,7 @@ namespace RageCoop.Client
|
||||
(!MainPed.IsSittingInVehicle() && !MainPed.IsBeingJacked))
|
||||
MainPed.SetIntoVehicle(CurrentVehicle.MainVehicle, Seat);
|
||||
if (MainPed.IsOnTurretSeat())
|
||||
Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y,
|
||||
Call(TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y,
|
||||
AimCoords.Z);
|
||||
|
||||
// Drive-by
|
||||
@ -663,13 +663,13 @@ namespace RageCoop.Client
|
||||
{
|
||||
if (IsAiming)
|
||||
{
|
||||
Function.Call(Hash.SET_DRIVEBY_TASK_TARGET, MainPed, 0, 0, AimCoords.X, AimCoords.Y,
|
||||
Call(SET_DRIVEBY_TASK_TARGET, MainPed, 0, 0, AimCoords.X, AimCoords.Y,
|
||||
AimCoords.Z);
|
||||
if (!_lastDriveBy)
|
||||
{
|
||||
_lastDriveBy = true;
|
||||
Function.Call(Hash.TASK_DRIVE_BY, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z,
|
||||
1, 100, 1, FiringPattern.SingleShot);
|
||||
Call(TASK_DRIVE_BY, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z,
|
||||
100000, 100, 1, FiringPattern.SingleShot);
|
||||
}
|
||||
}
|
||||
else if (_lastDriveBy || MainPed.IsTaskActive(TaskType.CTaskAimGunVehicleDriveBy))
|
||||
|
@ -16,7 +16,7 @@ namespace RageCoop.Client
|
||||
/// <summary>
|
||||
/// Indicates whether the current player is responsible for syncing this entity.
|
||||
/// </summary>
|
||||
public bool IsLocal => OwnerID == Main.LocalPlayerID;
|
||||
public bool IsLocal => OwnerID == LocalPlayerID;
|
||||
|
||||
/// <summary>
|
||||
/// Network ID for this entity
|
||||
@ -41,7 +41,7 @@ namespace RageCoop.Client
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public bool IsOutOfSync => Main.Ticked - LastSynced > 200 && ID != 0;
|
||||
public bool IsOutOfSync => Ticked - LastSynced > 200 && ID != 0;
|
||||
|
||||
internal bool IsReady => LastSynced > 0 || LastFullSynced == 0;
|
||||
|
||||
@ -60,7 +60,7 @@ namespace RageCoop.Client
|
||||
|
||||
internal void PauseUpdate(ulong frames)
|
||||
{
|
||||
LastUpdated = Main.Ticked + frames;
|
||||
LastUpdated = Ticked + frames;
|
||||
}
|
||||
|
||||
protected Vector3 Predict(Vector3 input)
|
||||
@ -93,10 +93,10 @@ namespace RageCoop.Client
|
||||
public void SetLastSynced(bool full)
|
||||
{
|
||||
LastSyncInterval = LastSentStopWatch.ElapsedMilliseconds;
|
||||
LastSynced = Main.Ticked;
|
||||
LastSynced = Ticked;
|
||||
if (full)
|
||||
{
|
||||
LastFullSynced = Main.Ticked;
|
||||
LastFullSynced = Ticked;
|
||||
}
|
||||
LastSyncedStopWatch.Restart();
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ namespace RageCoop.Client
|
||||
|
||||
MainProjectile.Velocity = Velocity + 10 * (Predict(Position) - MainProjectile.Position);
|
||||
MainProjectile.Rotation = Rotation;
|
||||
LastUpdated = Main.Ticked;
|
||||
LastUpdated = Ticked;
|
||||
}
|
||||
|
||||
private void CreateProjectile()
|
||||
@ -125,7 +125,7 @@ namespace RageCoop.Client
|
||||
Position = (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Shooter.Velocity +
|
||||
Position;
|
||||
var end = Position + Velocity;
|
||||
Function.Call(Hash.SHOOT_SINGLE_BULLET_BETWEEN_COORDS_IGNORE_ENTITY, Position.X, Position.Y, Position.Z,
|
||||
Call(SHOOT_SINGLE_BULLET_BETWEEN_COORDS_IGNORE_ENTITY, Position.X, Position.Y, Position.Z,
|
||||
end.X, end.Y, end.Z, 0, 1, WeaponHash, owner?.Handle ?? 0, 1, 0, -1);
|
||||
var ps = World.GetAllProjectiles();
|
||||
MainProjectile = ps[ps.Length - 1];
|
||||
|
@ -39,7 +39,7 @@ namespace RageCoop.Client
|
||||
MainProp.Position = Position;
|
||||
MainProp.Rotation = Rotation;
|
||||
MainProp.SetFrozen(true);
|
||||
LastUpdated = Main.Ticked;
|
||||
LastUpdated = Ticked;
|
||||
}
|
||||
}
|
||||
}
|
@ -49,9 +49,6 @@ namespace RageCoop.Client
|
||||
|
||||
#region FIXED-DATA
|
||||
|
||||
internal bool IsFlipped => IsMotorcycle ||
|
||||
(Quaternion * Vector3.RelativeTop).Z - (Quaternion * Vector3.RelativeBottom).Z < 0.5;
|
||||
|
||||
internal bool IsMotorcycle;
|
||||
internal bool IsAircraft;
|
||||
internal bool HasRocketBoost;
|
||||
@ -60,21 +57,14 @@ namespace RageCoop.Client
|
||||
internal bool IsSubmarineCar;
|
||||
internal bool IsDeluxo;
|
||||
internal bool IsTrain;
|
||||
internal Vector3 TopExtent;
|
||||
internal Vector3 BottomExtent;
|
||||
internal Vector3 FrontExtent;
|
||||
internal Vector3 RearExtent;
|
||||
internal Vector3 LeftExtent;
|
||||
internal Vector3 RightExtent;
|
||||
private const float RotCalMult = 0.2f;
|
||||
private const float ExtentLength = 5;
|
||||
private const float RotCalMult = 10f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PRIVATE
|
||||
|
||||
private byte[] _lastVehicleColors = { 0, 0 };
|
||||
private Dictionary<int, int> _lastVehicleMods = new Dictionary<int, int>();
|
||||
private Dictionary<int, int> _lastVehicleMods = new();
|
||||
private bool _lastHornActive;
|
||||
private bool _lastTransformed;
|
||||
internal int _lastLivery = -1;
|
||||
|
@ -48,11 +48,11 @@ namespace RageCoop.Client
|
||||
|
||||
if (MainVehicle.IsOnFire)
|
||||
{
|
||||
if (!Flags.HasVehFlag(VehicleDataFlags.IsOnFire)) Function.Call(Hash.STOP_ENTITY_FIRE, MainVehicle);
|
||||
if (!Flags.HasVehFlag(VehicleDataFlags.IsOnFire)) Call(STOP_ENTITY_FIRE, MainVehicle);
|
||||
}
|
||||
else if (Flags.HasVehFlag(VehicleDataFlags.IsOnFire))
|
||||
{
|
||||
Function.Call(Hash.START_ENTITY_FIRE, MainVehicle);
|
||||
Call(START_ENTITY_FIRE, MainVehicle);
|
||||
}
|
||||
|
||||
if (EngineRunning != MainVehicle.IsEngineRunning) MainVehicle.IsEngineRunning = EngineRunning;
|
||||
@ -100,13 +100,13 @@ namespace RageCoop.Client
|
||||
if (!_lastTransformed)
|
||||
{
|
||||
_lastTransformed = true;
|
||||
Function.Call(Hash.TRANSFORM_TO_SUBMARINE, MainVehicle.Handle, false);
|
||||
Call(TRANSFORM_TO_SUBMARINE, MainVehicle.Handle, false);
|
||||
}
|
||||
}
|
||||
else if (_lastTransformed)
|
||||
{
|
||||
_lastTransformed = false;
|
||||
Function.Call(Hash.TRANSFORM_TO_CAR, MainVehicle.Handle, false);
|
||||
Call(TRANSFORM_TO_CAR, MainVehicle.Handle, false);
|
||||
}
|
||||
}
|
||||
else if (IsDeluxo)
|
||||
@ -115,7 +115,7 @@ namespace RageCoop.Client
|
||||
if (IsDeluxoHovering) MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio);
|
||||
}
|
||||
|
||||
Function.Call(Hash.SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn);
|
||||
Call(SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn);
|
||||
}
|
||||
|
||||
MainVehicle.LockStatus = LockStatus;
|
||||
@ -125,7 +125,7 @@ namespace RageCoop.Client
|
||||
if (Flags.HasVehFlag(VehicleDataFlags.Repaired)) MainVehicle.Repair();
|
||||
if (Colors != null && Colors != _lastVehicleColors)
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_COLOURS, MainVehicle, Colors[0], Colors[1]);
|
||||
Call(SET_VEHICLE_COLOURS, MainVehicle, Colors[0], Colors[1]);
|
||||
|
||||
_lastVehicleColors = Colors;
|
||||
}
|
||||
@ -133,26 +133,26 @@ namespace RageCoop.Client
|
||||
MainVehicle.EngineHealth = EngineHealth;
|
||||
if (Mods != null && !Mods.Compare(_lastVehicleMods))
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0);
|
||||
Call(SET_VEHICLE_MOD_KIT, MainVehicle, 0);
|
||||
|
||||
foreach (var mod in Mods) MainVehicle.Mods[(VehicleModType)mod.Key].Index = mod.Value;
|
||||
|
||||
_lastVehicleMods = Mods;
|
||||
}
|
||||
|
||||
if (Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle) != LicensePlate)
|
||||
Function.Call(Hash.SET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle, LicensePlate);
|
||||
if (Call<string>(GET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle) != LicensePlate)
|
||||
Call(SET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle, LicensePlate);
|
||||
|
||||
if (_lastLivery != Livery)
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, Livery);
|
||||
Call(SET_VEHICLE_LIVERY, MainVehicle, Livery);
|
||||
_lastLivery = Livery;
|
||||
}
|
||||
|
||||
MainVehicle.SetDamageModel(DamageModel);
|
||||
}
|
||||
|
||||
LastUpdated = Main.Ticked;
|
||||
LastUpdated = Ticked;
|
||||
}
|
||||
|
||||
private void DisplayVehicle(bool updated)
|
||||
@ -174,18 +174,12 @@ namespace RageCoop.Client
|
||||
if (distSquared > 0.03 * 0.03)
|
||||
{
|
||||
if (IsTrain || distSquared > 20 * 20) MainVehicle.Velocity = Velocity + cali;
|
||||
else MainVehicle.ApplyForce(cali);
|
||||
else MainVehicle.ApplyWorldForceCenterOfMass(cali, ForceType.InternalImpulse, true);
|
||||
}
|
||||
|
||||
Quaternion predictedQuat = updated ? Quaternion :
|
||||
Quaternion.Lerp(LastQuaternion, Quaternion, 1 + LastSyncedStopWatch.ElapsedMilliseconds / (float)LastSyncInterval);
|
||||
var curQuat = MainVehicle.Quaternion;
|
||||
MainVehicle.ApplyForce((predictedQuat * TopExtent - curQuat * TopExtent) * RotCalMult, TopExtent);
|
||||
MainVehicle.ApplyForce((predictedQuat * FrontExtent - curQuat * FrontExtent) * RotCalMult, FrontExtent);
|
||||
// MainVehicle.ApplyForce((predictedQuat * BottomExtent - curQuat * BottomExtent) * RotCalMult, BottomExtent);
|
||||
|
||||
|
||||
|
||||
// Calibrate rotation
|
||||
var diff = MainVehicle.ReadQuaternion().Differentiate(Quaternion);
|
||||
MainVehicle.WorldRotationVelocity = diff.ToEulerAngles()*RotCalMult;
|
||||
}
|
||||
|
||||
private bool CreateVehicle()
|
||||
@ -226,7 +220,7 @@ namespace RageCoop.Client
|
||||
ID = EntityPool.RequestNewID();
|
||||
MainVehicle = v;
|
||||
MainVehicle.CanPretendOccupants = false;
|
||||
OwnerID = Main.LocalPlayerID;
|
||||
OwnerID = LocalPlayerID;
|
||||
SetUpFixedData();
|
||||
}
|
||||
|
||||
@ -242,13 +236,6 @@ namespace RageCoop.Client
|
||||
IsSubmarineCar = MainVehicle.IsSubmarineCar;
|
||||
IsDeluxo = MainVehicle.Model == 1483171323;
|
||||
IsTrain = MainVehicle.IsTrain;
|
||||
// var (rbl, ftr) = MainVehicle.Model.Dimensions;
|
||||
FrontExtent = new Vector3(0, ExtentLength, 0);
|
||||
RearExtent = -FrontExtent;
|
||||
TopExtent = new Vector3(0, ExtentLength, 5);
|
||||
BottomExtent = -TopExtent;
|
||||
RightExtent = new Vector3(5, ExtentLength, 0);
|
||||
LeftExtent = -RightExtent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -261,7 +248,7 @@ namespace RageCoop.Client
|
||||
internal SyncedVehicle(int id)
|
||||
{
|
||||
ID = id;
|
||||
LastSynced = Main.Ticked;
|
||||
LastSynced = Ticked;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -295,7 +282,7 @@ namespace RageCoop.Client
|
||||
private void StartPedalingAnim(bool fast)
|
||||
{
|
||||
MainVehicle.Driver?.Task.PlayAnimation(PedalingAnimDict(), PedalingAnimName(fast), 8.0f, -8.0f, -1,
|
||||
AnimationFlags.Loop | AnimationFlags.AllowRotation, 1.0f);
|
||||
AnimationFlags.Loop | AnimationFlags.Secondary, 1.0f);
|
||||
}
|
||||
|
||||
private void StopPedalingAnim(bool fast)
|
||||
|
@ -1,15 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Security.Cryptography;
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Client.Scripting;
|
||||
using SHVDN;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal class EntityPool
|
||||
internal static unsafe class EntityPool
|
||||
{
|
||||
public static object PedsLock = new object();
|
||||
#if BENCHMARK
|
||||
@ -47,8 +49,8 @@ namespace RageCoop.Client
|
||||
{
|
||||
foreach (var ped in PedsByID.Values.ToArray())
|
||||
{
|
||||
if ((keepPlayer && ped.ID == Main.LocalPlayerID) ||
|
||||
(keepMine && ped.OwnerID == Main.LocalPlayerID)) continue;
|
||||
if ((keepPlayer && ped.ID == LocalPlayerID) ||
|
||||
(keepMine && ped.OwnerID == LocalPlayerID)) continue;
|
||||
RemovePed(ped.ID);
|
||||
}
|
||||
|
||||
@ -57,7 +59,7 @@ namespace RageCoop.Client
|
||||
|
||||
foreach (var id in VehiclesByID.Keys.ToArray())
|
||||
{
|
||||
if (keepMine && VehiclesByID[id].OwnerID == Main.LocalPlayerID) continue;
|
||||
if (keepMine && VehiclesByID[id].OwnerID == LocalPlayerID) continue;
|
||||
RemoveVehicle(id);
|
||||
}
|
||||
|
||||
@ -65,7 +67,7 @@ namespace RageCoop.Client
|
||||
VehiclesByHandle.Clear();
|
||||
|
||||
foreach (var p in ProjectilesByID.Values.ToArray())
|
||||
if (p.Shooter.ID != Main.LocalPlayerID && p.MainProjectile != null && p.MainProjectile.Exists())
|
||||
if (p.Shooter.ID != LocalPlayerID && p.MainProjectile != null && p.MainProjectile.Exists())
|
||||
p.MainProjectile.Delete();
|
||||
ProjectilesByID.Clear();
|
||||
ProjectilesByHandle.Clear();
|
||||
@ -82,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<int> GetPedIDs()
|
||||
{
|
||||
@ -100,16 +98,16 @@ namespace RageCoop.Client
|
||||
{
|
||||
var p = Game.Player.Character;
|
||||
// var clipset=p.Gender==Gender.Male? "MOVE_M@TOUGH_GUY@" : "MOVE_F@TOUGH_GUY@";
|
||||
// Function.Call(Hash.SET_PED_MOVEMENT_CLIPSET,p,clipset,1f);
|
||||
var player = GetPedByID(Main.LocalPlayerID);
|
||||
// Call(SET_PED_MOVEMENT_CLIPSET,p,clipset,1f);
|
||||
var player = GetPedByID(LocalPlayerID);
|
||||
if (player == null)
|
||||
{
|
||||
Main.Logger.Debug($"Creating SyncEntity for player, handle:{p.Handle}");
|
||||
Log.Debug($"Creating SyncEntity for player, handle:{p.Handle}");
|
||||
var c = new SyncedPed(p);
|
||||
Main.LocalPlayerID = c.OwnerID = c.ID;
|
||||
LocalPlayerID = c.OwnerID = c.ID;
|
||||
Add(c);
|
||||
Main.Logger.Debug($"Local player ID is:{c.ID}");
|
||||
PlayerList.SetPlayer(c.ID, Main.Settings.Username);
|
||||
Log.Debug($"Local player ID is:{c.ID}");
|
||||
PlayerList.SetPlayer(c.ID, Settings.Username);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -157,7 +155,7 @@ namespace RageCoop.Client
|
||||
if (p != null)
|
||||
{
|
||||
if (PedsByHandle.ContainsKey(p.Handle)) PedsByHandle.Remove(p.Handle);
|
||||
// Main.Logger.Debug($"Removing ped {c.ID}. Reason:{reason}");
|
||||
// Log.Debug($"Removing ped {c.ID}. Reason:{reason}");
|
||||
p.AttachedBlip?.Delete();
|
||||
p.Kill();
|
||||
p.Model.MarkAsNoLongerNeeded();
|
||||
@ -177,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<int> GetVehicleIDs()
|
||||
{
|
||||
@ -214,7 +208,7 @@ namespace RageCoop.Client
|
||||
if (veh != null)
|
||||
{
|
||||
if (VehiclesByHandle.ContainsKey(veh.Handle)) VehiclesByHandle.Remove(veh.Handle);
|
||||
// Main.Logger.Debug($"Removing vehicle {v.ID}. Reason:{reason}");
|
||||
// Log.Debug($"Removing vehicle {v.ID}. Reason:{reason}");
|
||||
veh.AttachedBlip?.Delete();
|
||||
veh.Model.MarkAsNoLongerNeeded();
|
||||
veh.MarkAsNoLongerNeeded();
|
||||
@ -231,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)
|
||||
{
|
||||
@ -263,7 +255,7 @@ namespace RageCoop.Client
|
||||
if (p != null)
|
||||
{
|
||||
if (ProjectilesByHandle.ContainsKey(p.Handle)) ProjectilesByHandle.Remove(p.Handle);
|
||||
Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
|
||||
Log.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
|
||||
p.Explode();
|
||||
}
|
||||
|
||||
@ -288,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;
|
||||
@ -297,24 +299,28 @@ namespace RageCoop.Client
|
||||
public static void DoSync()
|
||||
{
|
||||
UpdateTargets();
|
||||
|
||||
#if BENCHMARK
|
||||
PerfCounter.Restart();
|
||||
Debug.TimeStamps[TimeStamp.CheckProjectiles] = PerfCounter.ElapsedTicks;
|
||||
#endif
|
||||
var allPeds = World.GetAllPeds();
|
||||
var allVehicles = World.GetAllVehicles();
|
||||
var allProjectiles = World.GetAllProjectiles();
|
||||
vehStatesPerFrame = allVehicles.Length * 2 / (int)Game.FPS + 1;
|
||||
pedStatesPerFrame = allPeds.Length * 2 / (int)Game.FPS + 1;
|
||||
|
||||
var allPeds = NativeMemory.GetPedHandles();
|
||||
var allVehicles = NativeMemory.GetVehicleHandles();
|
||||
var allProjectiles = NativeMemory.GetProjectileHandles();
|
||||
vehStatesPerFrame = allVehicles.Length * 2 / (int)FPS + 1;
|
||||
pedStatesPerFrame = allPeds.Length * 2 / (int)FPS + 1;
|
||||
|
||||
#if BENCHMARK
|
||||
Debug.TimeStamps[TimeStamp.GetAllEntities] = PerfCounter.ElapsedTicks;
|
||||
#endif
|
||||
|
||||
|
||||
lock (ProjectilesLock)
|
||||
{
|
||||
foreach (var p in allProjectiles)
|
||||
if (!ProjectilesByHandle.ContainsKey(p.Handle))
|
||||
Add(new SyncedProjectile(p));
|
||||
if (!ProjectilesByHandle.ContainsKey(p))
|
||||
Add(new SyncedProjectile(Projectile.FromHandle(p)));
|
||||
|
||||
foreach (var p in ProjectilesByID.Values.ToArray())
|
||||
// Outgoing sync
|
||||
@ -338,6 +344,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
i = -1;
|
||||
|
||||
lock (PedsLock)
|
||||
@ -346,18 +353,19 @@ namespace RageCoop.Client
|
||||
|
||||
foreach (var p in allPeds)
|
||||
{
|
||||
var c = GetPedByHandle(p.Handle);
|
||||
if (c == null && p != Game.Player.Character)
|
||||
var c = GetPedByHandle(p);
|
||||
if (c == null && p != Game.Player.Character.Handle)
|
||||
{
|
||||
if (allPeds.Length > Main.Settings.WorldPedSoftLimit &&
|
||||
p.PopulationType == EntityPopulationType.RandomAmbient && !p.IsInVehicle())
|
||||
var type = Util.GetPopulationType(p);
|
||||
if (allPeds.Length > Settings.WorldPedSoftLimit &&
|
||||
type == EntityPopulationType.RandomAmbient && !Call<bool>(IS_PED_IN_ANY_VEHICLE, p, 0))
|
||||
{
|
||||
p.Delete();
|
||||
Util.DeleteEntity(p);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Main.Logger.Trace($"Creating SyncEntity for ped, handle:{p.Handle}");
|
||||
c = new SyncedPed(p);
|
||||
// Log.Trace($"Creating SyncEntity for ped, handle:{p.Handle}");
|
||||
c = new SyncedPed((Ped)Entity.FromHandle(p));
|
||||
|
||||
Add(c);
|
||||
}
|
||||
@ -409,32 +417,35 @@ namespace RageCoop.Client
|
||||
#endif
|
||||
}
|
||||
|
||||
var check = Main.Ticked % 100 == 0;
|
||||
var check = Ticked % 100 == 0;
|
||||
i = -1;
|
||||
lock (VehiclesLock)
|
||||
{
|
||||
foreach (var veh in allVehicles)
|
||||
if (!VehiclesByHandle.ContainsKey(veh.Handle))
|
||||
if (!VehiclesByHandle.ContainsKey(veh))
|
||||
{
|
||||
if (allVehicles.Length > Main.Settings.WorldVehicleSoftLimit)
|
||||
var cveh = (Vehicle)Entity.FromHandle(veh);
|
||||
if (cveh == null)
|
||||
continue;
|
||||
if (allVehicles.Length > Settings.WorldVehicleSoftLimit)
|
||||
{
|
||||
var type = veh.PopulationType;
|
||||
var type = cveh.PopulationType;
|
||||
if (type == EntityPopulationType.RandomAmbient || type == EntityPopulationType.RandomParked)
|
||||
{
|
||||
foreach (var p in veh.Occupants)
|
||||
foreach (var p in cveh.Occupants)
|
||||
{
|
||||
p.Delete();
|
||||
var c = GetPedByHandle(p.Handle);
|
||||
if (c != null) RemovePed(c.ID, "ThrottleTraffic");
|
||||
}
|
||||
|
||||
veh.Delete();
|
||||
cveh.Delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Main.Logger.Debug($"Creating SyncEntity for vehicle, handle:{veh.Handle}");
|
||||
// Log.Debug($"Creating SyncEntity for vehicle, handle:{veh.Handle}");
|
||||
|
||||
Add(new SyncedVehicle(veh));
|
||||
Add(new SyncedVehicle(cveh));
|
||||
}
|
||||
#if BENCHMARK
|
||||
Debug.TimeStamps[TimeStamp.AddVehicles] = PerfCounter.ElapsedTicks;
|
||||
@ -480,7 +491,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
Networking.Targets = new List<NetConnection>(PlayerList.Players.Count) { Networking.ServerConnection };
|
||||
foreach (var p in PlayerList.Players.Values.ToArray())
|
||||
if (p.HasDirectConnection && p.Position.DistanceTo(Main.PlayerPosition) < 500)
|
||||
if (p.HasDirectConnection && p.Position.DistanceTo(PlayerPosition) < 500)
|
||||
Networking.Targets.Add(p.Connection);
|
||||
}
|
||||
|
||||
@ -514,8 +525,8 @@ namespace RageCoop.Client
|
||||
|
||||
private static void SetBudget(int b)
|
||||
{
|
||||
Function.Call(Hash.SET_PED_POPULATION_BUDGET, b); // 0 - 3
|
||||
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, b); // 0 - 3
|
||||
Call(SET_PED_POPULATION_BUDGET, b); // 0 - 3
|
||||
Call(SET_VEHICLE_POPULATION_BUDGET, b); // 0 - 3
|
||||
}
|
||||
|
||||
public static string DumpDebug()
|
||||
|
@ -74,7 +74,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
return;
|
||||
// p = Game.Player.Character;
|
||||
// Main.Logger.Warning("Failed to find owner for bullet");
|
||||
// Log.Warning("Failed to find owner for bullet");
|
||||
}
|
||||
|
||||
var damage = (int)p.GetWeaponDamage(weaponHash);
|
||||
|
@ -5,6 +5,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
internal static class Voice
|
||||
{
|
||||
private static bool _stopping = false;
|
||||
private static WaveInEvent _waveIn;
|
||||
|
||||
private static readonly BufferedWaveProvider _waveProvider =
|
||||
@ -30,7 +31,8 @@ namespace RageCoop.Client
|
||||
|
||||
if (_thread != null && _thread.IsAlive)
|
||||
{
|
||||
_thread.Abort();
|
||||
_stopping = true;
|
||||
_thread.Join();
|
||||
_thread = null;
|
||||
}
|
||||
}
|
||||
@ -51,9 +53,9 @@ namespace RageCoop.Client
|
||||
return;
|
||||
|
||||
// I tried without thread but the game will lag without
|
||||
_thread = new Thread(() =>
|
||||
_thread = ThreadManager.CreateThread(() =>
|
||||
{
|
||||
while (true)
|
||||
while (!_stopping && !IsUnloading)
|
||||
using (var wo = new WaveOutEvent())
|
||||
{
|
||||
wo.Init(_waveProvider);
|
||||
@ -61,8 +63,7 @@ namespace RageCoop.Client
|
||||
|
||||
while (wo.PlaybackState == PlaybackState.Playing) Thread.Sleep(100);
|
||||
}
|
||||
});
|
||||
_thread.Start();
|
||||
},"Voice");
|
||||
}
|
||||
|
||||
public static void StartRecording()
|
||||
|
84
Client/Scripts/ThreadManager.cs
Normal file
84
Client/Scripts/ThreadManager.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using RageCoop.Client.Scripting;
|
||||
using RageCoop.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Needed to properly stop all thread when the module unloads
|
||||
/// </summary>
|
||||
internal static class ThreadManager
|
||||
{
|
||||
private static readonly List<Thread> _threads = new();
|
||||
private static readonly Thread _watcher = new(RemoveStopped);
|
||||
static ThreadManager()
|
||||
{
|
||||
_watcher.Start();
|
||||
}
|
||||
private static void RemoveStopped()
|
||||
{
|
||||
while (!IsUnloading)
|
||||
{
|
||||
lock (_threads)
|
||||
{
|
||||
_threads.RemoveAll(t => !t.IsAlive);
|
||||
}
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
public static Thread CreateThread(Action callback, string name = "CoopThread", bool startNow = true)
|
||||
{
|
||||
lock (_threads)
|
||||
{
|
||||
var created = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
callback();
|
||||
}
|
||||
catch (ThreadInterruptedException) { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Unhandled exception caught in thread {Environment.CurrentManagedThreadId}", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.Debug($"Thread stopped: " + Environment.CurrentManagedThreadId);
|
||||
}
|
||||
})
|
||||
{
|
||||
Name = name
|
||||
};
|
||||
Log.Debug($"Thread created: {name}, id: {created.ManagedThreadId}");
|
||||
_threads.Add(created);
|
||||
if (startNow) created.Start();
|
||||
return created;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnUnload()
|
||||
{
|
||||
Log.Debug("Stopping background threads");
|
||||
lock (_threads)
|
||||
{
|
||||
foreach (var thread in _threads)
|
||||
{
|
||||
if (thread.IsAlive)
|
||||
{
|
||||
Log.Debug($"Waiting for thread {thread.ManagedThreadId} to stop");
|
||||
// thread.Interrupt(); PlatformNotSupportedException ?
|
||||
thread.Join();
|
||||
}
|
||||
}
|
||||
_threads.Clear();
|
||||
}
|
||||
Log.Debug("Stopping thread watcher");
|
||||
_watcher.Join();
|
||||
}
|
||||
}
|
||||
}
|
9
Client/Scripts/Trimming.xml
Normal file
9
Client/Scripts/Trimming.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<linker>
|
||||
<assembly fullname="RageCoop.Client" preserve="all" />
|
||||
<assembly fullname="RageCoop.Client.Scripting" preserve="all" />
|
||||
<assembly fullname="RageCoop.Core" preserve="all" />
|
||||
<assembly fullname="System.Collections">
|
||||
<type fullname="System.Collections.Generic.List`1" preserve="all" />
|
||||
<type fullname="System.Collections.Generic.Dictionary`2" preserve="all" />
|
||||
</assembly>
|
||||
</linker>
|
@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using RageCoop.Core;
|
||||
using SHVDN;
|
||||
using NativeMemory = SHVDN.NativeMemory;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
|
@ -93,7 +93,7 @@ namespace RageCoop.Client
|
||||
var num1 = new OutputArgument();
|
||||
var num2 = new OutputArgument();
|
||||
|
||||
if (!Function.Call<bool>(Hash.GET_SCREEN_COORD_FROM_WORLD_COORD, worldCoords.X, worldCoords.Y,
|
||||
if (!Call<bool>(GET_SCREEN_COORD_FROM_WORLD_COORD, worldCoords.X, worldCoords.Y,
|
||||
worldCoords.Z, num1, num2))
|
||||
{
|
||||
screenCoords = new Vector2();
|
||||
@ -106,13 +106,13 @@ namespace RageCoop.Client
|
||||
|
||||
public static void StayInCover(this Ped p)
|
||||
{
|
||||
Function.Call(Hash.TASK_STAY_IN_COVER, p);
|
||||
Call(TASK_STAY_IN_COVER, p);
|
||||
}
|
||||
|
||||
public static bool IsTurretSeat(this Vehicle veh, int seat)
|
||||
{
|
||||
if (Function.Call<bool>(Hash.IS_TURRET_SEAT, veh, seat)) return true;
|
||||
if (!Function.Call<bool>(Hash.DOES_VEHICLE_HAVE_WEAPONS, veh.Handle)) return false;
|
||||
if (Call<bool>(IS_TURRET_SEAT, veh, seat)) return true;
|
||||
if (!Call<bool>(DOES_VEHICLE_HAVE_WEAPONS, veh.Handle)) return false;
|
||||
|
||||
switch (seat)
|
||||
{
|
||||
@ -177,9 +177,9 @@ namespace RageCoop.Client
|
||||
var result = new byte[36];
|
||||
for (byte i = 0; i < 12; i++)
|
||||
{
|
||||
result[i] = (byte)Function.Call<short>(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i);
|
||||
result[i + 12] = (byte)Function.Call<short>(Hash.GET_PED_TEXTURE_VARIATION, ped.Handle, i);
|
||||
result[i + 24] = (byte)Function.Call<short>(Hash.GET_PED_PALETTE_VARIATION, ped.Handle, i);
|
||||
result[i] = (byte)Call<short>(GET_PED_DRAWABLE_VARIATION, ped.Handle, i);
|
||||
result[i + 12] = (byte)Call<short>(GET_PED_TEXTURE_VARIATION, ped.Handle, i);
|
||||
result[i + 24] = (byte)Call<short>(GET_PED_PALETTE_VARIATION, ped.Handle, i);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -214,13 +214,13 @@ namespace RageCoop.Client
|
||||
{
|
||||
flags |= PedDataFlags.IsInCover;
|
||||
if (ped.IsInCoverFacingLeft) flags |= PedDataFlags.IsInCover;
|
||||
if (!Function.Call<bool>(Hash.IS_PED_IN_HIGH_COVER, ped)) flags |= PedDataFlags.IsInLowCover;
|
||||
if (!Call<bool>(IS_PED_IN_HIGH_COVER, ped)) flags |= PedDataFlags.IsInLowCover;
|
||||
if (ped.IsTaskActive(TaskType.CTaskAimGunBlindFire)) flags |= PedDataFlags.IsBlindFiring;
|
||||
}
|
||||
|
||||
if (ped.IsInvincible) flags |= PedDataFlags.IsInvincible;
|
||||
|
||||
if (Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, ped)) flags |= PedDataFlags.IsInStealthMode;
|
||||
if (Call<bool>(GET_PED_STEALTH_MOVEMENT, ped)) flags |= PedDataFlags.IsInStealthMode;
|
||||
|
||||
|
||||
return flags;
|
||||
@ -309,38 +309,35 @@ namespace RageCoop.Client
|
||||
case WeaponHash.MG:
|
||||
return new string[2] { "weapons@machinegun@mg_str", "reload_aim" };
|
||||
default:
|
||||
Main.Logger.Warning(
|
||||
Log.Warning(
|
||||
$"~r~Reloading failed! Weapon ~g~[{ped.Weapons.Current.Hash}]~r~ could not be found!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<string, int> _vehicleDoors=new()
|
||||
{
|
||||
{ "door_dside_f", -1 },
|
||||
{ "door_pside_f", 0 },
|
||||
{ "door_dside_r", 1 },
|
||||
{ "door_pside_r", 2 }
|
||||
};
|
||||
public static VehicleSeat GetNearestSeat(this Ped ped, Vehicle veh, float distanceToignoreDoors = 50f)
|
||||
{
|
||||
var num = 99f;
|
||||
var result = -2;
|
||||
var dictionary = new Dictionary<string, int>();
|
||||
dictionary.Add("door_dside_f", -1);
|
||||
dictionary.Add("door_pside_f", 0);
|
||||
dictionary.Add("door_dside_r", 1);
|
||||
dictionary.Add("door_pside_r", 2);
|
||||
foreach (var text in dictionary.Keys)
|
||||
foreach (var text in _vehicleDoors.Keys)
|
||||
{
|
||||
var flag = veh.Bones[text].Position != Vector3.Zero;
|
||||
if (flag)
|
||||
{
|
||||
var num2 = ped.Position.DistanceTo(Function.Call<Vector3>(Hash.GET_WORLD_POSITION_OF_ENTITY_BONE,
|
||||
new InputArgument[]
|
||||
{
|
||||
veh,
|
||||
veh.Bones[text].Index
|
||||
}));
|
||||
var num2 = ped.Position.DistanceTo(Call<Vector3>(GET_WORLD_POSITION_OF_ENTITY_BONE, veh, veh.Bones[text].Index));
|
||||
var flag2 = num2 < distanceToignoreDoors && num2 < num &&
|
||||
IsSeatUsableByPed(ped, veh, dictionary[text]);
|
||||
IsSeatUsableByPed(ped, veh, _vehicleDoors[text]);
|
||||
if (flag2)
|
||||
{
|
||||
num = num2;
|
||||
result = dictionary[text];
|
||||
result = _vehicleDoors[text];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -366,11 +363,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
else
|
||||
{
|
||||
var num = Function.Call<int>(Hash.GET_RELATIONSHIP_BETWEEN_PEDS, new InputArgument[]
|
||||
{
|
||||
ped,
|
||||
veh.GetPedOnSeat(seat)
|
||||
});
|
||||
var num = Call<int>(GET_RELATIONSHIP_BETWEEN_PEDS, ped, veh.GetPedOnSeat(seat));
|
||||
var flag2 = num > 2;
|
||||
if (flag2) result = true;
|
||||
}
|
||||
@ -381,7 +374,7 @@ namespace RageCoop.Client
|
||||
|
||||
public static bool IsTaskActive(this Ped p, TaskType task)
|
||||
{
|
||||
return Function.Call<bool>(Hash.GET_IS_TASK_ACTIVE, p.Handle, task);
|
||||
return Call<bool>(GET_IS_TASK_ACTIVE, p.Handle, task);
|
||||
}
|
||||
|
||||
public static Vector3 GetAimCoord(this Ped p)
|
||||
@ -408,7 +401,7 @@ namespace RageCoop.Client
|
||||
|
||||
public static Vector3 GetLookingCoord(this Ped p)
|
||||
{
|
||||
if (p == Main.P && Function.Call<int>(Hash.GET_FOLLOW_PED_CAM_VIEW_MODE) == 4)
|
||||
if (p == P && Call<int>(GET_FOLLOW_PED_CAM_VIEW_MODE) == 4)
|
||||
return RaycastEverything(default);
|
||||
EntityBone b = p.Bones[Bone.FacialForehead];
|
||||
var v = b.UpVector.Normalized;
|
||||
@ -417,7 +410,7 @@ namespace RageCoop.Client
|
||||
|
||||
public static VehicleSeat GetSeatTryingToEnter(this Ped p)
|
||||
{
|
||||
return (VehicleSeat)Function.Call<int>(Hash.GET_SEAT_PED_IS_TRYING_TO_ENTER, p);
|
||||
return (VehicleSeat)Call<int>(GET_SEAT_PED_IS_TRYING_TO_ENTER, p);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -17,7 +17,7 @@ using Font = GTA.UI.Font;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class Util
|
||||
internal static partial class Util
|
||||
{
|
||||
/// <summary>
|
||||
/// The location of the cursor on screen between 0 and 1.
|
||||
@ -67,23 +67,6 @@ namespace RageCoop.Client
|
||||
return b.ForwardVector.ToEulerRotation(b.UpVector);
|
||||
}
|
||||
|
||||
public static void StartUpCheck()
|
||||
{
|
||||
if (AppDomain.CurrentDomain.GetData("RageCoop.Client.LoaderContext") == null)
|
||||
{
|
||||
var error = "Client not loaded with loader, please re-install using the installer to fix this issue";
|
||||
try
|
||||
{
|
||||
Notification.Show("~r~" + error);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
throw new Exception(error);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawTextFromCoord(Vector3 coord, string text, float scale = 0.5f, Point offset = default)
|
||||
{
|
||||
Point toDraw = default;
|
||||
@ -106,7 +89,7 @@ namespace RageCoop.Client
|
||||
unsafe
|
||||
{
|
||||
var res = ResolutionMaintainRatio;
|
||||
if (Function.Call<bool>(Hash.GET_SCREEN_COORD_FROM_WORLD_COORD, pos.X, pos.Y, pos.Z, &x, &y))
|
||||
if (Call<bool>(GET_SCREEN_COORD_FROM_WORLD_COORD, pos.X, pos.Y, pos.Z, &x, &y))
|
||||
{
|
||||
screenPos = new Point((int)(res.Width * x), (int)(y * 1080));
|
||||
return true;
|
||||
@ -116,39 +99,39 @@ namespace RageCoop.Client
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Settings ReadSettings(string path = null)
|
||||
public static ClientSettings ReadSettings(string path = null)
|
||||
{
|
||||
path = path ?? SettingsPath;
|
||||
|
||||
Directory.CreateDirectory(Directory.GetParent(path).FullName);
|
||||
Settings settings;
|
||||
ClientSettings settings;
|
||||
try
|
||||
{
|
||||
settings = JsonConvert.DeserializeObject<Settings>(File.ReadAllText(path));
|
||||
settings = JsonDeserialize<ClientSettings>(File.ReadAllText(path));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger?.Error(ex);
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(settings = new Settings(), Formatting.Indented));
|
||||
Log?.Error(ex);
|
||||
File.WriteAllText(path, JsonSerialize(settings = new ClientSettings()));
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public static bool SaveSettings(string path = null, Settings settings = null)
|
||||
public static bool SaveSettings(string path = null, ClientSettings settings = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
path = path ?? SettingsPath;
|
||||
settings = settings ?? Main.Settings;
|
||||
settings = settings ?? Settings;
|
||||
Directory.CreateDirectory(Directory.GetParent(path).FullName);
|
||||
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||
File.WriteAllText(path, JsonSerialize(settings));
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger?.Error(ex);
|
||||
Log?.Error(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -157,28 +140,28 @@ namespace RageCoop.Client
|
||||
public static Vehicle CreateVehicle(Model model, Vector3 position, float heading = 0f)
|
||||
{
|
||||
if (!model.IsLoaded) return null;
|
||||
return (Vehicle)Entity.FromHandle(Function.Call<int>(Hash.CREATE_VEHICLE, model.Hash, position.X,
|
||||
return (Vehicle)Entity.FromHandle(Call<int>(CREATE_VEHICLE, model.Hash, position.X,
|
||||
position.Y, position.Z, heading, false, false));
|
||||
}
|
||||
|
||||
public static Ped CreatePed(Model model, Vector3 position, float heading = 0f)
|
||||
{
|
||||
if (!model.IsLoaded) return null;
|
||||
return (Ped)Entity.FromHandle(Function.Call<int>(Hash.CREATE_PED, 26, model.Hash, position.X, position.Y,
|
||||
return (Ped)Entity.FromHandle(Call<int>(CREATE_PED, 26, model.Hash, position.X, position.Y,
|
||||
position.Z, heading, false, false));
|
||||
}
|
||||
|
||||
public static void SetOnFire(this Entity e, bool toggle)
|
||||
{
|
||||
if (toggle)
|
||||
Function.Call(Hash.START_ENTITY_FIRE, e.Handle);
|
||||
Call(START_ENTITY_FIRE, e.Handle);
|
||||
else
|
||||
Function.Call(Hash.STOP_ENTITY_FIRE, e.Handle);
|
||||
Call(STOP_ENTITY_FIRE, e.Handle);
|
||||
}
|
||||
|
||||
public static void SetFrozen(this Entity e, bool toggle)
|
||||
{
|
||||
Function.Call(Hash.FREEZE_ENTITY_POSITION, e, toggle);
|
||||
Call(FREEZE_ENTITY_POSITION, e, toggle);
|
||||
}
|
||||
|
||||
public static SyncedPed GetSyncEntity(this Ped p)
|
||||
@ -197,28 +180,31 @@ namespace RageCoop.Client
|
||||
return v;
|
||||
}
|
||||
|
||||
public static void ApplyForce(this Entity e, int boneIndex, Vector3 direction, Vector3 rotation = default,
|
||||
ForceType forceType = ForceType.MaxForceRot2)
|
||||
{
|
||||
Function.Call(Hash.APPLY_FORCE_TO_ENTITY, e.Handle, forceType, direction.X, direction.Y, direction.Z,
|
||||
rotation.X, rotation.Y, rotation.Z, boneIndex, false, true, true, false, true);
|
||||
}
|
||||
|
||||
public static byte GetPlayerRadioIndex()
|
||||
{
|
||||
return (byte)Function.Call<int>(Hash.GET_PLAYER_RADIO_STATION_INDEX);
|
||||
return (byte)Call<int>(GET_PLAYER_RADIO_STATION_INDEX);
|
||||
}
|
||||
|
||||
public static void SetPlayerRadioIndex(int index)
|
||||
{
|
||||
Function.Call(Hash.SET_RADIO_TO_STATION_INDEX, index);
|
||||
Call(SET_RADIO_TO_STATION_INDEX, index);
|
||||
}
|
||||
|
||||
public static EntityPopulationType GetPopulationType(int handle)
|
||||
=> (EntityPopulationType)Call<int>(GET_ENTITY_POPULATION_TYPE, handle);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern ulong GetTickCount64();
|
||||
public static unsafe void DeleteEntity(int handle)
|
||||
{
|
||||
Call(SET_ENTITY_AS_MISSION_ENTITY, handle, false, true);
|
||||
Call(DELETE_ENTITY, &handle);
|
||||
}
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
public static partial ulong GetTickCount64();
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
|
||||
public static partial IntPtr GetModuleHandleW([MarshalAs(UnmanagedType.LPWStr)] string lpModuleName);
|
||||
|
||||
#region -- POINTER --
|
||||
|
||||
private static int _steeringAngleOffset { get; set; }
|
||||
|
@ -27,9 +27,9 @@ namespace RageCoop.Client
|
||||
|
||||
if (veh.IsDead) flags |= VehicleDataFlags.IsDead;
|
||||
|
||||
if (Function.Call<bool>(Hash.IS_HORN_ACTIVE, veh.Handle)) flags |= VehicleDataFlags.IsHornActive;
|
||||
if (Call<bool>(IS_HORN_ACTIVE, veh.Handle)) flags |= VehicleDataFlags.IsHornActive;
|
||||
|
||||
if (v.IsSubmarineCar && Function.Call<bool>(Hash.IS_VEHICLE_IN_SUBMARINE_MODE, veh.Handle))
|
||||
if (v.IsSubmarineCar && Call<bool>(IS_VEHICLE_IN_SUBMARINE_MODE, veh.Handle))
|
||||
flags |= VehicleDataFlags.IsTransformed;
|
||||
|
||||
if (v.IsAircraft) flags |= VehicleDataFlags.IsAircraft;
|
||||
@ -150,7 +150,7 @@ namespace RageCoop.Client
|
||||
|
||||
public static void SetDeluxoHoverState(this Vehicle deluxo, bool hover)
|
||||
{
|
||||
Function.Call(Hash.SET_SPECIAL_FLIGHT_MODE_TARGET_RATIO, deluxo, hover ? 1f : 0f);
|
||||
Call(SET_SPECIAL_FLIGHT_MODE_TARGET_RATIO, deluxo, hover ? 1f : 0f);
|
||||
}
|
||||
|
||||
public static bool IsDeluxoHovering(this Vehicle deluxo)
|
||||
@ -160,7 +160,7 @@ namespace RageCoop.Client
|
||||
|
||||
public static void SetDeluxoWingRatio(this Vehicle v, float ratio)
|
||||
{
|
||||
Function.Call(Hash.SET_HOVER_MODE_WING_RATIO, v, ratio);
|
||||
Call(SET_HOVER_MODE_WING_RATIO, v, ratio);
|
||||
}
|
||||
|
||||
public static float GetDeluxoWingRatio(this Vehicle v)
|
||||
@ -170,7 +170,7 @@ namespace RageCoop.Client
|
||||
|
||||
public static float GetNozzleAngel(this Vehicle plane)
|
||||
{
|
||||
return Function.Call<float>(Hash.GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane);
|
||||
return Call<float>(GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane);
|
||||
}
|
||||
|
||||
public static bool HasNozzle(this Vehicle v)
|
||||
@ -199,7 +199,7 @@ namespace RageCoop.Client
|
||||
|
||||
public static void SetNozzleAngel(this Vehicle plane, float ratio)
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio);
|
||||
Call(SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -6,7 +6,6 @@ using GTA.Native;
|
||||
using Newtonsoft.Json;
|
||||
using RageCoop.Client.Scripting;
|
||||
using RageCoop.Core;
|
||||
using static RageCoop.Client.Shared;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
@ -26,16 +25,16 @@ namespace RageCoop.Client
|
||||
static WeaponUtil()
|
||||
{
|
||||
// Parse and load to memory
|
||||
foreach (var w in JsonConvert.DeserializeObject<VehicleWeaponInfo[]>(
|
||||
foreach (var w in JsonDeserialize<VehicleWeaponInfo[]>(
|
||||
File.ReadAllText(VehicleWeaponDataPath))) VehicleWeapons.Add(w.Hash, w);
|
||||
|
||||
Weapons = JsonConvert.DeserializeObject<Dictionary<uint, WeaponInfo>>(
|
||||
Weapons = JsonDeserialize<Dictionary<uint, WeaponInfo>>(
|
||||
File.ReadAllText(WeaponInfoDataPath));
|
||||
|
||||
if (File.Exists(WeaponFixDataPath))
|
||||
WeaponFix = JsonConvert.DeserializeObject<WeaponFix>(File.ReadAllText(WeaponFixDataPath));
|
||||
WeaponFix = JsonDeserialize<WeaponFix>(File.ReadAllText(WeaponFixDataPath));
|
||||
else
|
||||
API.Logger.Warning("Weapon fix data not found");
|
||||
Log.Warning("Weapon fix data not found");
|
||||
}
|
||||
|
||||
public static void DumpWeaponFix(string path)
|
||||
@ -47,14 +46,14 @@ namespace RageCoop.Client
|
||||
var fix = new WeaponFix();
|
||||
foreach (var w in Weapons)
|
||||
{
|
||||
Console.Info("Testing " + w.Value.Name);
|
||||
Console.PrintInfo("Testing " + w.Value.Name);
|
||||
if (w.Value.FireType != "PROJECTILE")
|
||||
{
|
||||
var asset = new WeaponAsset(w.Key);
|
||||
asset.Request(1000);
|
||||
World.ShootBullet(pos, pos + Vector3.WorldUp, P, asset, 0, 1000);
|
||||
if (!Function.Call<bool>(Hash.IS_BULLET_IN_AREA, pos.X, pos.Y, pos.Z, 10f, true) &&
|
||||
!Function.Call<bool>(Hash.IS_PROJECTILE_IN_AREA, pos.X - 10, pos.Y - 10, pos.Z - 10, pos.X + 10,
|
||||
if (!Call<bool>(IS_BULLET_IN_AREA, pos.X, pos.Y, pos.Z, 10f, true) &&
|
||||
!Call<bool>(IS_PROJECTILE_IN_AREA, pos.X - 10, pos.Y - 10, pos.Z - 10, pos.X + 10,
|
||||
pos.Y + 10, pos.Z + 10, true))
|
||||
switch (w.Value.DamageType)
|
||||
{
|
||||
@ -74,7 +73,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(fix, Formatting.Indented));
|
||||
File.WriteAllText(path, JsonSerialize(fix));
|
||||
|
||||
P.IsInvincible = false;
|
||||
}
|
||||
@ -112,7 +111,7 @@ namespace RageCoop.Client
|
||||
public static float GetWeaponDamage(this Ped P, uint hash)
|
||||
{
|
||||
var comp = P.Weapons.Current.Components.GetSuppressorComponent();
|
||||
return Function.Call<float>(Hash.GET_WEAPON_DAMAGE, hash,
|
||||
return Call<float>(GET_WEAPON_DAMAGE, hash,
|
||||
comp.Active ? comp.ComponentHash : WeaponComponentHash.Invalid);
|
||||
}
|
||||
|
||||
@ -194,7 +193,7 @@ namespace RageCoop.Client
|
||||
|
||||
public static WeaponGroup GetWeaponGroup(this WeaponHash hash)
|
||||
{
|
||||
return Function.Call<WeaponGroup>(Hash.GET_WEAPONTYPE_GROUP, hash);
|
||||
return Call<WeaponGroup>(GET_WEAPONTYPE_GROUP, hash);
|
||||
}
|
||||
}
|
||||
}
|
@ -115,7 +115,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/*
|
||||
public static Image CaptureWindow(IntPtr handle)
|
||||
{
|
||||
var windowDC = User32.GetWindowDC(handle);
|
||||
@ -134,7 +134,7 @@ namespace RageCoop.Client
|
||||
User32.ReleaseDC(handle, windowDC);
|
||||
return image;
|
||||
}
|
||||
|
||||
*/
|
||||
public static void ClearLastError()
|
||||
{
|
||||
SetLastErrorEx(0, 0);
|
||||
|
@ -12,8 +12,7 @@ namespace RageCoop.Client
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
[ScriptAttributes(Author = "RageCoop", NoDefaultInstance = false,
|
||||
SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V")]
|
||||
[ScriptAttributes(Author = "RageCoop", SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V")]
|
||||
internal class WorldThread : Script
|
||||
{
|
||||
public static Script Instance;
|
||||
@ -26,14 +25,22 @@ namespace RageCoop.Client
|
||||
/// </summary>
|
||||
public WorldThread()
|
||||
{
|
||||
Util.StartUpCheck();
|
||||
Instance = this;
|
||||
Tick += OnTick;
|
||||
Aborted += (sender, e) => { ChangeTraffic(true); };
|
||||
Aborted += (e) => { DoQueuedActions(); ChangeTraffic(true); };
|
||||
}
|
||||
|
||||
private void OnTick(object sender, EventArgs e)
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
while(Game.IsLoading)
|
||||
Yield();
|
||||
|
||||
Notification.Show(NotificationIcon.AllPlayersConf, "RAGECOOP", "Welcome!",
|
||||
$"Press ~g~{Settings.MenuKey}~s~ to open the menu.");
|
||||
}
|
||||
protected override void OnTick()
|
||||
{
|
||||
base.OnTick();
|
||||
|
||||
if (Game.IsLoading) return;
|
||||
|
||||
try
|
||||
@ -43,23 +50,23 @@ namespace RageCoop.Client
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error(ex);
|
||||
Log.Error(ex);
|
||||
}
|
||||
|
||||
if (!Networking.IsOnServer) return;
|
||||
|
||||
Game.DisableControlThisFrame(Control.FrontendPause);
|
||||
|
||||
if (Main.Settings.DisableAlternatePause) Game.DisableControlThisFrame(Control.FrontendPauseAlternate);
|
||||
if (Settings.DisableAlternatePause) Game.DisableControlThisFrame(Control.FrontendPauseAlternate);
|
||||
// Sets a value that determines how aggressive the ocean waves will be.
|
||||
// Values of 2.0 or more make for very aggressive waves like you see during a thunderstorm.
|
||||
Function.Call(Hash.SET_DEEP_OCEAN_SCALER, 0.0f); // Works only ~200 meters around the player
|
||||
Call(SET_DEEP_OCEAN_SCALER, 0.0f); // Works only ~200 meters around the player
|
||||
|
||||
if (Main.Settings.ShowEntityOwnerName)
|
||||
if (Settings.ShowEntityOwnerName)
|
||||
unsafe
|
||||
{
|
||||
int handle;
|
||||
if (Function.Call<bool>(Hash.GET_ENTITY_PLAYER_IS_FREE_AIMING_AT, 0, &handle))
|
||||
if (Call<bool>(GET_ENTITY_PLAYER_IS_FREE_AIMING_AT, 0, &handle))
|
||||
{
|
||||
var entity = Entity.FromHandle(handle);
|
||||
if (entity != null)
|
||||
@ -76,13 +83,13 @@ namespace RageCoop.Client
|
||||
|
||||
if (!_trafficEnabled)
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
|
||||
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
|
||||
Function.Call(Hash.SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
|
||||
Function.Call(Hash.SET_RANDOM_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
|
||||
Function.Call(Hash.SET_PARKED_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
|
||||
Function.Call(Hash.SUPPRESS_SHOCKING_EVENTS_NEXT_FRAME);
|
||||
Function.Call(Hash.SUPPRESS_AGITATION_EVENTS_NEXT_FRAME);
|
||||
Call(SET_VEHICLE_POPULATION_BUDGET, 0);
|
||||
Call(SET_PED_POPULATION_BUDGET, 0);
|
||||
Call(SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
|
||||
Call(SET_RANDOM_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
|
||||
Call(SET_PARKED_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
|
||||
Call(SUPPRESS_SHOCKING_EVENTS_NEXT_FRAME);
|
||||
Call(SUPPRESS_AGITATION_EVENTS_NEXT_FRAME);
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,35 +103,35 @@ namespace RageCoop.Client
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
Function.Call(Hash.REMOVE_SCENARIO_BLOCKING_AREAS);
|
||||
Function.Call(Hash.SET_CREATE_RANDOM_COPS, true);
|
||||
Function.Call(Hash.SET_RANDOM_TRAINS, true);
|
||||
Function.Call(Hash.SET_RANDOM_BOATS, true);
|
||||
Function.Call(Hash.SET_GARBAGE_TRUCKS, true);
|
||||
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 3); // 0 - 3
|
||||
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 3); // 0 - 3
|
||||
Function.Call(Hash.SET_ALL_VEHICLE_GENERATORS_ACTIVE);
|
||||
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, true);
|
||||
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, -1);
|
||||
Function.Call(Hash.SET_DISTANT_CARS_ENABLED, true);
|
||||
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, false);
|
||||
Call(REMOVE_SCENARIO_BLOCKING_AREAS);
|
||||
Call(SET_CREATE_RANDOM_COPS, true);
|
||||
Call(SET_RANDOM_TRAINS, true);
|
||||
Call(SET_RANDOM_BOATS, true);
|
||||
Call(SET_GARBAGE_TRUCKS, true);
|
||||
Call(SET_PED_POPULATION_BUDGET, 3); // 0 - 3
|
||||
Call(SET_VEHICLE_POPULATION_BUDGET, 3); // 0 - 3
|
||||
Call(SET_ALL_VEHICLE_GENERATORS_ACTIVE);
|
||||
Call(SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, true);
|
||||
Call(SET_NUMBER_OF_PARKED_VEHICLES, -1);
|
||||
Call(SET_DISTANT_CARS_ENABLED, true);
|
||||
Call(DISABLE_VEHICLE_DISTANTLIGHTS, false);
|
||||
}
|
||||
else if (Networking.IsOnServer)
|
||||
{
|
||||
Function.Call(Hash.ADD_SCENARIO_BLOCKING_AREA, -10000.0f, -10000.0f, -1000.0f, 10000.0f, 10000.0f,
|
||||
Call(ADD_SCENARIO_BLOCKING_AREA, -10000.0f, -10000.0f, -1000.0f, 10000.0f, 10000.0f,
|
||||
1000.0f, 0, 1, 1, 1);
|
||||
Function.Call(Hash.SET_CREATE_RANDOM_COPS, false);
|
||||
Function.Call(Hash.SET_RANDOM_TRAINS, false);
|
||||
Function.Call(Hash.SET_RANDOM_BOATS, false);
|
||||
Function.Call(Hash.SET_GARBAGE_TRUCKS, false);
|
||||
Function.Call(Hash.DELETE_ALL_TRAINS);
|
||||
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
|
||||
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
|
||||
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, false);
|
||||
Function.Call(Hash.SET_FAR_DRAW_VEHICLES, false);
|
||||
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, 0);
|
||||
Function.Call(Hash.SET_DISTANT_CARS_ENABLED, false);
|
||||
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, true);
|
||||
Call(SET_CREATE_RANDOM_COPS, false);
|
||||
Call(SET_RANDOM_TRAINS, false);
|
||||
Call(SET_RANDOM_BOATS, false);
|
||||
Call(SET_GARBAGE_TRUCKS, false);
|
||||
Call(DELETE_ALL_TRAINS);
|
||||
Call(SET_PED_POPULATION_BUDGET, 0);
|
||||
Call(SET_VEHICLE_POPULATION_BUDGET, 0);
|
||||
Call(SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, false);
|
||||
Call(SET_FAR_DRAW_VEHICLES, false);
|
||||
Call(SET_NUMBER_OF_PARKED_VEHICLES, 0);
|
||||
Call(SET_DISTANT_CARS_ENABLED, false);
|
||||
Call(DISABLE_VEHICLE_DISTANTLIGHTS, true);
|
||||
foreach (var ped in World.GetAllPeds())
|
||||
{
|
||||
if (ped == Game.Player.Character) continue;
|
||||
@ -132,7 +139,7 @@ namespace RageCoop.Client
|
||||
if (c == null || (c.IsLocal && ped.Handle != Game.Player.Character.Handle &&
|
||||
ped.PopulationType != EntityPopulationType.Mission))
|
||||
{
|
||||
Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
|
||||
Log.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
|
||||
ped.CurrentVehicle?.Delete();
|
||||
ped.Kill();
|
||||
ped.Delete();
|
||||
@ -147,7 +154,7 @@ namespace RageCoop.Client
|
||||
// Don't delete player's vehicle
|
||||
continue;
|
||||
if (v == null || (v.IsLocal && veh.PopulationType != EntityPopulationType.Mission))
|
||||
// Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
|
||||
// Log.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
|
||||
|
||||
veh.Delete();
|
||||
}
|
||||
@ -174,7 +181,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error(ex);
|
||||
Log.Error(ex);
|
||||
QueuedActions.Remove(action);
|
||||
}
|
||||
}
|
||||
|
@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<packages>
|
||||
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.ObjectPool" version="6.0.8" targetFramework="net48" />
|
||||
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Win32.Registry" version="4.7.0" targetFramework="net481" />
|
||||
<package id="NativeInvoker" version="1.0.0.1" targetFramework="net48" />
|
||||
<package id="NAudio" version="2.1.0" targetFramework="net48" />
|
||||
<package id="NAudio.Asio" version="2.1.0" targetFramework="net48" />
|
||||
<package id="NAudio.Core" version="2.1.0" targetFramework="net48" />
|
||||
<package id="NAudio.Midi" version="2.1.0" targetFramework="net48" />
|
||||
<package id="NAudio.Wasapi" version="2.1.0" targetFramework="net48" />
|
||||
<package id="NAudio.WinForms" version="2.1.0" targetFramework="net48" />
|
||||
<package id="NAudio.WinMM" version="2.1.0" targetFramework="net48" />
|
||||
<package id="NETStandard.Library" version="1.6.1" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />
|
||||
<package id="ScriptHookVDotNet3" version="3.5.1" targetFramework="net48" />
|
||||
<package id="SharpZipLib" version="1.4.0" targetFramework="net48" />
|
||||
<package id="System.AppContext" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
|
||||
<package id="System.Collections" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Console" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Data.DataSetExtensions" version="4.5.0" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Drawing.Common" version="4.5.0" targetFramework="net48" />
|
||||
<package id="System.Globalization" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO.Compression" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Linq" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
|
||||
<package id="System.Net.Http" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
|
||||
<package id="System.ObjectModel" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Reflection" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.AccessControl" version="4.7.0" targetFramework="net481" />
|
||||
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.Principal.Windows" version="4.7.0" targetFramework="net481" />
|
||||
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Threading" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net48" />
|
||||
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net48" />
|
||||
</packages>
|
@ -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()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
304
Core/Buffer.cs
Normal file
304
Core/Buffer.cs
Normal file
@ -0,0 +1,304 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using GTA.Math;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public unsafe abstract class Buffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Size of this buffer in memory
|
||||
/// </summary>
|
||||
public int Size { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current read/write index
|
||||
/// </summary>
|
||||
public int Position { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to the start of this buffer
|
||||
/// </summary>
|
||||
public byte* Address { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ensure memory safety and advance position by specified number of bytes
|
||||
/// </summary>
|
||||
/// <param name="cbSize"></param>
|
||||
/// <returns>Pointer to the current position in the buffer</returns>
|
||||
protected abstract byte* Alloc(int cbSize);
|
||||
|
||||
protected T* Alloc<T>(int count = 1) where T : unmanaged
|
||||
=> (T*)Alloc(count * sizeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Reset position to the start of this buffer
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
Position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void Resize(int size)
|
||||
{
|
||||
if (size < Size)
|
||||
{
|
||||
Size = size;
|
||||
return;
|
||||
}
|
||||
|
||||
var newAddr = (byte*)Marshal.AllocHGlobal(size);
|
||||
if (Address != null)
|
||||
{
|
||||
System.Buffer.MemoryCopy(Address, newAddr, size, Size);
|
||||
Marshal.FreeHGlobal((IntPtr)Address);
|
||||
}
|
||||
Size = size;
|
||||
Address = newAddr;
|
||||
}
|
||||
|
||||
protected override byte* Alloc(int cbSize)
|
||||
{
|
||||
var index = Position;
|
||||
Position += cbSize;
|
||||
|
||||
// Resize the buffer by at least 50% if there's no sufficient space
|
||||
if (Position > Size)
|
||||
Resize(Math.Max(Position + 1, (int)(Size * 1.5f)));
|
||||
|
||||
return Address + index;
|
||||
}
|
||||
|
||||
public void Write<T>(ref T value) where T : unmanaged
|
||||
{
|
||||
var addr = Alloc<T>();
|
||||
*addr = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For passing struct smaller than word size (4/8 bytes on 32/64 bit system)
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="value"></param>
|
||||
public void WriteVal<T>(T value) where T : unmanaged
|
||||
{
|
||||
var addr = Alloc<T>();
|
||||
*addr = value;
|
||||
}
|
||||
|
||||
|
||||
public void Write(ReadOnlySpan<char> str)
|
||||
{
|
||||
// Prefixed by its size in bytes
|
||||
var cbBody = Encoding.UTF8.GetByteCount(str);
|
||||
WriteVal(cbBody);
|
||||
|
||||
// Allocate and write string body
|
||||
var pBody = Alloc(cbBody);
|
||||
Encoding.UTF8.GetBytes(str, new(pBody, cbBody));
|
||||
}
|
||||
|
||||
// Struct in GTA.Math have pack/padding for memory alignment, we don't want it to waste the bandwidth
|
||||
|
||||
public void Write(ref Vector2 vec2)
|
||||
{
|
||||
var faddr = Alloc<float>(2);
|
||||
faddr[0] = vec2.X;
|
||||
faddr[1] = vec2.Y;
|
||||
}
|
||||
|
||||
public void Write(ref Vector3 vec3)
|
||||
{
|
||||
var faddr = Alloc<float>(3);
|
||||
faddr[0] = vec3.X;
|
||||
faddr[1] = vec3.Y;
|
||||
faddr[2] = vec3.Z;
|
||||
}
|
||||
|
||||
public void Write(ref Quaternion quat)
|
||||
{
|
||||
var faddr = Alloc<float>(4);
|
||||
faddr[0] = quat.X;
|
||||
faddr[1] = quat.Y;
|
||||
faddr[2] = quat.Z;
|
||||
faddr[3] = quat.W;
|
||||
}
|
||||
|
||||
public void Write<T>(ReadOnlySpan<T> source) where T : unmanaged
|
||||
{
|
||||
var len = source.Length;
|
||||
fixed (T* pSource = source)
|
||||
{
|
||||
System.Buffer.MemoryCopy(pSource, Alloc(sizeof(T) * len), len, len);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write<T>(Span<T> source) where T : unmanaged => Write((ReadOnlySpan<T>)source);
|
||||
|
||||
/// <summary>
|
||||
/// Write an array, prefix the data with its length so it can latter be read using <see cref="BufferReader.ReadArray{T}"/>
|
||||
/// </summary>
|
||||
public void WriteArray<T>(T[] values) where T : unmanaged
|
||||
{
|
||||
var len = values.Length;
|
||||
WriteVal(len);
|
||||
fixed (T* pFrom = values)
|
||||
{
|
||||
System.Buffer.MemoryCopy(pFrom, Alloc(sizeof(T) * len), len, len);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocate a byte array on managed heap and copy the data of specified size to it
|
||||
/// </summary>
|
||||
/// <param name="cbSize"></param>
|
||||
/// <returns>The newly created managed byte array</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public byte[] ToByteArray(int cbSize)
|
||||
{
|
||||
if (cbSize > Size)
|
||||
throw new ArgumentOutOfRangeException(nameof(cbSize));
|
||||
|
||||
var result = new byte[cbSize];
|
||||
fixed (byte* pResult = result)
|
||||
{
|
||||
System.Buffer.MemoryCopy(Address, pResult, cbSize, cbSize);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Free the associated memory allocated on the unmanaged heap
|
||||
/// </summary>
|
||||
public void Free() => Marshal.FreeHGlobal((IntPtr)Address);
|
||||
}
|
||||
|
||||
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>
|
||||
public BufferReader()
|
||||
{
|
||||
|
||||
}
|
||||
public BufferReader(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)
|
||||
{
|
||||
if (Address == null)
|
||||
throw new NullReferenceException("Address is null");
|
||||
|
||||
var index = Position;
|
||||
Position += cbSize;
|
||||
|
||||
if (Position > Size)
|
||||
throw new InvalidOperationException("Attempting to read beyond the existing buffer");
|
||||
|
||||
return Address + index;
|
||||
}
|
||||
|
||||
|
||||
public T ReadVal<T>() where T : unmanaged => *Alloc<T>();
|
||||
|
||||
public void Read<T>(out T result) where T : unmanaged
|
||||
=> result = *Alloc<T>();
|
||||
|
||||
public void Read(out string str)
|
||||
{
|
||||
var cbBody = ReadVal<int>();
|
||||
str = Encoding.UTF8.GetString(Alloc(cbBody), cbBody);
|
||||
}
|
||||
|
||||
public void Read(out Vector2 vec)
|
||||
{
|
||||
var faddr = Alloc<float>(2);
|
||||
vec = new()
|
||||
{
|
||||
X = faddr[0],
|
||||
Y = faddr[1],
|
||||
};
|
||||
}
|
||||
|
||||
public void Read(out Vector3 vec)
|
||||
{
|
||||
var faddr = Alloc<float>(3);
|
||||
vec = new()
|
||||
{
|
||||
X = faddr[0],
|
||||
Y = faddr[1],
|
||||
Z = faddr[2],
|
||||
};
|
||||
}
|
||||
|
||||
public void Read(out Quaternion quat)
|
||||
{
|
||||
var faddr = Alloc<float>(4);
|
||||
quat = new()
|
||||
{
|
||||
X = faddr[0],
|
||||
Y = faddr[1],
|
||||
Z = faddr[2],
|
||||
W = faddr[3],
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a span of type <typeparamref name="T"/> from current position to <paramref name="destination"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="destination"></param>
|
||||
public void Read<T>(Span<T> destination) where T : unmanaged
|
||||
{
|
||||
var len = destination.Length;
|
||||
fixed (T* pTo = destination)
|
||||
{
|
||||
System.Buffer.MemoryCopy(Alloc(len * sizeof(T)), pTo, len, len);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an array previously written using <see cref="BufferWriter.WriteArray{T}(T[])"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public T[] ReadArray<T>() where T : unmanaged
|
||||
{
|
||||
var len = ReadVal<int>();
|
||||
var from = Alloc<T>(len);
|
||||
var result = new T[len];
|
||||
fixed (T* pTo = result)
|
||||
{
|
||||
System.Buffer.MemoryCopy(from, pTo, len, len);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
@ -20,17 +21,19 @@ using RageCoop.Core.Scripting;
|
||||
|
||||
[assembly: InternalsVisibleTo("RageCoop.Server")]
|
||||
[assembly: InternalsVisibleTo("RageCoop.Client")]
|
||||
[assembly: InternalsVisibleTo("RageCoop.Client.Scripting")]
|
||||
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
|
||||
[assembly: InternalsVisibleTo("DataDumper")]
|
||||
[assembly: InternalsVisibleTo("UnitTest")]
|
||||
[assembly: InternalsVisibleTo("RageCoop.ResourceBuilder")]
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal static class CoreUtils
|
||||
{
|
||||
private static readonly Random random = new Random();
|
||||
private static readonly Random random = new();
|
||||
|
||||
private static readonly HashSet<string> ToIgnore = new HashSet<string>
|
||||
private static readonly HashSet<string> ToIgnore = new()
|
||||
{
|
||||
"RageCoop.Client",
|
||||
"RageCoop.Client.Loader",
|
||||
@ -39,7 +42,8 @@ namespace RageCoop.Core
|
||||
"RageCoop.Server",
|
||||
"ScriptHookVDotNet2",
|
||||
"ScriptHookVDotNet3",
|
||||
"ScriptHookVDotNet"
|
||||
"ScriptHookVDotNet",
|
||||
"ScriptHookVDotNetCore"
|
||||
};
|
||||
|
||||
public static string FormatToSharpStyle(string input, int offset)
|
||||
@ -90,29 +94,6 @@ namespace RageCoop.Core
|
||||
.Select(s => s[random.Next(s.Length)]).ToArray());
|
||||
}
|
||||
|
||||
public static void GetDependencies(Assembly assembly, ref HashSet<string> existing)
|
||||
{
|
||||
if (assembly.FullName.StartsWith("System")) return;
|
||||
foreach (var name in assembly.GetReferencedAssemblies())
|
||||
{
|
||||
if (name.FullName.StartsWith("System")) continue;
|
||||
try
|
||||
{
|
||||
var asm = Assembly.Load(name);
|
||||
GetDependencies(asm, ref existing);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!existing.Contains(assembly.FullName))
|
||||
{
|
||||
Console.WriteLine(assembly.FullName);
|
||||
existing.Add(assembly.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
public static Version GetLatestVersion(string branch = "dev-nightly")
|
||||
{
|
||||
var url =
|
||||
@ -127,7 +108,20 @@ namespace RageCoop.Core
|
||||
|
||||
public static bool CanBeIgnored(this string name)
|
||||
{
|
||||
return ToIgnore.Contains(Path.GetFileNameWithoutExtension(name));
|
||||
name = Path.GetFileNameWithoutExtension(name);
|
||||
return ToIgnore.Contains(name) || AssemblyLoadContext.Default.Assemblies.Any(x => x.GetName().Name == name);
|
||||
}
|
||||
|
||||
public static void ForceLoadAllAssemblies()
|
||||
{
|
||||
foreach (var a in AssemblyLoadContext.Default.Assemblies)
|
||||
LoadAllReferencedAssemblies(a.GetName());
|
||||
}
|
||||
|
||||
public static void LoadAllReferencedAssemblies(this AssemblyName assembly)
|
||||
{
|
||||
foreach (var child in Assembly.Load(assembly).GetReferencedAssemblies())
|
||||
LoadAllReferencedAssemblies(child);
|
||||
}
|
||||
|
||||
public static string ToFullPath(this string path)
|
||||
@ -311,7 +305,7 @@ namespace RageCoop.Core
|
||||
throw new Exception($"IPv4 request failed! [{(int)response.StatusCode}/{response.ReasonPhrase}]");
|
||||
|
||||
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||
return JsonConvert.DeserializeObject<IpInfo>(content);
|
||||
return JsonDeserialize<IpInfo>(content);
|
||||
}
|
||||
|
||||
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
|
||||
|
59
Core/JsonCoverters.cs
Normal file
59
Core/JsonCoverters.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
class IPAddressConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return (objectType == typeof(IPAddress));
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null) return null;
|
||||
return IPAddress.Parse((string)reader.Value);
|
||||
}
|
||||
}
|
||||
|
||||
class IPEndPointConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return (objectType == typeof(IPEndPoint));
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
IPEndPoint ep = (IPEndPoint)value;
|
||||
JObject jo = new()
|
||||
{
|
||||
{ "Address", JToken.FromObject(ep.Address, serializer) },
|
||||
{ "Port", ep.Port }
|
||||
};
|
||||
jo.WriteTo(writer);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null) return null;
|
||||
JObject jo = JObject.Load(reader);
|
||||
IPAddress address = jo["Address"].ToObject<IPAddress>(serializer);
|
||||
int port = (int)jo["Port"];
|
||||
return new IPEndPoint(address, port);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -46,11 +46,11 @@ namespace RageCoop.Core
|
||||
public string Name = "Logger";
|
||||
|
||||
private bool Stopping;
|
||||
public List<StreamWriter> Writers = new List<StreamWriter> { new StreamWriter(Console.OpenStandardOutput()) };
|
||||
public List<StreamWriter> Writers = new() { new StreamWriter(Console.OpenStandardOutput()) };
|
||||
|
||||
internal Logger()
|
||||
{
|
||||
Name = Process.GetCurrentProcess().Id.ToString();
|
||||
Name = Environment.ProcessId.ToString();
|
||||
if (!FlushImmediately)
|
||||
{
|
||||
LoggerThread = new Thread(() =>
|
||||
@ -164,20 +164,31 @@ namespace RageCoop.Core
|
||||
while (_queuedLines.TryDequeue(out var line))
|
||||
{
|
||||
var formatted = Format(line);
|
||||
Writers.ForEach(x =>
|
||||
Writers?.ForEach(x =>
|
||||
{
|
||||
x.WriteLine(formatted);
|
||||
x.Flush();
|
||||
try
|
||||
{
|
||||
x.WriteLine(formatted);
|
||||
x.Flush();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleError(ex);
|
||||
}
|
||||
});
|
||||
OnFlush?.Invoke(line, formatted);
|
||||
}
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleError(Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Logger {this} flush error: {ex}");
|
||||
}
|
||||
public class LogLine
|
||||
{
|
||||
public LogLevel LogLevel;
|
||||
|
@ -128,6 +128,11 @@ namespace RageCoop.Core
|
||||
return q.ToEulerAngles().ToDegree();
|
||||
}
|
||||
|
||||
public static Quaternion Differentiate(this Quaternion p, Quaternion q)
|
||||
{
|
||||
return q * Quaternion.Invert(p);
|
||||
}
|
||||
|
||||
public static Vector3 ToEulerAngles(this Quaternion q)
|
||||
{
|
||||
var angles = new Vector3();
|
||||
|
@ -7,15 +7,17 @@ namespace RageCoop.Core
|
||||
{
|
||||
internal class CoopPeer : NetPeer, IDisposable
|
||||
{
|
||||
private readonly Thread ListenerThread;
|
||||
private readonly Logger Log;
|
||||
private readonly Thread _receiver;
|
||||
private bool _stopping;
|
||||
public EventHandler<NetIncomingMessage> OnMessageReceived;
|
||||
|
||||
public CoopPeer(NetPeerConfiguration config) : base(config)
|
||||
public CoopPeer(NetPeerConfiguration config,Logger logger) : base(config)
|
||||
{
|
||||
Log = logger;
|
||||
Start();
|
||||
NetIncomingMessage msg;
|
||||
ListenerThread = new Thread(() =>
|
||||
_receiver = new Thread(() =>
|
||||
{
|
||||
while (!_stopping)
|
||||
{
|
||||
@ -23,7 +25,7 @@ namespace RageCoop.Core
|
||||
if (msg != null) OnMessageReceived?.Invoke(this, msg);
|
||||
}
|
||||
});
|
||||
ListenerThread.Start();
|
||||
_receiver.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -32,8 +34,18 @@ namespace RageCoop.Core
|
||||
public void Dispose()
|
||||
{
|
||||
_stopping = true;
|
||||
Shutdown("Bye!");
|
||||
ListenerThread.Join();
|
||||
if (Status == NetPeerStatus.Running)
|
||||
{
|
||||
Shutdown("Bye!");
|
||||
}
|
||||
if (_receiver.IsAlive)
|
||||
{
|
||||
Log?.Debug("Stopping message thread");
|
||||
_receiver.Join();
|
||||
}
|
||||
Log?.Debug("Stopping network thread");
|
||||
Join();
|
||||
Log?.Debug("CoopPeer disposed");
|
||||
}
|
||||
|
||||
public void SendTo(Packet p, NetConnection connection, ConnectionChannel channel = ConnectionChannel.Default,
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
|
||||
namespace RageCoop.Core
|
||||
@ -33,8 +34,8 @@ namespace RageCoop.Core
|
||||
SecurityProtocolType.Tls;
|
||||
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
|
||||
|
||||
var client = new WebClient();
|
||||
return client.DownloadString(url);
|
||||
var client = new HttpClient();
|
||||
return client.GetStringAsync(url).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,6 @@ namespace RageCoop.Core
|
||||
[Serializable]
|
||||
public class ServerInfo
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
public string address { get; set; }
|
||||
public int port { get; set; }
|
||||
public string name { get; set; }
|
||||
|
@ -9,7 +9,6 @@ namespace RageCoop.Core
|
||||
{
|
||||
internal class CustomEvent : Packet
|
||||
{
|
||||
public static Func<byte, NetIncomingMessage, object> ResolveHandle = null;
|
||||
public CustomEventFlags Flags;
|
||||
|
||||
public CustomEvent(CustomEventFlags flags = CustomEventFlags.None)
|
||||
@ -19,79 +18,20 @@ namespace RageCoop.Core
|
||||
|
||||
public override PacketType Type => PacketType.CustomEvent;
|
||||
public int Hash { get; set; }
|
||||
public object[] Args { get; set; }
|
||||
public byte[] Payload;
|
||||
|
||||
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);
|
||||
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++)
|
||||
{
|
||||
var type = m.ReadByte();
|
||||
switch (type)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
Payload = m.ReadBytes(m.LengthBytes - m.PositionInBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,6 +152,7 @@ namespace RageCoop.Core
|
||||
|
||||
internal abstract class Packet : IPacket
|
||||
{
|
||||
|
||||
public abstract PacketType Type { get; }
|
||||
|
||||
public virtual void Deserialize(NetIncomingMessage m)
|
||||
|
@ -1,61 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<AssemblyVersion>0.1</AssemblyVersion>
|
||||
<FileVersion>0.1</FileVersion>
|
||||
<Version>0.1</Version>
|
||||
<DebugType>embedded</DebugType>
|
||||
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<Configurations>Debug;Release;API</Configurations>
|
||||
<OutDir>..\bin\$(Configuration)\Core</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>true</ImplicitUsings>
|
||||
<NoWarn>1701;1702;CS1591</NoWarn>
|
||||
<TargetFrameworks>net7.0</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<AssemblyVersion>0.1</AssemblyVersion>
|
||||
<FileVersion>0.1</FileVersion>
|
||||
<Version>0.1</Version>
|
||||
<DebugType>embedded</DebugType>
|
||||
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<Configurations>Debug;Release;API</Configurations>
|
||||
<OutDir>..\bin\$(Configuration)\Core</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'API'">
|
||||
<OutDir>..\bin\API</OutDir>
|
||||
<Deterministic>true</Deterministic>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<ProduceOnlyReferenceAssembly>true</ProduceOnlyReferenceAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net6.0|AnyCPU'">
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0|AnyCPU'">
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard2.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Costura.Fody" Version="5.7.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Fody" Version="6.6.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.0" />
|
||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Lidgren.Network">
|
||||
<HintPath>..\libs\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3">
|
||||
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\libs\Lidgren.Network\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\libs\ScriptHookVDotNetCore\src\ScriptHookVDotNetCore\ScriptHookVDotNetCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
85
Core/Scripting/CustomEventHandler.cs
Normal file
85
Core/Scripting/CustomEventHandler.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using Newtonsoft.Json;
|
||||
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
|
||||
{
|
||||
// Make sure the handler doesn't get GC'd
|
||||
static List<CustomEventHandler> _handlers = new();
|
||||
|
||||
[ThreadStatic]
|
||||
static object _tag;
|
||||
public CustomEventHandler()
|
||||
{
|
||||
lock (_handlers)
|
||||
{
|
||||
_handlers.Add(this);
|
||||
}
|
||||
}
|
||||
public CustomEventHandler(IntPtr func) : this()
|
||||
{
|
||||
FunctionPtr = (ulong)func;
|
||||
Directory = SHVDN.Core.CurrentDirectory;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
private CustomEventHandlerDelegate _managedHandler; // Used to keep GC reference
|
||||
|
||||
[JsonProperty]
|
||||
public ulong FunctionPtr { get; private set; }
|
||||
|
||||
[JsonProperty]
|
||||
public string Directory { get; private set; }
|
||||
|
||||
/// <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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
using System;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
@ -93,8 +97,9 @@ namespace RageCoop.Core.Scripting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Common processing for custome client\server events
|
||||
/// </summary>
|
||||
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 +121,190 @@ namespace RageCoop.Core.Scripting
|
||||
internal static readonly CustomEventHash WeatherTimeSync = "RageCoop.WeatherTimeSync";
|
||||
internal static readonly CustomEventHash IsHost = "RageCoop.IsHost";
|
||||
|
||||
/// <summary>
|
||||
/// Get event hash from string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This method is obsoete, you should use implicit operator or <see cref="CustomEventHash.FromString(string)" />
|
||||
/// </remarks>
|
||||
[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(BufferWriter 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<byte, byte[]> value:
|
||||
b.WriteVal(value.Item1);
|
||||
b.Write(new ReadOnlySpan<byte>(value.Item2));
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unsupported object type: " + obj.GetType());
|
||||
}
|
||||
}
|
||||
}
|
||||
public static object[] ReadObjects(BufferReader r)
|
||||
{
|
||||
var Args = new object[r.ReadVal<int>()];
|
||||
for (var i = 0; i < Args.Length; i++)
|
||||
{
|
||||
var type = r.ReadVal<byte>();
|
||||
switch (type)
|
||||
{
|
||||
case T_BYTE:
|
||||
Args[i] = r.ReadVal<byte>();
|
||||
break;
|
||||
case T_SHORT:
|
||||
Args[i] = r.ReadVal<short>();
|
||||
break;
|
||||
case T_USHORT:
|
||||
Args[i] = r.ReadVal<ushort>();
|
||||
break;
|
||||
case T_INT:
|
||||
Args[i] = r.ReadVal<int>();
|
||||
break;
|
||||
case T_UINT:
|
||||
Args[i] = r.ReadVal<uint>();
|
||||
break;
|
||||
case T_LONG:
|
||||
Args[i] = r.ReadVal<long>();
|
||||
break;
|
||||
case T_ULONG:
|
||||
Args[i] = r.ReadVal<ulong>();
|
||||
break;
|
||||
case T_FLOAT:
|
||||
Args[i] = r.ReadVal<float>();
|
||||
break;
|
||||
case T_BOOL:
|
||||
Args[i] = r.ReadVal<bool>();
|
||||
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<Model>();
|
||||
break;
|
||||
case T_VEC2:
|
||||
r.Read(out Vector2 vec2);
|
||||
Args[i] = vec2;
|
||||
break;
|
||||
case T_BYTEARR:
|
||||
Args[i] = r.ReadArray<byte>();
|
||||
break;
|
||||
case T_ID_BLIP:
|
||||
case T_ID_PED:
|
||||
case T_ID_PROP:
|
||||
case T_ID_VEH:
|
||||
Args[i] = IdToHandle(type, r.ReadVal<int>());
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Unexpected type: {type}");
|
||||
}
|
||||
}
|
||||
return Args;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace RageCoop.Core.Scripting
|
||||
@ -10,16 +11,19 @@ namespace RageCoop.Core.Scripting
|
||||
/// <summary>
|
||||
/// Full name with relative path of this file
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string Name { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is a directory
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public bool IsDirectory { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get a stream that can be used to read file content.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public Func<Stream> GetStream { get; internal set; }
|
||||
}
|
||||
}
|
70
Core/Shared.cs
Normal file
70
Core/Shared.cs
Normal file
@ -0,0 +1,70 @@
|
||||
global using static RageCoop.Core.Shared;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public class JsonDontSerialize : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal class Shared
|
||||
{
|
||||
static Type JsonTypeCheck(Type type)
|
||||
{
|
||||
if (type?.GetCustomAttribute<JsonDontSerialize>() != null)
|
||||
throw new TypeAccessException($"The type {type} cannot be serialized");
|
||||
return type;
|
||||
}
|
||||
static object JsonTypeCheck(object obj)
|
||||
{
|
||||
JsonTypeCheck(obj?.GetType());
|
||||
return obj;
|
||||
}
|
||||
public static readonly JsonSerializerSettings JsonSettings = new();
|
||||
static Shared()
|
||||
{
|
||||
JsonSettings.Converters.Add(new IPAddressConverter());
|
||||
JsonSettings.Converters.Add(new IPEndPointConverter());
|
||||
JsonSettings.Formatting = Formatting.Indented;
|
||||
}
|
||||
|
||||
public static object JsonDeserialize(string text, Type type)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(text, JsonTypeCheck(type), JsonSettings);
|
||||
}
|
||||
|
||||
public static T JsonDeserialize<T>(string text) => (T)JsonDeserialize(text, typeof(T));
|
||||
|
||||
public static string JsonSerialize(object obj) => JsonConvert.SerializeObject(JsonTypeCheck(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;
|
||||
}
|
||||
}
|
||||
}
|
@ -24,9 +24,9 @@ namespace RageCoop.Core
|
||||
}
|
||||
|
||||
Console.WriteLine("Deserializing");
|
||||
var anims = JsonConvert.DeserializeObject<AnimDic[]>(File.ReadAllText(input));
|
||||
var anims = JsonDeserialize<AnimDic[]>(File.ReadAllText(input));
|
||||
Console.WriteLine("Serializing");
|
||||
File.WriteAllText(output, JsonConvert.SerializeObject(anims, Formatting.Indented));
|
||||
File.WriteAllText(output, JsonSerialize(anims));
|
||||
return anims;
|
||||
}
|
||||
}
|
||||
@ -113,12 +113,11 @@ namespace RageCoop.Core
|
||||
}
|
||||
|
||||
Console.WriteLine("Deserializing");
|
||||
var infos = JsonConvert.DeserializeObject<VehicleInfo[]>(File.ReadAllText(input));
|
||||
var infos = JsonDeserialize<VehicleInfo[]>(File.ReadAllText(input));
|
||||
Console.WriteLine("Serializing");
|
||||
File.WriteAllText(output,
|
||||
JsonConvert.SerializeObject(
|
||||
infos.Select(FromVehicle).Where(x => x != null),
|
||||
Formatting.Indented));
|
||||
JsonSerialize(
|
||||
infos.Select(FromVehicle).Where(x => x != null)));
|
||||
}
|
||||
|
||||
public static VehicleWeaponInfo FromVehicle(VehicleInfo info)
|
||||
|
54
Core/XSpan.cs
Normal file
54
Core/XSpan.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A light-weight and less restricted implementation of <see cref="Span{T}"/>, gonna be used at some point, maybe?
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public readonly unsafe struct XSpan<T> 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<T> dest, int destStart = 0)
|
||||
{
|
||||
for (int i = 0; i < Length; i++)
|
||||
{
|
||||
dest[destStart + i] = this[i];
|
||||
}
|
||||
}
|
||||
|
||||
public XSpan<byte> Slice(int start) => new(Address + start, Length - start);
|
||||
public XSpan<byte> Slice(int start, int len) => new(Address + start, len);
|
||||
|
||||
public static implicit operator Span<T>(XSpan<T> s)
|
||||
{
|
||||
return new Span<T>(s.Address, s.Length);
|
||||
}
|
||||
|
||||
public static implicit operator XSpan<T>(Span<T> s)
|
||||
{
|
||||
fixed (T* ptr = s)
|
||||
{
|
||||
return new XSpan<T>(ptr, s.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
124
RageCoop-V.sln
124
RageCoop-V.sln
@ -7,24 +7,30 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Server", "Server\R
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "Core\RageCoop.Core.csproj", "{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "Client\Scripts\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client", "Client\Scripts\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client.Installer", "Client\Installer\RageCoop.Client.Installer.csproj", "{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client.Loader", "Client\Loader\RageCoop.Client.Loader.csproj", "{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client", "Client", "{531656CF-7269-488D-B042-741BC96C3941}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{12E29AB7-74C4-4250-8975-C02D7FFC2D7B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client.CefHost", "Client\CefHost\RageCoop.Client.CefHost.csproj", "{BA750E08-5E41-4B56-8AD5-875716D2CCEA}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{70A1F09D-648D-4C8B-8947-E920B1A587A3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefTest", "Tools\CefTest\CefTest.csproj", "{874944F4-2D01-4423-B55C-C651CCBA6630}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataDumper", "Tools\DataDumper\DataDumper.csproj", "{6387D897-09AF-4464-B440-80438E3BB8D0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client.Scripting", "Client\Scripting\RageCoop.Client.Scripting.csproj", "{FE47AFBA-0613-4378-B318-892DEB7B3D88}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "Tools\UnitTest\UnitTest.csproj", "{4FE96671-3DC5-4394-B2E3-584399E57310}"
|
||||
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}"
|
||||
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}") = "CodeGen", "Tools\CodeGen\CodeGen.csproj", "{C4CF8A98-7393-42BD-97A1-2E850D12890A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
API|Any CPU = API|Any CPU
|
||||
@ -83,42 +89,6 @@ Global
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.API|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.API|Any CPU.Build.0 = Release|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.API|x64.ActiveCfg = Release|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.API|x64.Build.0 = Release|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.API|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.API|Any CPU.Build.0 = Release|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.API|x64.ActiveCfg = Release|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.API|x64.Build.0 = Release|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.API|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.API|Any CPU.Build.0 = Debug|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.API|x64.ActiveCfg = Debug|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.API|x64.Build.0 = Debug|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630}.Release|x64.Build.0 = Release|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.API|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.API|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.API|x64.ActiveCfg = Debug|Any CPU
|
||||
@ -131,6 +101,66 @@ Global
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.API|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.API|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.API|x64.ActiveCfg = Debug|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.API|x64.Build.0 = Debug|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.API|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.API|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.API|x64.ActiveCfg = Debug|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.API|x64.Build.0 = Debug|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4FE96671-3DC5-4394-B2E3-584399E57310}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.API|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.API|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.API|x64.ActiveCfg = Debug|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.API|x64.Build.0 = Debug|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B15EDABB-30AF-475A-823D-ACB9F75CFE13}.Release|x64.Build.0 = Release|Any CPU
|
||||
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.API|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.API|Any CPU.Build.0 = Debug|Any CPU
|
||||
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.API|x64.ActiveCfg = Debug|Any CPU
|
||||
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.API|x64.Build.0 = Debug|Any CPU
|
||||
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{02616B5A-2A68-42AA-A91E-311EF95FCF44}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{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
|
||||
{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
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.API|x64.Build.0 = Debug|Any CPU
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -138,10 +168,12 @@ Global
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681} = {531656CF-7269-488D-B042-741BC96C3941}
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B} = {531656CF-7269-488D-B042-741BC96C3941}
|
||||
{FC8CBDBB-6DC3-43AF-B34D-092E476410A5} = {531656CF-7269-488D-B042-741BC96C3941}
|
||||
{BA750E08-5E41-4B56-8AD5-875716D2CCEA} = {531656CF-7269-488D-B042-741BC96C3941}
|
||||
{874944F4-2D01-4423-B55C-C651CCBA6630} = {70A1F09D-648D-4C8B-8947-E920B1A587A3}
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0} = {70A1F09D-648D-4C8B-8947-E920B1A587A3}
|
||||
{FE47AFBA-0613-4378-B318-892DEB7B3D88} = {531656CF-7269-488D-B042-741BC96C3941}
|
||||
{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}
|
||||
{C4CF8A98-7393-42BD-97A1-2E850D12890A} = {70A1F09D-648D-4C8B-8947-E920B1A587A3}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6CC7EA75-E4FF-4534-8EB6-0AEECF2620B7}
|
||||
|
@ -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);
|
||||
|
@ -79,8 +79,7 @@ public partial class Server
|
||||
|
||||
if (!CanAnnounce)
|
||||
{
|
||||
var existing = JsonConvert
|
||||
.DeserializeObject<List<ServerInfo>>(
|
||||
var existing = JsonDeserialize<List<ServerInfo>>(
|
||||
HttpHelper.DownloadString(Util.GetFinalRedirect(Settings.MasterServer)))
|
||||
.Where(x => x.address == IpInfo.Address).FirstOrDefault();
|
||||
if (existing != null)
|
||||
@ -117,7 +116,7 @@ public partial class Server
|
||||
publicKeyModulus = Convert.ToBase64String(pModulus),
|
||||
publicKeyExponent = Convert.ToBase64String(pExpoenet)
|
||||
};
|
||||
var msg = JsonConvert.SerializeObject(serverInfo);
|
||||
var msg = JsonSerialize(serverInfo);
|
||||
|
||||
var realUrl = Util.GetFinalRedirect(Settings.MasterServer);
|
||||
response = httpClient.PostAsync(realUrl, new StringContent(msg, Encoding.UTF8, "application/json"))
|
||||
|
@ -15,7 +15,7 @@ using System.Resources;
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information
|
||||
[assembly: AssemblyVersion("1.6.0.24")]
|
||||
[assembly: AssemblyFileVersion("1.6.0.24")]
|
||||
[assembly: AssemblyVersion("1.6.0.55")]
|
||||
[assembly: AssemblyFileVersion("1.6.0.55")]
|
||||
[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'">
|
||||
@ -55,7 +56,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Costura.Fody" Version="5.7.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<IncludeAssets>compile; runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" />
|
||||
<PackageReference Include="Fody" Version="6.6.3">
|
||||
@ -72,15 +73,6 @@
|
||||
<ProjectReference Include="..\Core\RageCoop.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Lidgren.Network">
|
||||
<HintPath>..\libs\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3">
|
||||
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Properties\AssemblyInfo.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
@ -101,8 +93,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition=" '$(DevEnvDir)' != '*Undefined*'">
|
||||
<Exec
|
||||
Command=""$(DevEnvDir)TextTransform.exe" -a !!BuildConfiguration!$(Configuration) "$(ProjectDir)Properties\AssemblyInfo.tt"" />
|
||||
<Exec Command=""$(DevEnvDir)TextTransform.exe" -a !!BuildConfiguration!$(Configuration) "$(ProjectDir)Properties\AssemblyInfo.tt"" />
|
||||
</Target>
|
||||
|
||||
|
||||
|
@ -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)
|
||||
@ -344,16 +359,18 @@ 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>
|
||||
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>
|
||||
|
@ -22,6 +22,11 @@ internal class Resources
|
||||
Logger = server.Logger;
|
||||
}
|
||||
|
||||
static Resources()
|
||||
{
|
||||
CoreUtils.ForceLoadAllAssemblies();
|
||||
}
|
||||
|
||||
public bool IsLoaded { get; private set; }
|
||||
|
||||
public void LoadAll()
|
||||
@ -98,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)..]);
|
||||
}
|
||||
|
||||
@ -263,7 +267,7 @@ internal class Resources
|
||||
}
|
||||
|
||||
if (Server.GetResponse<Packets.FileTransferResponse>(client, new Packets.AllResourcesSent(),
|
||||
ConnectionChannel.RequestResponse, 30000)?.Response == FileResponse.Loaded)
|
||||
ConnectionChannel.File, 30000)?.Response == FileResponse.Loaded)
|
||||
{
|
||||
client.IsReady = true;
|
||||
Server.API.Events.InvokePlayerReady(client);
|
||||
|
@ -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
|
||||
/// <summary>
|
||||
/// Pass this as an argument in CustomEvent or NativeCall to convert this object to handle at client side.
|
||||
/// </summary>
|
||||
public Tuple<byte, byte[]> Handle => new(60, BitConverter.GetBytes(ID));
|
||||
public Tuple<byte, byte[]> Handle => new(T_ID_BLIP, BitConverter.GetBytes(ID));
|
||||
|
||||
/// <summary>
|
||||
/// Network ID (not handle!)
|
||||
|
@ -47,6 +47,10 @@ public class ServerResource : PluginLoader
|
||||
|
||||
internal static ServerResource LoadFrom(string resDir, string dataFolder, Logger logger = null)
|
||||
{
|
||||
string mainAssemblyPath = Path.Combine(resDir, Path.GetFileName(resDir) + ".dll");
|
||||
if (!File.Exists(mainAssemblyPath))
|
||||
throw new FileNotFoundException($"Main assemby not found: {mainAssemblyPath}");
|
||||
|
||||
var runtimeLibs = Path.Combine(resDir, "RuntimeLibs", CoreUtils.GetInvariantRID());
|
||||
if (Directory.Exists(runtimeLibs))
|
||||
{
|
||||
@ -61,16 +65,18 @@ public class ServerResource : PluginLoader
|
||||
CoreUtils.CopyFilesRecursively(new DirectoryInfo(runtimeLibs), new DirectoryInfo(resDir));
|
||||
}
|
||||
|
||||
var conf = new PluginConfig(Path.GetFullPath(Path.Combine(resDir, Path.GetFileName(resDir) + ".dll")))
|
||||
var conf = new PluginConfig(Path.GetFullPath(mainAssemblyPath))
|
||||
{
|
||||
PreferSharedTypes = true,
|
||||
EnableHotReload = false,
|
||||
IsUnloadable = false,
|
||||
LoadInMemory = true
|
||||
};
|
||||
ServerResource r = new(conf);
|
||||
r.Logger = logger;
|
||||
r.Name = Path.GetFileName(resDir);
|
||||
ServerResource r = new(conf)
|
||||
{
|
||||
Logger = logger,
|
||||
Name = Path.GetFileName(resDir)
|
||||
};
|
||||
if (!File.Exists(conf.MainAssemblyPath))
|
||||
{
|
||||
r.Dispose();
|
||||
|
9
Server/Shared.cs
Normal file
9
Server/Shared.cs
Normal file
@ -0,0 +1,9 @@
|
||||
global using static RageCoop.Core.Shared;
|
||||
global using static RageCoop.Server.Shared;
|
||||
global using RageCoop.Core;
|
||||
namespace RageCoop.Server
|
||||
{
|
||||
internal class Shared
|
||||
{
|
||||
}
|
||||
}
|
14
Tools/CodeGen/CodeGen.csproj
Normal file
14
Tools/CodeGen/CodeGen.csproj
Normal file
@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<OutDir>$(SolutionDir)bin\Tools\CodeGen</OutDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Client\Scripts\RageCoop.Client.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
82
Tools/CodeGen/Program.cs
Normal file
82
Tools/CodeGen/Program.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using RageCoop.Client.Scripting;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace CodeGen
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Directory.SetCurrentDirectory(@"..\..\..\");
|
||||
var props = new StringBuilder();
|
||||
var config = new StringBuilder();
|
||||
var funcs = new StringBuilder();
|
||||
foreach (var prop in typeof(API.Config).GetProperties(BindingFlags.Public | BindingFlags.Static))
|
||||
{
|
||||
config.AppendLine($"public static {prop.PropertyType.ToTypeName()} {prop.Name} => GetConfig<{prop.PropertyType.ToTypeName()}>(\"{prop.Name}\");");
|
||||
}
|
||||
foreach (var prop in typeof(API).GetProperties(BindingFlags.Public | BindingFlags.Static))
|
||||
{
|
||||
props.AppendLine($"public static {prop.PropertyType.ToTypeName()} {prop.Name} => GetProperty<{prop.PropertyType.ToTypeName()}>(\"{prop.Name}\");");
|
||||
}
|
||||
foreach (var f in
|
||||
typeof(API).GetMethods(BindingFlags.Public | BindingFlags.Static).
|
||||
Where(x =>
|
||||
{
|
||||
var attri = x.GetCustomAttribute<RemotingAttribute>();
|
||||
if (attri ==null) return false;
|
||||
return attri.GenBridge;
|
||||
}))
|
||||
{
|
||||
var ret = f.ReturnType.ToTypeName();
|
||||
var gReturn = $"<{ret}>";
|
||||
if (ret == "System.Void") { ret = "void"; gReturn = string.Empty; }
|
||||
var ps = f.GetParameters();
|
||||
var paras = string.Join(',', ps.Select(x => $"{x.ParameterType.ToTypeName()} {x.Name}"));
|
||||
var parasNoType = string.Join(',', ps.Select(x => x.Name));
|
||||
if (ps.Length > 0)
|
||||
{
|
||||
parasNoType = "," + parasNoType;
|
||||
}
|
||||
funcs.AppendLine($"public static {ret} {f.Name}({paras}) => InvokeCommand{gReturn}(\"{f.Name}\"{parasNoType});");
|
||||
}
|
||||
var code = $@"namespace RageCoop.Client.Scripting
|
||||
{{
|
||||
public static unsafe partial class APIBridge
|
||||
{{
|
||||
|
||||
public static class Config
|
||||
{{
|
||||
{config}
|
||||
}}
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
{props}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FUNCTIONS
|
||||
|
||||
{funcs}
|
||||
|
||||
#endregion
|
||||
}}
|
||||
}}
|
||||
";
|
||||
File.WriteAllText(@"Client\Scripting\APIBridge.Generated.cs", code);
|
||||
}
|
||||
static string ToTypeName(this Type type)
|
||||
{
|
||||
var name = type.ToString();
|
||||
if (type.GenericTypeArguments.Length > 0)
|
||||
{
|
||||
name = $"{name.Substring(0, name.IndexOf('`'))}<{string.Join(',', type.GenericTypeArguments.Select(ToTypeName))}>";
|
||||
}
|
||||
name = name.Replace("RageCoop.Client.Player", "RageCoop.Client.Scripting.PlayerInfo");
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<NoAotCompile>true</NoAotCompile>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<OutDir>..\..\bin\Tools\DataDumper</OutDir>
|
||||
<OutDir>$(SolutionDir)bin\Tools\DataDumper</OutDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
92
Tools/UnitTest/Program.cs
Normal file
92
Tools/UnitTest/Program.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using GTA.Math;
|
||||
using RageCoop.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class TestElement
|
||||
{
|
||||
static SHA256 sha = SHA256.Create();
|
||||
static int blah = new Random().Next();
|
||||
public TestElement(int i)
|
||||
{
|
||||
num = (i + blah) * 1024;
|
||||
vec2 = new(num * 10, num * 20);
|
||||
vec3 = new(num * 10, num * 20, num * 30);
|
||||
quat = new(num * 10, num * 20, num * 30, num * 40);
|
||||
str = sha.ComputeHash(BitConverter.GetBytes(num)).Dump();
|
||||
}
|
||||
public int num;
|
||||
public Vector2 vec2;
|
||||
public Vector3 vec3;
|
||||
public Quaternion quat;
|
||||
public string str;
|
||||
}
|
||||
internal unsafe class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
TestElement[] test = new TestElement[1024];
|
||||
Console.WriteLine("Testing buffers");
|
||||
var buf = new BufferWriter(1024);
|
||||
for (int i = 0; i < 1024; i++)
|
||||
{
|
||||
var e = test[i] = new TestElement(i);
|
||||
buf.WriteVal(e.num);
|
||||
buf.Write(ref e.vec2);
|
||||
buf.Write(ref e.vec3);
|
||||
buf.Write(ref e.quat);
|
||||
buf.Write(e.str);
|
||||
}
|
||||
Console.WriteLine($"Buffer size: {buf.Size}");
|
||||
Console.WriteLine($"Current position: {buf.Position}");
|
||||
|
||||
Console.WriteLine("Validating data");
|
||||
var reader = new BufferReader(buf.Address, buf.Size);
|
||||
for (int i = 0; i < 1024; i++)
|
||||
{
|
||||
var e = test[i];
|
||||
reader.Read(out int num);
|
||||
reader.Read(out Vector2 vec2);
|
||||
reader.Read(out Vector3 vec3);
|
||||
reader.Read(out Quaternion quat);
|
||||
reader.Read(out string str);
|
||||
|
||||
if (num != e.num)
|
||||
throw new Exception("POCO fail");
|
||||
|
||||
if (vec2 != e.vec2)
|
||||
throw new Exception("vec2 fail");
|
||||
|
||||
if (vec3 != e.vec3)
|
||||
throw new Exception("vec3 fail");
|
||||
|
||||
if (quat != e.quat)
|
||||
throw new Exception("quat fail");
|
||||
|
||||
if (str != e.str)
|
||||
throw new Exception("str fail");
|
||||
}
|
||||
|
||||
Console.WriteLine("Buffers OK");
|
||||
|
||||
Console.WriteLine("Testing CustomEvents");
|
||||
var objs = new object[] { (byte)236, (short)82, (ushort)322,
|
||||
"test", 123, 123U, 456UL, 345L, 5F, new Vector2(15, 54), new Vector3(22, 45, 25), new Quaternion(2, 3, 222, 5) };
|
||||
|
||||
buf.Reset();
|
||||
CustomEvents.WriteObjects(buf, objs);
|
||||
var payload = buf.ToByteArray(buf.Position);
|
||||
fixed(byte* p = payload)
|
||||
{
|
||||
reader.Initialise(p, payload.Length);
|
||||
}
|
||||
|
||||
if (!CustomEvents.ReadObjects(reader).SequenceEqual(objs))
|
||||
throw new Exception("CustomEvents fail");
|
||||
|
||||
Console.WriteLine("CustomEvents OK");
|
||||
}
|
||||
}
|
||||
}
|
14
Tools/UnitTest/UnitTest.csproj
Normal file
14
Tools/UnitTest/UnitTest.csproj
Normal file
@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\RageCoop.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,8 +1,9 @@
|
||||
cd "%~dp0"
|
||||
git submodule update --init
|
||||
if exist "bin" rmdir /s /q "bin"
|
||||
dotnet build -c Release
|
||||
dotnet build -c API Core\RageCoop.Core.csproj
|
||||
dotnet build -c API Server\RageCoop.Server.csproj
|
||||
cd %~dp0
|
||||
copy .\Client\Scripts\obj\Release\ref\RageCoop.Client.dll .\bin\API\RageCoop.Client.dll /y
|
||||
dotnet build -c API Client\RageCoop.Client.Scripting.csproj
|
||||
del .\bin\API\RageCoop.Server.exe
|
||||
rmdir .\bin\Release\Client\SubProcess /s /q
|
||||
if exist .\bin\Release\Client\SubProcess rmdir .\bin\Release\Client\SubProcess /s /q
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user