Compare commits
30 Commits
sync-struc
...
main
Author | SHA1 | Date | |
---|---|---|---|
3b901d8e6c | |||
9764484f4a | |||
5f781f6f48 | |||
fcd7e18d9b | |||
564d1467f6 | |||
8bd6ea15a3 | |||
3c7f16f7a4 | |||
393a401860 | |||
39b780e518 | |||
4a54851c51 | |||
f0eefa575c | |||
3fc813b2d8 | |||
fbff72ff14 | |||
4e52407591 | |||
4e6fde129d | |||
92e1a970a8 | |||
d0eb0b7818 | |||
14151d7b2c | |||
1f8d70a520 | |||
2cb5baf5f7 | |||
34e33937e2 | |||
9287aba0f9 | |||
e88e903096 | |||
99642fd40c | |||
2fbf06b504 | |||
13b771ec9f | |||
3b987f59e0 | |||
de96f29097 | |||
6136cbfc14 | |||
ed145aedd6 |
@ -1,4 +0,0 @@
|
||||
[*.cs]
|
||||
|
||||
# CS0649: Field 'VehicleInfo.Name' is never assigned to, and will always have its default value null
|
||||
dotnet_diagnostic.CS0649.severity = silent
|
21
.github/workflows/build-test.yaml
vendored
21
.github/workflows/build-test.yaml
vendored
@ -2,7 +2,9 @@ name: Build test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
branches:
|
||||
- '*' # matches every branch that doesn't contain a '/'
|
||||
- '*/*' # matches every branch containing a single '/'
|
||||
- '**' # matches every branch
|
||||
- '!main' # excludes main
|
||||
- '!dev-nightly' # excludes nightly
|
||||
@ -16,7 +18,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
dotnet-version: ['7.0.x']
|
||||
dotnet-version: ['6.0.x']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -24,30 +26,23 @@ 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
|
||||
|
||||
- name: Restore nuget packages
|
||||
run: nuget restore
|
||||
|
||||
- name: Build
|
||||
run: .\build.cmd
|
||||
|
||||
- name: Build client and installer
|
||||
run: dotnet build RageCoop.Client.Installer/RageCoop.Client.Installer.csproj --configuration Release -o bin/Release/Client/RageCoop
|
||||
- name: Build server win-x64
|
||||
run: dotnet build RageCoop.Server/RageCoop.Server.csproj -o bin/Release/Server
|
||||
- name: Upload server
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: RageCoop.Server
|
||||
path: bin/Release/Server
|
||||
|
||||
- name: Upload Client
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: RageCoop.Client
|
||||
path: bin/Release/Client
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
|
32
.github/workflows/docker-build.yaml
vendored
Normal file
32
.github/workflows/docker-build.yaml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Build and Push Docker Image to GHCR
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set repo owner to lowercase
|
||||
run: echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image to GHCR
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ghcr.io/${{ env.REPO_OWNER }}/ragecoop-v:latest
|
||||
dockerfile: Dockerfile
|
74
.github/workflows/nightly-build.yaml
vendored
74
.github/workflows/nightly-build.yaml
vendored
@ -1,16 +1,14 @@
|
||||
name: Nightly-build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "dev-nightly" ]
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-latest
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
dotnet-version: ['7.0.x']
|
||||
dotnet-version: ['6.0.x']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -18,72 +16,30 @@ 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
|
||||
|
||||
- name: Restore nuget packages
|
||||
run: nuget restore
|
||||
|
||||
- name: Build artifacts
|
||||
run: .\build.cmd
|
||||
|
||||
- name: Build server for different platforms
|
||||
run: .\publish-server.cmd
|
||||
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y nuget
|
||||
nuget restore
|
||||
- name: Build client and installer
|
||||
run: dotnet build RageCoop.Client.Installer/RageCoop.Client.Installer.csproj --configuration Release -o bin/Release/Client/RageCoop
|
||||
- name: Build server win-x64
|
||||
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r win-x64 -o bin/Release/Server/win-x64 -c Release
|
||||
- name: Build server linux-x64
|
||||
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r linux-x64 -o bin/Release/Server/linux-x64 -c Release
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: bin/Release/Client
|
||||
dest: RageCoop.Client.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: bin/API
|
||||
dest: bin/Artifacts/SDK.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: bin/Release/Server/win-x64
|
||||
dest: bin/Artifacts/RageCoop.Server-win-x64.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: bin/Release/Server/win-x86
|
||||
dest: bin/Artifacts/RageCoop.Server-win-x86.zip
|
||||
dest: RageCoop.Server-win-x64.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: bin/Release/Server/linux-x64
|
||||
dest: bin/Artifacts/RageCoop.Server-linux-x64.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: bin/Release/Server/linux-arm
|
||||
dest: bin/Artifacts/RageCoop.Server-linux-arm.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: bin/Release/Server/linux-arm
|
||||
dest: bin/Artifacts/RageCoop.Server-linux-arm.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: bin/Release/Server/linux-arm64
|
||||
dest: bin/Artifacts/RageCoop.Server-linux-arm64.zip
|
||||
|
||||
- name: Deploy binaries
|
||||
uses: Sardelka9515/deploy-nightly@v0.7
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/RAGECOOP/GTAV-RESOURCES/releases/75463254/assets{?name,label}
|
||||
release_id: 75463254
|
||||
asset_path: bin/Artifacts
|
||||
asset_content_type: application/zip
|
||||
max_releases: 7
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
dest: RageCoop.Server-linux-x64.zip
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,4 +2,5 @@
|
||||
**/obj
|
||||
**/packages
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/.vscode
|
||||
**/.idea
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,6 +0,0 @@
|
||||
[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
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
</configuration>
|
@ -1,314 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Runtime.Remoting;
|
||||
using System.Runtime.Remoting.Channels;
|
||||
using System.Runtime.Remoting.Channels.Ipc;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CefSharp;
|
||||
using CefSharp.OffScreen;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace RageCoop.Client.CefHost
|
||||
{
|
||||
public enum BufferMode
|
||||
{
|
||||
Full = 1,
|
||||
Dirty = 2
|
||||
}
|
||||
|
||||
public enum MouseButton
|
||||
{
|
||||
Left,
|
||||
Middle,
|
||||
Right
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hosted by CefHost for managing cef instances.
|
||||
/// </summary>
|
||||
public class CefController : MarshalByRefObject, IDisposable
|
||||
{
|
||||
private static Process _host;
|
||||
private static ActivatedClientTypeEntry _controllerEntry;
|
||||
private static IpcChannel _adapterChannel;
|
||||
public static Action<string> OnCefMessage;
|
||||
private ChromiumWebBrowser _browser;
|
||||
private MemoryMappedFile _mmf;
|
||||
private string _mmfName;
|
||||
private SafeMemoryMappedViewHandle _mmfView;
|
||||
private BufferMode _mode;
|
||||
private CefProcessor _processor;
|
||||
public IntPtr PtrBuffer { get; private set; }
|
||||
|
||||
public int FrameRate
|
||||
{
|
||||
get => _browser.GetBrowserHost().WindowlessFrameRate;
|
||||
set => _browser.GetBrowserHost().WindowlessFrameRate = value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_browser?.Dispose();
|
||||
|
||||
_mmf?.Dispose();
|
||||
if (PtrBuffer != IntPtr.Zero) _mmfView?.ReleasePointer();
|
||||
_mmfView?.Dispose();
|
||||
|
||||
PtrBuffer = IntPtr.Zero;
|
||||
_mmf = null;
|
||||
_mmfView = null;
|
||||
}
|
||||
|
||||
|
||||
public static void Initialize(string fileName = "RageCoop.Client.CefHost.exe")
|
||||
{
|
||||
_host = new Process();
|
||||
_host.StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
_host.EnableRaisingEvents = true;
|
||||
_host.Start();
|
||||
RegisterChannels(_host.StandardOutput.ReadLine());
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (_host?.HasExited == false) OnCefMessage?.Invoke("[CEF]: " + _host.StandardOutput.ReadLine());
|
||||
});
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (_host?.HasExited == false)
|
||||
OnCefMessage?.Invoke("[CEF][ERROR]: " + _host.StandardError.ReadLine());
|
||||
});
|
||||
}
|
||||
|
||||
public static void ShutDown()
|
||||
{
|
||||
if (_host == null) return;
|
||||
_host.StandardInput.WriteLine("exit");
|
||||
_host.WaitForExit(1000);
|
||||
_host.Kill();
|
||||
_host = null;
|
||||
}
|
||||
|
||||
private static void RegisterChannels(string hostChannel)
|
||||
{
|
||||
var service = Guid.NewGuid().ToString();
|
||||
Console.WriteLine("Registering adapter channel: " + service);
|
||||
_adapterChannel = new IpcChannel(service);
|
||||
ChannelServices.RegisterChannel(_adapterChannel, false);
|
||||
|
||||
_controllerEntry = new ActivatedClientTypeEntry(typeof(CefController), "ipc://" + hostChannel);
|
||||
RemotingConfiguration.RegisterActivatedClientType(_controllerEntry);
|
||||
Console.WriteLine("Registered controller entry: " + "ipc://" + hostChannel);
|
||||
|
||||
|
||||
RemotingConfiguration.RegisterActivatedServiceType(typeof(CefAdapter));
|
||||
Console.WriteLine("Registered service: " + nameof(CefAdapter));
|
||||
_host.StandardInput.WriteLine("ipc://" + service);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called inside client process
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="adapter"></param>
|
||||
/// <param name="bufferMode"></param>
|
||||
/// <param name="bufferSize"></param>
|
||||
/// <returns></returns>
|
||||
public static CefController Create(int id, Size size, out CefAdapter adapter, BufferMode bufferMode,
|
||||
long bufferSize = 1024 * 1024 * 16)
|
||||
{
|
||||
if (RemotingConfiguration.IsRemotelyActivatedClientType(typeof(CefController)) == null)
|
||||
throw new RemotingException();
|
||||
var controller = new CefController();
|
||||
controller.Activate(id, size, bufferMode, bufferSize);
|
||||
adapter = CefAdapter.Adapters[id];
|
||||
controller.Ping();
|
||||
return controller;
|
||||
}
|
||||
|
||||
public unsafe void Activate(int id, Size size, BufferMode mode = BufferMode.Dirty,
|
||||
long sharedMemorySize = 1024 * 1024 * 16)
|
||||
{
|
||||
_mode = mode;
|
||||
_mmfName = Guid.NewGuid().ToString();
|
||||
|
||||
// Set up shared memory
|
||||
_mmf = MemoryMappedFile.CreateNew(_mmfName, sharedMemorySize);
|
||||
_mmfView = _mmf.CreateViewAccessor().SafeMemoryMappedViewHandle;
|
||||
byte* pBuf = null;
|
||||
try
|
||||
{
|
||||
_mmfView.AcquirePointer(ref pBuf);
|
||||
PtrBuffer = (IntPtr)pBuf;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
var adapter = new CefAdapter();
|
||||
adapter.Register(id, mode, _mmfName);
|
||||
_browser = new ChromiumWebBrowser();
|
||||
_browser.RenderHandler = _processor = new CefProcessor(size, adapter, PtrBuffer, mode);
|
||||
while (_browser.GetBrowserHost() == null) Thread.Sleep(20); // Wait till the browser is actually created
|
||||
Console.WriteLine("CefController created: " + size);
|
||||
}
|
||||
|
||||
|
||||
public void LoadUrl(string url)
|
||||
{
|
||||
_browser.LoadUrl(url);
|
||||
}
|
||||
|
||||
public void Resize(Size size)
|
||||
{
|
||||
_browser.Size = size;
|
||||
_processor.Size = size;
|
||||
}
|
||||
|
||||
public void SendMouseClick(int x, int y, int modifiers, MouseButton button, bool mouseUp, int clicks)
|
||||
{
|
||||
var e = new MouseEvent(x, y, (CefEventFlags)modifiers);
|
||||
_browser.GetBrowserHost()
|
||||
?.SendMouseClickEvent(e, (MouseButtonType)button, mouseUp, clicks);
|
||||
}
|
||||
|
||||
public void SendMouseMove(int x, int y, bool leave = false)
|
||||
{
|
||||
var e = new MouseEvent(x, y, 0);
|
||||
|
||||
_browser.GetBrowserHost()?.SendMouseMoveEvent(e, leave);
|
||||
}
|
||||
|
||||
public DateTime Ping()
|
||||
{
|
||||
return DateTime.UtcNow;
|
||||
;
|
||||
}
|
||||
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hosted by client for receiving rendering data
|
||||
/// </summary>
|
||||
public class CefAdapter : MarshalByRefObject, IDisposable
|
||||
{
|
||||
public delegate void PaintDelegate(int bufferSize, Rectangle dirtyRect);
|
||||
|
||||
public delegate void ResizeDelegate(Size newSize);
|
||||
|
||||
public static Dictionary<int, CefAdapter> Adapters = new Dictionary<int, CefAdapter>();
|
||||
|
||||
private MemoryMappedFile _mmf;
|
||||
private SafeMemoryMappedViewHandle _mmfView;
|
||||
public int Id;
|
||||
|
||||
public CefAdapter()
|
||||
{
|
||||
Console.WriteLine("Adapter created");
|
||||
}
|
||||
|
||||
public Size Size { get; private set; }
|
||||
|
||||
public IntPtr PtrBuffer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum buffer size for a paint event, use this property to allocate memory.
|
||||
/// </summary>
|
||||
/// <remarks>Value is equal to <see cref="Size" />*4, therefore will change upon resize</remarks>
|
||||
public int MaxBufferSize => Size.Height * Size.Width * 4;
|
||||
|
||||
public BufferMode BufferMode { get; private set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_mmf?.Dispose();
|
||||
if (PtrBuffer != IntPtr.Zero) _mmfView?.ReleasePointer();
|
||||
_mmfView?.Dispose();
|
||||
|
||||
PtrBuffer = IntPtr.Zero;
|
||||
_mmf = null;
|
||||
_mmfView = null;
|
||||
|
||||
lock (Adapters)
|
||||
{
|
||||
if (Adapters.ContainsKey(Id)) Adapters.Remove(Id);
|
||||
}
|
||||
}
|
||||
|
||||
public event PaintDelegate OnPaint;
|
||||
public event ResizeDelegate OnResize;
|
||||
|
||||
public void Resized(Size newSize)
|
||||
{
|
||||
Size = newSize;
|
||||
OnResize?.Invoke(newSize);
|
||||
}
|
||||
|
||||
public void Paint(Rectangle dirtyRect)
|
||||
{
|
||||
var size = BufferMode == BufferMode.Dirty
|
||||
? dirtyRect.Width * dirtyRect.Height * 4
|
||||
: Size.Width * Size.Height * 4;
|
||||
OnPaint?.Invoke(size, dirtyRect);
|
||||
}
|
||||
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public unsafe void Register(int id, BufferMode mode, string mmfName)
|
||||
{
|
||||
lock (Adapters)
|
||||
{
|
||||
if (Adapters.ContainsKey(id)) throw new ArgumentException("Specified id is already used", nameof(id));
|
||||
|
||||
// Set up shared memory
|
||||
_mmf = MemoryMappedFile.OpenExisting(mmfName);
|
||||
_mmfView = _mmf.CreateViewAccessor().SafeMemoryMappedViewHandle;
|
||||
byte* pBuf = null;
|
||||
try
|
||||
{
|
||||
_mmfView.AcquirePointer(ref pBuf);
|
||||
PtrBuffer = (IntPtr)pBuf;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
|
||||
Id = id;
|
||||
BufferMode = mode;
|
||||
Adapters.Add(id, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure ipc connection
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DateTime Ping()
|
||||
{
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using BitmapUtil;
|
||||
using CefSharp;
|
||||
using CefSharp.Enums;
|
||||
using CefSharp.OffScreen;
|
||||
using CefSharp.Structs;
|
||||
using Size = System.Drawing.Size;
|
||||
|
||||
namespace RageCoop.Client.CefHost
|
||||
{
|
||||
internal class CefProcessor : IRenderHandler
|
||||
{
|
||||
private readonly CefAdapter _adapter;
|
||||
private readonly BufferMode _mode;
|
||||
private readonly IntPtr _pSharedBuffer;
|
||||
private Rect _rect;
|
||||
|
||||
public CefProcessor(Size size, CefAdapter adapter, IntPtr pSharedBuffer, BufferMode mode)
|
||||
{
|
||||
_adapter = adapter;
|
||||
_rect = new Rect(0, 0, size.Width, size.Height);
|
||||
_pSharedBuffer = pSharedBuffer;
|
||||
_mode = mode;
|
||||
_adapter?.Resized(size);
|
||||
}
|
||||
|
||||
public Size Size
|
||||
{
|
||||
get => new Size(_rect.Width, _rect.Height);
|
||||
set
|
||||
{
|
||||
_rect = new Rect(0, 0, value.Width, value.Height);
|
||||
_adapter?.Resized(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public ScreenInfo? GetScreenInfo()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Rect GetViewRect()
|
||||
{
|
||||
return _rect;
|
||||
}
|
||||
|
||||
public bool GetScreenPoint(int viewX, int viewY, out int screenX, out int screenY)
|
||||
{
|
||||
screenX = viewX;
|
||||
screenY = viewY;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnAcceleratedPaint(PaintElementType type, Rect dirtyRect, IntPtr sharedHandle)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buffer, int width, int height)
|
||||
{
|
||||
var dirty = new Rectangle
|
||||
{
|
||||
Width = dirtyRect.Width,
|
||||
Height = dirtyRect.Height,
|
||||
X = dirtyRect.X,
|
||||
Y = dirtyRect.Y
|
||||
};
|
||||
var source = new BitmapInfo
|
||||
{
|
||||
Width = width,
|
||||
Height = height,
|
||||
BytesPerPixel = 4,
|
||||
Scan0 = buffer
|
||||
};
|
||||
|
||||
switch (_mode)
|
||||
{
|
||||
case BufferMode.Dirty:
|
||||
Unsafe.CopyRegion(source, _pSharedBuffer, dirty);
|
||||
break;
|
||||
case BufferMode.Full:
|
||||
{
|
||||
var target = source;
|
||||
target.Scan0 = _pSharedBuffer;
|
||||
Unsafe.UpdateRegion(source, target, dirty, dirty.Location);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_adapter?.Paint(dirty);
|
||||
}
|
||||
|
||||
public void OnCursorChange(IntPtr cursor, CursorType type, CursorInfo customCursorInfo)
|
||||
{
|
||||
}
|
||||
|
||||
public bool StartDragging(IDragData dragData, DragOperationsMask mask, int x, int y)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateDragCursor(DragOperationsMask operation)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnPopupShow(bool show)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnPopupSize(Rect rect)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnImeCompositionRangeChanged(Range selectedRange, Rect[] characterBounds)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public void OnVirtualKeyboardRequested(IBrowser browser, TextInputMode inputMode)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Remoting;
|
||||
using System.Runtime.Remoting.Channels;
|
||||
using System.Runtime.Remoting.Channels.Ipc;
|
||||
using System.Security.Permissions;
|
||||
using System.Threading.Tasks;
|
||||
using CefSharp;
|
||||
using CefSharp.OffScreen;
|
||||
|
||||
namespace RageCoop.Client.CefHost
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
[SecurityPermission(SecurityAction.Demand)]
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
Cef.Initialize(new CefSettings
|
||||
{
|
||||
BackgroundColor = 0x00
|
||||
});
|
||||
|
||||
var name = Guid.NewGuid().ToString();
|
||||
|
||||
var channel = new IpcChannel(name);
|
||||
ChannelServices.RegisterChannel(channel, false);
|
||||
RemotingConfiguration.RegisterActivatedServiceType(typeof(CefController));
|
||||
|
||||
|
||||
// Write to stdout so it can be read by the client
|
||||
Console.WriteLine(name);
|
||||
|
||||
var adapterUrl = Console.ReadLine();
|
||||
var adapterEntry = new ActivatedClientTypeEntry(typeof(CefAdapter), adapterUrl);
|
||||
Console.WriteLine("Registered adapter entry: " + adapterUrl);
|
||||
RemotingConfiguration.RegisterActivatedClientType(adapterEntry);
|
||||
|
||||
var channelData = (ChannelDataStore)channel.ChannelData;
|
||||
foreach (var uri in channelData.ChannelUris) Console.WriteLine("Channel URI: {0}", uri);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Util.GetParentProcess().WaitForExit();
|
||||
Console.WriteLine("Parent process terminated, exiting...");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
});
|
||||
|
||||
while (true)
|
||||
switch (Console.ReadLine())
|
||||
{
|
||||
case "exit":
|
||||
Cef.Shutdown();
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A utility class to determine a process parent.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Util
|
||||
{
|
||||
// These members must match PROCESS_BASIC_INFORMATION
|
||||
internal IntPtr Reserved1;
|
||||
internal IntPtr PebBaseAddress;
|
||||
internal IntPtr Reserved2_0;
|
||||
internal IntPtr Reserved2_1;
|
||||
internal IntPtr UniqueProcessId;
|
||||
internal IntPtr InheritedFromUniqueProcessId;
|
||||
|
||||
[DllImport("ntdll.dll")]
|
||||
private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass,
|
||||
ref Util processInformation, int processInformationLength, out int returnLength);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent process of the current process.
|
||||
/// </summary>
|
||||
/// <returns>An instance of the Process class.</returns>
|
||||
public static Process GetParentProcess()
|
||||
{
|
||||
return GetParentProcess(Process.GetCurrentProcess().Handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent process of specified process.
|
||||
/// </summary>
|
||||
/// <param name="id">The process id.</param>
|
||||
/// <returns>An instance of the Process class.</returns>
|
||||
public static Process GetParentProcess(int id)
|
||||
{
|
||||
var process = Process.GetProcessById(id);
|
||||
return GetParentProcess(process.Handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent process of a specified process.
|
||||
/// </summary>
|
||||
/// <param name="handle">The process handle.</param>
|
||||
/// <returns>An instance of the Process class.</returns>
|
||||
public static Process GetParentProcess(IntPtr handle)
|
||||
{
|
||||
var pbi = new Util();
|
||||
int returnLength;
|
||||
var status = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), out returnLength);
|
||||
if (status != 0)
|
||||
throw new Win32Exception(status);
|
||||
|
||||
try
|
||||
{
|
||||
return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// not found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("RageCoop.Client.CefHost")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("RageCoop.Client.CefHost")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2022")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("ba750e08-5e41-4b56-8ad5-875716d2ccea")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
@ -1,118 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\packages\CefSharp.Common.106.0.290\build\CefSharp.Common.props"
|
||||
Condition="Exists('..\..\packages\CefSharp.Common.106.0.290\build\CefSharp.Common.props')" />
|
||||
<Import Project="..\..\packages\cef.redist.x86.106.0.29\build\cef.redist.x86.props"
|
||||
Condition="Exists('..\..\packages\cef.redist.x86.106.0.29\build\cef.redist.x86.props')" />
|
||||
<Import Project="..\..\packages\cef.redist.x64.106.0.29\build\cef.redist.x64.props"
|
||||
Condition="Exists('..\..\packages\cef.redist.x64.106.0.29\build\cef.redist.x64.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
|
||||
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{BA750E08-5E41-4B56-8AD5-875716D2CCEA}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>RageCoop.Client.CefHost</RootNamespace>
|
||||
<AssemblyName>RageCoop.Client.CefHost</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<OutDir>..\..\bin\$(Configuration)\Client\SubProcess</OutDir>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="BitmapUtil">
|
||||
<HintPath>..\..\libs\BitmapUtil.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference
|
||||
Include="CefSharp, Version=106.0.290.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL"
|
||||
PrivateAssets="All">
|
||||
<HintPath>..\..\packages\CefSharp.Common.106.0.290\lib\net452\CefSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference
|
||||
Include="CefSharp.Core, Version=106.0.290.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL"
|
||||
PrivateAssets="All">
|
||||
<HintPath>..\..\packages\CefSharp.Common.106.0.290\lib\net452\CefSharp.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference
|
||||
Include="CefSharp.OffScreen, Version=106.0.290.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL"
|
||||
PrivateAssets="All">
|
||||
<HintPath>..\..\packages\CefSharp.OffScreen.106.0.290\lib\net462\CefSharp.OffScreen.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Runtime.Remoting" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CefController.cs" />
|
||||
<Compile Include="CefProcessor.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\cef.redist.x64.106.0.29\build\cef.redist.x64.props')"
|
||||
Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\cef.redist.x64.106.0.29\build\cef.redist.x64.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\cef.redist.x86.106.0.29\build\cef.redist.x86.props')"
|
||||
Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\cef.redist.x86.106.0.29\build\cef.redist.x86.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\CefSharp.Common.106.0.290\build\CefSharp.Common.props')"
|
||||
Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\CefSharp.Common.106.0.290\build\CefSharp.Common.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\CefSharp.Common.106.0.290\build\CefSharp.Common.targets')"
|
||||
Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\CefSharp.Common.106.0.290\build\CefSharp.Common.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\..\packages\CefSharp.Common.106.0.290\build\CefSharp.Common.targets"
|
||||
Condition="Exists('..\..\packages\CefSharp.Common.106.0.290\build\CefSharp.Common.targets')" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
if not exist "ref" mkdir "ref"
|
||||
copy "$(TargetFileName)" "ref\$(TargetName).dll" /y
|
||||
copy "CefSharp.OffScreen.dll" "ref\CefSharp.OffScreen.dll" /y
|
||||
copy "CefSharp.dll" "ref\CefSharp.dll" /y
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
|
||||
<StartAction>Program</StartAction>
|
||||
<StartProgram>M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\bin\Debug\Client\SubProcess\RageCoop.Client.CefHost.exe</StartProgram>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<packages>
|
||||
<package id="cef.redist.x64" version="106.0.29" targetFramework="net48" />
|
||||
<package id="cef.redist.x86" version="106.0.29" targetFramework="net48" />
|
||||
<package id="CefSharp.Common" version="106.0.290" targetFramework="net48" />
|
||||
<package id="CefSharp.OffScreen" version="106.0.290" targetFramework="net48" />
|
||||
</packages>
|
335214
Client/Data/Animations.json
335214
Client/Data/Animations.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,99 +0,0 @@
|
||||
{
|
||||
"Bullet": {
|
||||
"2861067768": "VEHICLE_WEAPON_INSURGENT_MINIGUN",
|
||||
"3683206664": "VEHICLE_WEAPON_TECHNICAL_MINIGUN",
|
||||
"3048454573": "VEHICLE_WEAPON_AKULA_TURRET_SINGLE",
|
||||
"476907586": "VEHICLE_WEAPON_AKULA_TURRET_DUAL",
|
||||
"431576697": "VEHICLE_WEAPON_AKULA_MINIGUN",
|
||||
"3405172033": "VEHICLE_WEAPON_ANNIHILATOR2_MINI",
|
||||
"1000258817": "VEHICLE_WEAPON_BARRAGE_TOP_MINIGUN",
|
||||
"525623141": "VEHICLE_WEAPON_BARRAGE_REAR_MINIGUN",
|
||||
"1338760315": "VEHICLE_WEAPON_CARACARA_MINIGUN",
|
||||
"490982948": "VEHICLE_WEAPON_DEATHBIKE_DUALMINIGUN",
|
||||
"3909880809": "VEHICLE_WEAPON_DEATHBIKE2_MINIGUN_LASER",
|
||||
"2600428406": "VEHICLE_WEAPON_DELUXO_MG",
|
||||
"1416047217": "VEHICLE_WEAPON_DUNE_MINIGUN",
|
||||
"3003147322": "VEHICLE_WEAPON_FLAMETHROWER",
|
||||
"2182329506": "VEHICLE_WEAPON_FLAMETHROWER_SCIFI",
|
||||
"855547631": "VEHICLE_WEAPON_HAVOK_MINIGUN",
|
||||
"2263283790": "VEHICLE_WEAPON_POUNDER2_MINI",
|
||||
"2431961420": "VEHICLE_WEAPON_SEASPARROW2_MINIGUN",
|
||||
"2667462330": "VEHICLE_WEAPON_SPEEDO4_TURRET_MINI",
|
||||
"3670375085": "VEHICLE_WEAPON_TAMPA_FIXEDMINIGUN",
|
||||
"1744687076": "VEHICLE_WEAPON_TAMPA_DUALMINIGUN",
|
||||
"1697521053": "VEHICLE_WEAPON_THRUSTER_MG",
|
||||
"376489128": "VEHICLE_WEAPON_TULA_MINIGUN",
|
||||
"4109257098": "VEHICLE_WEAPON_RCTANK_FLAME",
|
||||
"3959029566": "VEHICLE_WEAPON_CANNON_BLAZER",
|
||||
"1119849093": "WEAPON_MINIGUN",
|
||||
"4256881901": "WEAPON_DIGISCANNER",
|
||||
"1186503822": "VEHICLE_WEAPON_PLAYER_BUZZARD",
|
||||
"3056410471": "WEAPON_RAYMINIGUN",
|
||||
"729375873": "VEHICLE_WEAPON_TURRET_LIMO",
|
||||
"2756787765": "VEHICLE_WEAPON_TURRET_VALKYRIE",
|
||||
"50118905": "VEHICLE_WEAPON_RUINER_BULLET"
|
||||
},
|
||||
"Lazer": {
|
||||
"955522731": "VEHICLE_WEAPON_STRIKEFORCE_CANNON",
|
||||
"539292904": "WEAPON_EXPLOSION",
|
||||
"1638077257": "VEHICLE_WEAPON_PLAYER_SAVAGE"
|
||||
},
|
||||
"Others": {
|
||||
"3441901897": "WEAPON_BATTLEAXE",
|
||||
"4192643659": "WEAPON_BOTTLE",
|
||||
"2460120199": "WEAPON_DAGGER",
|
||||
"2343591895": "WEAPON_FLASHLIGHT",
|
||||
"3794977420": "WEAPON_GARBAGEBAG",
|
||||
"3494679629": "WEAPON_HANDCUFFS",
|
||||
"4191993645": "WEAPON_HATCHET",
|
||||
"3638508604": "WEAPON_KNUCKLE",
|
||||
"3713923289": "WEAPON_MACHETE",
|
||||
"2484171525": "WEAPON_POOLCUE",
|
||||
"2725352035": "WEAPON_UNARMED",
|
||||
"4194021054": "WEAPON_ANIMAL",
|
||||
"148160082": "WEAPON_COUGAR",
|
||||
"2578778090": "WEAPON_KNIFE",
|
||||
"1737195953": "WEAPON_NIGHTSTICK",
|
||||
"1317494643": "WEAPON_HAMMER",
|
||||
"2508868239": "WEAPON_BAT",
|
||||
"1141786504": "WEAPON_GOLFCLUB",
|
||||
"2227010557": "WEAPON_CROWBAR",
|
||||
"101631238": "WEAPON_FIREEXTINGUISHER",
|
||||
"883325847": "WEAPON_PETROLCAN",
|
||||
"2294779575": "WEAPON_BRIEFCASE",
|
||||
"28811031": "WEAPON_BRIEFCASE_02",
|
||||
"3450622333": "VEHICLE_WEAPON_SEARCHLIGHT",
|
||||
"3530961278": "VEHICLE_WEAPON_RADAR",
|
||||
"1223143800": "WEAPON_BARBED_WIRE",
|
||||
"4284007675": "WEAPON_DROWNING",
|
||||
"1936677264": "WEAPON_DROWNING_IN_VEHICLE",
|
||||
"2339582971": "WEAPON_BLEEDING",
|
||||
"2461879995": "WEAPON_ELECTRIC_FENCE",
|
||||
"3452007600": "WEAPON_FALL",
|
||||
"910830060": "WEAPON_EXHAUSTION",
|
||||
"3425972830": "WEAPON_HIT_BY_WATER_CANNON",
|
||||
"133987706": "WEAPON_RAMMED_BY_CAR",
|
||||
"2741846334": "WEAPON_RUN_OVER_BY_CAR",
|
||||
"341774354": "WEAPON_HELI_CRASH",
|
||||
"2971687502": "VEHICLE_WEAPON_ROTORS",
|
||||
"3750660587": "WEAPON_FIRE",
|
||||
"3854032506": "WEAPON_ANIMAL_RETRIEVER",
|
||||
"3146768957": "WEAPON_SMALL_DOG",
|
||||
"743550225": "WEAPON_TIGER_SHARK",
|
||||
"3030980043": "WEAPON_HAMMERHEAD_SHARK",
|
||||
"4198358245": "WEAPON_KILLER_WHALE",
|
||||
"861723357": "WEAPON_BOAR",
|
||||
"1205296881": "WEAPON_PIG",
|
||||
"1161062353": "WEAPON_COYOTE",
|
||||
"4106648222": "WEAPON_DEER",
|
||||
"955837630": "WEAPON_HEN",
|
||||
"2793925639": "WEAPON_RABBIT",
|
||||
"3799318422": "WEAPON_CAT",
|
||||
"94548753": "WEAPON_COW",
|
||||
"940833800": "WEAPON_STONE_HATCHET",
|
||||
"3756226112": "WEAPON_SWITCHBLADE",
|
||||
"419712736": "WEAPON_WRENCH",
|
||||
"406929569": "WEAPON_FERTILIZERCAN",
|
||||
"3126027122": "WEAPON_HAZARDCAN"
|
||||
}
|
||||
}
|
@ -1,329 +0,0 @@
|
||||
public enum WeaponHash : uint
|
||||
{
|
||||
Advancedrifle = 0xAF113F99,
|
||||
AirDefenceGun = 0x2C082D7D,
|
||||
AirstrikeRocket = 0x13579279,
|
||||
Animal = 0xF9FBAEBE,
|
||||
AnimalRetriever = 0xE5B7DE7A,
|
||||
Appistol = 0x22D8FE39,
|
||||
ArenaHomingMissile = 0x648A81D0,
|
||||
ArenaMachineGun = 0x34FDFF66,
|
||||
Assaultrifle = 0xBFEFFF6D,
|
||||
AssaultrifleMk2 = 0x394F415C,
|
||||
Assaultshotgun = 0xE284C527,
|
||||
Assaultsmg = 0xEFE7E2DF,
|
||||
Autoshotgun = 0x12E82D3D,
|
||||
Ball = 0x23C9F95C,
|
||||
BarbedWire = 0x48E7B178,
|
||||
Bat = 0x958A4A8F,
|
||||
Battleaxe = 0xCD274149,
|
||||
BirdCrap = 0x6D5E2801,
|
||||
Bleeding = 0x8B7333FB,
|
||||
Boar = 0x335CDADD,
|
||||
Bottle = 0xF9E6AA4B,
|
||||
Briefcase = 0x88C78EB7,
|
||||
Briefcase02 = 0x1B79F17,
|
||||
Bullpuprifle = 0x7F229F94,
|
||||
BullpuprifleMk2 = 0x84D6FAFD,
|
||||
Bullpupshotgun = 0x9D61E50F,
|
||||
Bzgas = 0xA0973D5E,
|
||||
Carbinerifle = 0x83BF0278,
|
||||
CarbinerifleMk2 = 0xFAD1F1C9,
|
||||
Cat = 0xE274FF96,
|
||||
Ceramicpistol = 0x2B5EF5EC,
|
||||
Combatmg = 0x7FD62962,
|
||||
CombatmgMk2 = 0xDBBD7280,
|
||||
Combatpdw = 0xA3D4D34,
|
||||
Combatpistol = 0x5EF9FEC4,
|
||||
Combatshotgun = 0x5A96BA4,
|
||||
Compactlauncher = 0x781FE4A,
|
||||
Compactrifle = 0x624FE830,
|
||||
Cougar = 0x8D4BE52,
|
||||
Cow = 0x5A2B311,
|
||||
Coyote = 0x453467D1,
|
||||
Crowbar = 0x84BD7BFD,
|
||||
Dagger = 0x92A27487,
|
||||
Dbshotgun = 0xEF951FBB,
|
||||
Deer = 0xF4C67A9E,
|
||||
Digiscanner = 0xFDBADCED,
|
||||
Doubleaction = 0x97EA20B8,
|
||||
Drowning = 0xFF58C4FB,
|
||||
DrowningInVehicle = 0x736F5990,
|
||||
ElectricFence = 0x92BD4EBB,
|
||||
Emplauncher = 0xDB26713A,
|
||||
Exhaustion = 0x364A29EC,
|
||||
Explosion = 0x2024F4E8,
|
||||
Fall = 0xCDC174B0,
|
||||
Fertilizercan = 0x184140A1,
|
||||
Fire = 0xDF8E89EB,
|
||||
Fireextinguisher = 0x60EC506,
|
||||
Firework = 0x7F7497E5,
|
||||
Flare = 0x497FACC3,
|
||||
Flaregun = 0x47757124,
|
||||
Flashlight = 0x8BB05FD7,
|
||||
Gadgetpistol = 0x57A4368C,
|
||||
Garbagebag = 0xE232C28C,
|
||||
Golfclub = 0x440E4788,
|
||||
Grenade = 0x93E220BD,
|
||||
Grenadelauncher = 0xA284510B,
|
||||
GrenadelauncherSmoke = 0x4DD2DC56,
|
||||
Gusenberg = 0x61012683,
|
||||
Hammer = 0x4E875F73,
|
||||
HammerheadShark = 0xB4A915CB,
|
||||
Handcuffs = 0xD04C944D,
|
||||
Hatchet = 0xF9DCBF2D,
|
||||
Hazardcan = 0xBA536372,
|
||||
Heavypistol = 0xD205520E,
|
||||
Heavyrifle = 0xC78D71B4,
|
||||
Heavyshotgun = 0x3AABBBAA,
|
||||
Heavysniper = 0xC472FE2,
|
||||
HeavysniperMk2 = 0xA914799,
|
||||
HeliCrash = 0x145F1012,
|
||||
Hen = 0x38F8ECBE,
|
||||
HitByWaterCannon = 0xCC34325E,
|
||||
Hominglauncher = 0x63AB0442,
|
||||
KillerWhale = 0xFA3DDCE5,
|
||||
Knife = 0x99B507EA,
|
||||
Knuckle = 0xD8DF3C3C,
|
||||
Machete = 0xDD5DF8D9,
|
||||
Machinepistol = 0xDB1AA450,
|
||||
Marksmanpistol = 0xDC4DB296,
|
||||
Marksmanrifle = 0xC734385A,
|
||||
MarksmanrifleMk2 = 0x6A6C02E0,
|
||||
Mg = 0x9D07F764,
|
||||
Microsmg = 0x13532244,
|
||||
Militaryrifle = 0x9D1F17E6,
|
||||
Minigun = 0x42BF8A85,
|
||||
Minismg = 0xBD248B55,
|
||||
Molotov = 0x24B17070,
|
||||
Musket = 0xA89CB99E,
|
||||
Navyrevolver = 0x917F6C8C,
|
||||
Nightstick = 0x678B81B1,
|
||||
PassengerRocket = 0x166218FF,
|
||||
Petrolcan = 0x34A67B97,
|
||||
Pig = 0x47D75EF1,
|
||||
Pipebomb = 0xBA45E8B8,
|
||||
Pistol = 0x1B06D571,
|
||||
Pistol50 = 0x99AEEB3B,
|
||||
PistolMk2 = 0xBFE256D4,
|
||||
Poolcue = 0x94117305,
|
||||
Proxmine = 0xAB564B93,
|
||||
Pumpshotgun = 0x1D073A89,
|
||||
PumpshotgunMk2 = 0x555AF99A,
|
||||
Rabbit = 0xA687EC07,
|
||||
Railgun = 0x6D544C99,
|
||||
RammedByCar = 0x7FC7D7A,
|
||||
Raycarbine = 0x476BF155,
|
||||
Rayminigun = 0xB62D1F67,
|
||||
Raypistol = 0xAF3696A1,
|
||||
Remotesniper = 0x33058E22,
|
||||
Revolver = 0xC1B3C3D1,
|
||||
RevolverMk2 = 0xCB96392F,
|
||||
Rpg = 0xB1CA77B1,
|
||||
RunOverByCar = 0xA36D413E,
|
||||
Sawnoffshotgun = 0x7846A318,
|
||||
SmallDog = 0xBB8FE23D,
|
||||
Smg = 0x2BE6766B,
|
||||
SmgMk2 = 0x78A97CD0,
|
||||
Smokegrenade = 0xFDBC8A50,
|
||||
Sniperrifle = 0x5FC3C11,
|
||||
Snowball = 0x787F0BB,
|
||||
Snspistol = 0xBFD21232,
|
||||
SnspistolMk2 = 0x88374054,
|
||||
Specialcarbine = 0xC0A3098D,
|
||||
SpecialcarbineMk2 = 0x969C3D67,
|
||||
Stickybomb = 0x2C3731D9,
|
||||
Stinger = 0x687652CE,
|
||||
StoneHatchet = 0x3813FC08,
|
||||
Stungun = 0x3656C8C1,
|
||||
StungunMp = 0x45CD9CF3,
|
||||
Switchblade = 0xDFE37640,
|
||||
TigerShark = 0x2C51AD11,
|
||||
Tranquilizer = 0x32A888BD,
|
||||
Unarmed = 0xA2719263,
|
||||
VehicleRocket = 0xBEFDC581,
|
||||
Vintagepistol = 0x83839C4,
|
||||
Wrench = 0x19044EE0,
|
||||
}
|
||||
|
||||
public enum VehicleWeaponHash : uint
|
||||
{
|
||||
Invalid = 0xFFFFFFFF,
|
||||
AkulaBarrage = 0x880D14F2,
|
||||
AkulaMinigun = 0x19B95679,
|
||||
AkulaMissile = 0x7CBE304C,
|
||||
AkulaTurretDual = 0x1C6D0842,
|
||||
AkulaTurretSingle = 0xB5B3B9AD,
|
||||
Annihilator2Barrage = 0x35D8CC90,
|
||||
Annihilator2Mini = 0xCAF6CD41,
|
||||
Annihilator2Missile = 0xC76BC6B7,
|
||||
ApcCannon = 0x138F71D8,
|
||||
ApcMg = 0xB56E4E4,
|
||||
ApcMissile = 0x44A56189,
|
||||
ArdentMg = 0xC44E4341,
|
||||
AvengerCannon = 0x9867203B,
|
||||
BarrageRearGl = 0xA44C228D,
|
||||
BarrageRearMg = 0x47894765,
|
||||
BarrageRearMinigun = 0x1F545F65,
|
||||
BarrageTopMg = 0xF7498994,
|
||||
BarrageTopMinigun = 0x3B9EBD01,
|
||||
Bomb = 0x9AF0B90C,
|
||||
BombCluster = 0xD28BCA3,
|
||||
BombGas = 0x5540A91E,
|
||||
BombIncendiary = 0x6AF7A717,
|
||||
BombStandardWide = 0x6EA548D0,
|
||||
BombushkaCannon = 0xD8443A59,
|
||||
BombushkaDualmg = 0x2C2B2D58,
|
||||
BombWater = 0xF444C4C8,
|
||||
Bruiser250calLaser = 0x3D6A0196,
|
||||
Bruiser50cal = 0xD73DC601,
|
||||
Brutus250calLaser = 0x68C7A4C3,
|
||||
Brutus50cal = 0xEB5E5C0A,
|
||||
CannonBlazer = 0xEBF9FF3E,
|
||||
CaracaraMg = 0x6C516BA8,
|
||||
CaracaraMinigun = 0x4FCBDC7B,
|
||||
ChernoMissile = 0xA247D03E,
|
||||
CometMg = 0xEAA835F3,
|
||||
Deathbike2MinigunLaser = 0xE90C0BE9,
|
||||
DeathbikeDualminigun = 0x1D43CE24,
|
||||
DeluxoMg = 0x9AFF6376,
|
||||
DeluxoMissile = 0xB4F96934,
|
||||
DogfighterMg = 0x5F1834E2,
|
||||
DogfighterMissile = 0xCA46F87D,
|
||||
Dominator450cal = 0xF80C9B0F,
|
||||
Dominator550calLaser = 0xB4246A5F,
|
||||
DuneGrenadelauncher = 0xA0FC710D,
|
||||
DuneMg = 0xD11507CF,
|
||||
DuneMinigun = 0x54672A71,
|
||||
EnemyLaser = 0x5D6660AB,
|
||||
Flamethrower = 0xB300643A,
|
||||
FlamethrowerScifi = 0x8213B4A2,
|
||||
Granger2Mg = 0xEAE2E19A,
|
||||
HackerMissile = 0x766FF7B1,
|
||||
HackerMissileHoming = 0x77EACF96,
|
||||
HalftrackDualmg = 0x4F6384FB,
|
||||
HalftrackQuadmg = 0x491B2E74,
|
||||
HavokMinigun = 0x32FE9EEF,
|
||||
HunterBarrage = 0x2ED14835,
|
||||
HunterCannon = 0x2A00AB1A,
|
||||
HunterMg = 0x42BA80A7,
|
||||
HunterMissile = 0x924A5F5,
|
||||
Impaler250cal = 0x5F565C09,
|
||||
Impaler350calLaser = 0x8CBDFC88,
|
||||
Imperator250calLaser = 0x7817C526,
|
||||
Imperator50cal = 0xB662C67B,
|
||||
InsurgentMinigun = 0xAA886DF8,
|
||||
Issi450cal = 0x7648E34D,
|
||||
Issi550calLaser = 0x767F6925,
|
||||
Jb700Mg = 0x373AD53C,
|
||||
KhanjaliCannon = 0x1E3ACFA0,
|
||||
KhanjaliCannonHeavy = 0x838B716D,
|
||||
KhanjaliGl = 0x178605E2,
|
||||
KhanjaliMg = 0x2A6F8E1D,
|
||||
KosatkaTorpedo = 0x62E2140E,
|
||||
MenacerMg = 0xDFCAF8A4,
|
||||
MicrolightMg = 0xC4E0216C,
|
||||
Mine = 0x59EAE9A4,
|
||||
MineEmp = 0x69E10D60,
|
||||
MineEmpRc = 0x5454B4C6,
|
||||
MineKinetic = 0x3C09584E,
|
||||
MineKineticRc = 0x252AF560,
|
||||
MineSlick = 0x56FACAC7,
|
||||
MineSlickRc = 0x84E87B17,
|
||||
MineSpike = 0xD96DA06C,
|
||||
MineSpikeRc = 0x7C2AFE51,
|
||||
MineTar = 0xF4418BA0,
|
||||
MineTarRc = 0x7D3474D6,
|
||||
MobileopsCannon = 0xE53E69A4,
|
||||
MogulDualnose = 0xE5F3AE2F,
|
||||
MogulDualturret = 0xBA277C01,
|
||||
MogulNose = 0xF6189F4A,
|
||||
MogulTurret = 0xE2FD135E,
|
||||
Monster3Glkin = 0xE5AE53DD,
|
||||
MortarExplosive = 0xA1A8CCD2,
|
||||
MortarKinetic = 0x632A22FD,
|
||||
Mule4Mg = 0x84558727,
|
||||
Mule4Missile = 0x4772F84B,
|
||||
Mule4TurretGl = 0xDD124A65,
|
||||
NightsharkMg = 0xA61AC574,
|
||||
NoseTurretValkyrie = 0x4170E491,
|
||||
Oppressor2Cannon = 0xD64D3469,
|
||||
Oppressor2Mg = 0xE2451DD6,
|
||||
Oppressor2Missile = 0x753A78F1,
|
||||
OppressorMg = 0xD9322EDD,
|
||||
OppressorMissile = 0x8BB7C63E,
|
||||
Paragon2Mg = 0x2CAC4286,
|
||||
PatrolboatDualmg = 0x4C2FB4E9,
|
||||
PlaneRocket = 0xCF0896E0,
|
||||
PlayerBullet = 0x4B139B2D,
|
||||
PlayerBuzzard = 0x46B89C8E,
|
||||
PlayerHunter = 0x9F1A91DE,
|
||||
PlayerLaser = 0xEFFD014B,
|
||||
PlayerLazer = 0xE2822A29,
|
||||
PlayerSavage = 0x61A31349,
|
||||
Pounder2Barrage = 0x926B8CE4,
|
||||
Pounder2Gl = 0x9318FF16,
|
||||
Pounder2Mini = 0x86E6F84E,
|
||||
Pounder2Missile = 0x9A8EA9A,
|
||||
Radar = 0xD276317E,
|
||||
RctankFlame = 0xF4EE498A,
|
||||
RctankGun = 0x52FCA619,
|
||||
RctankLazer = 0x57F22C50,
|
||||
RctankRocket = 0x76F744CB,
|
||||
RevolterMg = 0xBD5E626A,
|
||||
RogueCannon = 0xE72ABBC2,
|
||||
RogueMg = 0x97273CD,
|
||||
RogueMissile = 0x6C88E47D,
|
||||
Rotors = 0xB1205A4E,
|
||||
RuinerBullet = 0x2FCC0F9,
|
||||
RuinerRocket = 0x50DC6AB,
|
||||
SavestraMg = 0xEB41E84E,
|
||||
Scarab250calLaser = 0xE22DEDCC,
|
||||
Scarab50cal = 0x217FEF28,
|
||||
ScramjetMg = 0xDCE6112,
|
||||
ScramjetMissile = 0xBCE908DB,
|
||||
SeabreezeMg = 0x51B8D4E8,
|
||||
Searchlight = 0xCDAC517D,
|
||||
Seasparrow2Minigun = 0x90F4C94C,
|
||||
Slamvan450cal = 0x3AAB6E6B,
|
||||
Slamvan550calLaser = 0x519543AE,
|
||||
SpaceRocket = 0xF8A3939F,
|
||||
Speedo4Mg = 0xC7FCF93C,
|
||||
Speedo4TurretMg = 0xD6561141,
|
||||
Speedo4TurretMini = 0x9EFE3EBA,
|
||||
StrikeforceBarrage = 0x39BC6683,
|
||||
StrikeforceCannon = 0x38F41EAB,
|
||||
StrikeforceMissile = 0x1EF01D8A,
|
||||
SubcarMg = 0x461DDDB0,
|
||||
SubcarMissile = 0xD4897C0E,
|
||||
SubcarTorpedo = 0xE783C3BA,
|
||||
SubMissileHoming = 0xAAE74AC1,
|
||||
TampaDualminigun = 0x67FDCFE4,
|
||||
TampaFixedminigun = 0xDAC57AAD,
|
||||
TampaMissile = 0x9E5840A2,
|
||||
TampaMortar = 0x3C83C410,
|
||||
Tank = 0x73F7C04B,
|
||||
TechnicalMinigun = 0xDB894608,
|
||||
ThrusterMg = 0x652E1D9D,
|
||||
ThrusterMissile = 0x4635DD15,
|
||||
TrailerDualaa = 0x808C4D4C,
|
||||
TrailerMissile = 0x145599F7,
|
||||
TrailerQuadmg = 0x4711B02C,
|
||||
TulaDualmg = 0xB0D15C0B,
|
||||
TulaMg = 0x488BD081,
|
||||
TulaMinigun = 0x1670C4A8,
|
||||
TulaNosemg = 0x419D8E15,
|
||||
TurretBoxville = 0xB54F4918,
|
||||
TurretDinghy550cal = 0xB3B155FD,
|
||||
TurretInsurgent = 0x44DB5498,
|
||||
TurretLimo = 0x2B796481,
|
||||
TurretPatrolboat50cal = 0xDF4EA041,
|
||||
TurretTechnical = 0x7FD2EA0B,
|
||||
TurretValkyrie = 0xA4513E35,
|
||||
VigilanteMg = 0xF4077EE7,
|
||||
VigilanteMissile = 0x504DA665,
|
||||
ViserisMg = 0x87A02E06,
|
||||
VolatolDualmg = 0x4497AC40,
|
||||
Zr380250calLaser = 0x220093BC,
|
||||
Zr38050cal = 0x6AB93C82,
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +0,0 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
@ -1,52 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<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="..\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>
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resource.resx">
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,24 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace GTA
|
||||
{
|
||||
internal class Console
|
||||
{
|
||||
private static readonly SHVDN.Console console = AppDomain.CurrentDomain.GetData("Console") as SHVDN.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,216 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using GTA.UI;
|
||||
using SHVDN;
|
||||
using Console = GTA.Console;
|
||||
|
||||
namespace RageCoop.Client.Loader
|
||||
{
|
||||
public class LoaderContext : MarshalByRefObject, IDisposable
|
||||
{
|
||||
#region PRIMARY-LOADING-LOGIC
|
||||
|
||||
public static ConcurrentDictionary<string, LoaderContext> LoadedDomains =>
|
||||
new ConcurrentDictionary<string, LoaderContext>(_loadedDomains);
|
||||
|
||||
private static readonly ConcurrentDictionary<string, LoaderContext> _loadedDomains =
|
||||
new ConcurrentDictionary<string, LoaderContext>();
|
||||
|
||||
public bool UnloadRequested;
|
||||
public string BaseDirectory => AppDomain.CurrentDomain.BaseDirectory;
|
||||
private ScriptDomain CurrentDomain => ScriptDomain.CurrentDomain;
|
||||
|
||||
public static void CheckForUnloadRequest()
|
||||
{
|
||||
lock (_loadedDomains)
|
||||
{
|
||||
foreach (var p in _loadedDomains.Values)
|
||||
if (p.UnloadRequested)
|
||||
Unload(p);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsLoaded(string dir)
|
||||
{
|
||||
return _loadedDomains.ContainsKey(Path.GetFullPath(dir).ToLower());
|
||||
}
|
||||
|
||||
public static LoaderContext Load(string dir)
|
||||
{
|
||||
lock (_loadedDomains)
|
||||
{
|
||||
dir = Path.GetFullPath(dir).ToLower();
|
||||
if (IsLoaded(dir)) throw new Exception("Already loaded");
|
||||
ScriptDomain newDomain = null;
|
||||
try
|
||||
{
|
||||
dir = Path.GetFullPath(dir).ToLower();
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
// Delete API assemblies
|
||||
Directory.GetFiles(dir, "ScriptHookVDotNet*", SearchOption.AllDirectories).ToList()
|
||||
.ForEach(x => File.Delete(x));
|
||||
|
||||
|
||||
newDomain = ScriptDomain.Load(
|
||||
Directory.GetParent(typeof(ScriptDomain).Assembly.Location).FullName, dir);
|
||||
|
||||
newDomain.AppDomain.SetData("Primary", ScriptDomain.CurrentDomain);
|
||||
|
||||
newDomain.AppDomain.SetData("Console",
|
||||
ScriptDomain.CurrentDomain.AppDomain.GetData("Console"));
|
||||
|
||||
// Copy to target domain base directory
|
||||
// Delete loader assembly
|
||||
var loaderPath = Path.Combine(dir, Path.GetFileName(typeof(LoaderContext).Assembly.Location));
|
||||
if (File.Exists(loaderPath)) File.Delete(loaderPath);
|
||||
|
||||
var context = (LoaderContext)newDomain.AppDomain.CreateInstanceFromAndUnwrap(
|
||||
typeof(LoaderContext).Assembly.Location,
|
||||
typeof(LoaderContext).FullName, false,
|
||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||||
null,
|
||||
new object[] { }
|
||||
, null, null);
|
||||
|
||||
newDomain.AppDomain.SetData("RageCoop.Client.LoaderContext", context);
|
||||
newDomain.Start();
|
||||
|
||||
_loadedDomains.TryAdd(dir, context);
|
||||
return _loadedDomains[dir];
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Notification.Show(ex.ToString());
|
||||
Console.Error(ex);
|
||||
if (newDomain != null) ScriptDomain.Unload(newDomain);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Assembly ResolveLoader(object sender, ResolveEventArgs args)
|
||||
{
|
||||
Log.Message(Log.Level.Debug, "resolving assembly " + args.Name);
|
||||
if (args.Name == typeof(LoaderContext).Assembly.GetName().Name) return typeof(LoaderContext).Assembly;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void Unload(LoaderContext domain)
|
||||
{
|
||||
lock (_loadedDomains)
|
||||
{
|
||||
var name = domain.CurrentDomain.Name;
|
||||
Console.Info("Unloading domain: " + name);
|
||||
if (!_loadedDomains.TryRemove(domain.BaseDirectory.ToLower(), out _))
|
||||
throw new Exception("Failed to remove domain from list");
|
||||
domain.Dispose();
|
||||
ScriptDomain.Unload(domain.CurrentDomain);
|
||||
Console.Info("Unloaded domain: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Unload(string dir)
|
||||
{
|
||||
lock (_loadedDomains)
|
||||
{
|
||||
Unload(_loadedDomains[Path.GetFullPath(dir).ToLower()]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TickAll()
|
||||
{
|
||||
lock (_loadedDomains)
|
||||
{
|
||||
foreach (var c in _loadedDomains.Values) c.DoTick();
|
||||
}
|
||||
}
|
||||
|
||||
public static void KeyEventAll(Keys keys, bool status)
|
||||
{
|
||||
lock (_loadedDomains)
|
||||
{
|
||||
foreach (var c in _loadedDomains.Values) c.DoKeyEvent(keys, status);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnloadAll()
|
||||
{
|
||||
lock (_loadedDomains)
|
||||
{
|
||||
foreach (var d in _loadedDomains.Values.ToArray()) Unload(d);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LOAD-CONTEXT
|
||||
|
||||
private readonly Action _domainDoTick;
|
||||
private readonly Action<Keys, bool> _domainDoKeyEvent;
|
||||
|
||||
private LoaderContext()
|
||||
{
|
||||
AppDomain.CurrentDomain.DomainUnload += (s, e) => Dispose();
|
||||
|
||||
var tickMethod = typeof(ScriptDomain).GetMethod("DoTick", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var doKeyEventMethod =
|
||||
typeof(ScriptDomain).GetMethod("DoKeyEvent", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
// Create delegates to avoid using reflection to call method each time, which is slow
|
||||
_domainDoTick = (Action)Delegate.CreateDelegate(typeof(Action), CurrentDomain, tickMethod);
|
||||
_domainDoKeyEvent =
|
||||
(Action<Keys, bool>)Delegate.CreateDelegate(typeof(Action<Keys, bool>), CurrentDomain,
|
||||
doKeyEventMethod);
|
||||
|
||||
Console.Info(
|
||||
$"Loaded domain: {AppDomain.CurrentDomain.FriendlyName}, {AppDomain.CurrentDomain.BaseDirectory}");
|
||||
}
|
||||
|
||||
public static ScriptDomain PrimaryDomain => AppDomain.CurrentDomain.GetData("Primary") as ScriptDomain;
|
||||
|
||||
public static LoaderContext CurrentContext =>
|
||||
(LoaderContext)AppDomain.CurrentDomain.GetData("RageCoop.Client.LoaderContext");
|
||||
|
||||
/// <summary>
|
||||
/// Request the current domain to be unloaded
|
||||
/// </summary>
|
||||
public static void RequestUnload()
|
||||
{
|
||||
if (PrimaryDomain == null)
|
||||
throw new NotSupportedException(
|
||||
"Current domain not loaded by the loader therefore cannot be unloaded automatically");
|
||||
CurrentContext.UnloadRequested = true;
|
||||
}
|
||||
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DoTick()
|
||||
{
|
||||
_domainDoTick();
|
||||
}
|
||||
|
||||
public void DoKeyEvent(Keys key, bool status)
|
||||
{
|
||||
_domainDoKeyEvent(key, status);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (PrimaryDomain == null) return;
|
||||
AppDomain.CurrentDomain.SetData("Primary", null);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using GTA;
|
||||
using GTA.UI;
|
||||
using SHVDN;
|
||||
using Script = GTA.Script;
|
||||
|
||||
namespace RageCoop.Client.Loader
|
||||
{
|
||||
[ScriptAttributes(Author = "RageCoop", NoScriptThread = true,
|
||||
SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V")]
|
||||
public class Main : Script
|
||||
{
|
||||
private static readonly string GameDir = Directory.GetParent(typeof(ScriptDomain).Assembly.Location).FullName;
|
||||
private static readonly string ScriptsLocation = Path.Combine(GameDir, "RageCoop", "Scripts");
|
||||
private bool _loaded;
|
||||
|
||||
public Main()
|
||||
{
|
||||
if (LoaderContext.PrimaryDomain != null) return;
|
||||
Tick += OnTick;
|
||||
KeyDown += (s, e) => LoaderContext.KeyEventAll(e.KeyCode, true);
|
||||
KeyUp += (s, e) => LoaderContext.KeyEventAll(e.KeyCode, false);
|
||||
Aborted += (s, e) => LoaderContext.UnloadAll();
|
||||
}
|
||||
|
||||
private void OnTick(object sender, EventArgs e)
|
||||
{
|
||||
if (!_loaded)
|
||||
{
|
||||
_loaded = !Game.IsLoading;
|
||||
return;
|
||||
}
|
||||
|
||||
LoaderContext.CheckForUnloadRequest();
|
||||
if (!LoaderContext.IsLoaded(ScriptsLocation))
|
||||
{
|
||||
if (!File.Exists(Path.Combine(ScriptsLocation, "RageCoop.Client.dll")))
|
||||
{
|
||||
Notification.Show("~r~Main assembly is missing, please re-install the client");
|
||||
Abort();
|
||||
return;
|
||||
}
|
||||
|
||||
LoaderContext.Load(ScriptsLocation);
|
||||
}
|
||||
|
||||
LoaderContext.TickAll();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("RageCoop.Client.Loader")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("RageCoop.Client.Loader")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2022")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("fc8cbdbb-6dc3-43af-b34d-092e476410a5")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
@ -1,58 +0,0 @@
|
||||
<?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>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>RageCoop.Client.Loader</RootNamespace>
|
||||
<AssemblyName>RageCoop.Client.Loader</AssemblyName>
|
||||
<OutPutPath>..\..\bin\$(Configuration)\Client\Loader</OutPutPath>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>DEBUG</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ScriptHookVDotNet3, Version=3.5.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>True</SpecificVersion>
|
||||
<HintPath>..\..\libs\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
|
||||
<Reference Include="ScriptHookVDotNet">
|
||||
<HintPath>..\..\libs\ScriptHookVDotNet.asi</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Console.cs" />
|
||||
<Compile Include="LoaderContext.cs" />
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
@ -1,47 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using RageCoop.Core.Scripting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
public class ClientFile : ResourceFile
|
||||
{
|
||||
public ClientFile() {
|
||||
GetStream = GetStreamMethod;
|
||||
}
|
||||
|
||||
[JsonInclude]
|
||||
public string FullPath { get; internal set; }
|
||||
Stream GetStreamMethod()
|
||||
{
|
||||
if (IsDirectory)
|
||||
{
|
||||
return File.Open(FullPath, FileMode.Open);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot open directory as file");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
using RageCoop.Core.Scripting;
|
||||
using SHVDN;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class ClientResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the resource
|
||||
/// </summary>
|
||||
[JsonInclude]
|
||||
public string Name { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Directory where the scripts is loaded from
|
||||
/// </summary>
|
||||
[JsonInclude]
|
||||
public string ScriptsDirectory { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// A resource-specific folder that can be used to store your files.
|
||||
/// </summary>
|
||||
[JsonInclude]
|
||||
public string DataFolder { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="ClientFile" /> where this script is loaded from.
|
||||
/// </summary>
|
||||
[JsonInclude]
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
<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>
|
@ -1,28 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
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,93 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using GTA;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
[ScriptAttributes(Author = "RageCoop", SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V")]
|
||||
internal class DevTool : Script
|
||||
{
|
||||
public static Vehicle ToMark;
|
||||
public static Script Instance;
|
||||
|
||||
public DevTool()
|
||||
{
|
||||
Instance = this;
|
||||
Pause();
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
base.OnTick();
|
||||
|
||||
foreach (var p in World.GetAllPeds()) DrawWeaponBone(p);
|
||||
if (ToMark == null) return;
|
||||
|
||||
if (WeaponUtil.VehicleWeapons.TryGetValue((uint)(int)ToMark.Model, out var info))
|
||||
foreach (var ws in info.Weapons)
|
||||
foreach (var w in ws.Value.Bones)
|
||||
DrawBone(w.BoneName, ws.Value.Name + ":" + ws.Key.ToHex());
|
||||
var P = Game.Player.Character;
|
||||
var b = ToMark.GetMuzzleBone(P.VehicleWeapon);
|
||||
if (b != null) World.DrawLine(b.Position, b.Position + b.ForwardVector * 5, Color.Brown);
|
||||
}
|
||||
|
||||
public static void DrawWeaponBone(Ped p)
|
||||
{
|
||||
var wb = p.Weapons?.CurrentWeaponObject?.Bones["gun_muzzle"];
|
||||
if (wb?.IsValid == true) World.DrawLine(wb.Position, wb.Position + wb.RightVector, Color.Blue);
|
||||
if (wb?.IsValid == true) World.DrawLine(wb.Position, wb.Position + wb.ForwardVector, Color.Red);
|
||||
if (wb?.IsValid == true) World.DrawLine(wb.Position, wb.Position + wb.UpVector, Color.Green);
|
||||
|
||||
}
|
||||
private void FindAndDraw()
|
||||
{
|
||||
DrawBone("weapon_1a");
|
||||
DrawBone("weapon_1b");
|
||||
DrawBone("weapon_1c");
|
||||
DrawBone("weapon_1d");
|
||||
DrawBone("weapon_2a");
|
||||
DrawBone("weapon_2b");
|
||||
DrawBone("weapon_2c");
|
||||
DrawBone("weapon_2d");
|
||||
DrawBone("weapon_3a");
|
||||
DrawBone("weapon_3b");
|
||||
DrawBone("weapon_3c");
|
||||
DrawBone("weapon_3d");
|
||||
DrawBone("weapon_4a");
|
||||
DrawBone("weapon_4b");
|
||||
DrawBone("weapon_4c");
|
||||
DrawBone("weapon_4d");
|
||||
DrawBone("weapon_1e");
|
||||
DrawBone("weapon_1f");
|
||||
DrawBone("weapon_1g");
|
||||
DrawBone("weapon_1h");
|
||||
DrawBone("weapon_2e");
|
||||
DrawBone("weapon_2f");
|
||||
DrawBone("weapon_2g");
|
||||
DrawBone("weapon_2h");
|
||||
DrawBone("weapon_3e");
|
||||
DrawBone("weapon_3f");
|
||||
DrawBone("weapon_3g");
|
||||
DrawBone("weapon_3h");
|
||||
DrawBone("weapon_4e");
|
||||
DrawBone("weapon_4f");
|
||||
DrawBone("weapon_4g");
|
||||
DrawBone("weapon_4h");
|
||||
}
|
||||
|
||||
private void DrawBone(string name, string text = null)
|
||||
{
|
||||
text = text ?? name;
|
||||
var b = ToMark.Bones[name];
|
||||
if (b.IsValid)
|
||||
{
|
||||
var start = b.Position;
|
||||
var end = b.Position + b.ForwardVector * 5;
|
||||
World.DrawLine(start, end, Color.AliceBlue);
|
||||
Util.DrawTextFromCoord(end, text, 0.35f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using DXHook.Hook.Common;
|
||||
using GTA;
|
||||
using RageCoop.Client.CefHost;
|
||||
using RageCoop.Client.Scripting;
|
||||
|
||||
namespace RageCoop.Client.GUI
|
||||
{
|
||||
public class CefClient
|
||||
{
|
||||
public readonly int Id;
|
||||
internal CefAdapter Adapter;
|
||||
internal CefController Controller;
|
||||
internal ImageElement MainFrame;
|
||||
|
||||
internal CefClient(int id, Size size)
|
||||
{
|
||||
Id = id;
|
||||
Controller = CefController.Create(id, size, out Adapter, BufferMode.Full);
|
||||
MainFrame = new ImageElement(size.Width, size.Height, 4, Adapter.PtrBuffer);
|
||||
Adapter.OnPaint += (len, dirty) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Image is using same shared buffer, so just need to make it re-copied to GPU
|
||||
MainFrame.Invalidate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
API.Logger.Error(ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
internal void Destroy()
|
||||
{
|
||||
Controller.Dispose();
|
||||
Adapter.Dispose();
|
||||
MainFrame.Dispose();
|
||||
}
|
||||
|
||||
public Point GetLocationInFrame(Point screenPos)
|
||||
{
|
||||
screenPos.X -= MainFrame.Location.X;
|
||||
screenPos.Y -= MainFrame.Location.Y;
|
||||
return screenPos;
|
||||
}
|
||||
|
||||
public Point GetLocationInCef(Point screenPos)
|
||||
{
|
||||
var p = GetLocationInFrame(screenPos);
|
||||
p.X = (int)(p.X / Scale);
|
||||
p.Y = (int)(p.Y / Scale);
|
||||
return p;
|
||||
}
|
||||
|
||||
internal bool PointInArea(Point screen)
|
||||
{
|
||||
screen = GetLocationInFrame(screen);
|
||||
return screen.X.IsBetween(0, Width) && screen.Y.IsBetween(0, Height);
|
||||
}
|
||||
|
||||
internal void Tick()
|
||||
{
|
||||
var mousePos = Util.CursorPosition;
|
||||
if (!PointInArea(mousePos)) return;
|
||||
var pos = GetLocationInCef(mousePos);
|
||||
if (Game.IsControlJustPressed(Control.CursorAccept))
|
||||
Controller.SendMouseClick(pos.X, pos.Y, 0, MouseButton.Left, false, 1);
|
||||
else if (Game.IsControlJustReleased(Control.CursorAccept))
|
||||
Controller.SendMouseClick(pos.X, pos.Y, 0, MouseButton.Left, true, 1);
|
||||
}
|
||||
|
||||
#region FRAME-APPERANCE
|
||||
|
||||
public float Scale
|
||||
{
|
||||
get => MainFrame.Scale;
|
||||
set => MainFrame.Scale = value;
|
||||
}
|
||||
|
||||
public Color Tint
|
||||
{
|
||||
get => MainFrame.Tint;
|
||||
set => MainFrame.Tint = value;
|
||||
}
|
||||
|
||||
public byte Opacity
|
||||
{
|
||||
get => MainFrame.Opacity;
|
||||
set => MainFrame.Opacity = value;
|
||||
}
|
||||
|
||||
public Point Location
|
||||
{
|
||||
get => MainFrame.Location;
|
||||
set => MainFrame.Location = value;
|
||||
}
|
||||
|
||||
public int Width => MainFrame.Width;
|
||||
public int Height => MainFrame.Height;
|
||||
|
||||
public bool Hidden
|
||||
{
|
||||
get => MainFrame.Hidden;
|
||||
set => MainFrame.Hidden = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
using RageCoop.Client.CefHost;
|
||||
using RageCoop.Client.Scripting;
|
||||
using RageCoop.Core;
|
||||
using static RageCoop.Client.Shared;
|
||||
|
||||
namespace RageCoop.Client.GUI
|
||||
{
|
||||
internal static class CefManager
|
||||
{
|
||||
private static readonly ConcurrentDictionary<int, CefClient> Clients =
|
||||
new ConcurrentDictionary<int, CefClient>();
|
||||
|
||||
private static readonly Overlay CefOverlay = new Overlay
|
||||
{
|
||||
Elements = new List<IOverlayElement>(),
|
||||
Hidden = false
|
||||
};
|
||||
|
||||
static CefManager()
|
||||
{
|
||||
Main.CefRunning = true;
|
||||
HookManager.Initialize();
|
||||
CefController.Initialize(CefSubProcessPath);
|
||||
CefController.OnCefMessage = m => API.Logger.Debug(m);
|
||||
HookManager.AddOverLay(CefOverlay);
|
||||
}
|
||||
|
||||
public static CefClient ActiveClient { get; set; }
|
||||
|
||||
public static void Tick()
|
||||
{
|
||||
if (ActiveClient != null)
|
||||
{
|
||||
Game.DisableAllControlsThisFrame();
|
||||
Function.Call(Hash.SET_MOUSE_CURSOR_THIS_FRAME);
|
||||
ActiveClient.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
public static void KeyDown(Keys key)
|
||||
{
|
||||
}
|
||||
|
||||
public static void KeyUp(Keys key)
|
||||
{
|
||||
}
|
||||
|
||||
public static bool DestroyClient(CefClient client)
|
||||
{
|
||||
lock (Clients)
|
||||
{
|
||||
if (Clients.TryRemove(client.Id, out var c) && client == c)
|
||||
{
|
||||
client.Destroy();
|
||||
CefOverlay.Elements.Remove(client.MainFrame);
|
||||
if (ActiveClient == client) ActiveClient = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static CefClient CreateClient(Size size)
|
||||
{
|
||||
lock (Clients)
|
||||
{
|
||||
var id = 0;
|
||||
while (id == 0 || Clients.ContainsKey(id)) id = CoreUtils.RandInt(0, int.MaxValue);
|
||||
var client = new CefClient(id, size);
|
||||
if (Clients.TryAdd(id, client))
|
||||
{
|
||||
CefOverlay.Elements.Add(client.MainFrame);
|
||||
return client;
|
||||
}
|
||||
|
||||
API.Logger.Warning("Failed to create CefClient");
|
||||
client.Destroy();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CleanUp()
|
||||
{
|
||||
Main.CefRunning = false;
|
||||
ActiveClient = null;
|
||||
try
|
||||
{
|
||||
lock (Clients)
|
||||
{
|
||||
foreach (var c in Clients.Values) DestroyClient(c);
|
||||
|
||||
Clients.Clear();
|
||||
}
|
||||
|
||||
CefController.ShutDown();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
API.Logger.Error(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using DXHook.Hook;
|
||||
using DXHook.Hook.Common;
|
||||
using DXHook.Interface;
|
||||
using RageCoop.Client.Scripting;
|
||||
|
||||
namespace RageCoop.Client.GUI
|
||||
{
|
||||
internal static class HookManager
|
||||
{
|
||||
public static readonly CaptureInterface Interface = new CaptureInterface();
|
||||
private static DXHookD3D11 _hook;
|
||||
public static Overlay DefaultOverlay = new Overlay();
|
||||
public static bool Hooked => _hook != null;
|
||||
|
||||
public static void GetOverlays()
|
||||
{
|
||||
new List<IOverlay>(_hook.Overlays);
|
||||
}
|
||||
|
||||
public static void AddOverLay(IOverlay ol)
|
||||
{
|
||||
_hook.Overlays.Add(ol);
|
||||
_hook.IsOverlayUpdatePending = true;
|
||||
}
|
||||
|
||||
public static void RemoveOverlay(IOverlay ol)
|
||||
{
|
||||
_hook.Overlays.Remove(ol);
|
||||
_hook.IsOverlayUpdatePending = true;
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (_hook != null) return;
|
||||
_hook = new DXHookD3D11(Interface);
|
||||
_hook.Config = new CaptureConfig
|
||||
{
|
||||
Direct3DVersion = Direct3DVersion.Direct3D11,
|
||||
ShowOverlay = true
|
||||
};
|
||||
_hook.Overlays = new List<IOverlay>();
|
||||
_hook.Hook();
|
||||
_hook.OnPresent += Present;
|
||||
DefaultOverlay.Elements = new List<IOverlayElement>();
|
||||
AddOverLay(DefaultOverlay);
|
||||
Interface.RemoteMessage += m => { API.Logger.Debug("DXHook: " + m.Message); };
|
||||
API.Logger.Debug("Hooked DX3D11");
|
||||
}
|
||||
|
||||
private static void Present()
|
||||
{
|
||||
}
|
||||
|
||||
public static void CleanUp()
|
||||
{
|
||||
_hook?.Cleanup();
|
||||
_hook?.Dispose();
|
||||
_hook = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,420 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
using GTA.UI;
|
||||
using LemonUI.Elements;
|
||||
using LemonUI.Menus;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Client.Menus;
|
||||
using RageCoop.Client.Scripting;
|
||||
using RageCoop.Core;
|
||||
using Control = GTA.Control;
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
[ScriptAttributes(Author = "RageCoop", SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V", NoScriptThread = true)]
|
||||
internal class Main : Script
|
||||
{
|
||||
internal static Version ModVersion = typeof(Main).Assembly.GetName().Version;
|
||||
|
||||
internal static int LocalPlayerID = 0;
|
||||
|
||||
internal static RelationshipGroup SyncedPedsGroup;
|
||||
|
||||
internal static ClientSettings Settings = null;
|
||||
internal static Chat MainChat = null;
|
||||
internal static Stopwatch Counter = new();
|
||||
internal static Logger Log = null;
|
||||
internal static ulong Ticked = 0;
|
||||
internal static Vector3 PlayerPosition;
|
||||
internal static Resources MainRes = null;
|
||||
|
||||
public static Ped P;
|
||||
public static Vehicle V;
|
||||
public static Vehicle LastV;
|
||||
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()
|
||||
{
|
||||
Instance = this;
|
||||
Directory.CreateDirectory(DataPath);
|
||||
try
|
||||
{
|
||||
Settings = Util.ReadSettings();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Notification.Show("Malformed configuration, overwriting with default values...");
|
||||
Settings = new();
|
||||
Util.SaveSettings();
|
||||
}
|
||||
|
||||
Log = new Logger()
|
||||
{
|
||||
FlushImmediately = true,
|
||||
Writers = null,
|
||||
#if DEBUG
|
||||
LogLevel = 0,
|
||||
#else
|
||||
LogLevel = Settings.LogLevel,
|
||||
#endif
|
||||
};
|
||||
Log.OnFlush += (line, formatted) =>
|
||||
{
|
||||
SHVDN.Logger.Write($"[RageCoop] {line.Message}", (uint)line.LogLevel);
|
||||
};
|
||||
|
||||
// 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)
|
||||
{
|
||||
throw new NotSupportedException("Please update your GTA5 to v1.0.1290 or newer!");
|
||||
}
|
||||
|
||||
MainRes = new();
|
||||
|
||||
|
||||
|
||||
Log.Info(
|
||||
$"Main script initialized");
|
||||
|
||||
BaseScript.OnStart();
|
||||
SyncedPedsGroup = World.AddRelationshipGroup("SYNCPED");
|
||||
Game.Player.Character.RelationshipGroup.SetRelationshipBetweenGroups(SyncedPedsGroup, Relationship.Neutral,
|
||||
true);
|
||||
MainChat = new Chat();
|
||||
|
||||
Util.NativeMemory();
|
||||
Counter.Restart();
|
||||
|
||||
}
|
||||
protected override void OnTick()
|
||||
{
|
||||
base.OnTick();
|
||||
|
||||
var lastVehicleHandle = Call<int>(GET_PLAYERS_LAST_VEHICLE);
|
||||
var playerHandle = Call<int>(PLAYER_PED_ID);
|
||||
|
||||
if (LastV?.Handle != lastVehicleHandle)
|
||||
LastV = Entity.FromHandle(lastVehicleHandle) as Vehicle;
|
||||
|
||||
if (P?.Handle != playerHandle)
|
||||
P = (Ped)Entity.FromHandle(playerHandle);
|
||||
|
||||
var playerVehHandle = Call<int>(GET_VEHICLE_PED_IS_IN, P.Handle, false);
|
||||
if (V?.Handle != playerVehHandle)
|
||||
V = Entity.FromHandle(playerVehHandle) as Vehicle;
|
||||
|
||||
PlayerPosition = P.ReadPosition();
|
||||
FPS = Game.FPS;
|
||||
|
||||
#if CEF
|
||||
if (CefRunning)
|
||||
{
|
||||
CefManager.Tick();
|
||||
}
|
||||
#endif
|
||||
if (!Networking.IsOnServer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
EntityPool.DoSync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
}
|
||||
|
||||
|
||||
if (Game.TimeScale != 1.0f)
|
||||
{
|
||||
Game.TimeScale = 1;
|
||||
}
|
||||
|
||||
if (Networking.ShowNetworkInfo)
|
||||
{
|
||||
new ScaledText(new PointF(200, 0),
|
||||
$"L: {Networking.Latency * 1000:N0}ms", 0.5f)
|
||||
{ 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(200, 60),
|
||||
$"S: {NetUtility.ToHumanReadable(Statistics.BytesUpPerSecond)}/s", 0.5f)
|
||||
{ Alignment = Alignment.Center }.Draw();
|
||||
}
|
||||
|
||||
MainChat.Tick();
|
||||
PlayerList.Tick();
|
||||
if (!API.Config.EnableAutoRespawn)
|
||||
{
|
||||
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)
|
||||
{
|
||||
Call(SET_FADE_OUT_AFTER_DEATH, false);
|
||||
|
||||
if (P.Health != 1)
|
||||
{
|
||||
P.Health = 1;
|
||||
Game.Player.WantedLevel = 0;
|
||||
Log.Debug("Player died.");
|
||||
API.Events.InvokePlayerDied();
|
||||
}
|
||||
|
||||
Screen.StopEffects();
|
||||
}
|
||||
else
|
||||
{
|
||||
Call(DISPLAY_HUD, true);
|
||||
}
|
||||
}
|
||||
else if (P.IsDead && !_lastDead)
|
||||
{
|
||||
API.Events.InvokePlayerDied();
|
||||
}
|
||||
|
||||
_lastDead = P.IsDead;
|
||||
Ticked++;
|
||||
}
|
||||
|
||||
protected override void OnKeyUp(GTA.KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyUp(e);
|
||||
#if CEF
|
||||
if (CefRunning)
|
||||
{
|
||||
CefManager.KeyUp(e.KeyCode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
if (Game.IsControlPressed(Control.PushToTalk))
|
||||
{
|
||||
Voice.StartRecording();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Voice.IsRecording())
|
||||
{
|
||||
Voice.StopRecording();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Game.IsControlPressed(Control.FrontendPause))
|
||||
{
|
||||
Call(ACTIVATE_FRONTEND_MENU,
|
||||
SHVDN.NativeMemory.GetHashKey("FE_MENU_VERSION_SP_PAUSE"), false, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Game.IsControlPressed(Control.FrontendPauseAlternate) && Settings.DisableAlternatePause)
|
||||
{
|
||||
Call(ACTIVATE_FRONTEND_MENU,
|
||||
SHVDN.NativeMemory.GetHashKey("FE_MENU_VERSION_SP_PAUSE"), false, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.KeyCode == Settings.MenuKey)
|
||||
{
|
||||
if (CoopMenu.MenuPool.AreAnyVisible)
|
||||
{
|
||||
CoopMenu.MenuPool.ForEach<NativeMenu>(x =>
|
||||
{
|
||||
if (x.Visible)
|
||||
{
|
||||
CoopMenu.LastMenu = x;
|
||||
x.Visible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
CoopMenu.LastMenu.Visible = true;
|
||||
}
|
||||
}
|
||||
else if (Game.IsControlJustPressed(Control.MpTextChatAll))
|
||||
{
|
||||
if (Networking.IsOnServer)
|
||||
{
|
||||
MainChat.Focused = true;
|
||||
}
|
||||
}
|
||||
else if (MainChat.Focused)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (Game.IsControlJustPressed(Control.MultiplayerInfo))
|
||||
{
|
||||
if (Networking.IsOnServer)
|
||||
{
|
||||
ulong currentTimestamp = Util.GetTickCount64();
|
||||
PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000
|
||||
? (currentTimestamp - 6000)
|
||||
: currentTimestamp;
|
||||
}
|
||||
}
|
||||
else if (e.KeyCode == Settings.PassengerKey)
|
||||
{
|
||||
if (P == null || P.IsInVehicle())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (P.IsTaskActive(TaskType.CTaskEnterVehicle))
|
||||
{
|
||||
P.Task.ClearAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
var V = World.GetClosestVehicle(P.ReadPosition(), 15);
|
||||
|
||||
if (V != null)
|
||||
{
|
||||
var seat = P.GetNearestSeat(V);
|
||||
var p = V.GetPedOnSeat(seat);
|
||||
if (p != null && !p.IsDead)
|
||||
{
|
||||
for (int i = -1; i < V.PassengerCapacity; i++)
|
||||
{
|
||||
seat = (VehicleSeat)i;
|
||||
p = V.GetPedOnSeat(seat);
|
||||
if (p == null || p.IsDead)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
P.Task.EnterVehicle(V, seat, -1, 5, EnterVehicleFlags.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Connected()
|
||||
{
|
||||
Memory.ApplyPatches();
|
||||
if (Settings.Voice && !Voice.WasInitialized())
|
||||
{
|
||||
Voice.Init();
|
||||
}
|
||||
|
||||
API.QueueAction(() =>
|
||||
{
|
||||
WorldThread.Traffic(!Settings.DisableTraffic);
|
||||
Call(SET_ENABLE_VEHICLE_SLIPSTREAMING, true);
|
||||
CoopMenu.ConnectedMenuSetting();
|
||||
MainChat.Init();
|
||||
Notification.Show("~g~Connected!");
|
||||
});
|
||||
|
||||
Log.Info(">> Connected <<");
|
||||
}
|
||||
|
||||
private static readonly object _cleanupLock = new();
|
||||
public static void CleanUp(string reason)
|
||||
{
|
||||
lock (_cleanupLock)
|
||||
{
|
||||
|
||||
if (reason != "Abort")
|
||||
{
|
||||
Log.Info($">> Disconnected << reason: {reason}");
|
||||
Notification.Show("~r~Disconnected: " + reason);
|
||||
}
|
||||
|
||||
if (MainChat.Focused)
|
||||
{
|
||||
MainChat.Focused = false;
|
||||
}
|
||||
|
||||
PlayerList.Cleanup();
|
||||
MainChat.Clear();
|
||||
EntityPool.Cleanup();
|
||||
WorldThread.Traffic(true);
|
||||
Call(SET_ENABLE_VEHICLE_SLIPSTREAMING, false);
|
||||
CoopMenu.DisconnectedMenuSetting();
|
||||
LocalPlayerID = default;
|
||||
MainRes.Unload();
|
||||
Memory.RestorePatches();
|
||||
#if CEF
|
||||
if (CefRunning)
|
||||
{
|
||||
CefManager.CleanUp();
|
||||
}
|
||||
#endif
|
||||
|
||||
DownloadManager.Cleanup();
|
||||
Voice.ClearAll();
|
||||
Networking.Peer?.Dispose();
|
||||
Networking.Peer = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using GTA.UI;
|
||||
using LemonUI.Menus;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class DebugMenu
|
||||
{
|
||||
public static NativeMenu Menu = new("RAGECOOP", "Debug", "Debug settings")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
public static NativeMenu DiagnosticMenu = new("RAGECOOP", "Diagnostic", "Performence and Diagnostic")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
|
||||
public static NativeMenu TuneMenu = new("RAGECOOP", "Change tunable values")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
public static NativeItem SimulatedLatencyItem =
|
||||
new("Simulated network latency", "Simulated network latency in ms (one way)", "0");
|
||||
|
||||
public static NativeCheckboxItem ShowOwnerItem = new("Show entity owner",
|
||||
"Show the owner name of the entity you're aiming at", false);
|
||||
|
||||
private static readonly NativeCheckboxItem ShowNetworkInfoItem =
|
||||
new("Show Network Info", Networking.ShowNetworkInfo);
|
||||
|
||||
static DebugMenu()
|
||||
{
|
||||
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
|
||||
Menu.Title.Color = Color.FromArgb(255, 165, 0);
|
||||
|
||||
TuneMenu.Opening += (s, e) =>
|
||||
{
|
||||
TuneMenu.Clear();
|
||||
foreach (var t in typeof(Main).Assembly.GetTypes())
|
||||
{
|
||||
foreach (var field in t.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic))
|
||||
{
|
||||
var attri = field.GetCustomAttribute<DebugTunableAttribute>();
|
||||
if (attri == null)
|
||||
continue;
|
||||
var item = new NativeItem(field.Name);
|
||||
item.AltTitle = field.GetValue(null).ToString();
|
||||
item.Activated += (s, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
field.SetValue(null, Convert.ChangeType(Game.GetUserInput(), field.FieldType));
|
||||
item.AltTitle = field.GetValue(null).ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
}
|
||||
};
|
||||
TuneMenu.Add(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
DiagnosticMenu.Opening += (sender, e) =>
|
||||
{
|
||||
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()));
|
||||
};
|
||||
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
|
||||
{
|
||||
SimulatedLatencyItem.AltTitle =
|
||||
((Networking.SimulatedLatency =
|
||||
int.Parse(Game.GetUserInput(SimulatedLatencyItem.AltTitle)) * 0.002f) * 500).ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
}
|
||||
};
|
||||
Menu.Add(SimulatedLatencyItem);
|
||||
#endif
|
||||
Menu.Add(ShowNetworkInfoItem);
|
||||
Menu.Add(ShowOwnerItem);
|
||||
Menu.AddSubMenu(DiagnosticMenu);
|
||||
Menu.AddSubMenu(TuneMenu);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
using GTA.UI;
|
||||
using LemonUI.Menus;
|
||||
using RageCoop.Core;
|
||||
|
||||
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class DevToolMenu
|
||||
{
|
||||
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "DevTool", "Internal testing tools")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
private static readonly NativeCheckboxItem enableItem = new NativeCheckboxItem("Show weapon bones");
|
||||
public static readonly NativeItem DumpFixItem = new NativeItem("Dump weapon fixes");
|
||||
public static readonly NativeItem GetAnimItem = new NativeItem("Get current animation");
|
||||
public static readonly NativeItem DumpVwHashItem = new NativeItem("Dump VehicleWeaponHash.cs");
|
||||
|
||||
static DevToolMenu()
|
||||
{
|
||||
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
|
||||
Menu.Title.Color = Color.FromArgb(255, 165, 0);
|
||||
|
||||
enableItem.Activated += ShowBones;
|
||||
enableItem.Checked = false;
|
||||
DumpFixItem.Activated += (s, e) => WeaponUtil.DumpWeaponFix(WeaponFixDataPath);
|
||||
GetAnimItem.Activated += (s, e) =>
|
||||
{
|
||||
if (File.Exists(AnimationsDataPath))
|
||||
{
|
||||
var anims = JsonDeserialize<AnimDic[]>(File.ReadAllText(AnimationsDataPath));
|
||||
foreach (var anim in anims)
|
||||
foreach (var a in anim.Animations)
|
||||
if (Call<bool>(IS_ENTITY_PLAYING_ANIM, P, anim.DictionaryName, a, 3))
|
||||
{
|
||||
Console.PrintInfo(anim.DictionaryName + " : " + a);
|
||||
Notification.Show(anim.DictionaryName + " : " + a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Notification.Show($"~r~{AnimationsDataPath} not found");
|
||||
}
|
||||
};
|
||||
|
||||
Menu.Add(enableItem);
|
||||
Menu.Add(DumpVwHashItem);
|
||||
Menu.Add(DumpFixItem);
|
||||
Menu.Add(GetAnimItem);
|
||||
}
|
||||
|
||||
private static void ShowBones(object sender, EventArgs e)
|
||||
{
|
||||
if (enableItem.Checked)
|
||||
{
|
||||
DevTool.ToMark = Game.Player.Character.CurrentVehicle;
|
||||
DevTool.Instance.Resume();
|
||||
}
|
||||
else
|
||||
{
|
||||
DevTool.Instance.Pause();
|
||||
DevTool.ToMark = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
using GTA;
|
||||
using GTA.UI;
|
||||
using LemonUI.Menus;
|
||||
using RageCoop.Client.Scripting;
|
||||
|
||||
namespace RageCoop.Client.Menus
|
||||
{
|
||||
internal static class SettingsMenu
|
||||
{
|
||||
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Settings", "Go to the settings")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Settings.FlipMenu ? Alignment.Right : Alignment.Left
|
||||
};
|
||||
|
||||
private static readonly NativeCheckboxItem _disableTrafficItem =
|
||||
new("Disable Traffic (NPCs/Vehicles)", "Local traffic only",
|
||||
Settings.DisableTraffic);
|
||||
|
||||
private static readonly NativeCheckboxItem _flipMenuItem =
|
||||
new("Flip menu", Settings.FlipMenu);
|
||||
|
||||
private static readonly NativeCheckboxItem _disablePauseAlt = new("Disable Alternate Pause",
|
||||
"Don't freeze game time when Esc pressed", Settings.DisableAlternatePause);
|
||||
|
||||
private static readonly NativeCheckboxItem _disableVoice = new("Enable voice",
|
||||
"Check your GTA:V settings to find the right key on your keyboard for PushToTalk and talk to your friends",
|
||||
Settings.Voice);
|
||||
|
||||
private static readonly NativeCheckboxItem _showBlip = new("Show player blip",
|
||||
"Show other player's blip on map, can be overridden by server resource ",
|
||||
Settings.ShowPlayerBlip);
|
||||
|
||||
private static readonly NativeCheckboxItem _showNametag = new("Show player nametag",
|
||||
"Show other player's nametag on your screen, only effective if server didn't disable nametag display",
|
||||
Settings.ShowPlayerNameTag);
|
||||
|
||||
private static readonly NativeItem _menuKey =
|
||||
new("Menu Key", "The key to open menu", Settings.MenuKey.ToString());
|
||||
|
||||
private static readonly NativeItem _passengerKey = new("Passenger Key",
|
||||
"The key to enter a vehicle as passenger", Settings.PassengerKey.ToString());
|
||||
|
||||
private static readonly NativeItem _vehicleSoftLimit = new("Vehicle limit (soft)",
|
||||
"The game won't spawn more NPC traffic if the limit is exceeded. \n-1 for unlimited (not recommended).",
|
||||
Settings.WorldVehicleSoftLimit.ToString());
|
||||
|
||||
static SettingsMenu()
|
||||
{
|
||||
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
|
||||
Menu.Title.Color = Color.FromArgb(255, 165, 0);
|
||||
|
||||
_disableTrafficItem.CheckboxChanged += DisableTrafficCheckboxChanged;
|
||||
_disablePauseAlt.CheckboxChanged += DisablePauseAltCheckboxChanged;
|
||||
_disableVoice.CheckboxChanged += DisableVoiceCheckboxChanged;
|
||||
_flipMenuItem.CheckboxChanged += FlipMenuCheckboxChanged;
|
||||
_menuKey.Activated += ChaneMenuKey;
|
||||
_passengerKey.Activated += ChangePassengerKey;
|
||||
_vehicleSoftLimit.Activated += VehicleSoftLimitActivated;
|
||||
_showBlip.Activated += (s, e) =>
|
||||
{
|
||||
Settings.ShowPlayerBlip = _showBlip.Checked;
|
||||
Util.SaveSettings();
|
||||
};
|
||||
_showNametag.Activated += (s, e) =>
|
||||
{
|
||||
API.Config.ShowPlayerNameTag = _showNametag.Checked;
|
||||
};
|
||||
|
||||
Menu.Add(_disableTrafficItem);
|
||||
Menu.Add(_disablePauseAlt);
|
||||
Menu.Add(_flipMenuItem);
|
||||
Menu.Add(_disableVoice);
|
||||
Menu.Add(_menuKey);
|
||||
Menu.Add(_passengerKey);
|
||||
Menu.Add(_vehicleSoftLimit);
|
||||
Menu.Add(_showBlip);
|
||||
Menu.Add(_showNametag);
|
||||
}
|
||||
|
||||
private static void DisableVoiceCheckboxChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_disableVoice.Checked)
|
||||
{
|
||||
if (Networking.IsOnServer && !Voice.WasInitialized()) Voice.Init();
|
||||
}
|
||||
else
|
||||
{
|
||||
Voice.ClearAll();
|
||||
}
|
||||
|
||||
Settings.Voice = _disableVoice.Checked;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
|
||||
private static void DisablePauseAltCheckboxChanged(object sender, EventArgs e)
|
||||
{
|
||||
Settings.DisableAlternatePause = _disablePauseAlt.Checked;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
|
||||
private static void VehicleSoftLimitActivated(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Settings.WorldVehicleSoftLimit = int.Parse(
|
||||
Game.GetUserInput(WindowTitle.EnterMessage20,
|
||||
Settings.WorldVehicleSoftLimit.ToString(), 20));
|
||||
_menuKey.AltTitle = Settings.WorldVehicleSoftLimit.ToString();
|
||||
Util.SaveSettings();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void ChaneMenuKey(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Settings.MenuKey = (Keys)Enum.Parse(
|
||||
typeof(Keys),
|
||||
Game.GetUserInput(WindowTitle.EnterMessage20,
|
||||
Settings.MenuKey.ToString(), 20));
|
||||
_menuKey.AltTitle = Settings.MenuKey.ToString();
|
||||
Util.SaveSettings();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void ChangePassengerKey(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Settings.PassengerKey = (Keys)Enum.Parse(
|
||||
typeof(Keys),
|
||||
Game.GetUserInput(WindowTitle.EnterMessage20,
|
||||
Settings.PassengerKey.ToString(), 20));
|
||||
_passengerKey.AltTitle = Settings.PassengerKey.ToString();
|
||||
Util.SaveSettings();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static void DisableTrafficCheckboxChanged(object a, EventArgs b)
|
||||
{
|
||||
WorldThread.Traffic(!_disableTrafficItem.Checked);
|
||||
Settings.DisableTraffic = _disableTrafficItem.Checked;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
|
||||
public static void FlipMenuCheckboxChanged(object a, EventArgs b)
|
||||
{
|
||||
CoopMenu.Menu.Alignment = _flipMenuItem.Checked ? Alignment.Right : Alignment.Left;
|
||||
|
||||
Menu.Alignment = _flipMenuItem.Checked ? Alignment.Right : Alignment.Left;
|
||||
Settings.FlipMenu = _flipMenuItem.Checked;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Client.Scripting;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static partial class Networking
|
||||
{
|
||||
public static int SyncInterval = 30;
|
||||
public static List<NetConnection> Targets = new List<NetConnection>();
|
||||
|
||||
public static void SendSync(Packet p, ConnectionChannel channel = ConnectionChannel.Default,
|
||||
NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
|
||||
{
|
||||
Peer.SendTo(p, Targets, channel, method);
|
||||
}
|
||||
|
||||
public static void SendPed(SyncedPed sp, bool full)
|
||||
{
|
||||
if (sp.LastSentStopWatch.ElapsedMilliseconds < SyncInterval) return;
|
||||
var ped = sp.MainPed;
|
||||
var p = SendPackets.PedPacket;
|
||||
p.ID = sp.ID;
|
||||
p.OwnerID = sp.OwnerID;
|
||||
p.Health = ped.Health;
|
||||
p.Rotation = ped.ReadRotation();
|
||||
p.Velocity = ped.ReadVelocity();
|
||||
p.Speed = ped.GetPedSpeed();
|
||||
p.Flags = ped.GetPedFlags();
|
||||
p.Heading = ped.Heading;
|
||||
if (p.Flags.HasPedFlag(PedDataFlags.IsAiming)) p.AimCoords = ped.GetAimCoord();
|
||||
if (p.Flags.HasPedFlag(PedDataFlags.IsRagdoll))
|
||||
{
|
||||
p.HeadPosition = ped.Bones[Bone.SkelHead].Position;
|
||||
p.RightFootPosition = ped.Bones[Bone.SkelRightFoot].Position;
|
||||
p.LeftFootPosition = ped.Bones[Bone.SkelLeftFoot].Position;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Seat sync
|
||||
if (p.Speed >= 4)
|
||||
{
|
||||
var veh = ped.CurrentVehicle?.GetSyncEntity() ??
|
||||
ped.VehicleTryingToEnter?.GetSyncEntity() ?? ped.LastVehicle?.GetSyncEntity();
|
||||
p.VehicleID = veh?.ID ?? 0;
|
||||
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 = LocalPlayerID;
|
||||
SyncEvents.TriggerChangeOwner(veh.ID, LocalPlayerID);
|
||||
}
|
||||
}
|
||||
|
||||
p.Position = ped.ReadPosition();
|
||||
}
|
||||
|
||||
sp.LastSentStopWatch.Restart();
|
||||
if (full)
|
||||
{
|
||||
if (p.Speed == 4)
|
||||
p.VehicleWeapon = ped.VehicleWeapon;
|
||||
p.CurrentWeapon = ped.Weapons.Current.Hash;
|
||||
p.Flags |= PedDataFlags.IsFullSync;
|
||||
p.Clothes = ped.GetPedClothes();
|
||||
p.ModelHash = ped.Model.Hash;
|
||||
p.WeaponComponents = ped.Weapons.Current.GetWeaponComponents();
|
||||
p.WeaponTint = (byte)Call<int>(GET_PED_WEAPON_TINT_INDEX, ped, ped.Weapons.Current.Hash);
|
||||
|
||||
Blip b;
|
||||
if (sp.IsPlayer)
|
||||
{
|
||||
p.BlipColor = API.Config.BlipColor;
|
||||
p.BlipSprite = API.Config.BlipSprite;
|
||||
p.BlipScale = API.Config.BlipScale;
|
||||
}
|
||||
else if ((b = ped.AttachedBlip) != null)
|
||||
{
|
||||
p.BlipColor = b.Color;
|
||||
p.BlipSprite = b.Sprite;
|
||||
|
||||
if (p.BlipSprite == BlipSprite.PoliceOfficer || p.BlipSprite == BlipSprite.PoliceOfficer2)
|
||||
p.BlipScale = 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
p.BlipColor = (BlipColor)255;
|
||||
}
|
||||
}
|
||||
|
||||
SendSync(p, ConnectionChannel.PedSync);
|
||||
}
|
||||
|
||||
public static void SendVehicle(SyncedVehicle v, bool full)
|
||||
{
|
||||
if (v.LastSentStopWatch.ElapsedMilliseconds < SyncInterval) return;
|
||||
var veh = v.MainVehicle;
|
||||
var packet = SendPackets.VehicelPacket;
|
||||
packet.ED.ID = v.ID;
|
||||
packet.ED.OwnerID = v.OwnerID;
|
||||
packet.ED.Position = veh.ReadPosition();
|
||||
packet.ED.Velocity = veh.Velocity;
|
||||
packet.ED.Quaternion = veh.ReadQuaternion();
|
||||
packet.ED.ModelHash = veh.Model.Hash;
|
||||
packet.VD.Flags = v.GetVehicleFlags();
|
||||
packet.VD.SteeringAngle = veh.SteeringAngle;
|
||||
packet.VD.ThrottlePower = veh.ThrottlePower;
|
||||
packet.VD.BrakePower = veh.BrakePower;
|
||||
packet.VD.Flags |= VehicleDataFlags.IsFullSync;
|
||||
packet.VD.LockStatus = veh.LockStatus;
|
||||
|
||||
v.LastSentStopWatch.Restart();
|
||||
if (packet.VD.Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering))
|
||||
packet.VD.DeluxoWingRatio = v.MainVehicle.GetDeluxoWingRatio();
|
||||
if (full)
|
||||
{
|
||||
byte primaryColor = 0;
|
||||
byte secondaryColor = 0;
|
||||
unsafe
|
||||
{
|
||||
Call<byte>(GET_VEHICLE_COLOURS, veh, &primaryColor, &secondaryColor);
|
||||
}
|
||||
|
||||
packet.VDF.LandingGear = veh.IsAircraft ? (byte)veh.LandingGearState : (byte)0;
|
||||
packet.VDF.RoofState = (byte)veh.RoofState;
|
||||
packet.VDF.Colors = (primaryColor, secondaryColor);
|
||||
packet.VDF.DamageModel = veh.GetVehicleDamageModel();
|
||||
packet.VDF.EngineHealth = veh.EngineHealth;
|
||||
packet.VDF.Livery = Call<int>(GET_VEHICLE_LIVERY, veh);
|
||||
packet.VDF.HeadlightColor = (byte)Call<int>(GET_VEHICLE_XENON_LIGHT_COLOR_INDEX, veh);
|
||||
packet.VDF.ExtrasMask = v.GetVehicleExtras();
|
||||
packet.VDF.RadioStation = v.MainVehicle == LastV
|
||||
? Util.GetPlayerRadioIndex() : byte.MaxValue;
|
||||
if (packet.VDF.EngineHealth > v.LastEngineHealth) packet.VD.Flags |= VehicleDataFlags.Repaired;
|
||||
|
||||
packet.VDV.Mods = v.GetVehicleMods(out packet.VDF.ToggleModsMask);
|
||||
packet.VDV.LicensePlate = Call<string>(GET_VEHICLE_NUMBER_PLATE_TEXT, veh);
|
||||
v.LastEngineHealth = packet.VDF.EngineHealth;
|
||||
}
|
||||
|
||||
SendSync(packet, ConnectionChannel.VehicleSync);
|
||||
}
|
||||
|
||||
public static void SendProjectile(SyncedProjectile sp)
|
||||
{
|
||||
sp.ExtractData(ref SendPackets.ProjectilePacket);
|
||||
if (sp.MainProjectile.IsDead) EntityPool.RemoveProjectile(sp.ID, "Dead");
|
||||
SendSync(SendPackets.ProjectilePacket, ConnectionChannel.ProjectileSync);
|
||||
}
|
||||
|
||||
public static void SendChatMessage(string message)
|
||||
{
|
||||
Peer.SendTo(new Packets.ChatMessage(s => Security.Encrypt(s.GetBytes()))
|
||||
{ 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 = LocalPlayerID, Buffer = buffer, Recorded = recorded },
|
||||
ConnectionChannel.Voice, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reduce GC pressure by reusing frequently used packets
|
||||
/// </summary>
|
||||
private static class SendPackets
|
||||
{
|
||||
public static readonly Packets.PedSync PedPacket = new Packets.PedSync();
|
||||
public static readonly Packets.VehicleSync VehicelPacket = new Packets.VehicleSync();
|
||||
public static Packets.ProjectileSync ProjectilePacket = new Packets.ProjectileSync();
|
||||
}
|
||||
|
||||
|
||||
#region SYNC EVENTS
|
||||
|
||||
public static void SendBullet(int ownerID, uint weapon, Vector3 end)
|
||||
{
|
||||
SendSync(new Packets.BulletShot
|
||||
{
|
||||
EndPosition = end,
|
||||
OwnerID = ownerID,
|
||||
WeaponHash = weapon
|
||||
}, ConnectionChannel.SyncEvents);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<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>
|
||||
<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="SharpZipLib" Version="1.4.0" />
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(SolutionDir)' != '*Undefined*' and '$(Configuration)' != 'API'">
|
||||
<Exec Command="xcopy "$(SolutionDir)Client\Data" "$(OutDir)..\Data" /i /s /y" />
|
||||
</Target>
|
||||
</Project>
|
@ -1,3 +0,0 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=shared/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=util/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
@ -1,140 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
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,487 +0,0 @@
|
||||
#undef DEBUG
|
||||
using Lidgren.Network;
|
||||
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>
|
||||
/// Provides vital functionality to interact with RAGECOOP
|
||||
/// </summary>
|
||||
internal static unsafe partial class API
|
||||
{
|
||||
#region INTERNAL
|
||||
|
||||
internal static Dictionary<int, List<CustomEventHandler>> CustomEventHandlers =
|
||||
new();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Client configuration, this will conflict with server-side config.
|
||||
/// </summary>
|
||||
public static class Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Get or set local player's username, set won't be effective if already connected to a server.
|
||||
/// </summary>
|
||||
public static string Username
|
||||
{
|
||||
get => Settings.Username;
|
||||
set
|
||||
{
|
||||
if (Networking.IsOnServer || string.IsNullOrEmpty(value)) return;
|
||||
Settings.Username = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable automatic respawn for this player.
|
||||
/// </summary>
|
||||
public static bool EnableAutoRespawn { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set player's blip color
|
||||
/// </summary>
|
||||
public static BlipColor BlipColor { get; set; } = BlipColor.White;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set player's blip sprite
|
||||
/// </summary>
|
||||
public static BlipSprite BlipSprite { get; set; } = BlipSprite.Standard;
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// Base events for RageCoop
|
||||
/// </summary>
|
||||
public static class Events
|
||||
{
|
||||
/// <summary>
|
||||
/// The local player is dead
|
||||
/// </summary>
|
||||
public static event EmptyEvent OnPlayerDied;
|
||||
|
||||
/// <summary>
|
||||
/// A local vehicle is spawned
|
||||
/// </summary>
|
||||
public static event EventHandler<SyncedVehicle> OnVehicleSpawned;
|
||||
|
||||
/// <summary>
|
||||
/// A local vehicle is deleted
|
||||
/// </summary>
|
||||
public static event EventHandler<SyncedVehicle> OnVehicleDeleted;
|
||||
|
||||
/// <summary>
|
||||
/// A local ped is spawned
|
||||
/// </summary>
|
||||
public static event EventHandler<SyncedPed> OnPedSpawned;
|
||||
|
||||
/// <summary>
|
||||
/// A local ped is deleted
|
||||
/// </summary>
|
||||
public static event EventHandler<SyncedPed> OnPedDeleted;
|
||||
|
||||
#region DELEGATES
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public delegate void EmptyEvent();
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="hash"></param>
|
||||
/// <param name="args"></param>
|
||||
public delegate void CustomEvent(int hash, List<object> args);
|
||||
|
||||
#endregion
|
||||
|
||||
#region INVOKE
|
||||
|
||||
internal static void InvokeVehicleSpawned(SyncedVehicle v)
|
||||
{
|
||||
OnVehicleSpawned?.Invoke(null, v);
|
||||
}
|
||||
|
||||
internal static void InvokeVehicleDeleted(SyncedVehicle v)
|
||||
{
|
||||
OnVehicleDeleted?.Invoke(null, v);
|
||||
}
|
||||
|
||||
internal static void InvokePedSpawned(SyncedPed p)
|
||||
{
|
||||
OnPedSpawned?.Invoke(null, p);
|
||||
}
|
||||
|
||||
internal static void InvokePedDeleted(SyncedPed p)
|
||||
{
|
||||
OnPedDeleted?.Invoke(null, p);
|
||||
}
|
||||
|
||||
internal static void InvokePlayerDied()
|
||||
{
|
||||
OnPlayerDied?.Invoke();
|
||||
}
|
||||
|
||||
internal static void InvokeCustomEventReceived(Packets.CustomEvent p)
|
||||
{
|
||||
|
||||
// Log.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
|
||||
|
||||
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);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("InvokeCustomEvent", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
/// <summary>
|
||||
/// Get the local player's ID
|
||||
/// </summary>
|
||||
/// <returns>PlayerID</returns>
|
||||
public static int LocalPlayerID => Main.LocalPlayerID;
|
||||
|
||||
/// <summary>
|
||||
/// Check if player is connected to a server
|
||||
/// </summary>
|
||||
public static bool IsOnServer => Networking.IsOnServer;
|
||||
|
||||
/// <summary>
|
||||
/// Get an <see cref="System.Net.IPEndPoint" /> that the player is currently connected to, or null if not connected to
|
||||
/// the server
|
||||
/// </summary>
|
||||
public static IPEndPoint ServerEndPoint =>
|
||||
Networking.IsOnServer ? Networking.ServerConnection?.RemoteEndPoint : null;
|
||||
|
||||
/// <summary>
|
||||
/// Check if a RAGECOOP menu is visible
|
||||
/// </summary>
|
||||
public static bool IsMenuVisible => CoopMenu.MenuPool.AreAnyVisible;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the RAGECOOP chat is visible
|
||||
/// </summary>
|
||||
public static bool IsChatFocused => MainChat.Focused;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the RAGECOOP list of players is visible
|
||||
/// </summary>
|
||||
public static bool IsPlayerListVisible => Util.GetTickCount64() - PlayerList.Pressed < 5000;
|
||||
|
||||
/// <summary>
|
||||
/// Get the version of RAGECOOP
|
||||
/// </summary>
|
||||
public static Version CurrentVersion => Main.ModVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Get all players indexed by their ID
|
||||
/// </summary>
|
||||
public static Dictionary<int, Player> Players => new(PlayerList.Players);
|
||||
|
||||
#endregion
|
||||
|
||||
#region FUNCTIONS
|
||||
|
||||
/// <summary>
|
||||
/// Queue an action to be executed on next tick.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
public static void QueueAction(Action a)
|
||||
{
|
||||
WorldThread.QueueAction(a);
|
||||
}
|
||||
|
||||
public static void QueueActionAndWait(Action a, int timeout = 15000)
|
||||
{
|
||||
var done = new AutoResetEvent(false);
|
||||
Exception e = null;
|
||||
QueueAction(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
a();
|
||||
done.Set();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
e = ex;
|
||||
}
|
||||
});
|
||||
if (e != null)
|
||||
throw e;
|
||||
if (!done.WaitOne(timeout)) throw new TimeoutException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue an action to be executed on next tick, allowing you to call scripting API from another thread.
|
||||
/// </summary>
|
||||
/// <param name="a">
|
||||
/// An <see cref="Func{T, TResult}" /> to be executed with a return value indicating whether it can be
|
||||
/// removed after execution.
|
||||
/// </param>
|
||||
public static void QueueAction(Func<bool> a)
|
||||
{
|
||||
WorldThread.QueueAction(a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send an event and data to the server.
|
||||
/// </summary>
|
||||
/// <param name="eventHash">An unique identifier of the event</param>
|
||||
/// <param name="args">
|
||||
/// The objects conataing your data, see <see cref="CustomEventReceivedArgs" /> for a list of supported
|
||||
/// types
|
||||
/// </param>
|
||||
public static void SendCustomEvent(CustomEventHash eventHash, params object[] args)
|
||||
=> SendCustomEvent(CustomEventFlags.None, eventHash, args);
|
||||
|
||||
/// <summary>
|
||||
/// Send an event and data to the server
|
||||
/// </summary>
|
||||
/// <param name="flags"></param>
|
||||
/// <param name="eventHash">An unique identifier of the event</param>
|
||||
/// <param name="args">
|
||||
/// The objects conataing your data, see <see cref="CustomEventReceivedArgs" /> for a list of supported
|
||||
/// types
|
||||
/// </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)
|
||||
{
|
||||
|
||||
Payload = writer.ToByteArray(writer.Position),
|
||||
Hash = eventHash
|
||||
}, Networking.ServerConnection, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public static void RegisterCustomEventHandler(CustomEventHash hash, Action<CustomEventReceivedArgs> handler)
|
||||
=> RegisterCustomEventHandler(hash, (CustomEventHandler)handler);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static void RequestSharedFile(string name, Action<string> callback)
|
||||
{
|
||||
EventHandler<string> handler = (s, e) =>
|
||||
{
|
||||
if (e.EndsWith(name)) callback(e);
|
||||
};
|
||||
DownloadManager.DownloadCompleted += handler;
|
||||
Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest
|
||||
{
|
||||
Name = name
|
||||
},
|
||||
p =>
|
||||
{
|
||||
if (p.Response != FileResponse.Loaded)
|
||||
{
|
||||
DownloadManager.DownloadCompleted -= handler;
|
||||
throw new ArgumentException("Requested file was not found on the server: " + name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <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
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
using GTA;
|
||||
using RageCoop.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
public abstract class ClientScript : Script
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the <see cref="ResourceFile" /> instance where this script is loaded from.
|
||||
/// </summary>
|
||||
public ResourceFile CurrentFile { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="ClientResource" /> that this script belongs to.
|
||||
/// </summary>
|
||||
public ClientResource CurrentResource { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Eqivalent of <see cref="ClientResource.Logger" /> in <see cref="CurrentResource" />
|
||||
/// </summary>
|
||||
public Logger Logger => CurrentResource.Logger;
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
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.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
using SHVDN;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
internal class Resources
|
||||
{
|
||||
public static string TempPath = Path.Combine(Path.GetTempPath(), "RageCoop");
|
||||
|
||||
internal readonly ConcurrentDictionary<string, ClientResource> LoadedResources = new();
|
||||
|
||||
static Resources()
|
||||
{
|
||||
if (Directory.Exists(TempPath))
|
||||
try
|
||||
{
|
||||
Directory.Delete(TempPath, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
TempPath = CoreUtils.GetTempDirectory(TempPath);
|
||||
Directory.CreateDirectory(TempPath);
|
||||
}
|
||||
|
||||
|
||||
public unsafe void Load(string path, string[] zips)
|
||||
{
|
||||
LoadedResources.Clear();
|
||||
foreach (var zip in zips)
|
||||
{
|
||||
var zipPath = Path.Combine(path, zip);
|
||||
Log?.Info($"Loading resource: {Path.GetFileNameWithoutExtension(zip)}");
|
||||
Unpack(zipPath, Path.Combine(path, "Data"));
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Unload()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
private unsafe ClientResource Unpack(string zipPath, string dataFolderRoot)
|
||||
{
|
||||
var r = new ClientResource
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(zipPath),
|
||||
DataFolder = Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(zipPath)),
|
||||
ScriptsDirectory = Path.Combine(TempPath, Path.GetFileNameWithoutExtension(zipPath))
|
||||
};
|
||||
Directory.CreateDirectory(r.DataFolder);
|
||||
var scriptsDir = r.ScriptsDirectory;
|
||||
if (Directory.Exists(scriptsDir))
|
||||
Directory.Delete(scriptsDir, true);
|
||||
else if (File.Exists(scriptsDir)) File.Delete(scriptsDir);
|
||||
Directory.CreateDirectory(scriptsDir);
|
||||
|
||||
new FastZip().ExtractZip(zipPath, scriptsDir, null);
|
||||
|
||||
|
||||
foreach (var dir in Directory.GetDirectories(scriptsDir, "*", SearchOption.AllDirectories))
|
||||
r.Files.Add(dir, new ClientFile
|
||||
{
|
||||
IsDirectory = true,
|
||||
Name = dir.Substring(scriptsDir.Length + 1).Replace('\\', '/'),
|
||||
FullPath = dir
|
||||
});
|
||||
foreach (var file in Directory.GetFiles(scriptsDir, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
var relativeName = file.Substring(scriptsDir.Length + 1).Replace('\\', '/');
|
||||
var rfile = new ClientFile
|
||||
{
|
||||
IsDirectory = false,
|
||||
Name = relativeName,
|
||||
FullPath = file
|
||||
};
|
||||
r.Files.Add(relativeName, rfile);
|
||||
}
|
||||
SHVDN.Core.RuntimeController.RequestLoad(scriptsDir);
|
||||
LoadedResources.TryAdd(r.Name, r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
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;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
class DebugTunableAttribute : Attribute
|
||||
{
|
||||
}
|
||||
internal static class Shared
|
||||
{
|
||||
private static unsafe string GetBasePath()
|
||||
{
|
||||
using var fs = File.OpenRead(Instance.FilePath);
|
||||
var buf = stackalloc char[1024];
|
||||
GetFinalPathNameByHandleW(fs.SafeFileHandle.DangerousGetHandle(), buf, 1024, 0);
|
||||
ErrorCheck32();
|
||||
var scriptDir = Directory.GetParent(Marshal.PtrToStringUni((IntPtr)buf)).FullName;
|
||||
if (Path.GetFileName(scriptDir).ToLower() != "scripts")
|
||||
throw new Exception("Unexpected script location");
|
||||
|
||||
var result = Directory.GetParent(scriptDir).FullName;
|
||||
Logger.Debug($"Base path is: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
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,161 +0,0 @@
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
public partial class SyncedPed
|
||||
{
|
||||
private void DisplaySpeaking(bool speaking)
|
||||
{
|
||||
if (!MainPed.IsHuman)
|
||||
return;
|
||||
|
||||
if (speaking)
|
||||
{
|
||||
Call(PLAY_FACIAL_ANIM, MainPed.Handle, "mic_chatter", "mp_facial");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (MainPed.Gender)
|
||||
{
|
||||
case Gender.Male:
|
||||
Call(PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1",
|
||||
"facials@gen_male@variations@normal");
|
||||
break;
|
||||
case Gender.Female:
|
||||
Call(PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1",
|
||||
"facials@gen_female@variations@normal");
|
||||
break;
|
||||
default:
|
||||
Call(PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1",
|
||||
"facials@mime@variations@normal");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayInCover()
|
||||
{
|
||||
var ourAnim = GetCoverAnim();
|
||||
var animDict = GetCoverIdleAnimDict();
|
||||
|
||||
if (ourAnim != null && animDict != null)
|
||||
{
|
||||
var flag = AnimationFlags.Loop;
|
||||
if (!Call<bool>(IS_ENTITY_PLAYING_ANIM, MainPed, animDict, ourAnim, 3))
|
||||
{
|
||||
if (LoadAnim(animDict) == null) return;
|
||||
|
||||
MainPed.Task.ClearAll();
|
||||
Call(TASK_PLAY_ANIM, MainPed, animDict, ourAnim, 8f, 10f, -1, flag, -8f, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal string GetCoverAnim()
|
||||
{
|
||||
if (IsInCover)
|
||||
{
|
||||
if (IsBlindFiring)
|
||||
{
|
||||
if (IsInCover)
|
||||
return IsInCoverFacingLeft ? "blindfire_low_l_aim_med" : "blindfire_low_r_aim_med";
|
||||
return IsInCoverFacingLeft ? "blindfire_hi_l_aim_med" : "blindfire_hi_r_aim_med";
|
||||
}
|
||||
|
||||
return IsInCoverFacingLeft ? "idle_l_corner" : "idle_r_corner";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal string GetCoverIdleAnimDict()
|
||||
{
|
||||
if (!IsInCover) return "";
|
||||
var altitude = IsInLowCover ? "low" : "high";
|
||||
|
||||
var hands = GetWeaponHandsHeld((uint)CurrentWeapon);
|
||||
if (IsBlindFiring)
|
||||
{
|
||||
if (hands == 1) return "cover@weapon@1h";
|
||||
if (hands == 2 || hands == 5) return "cover@weapon@2h";
|
||||
}
|
||||
|
||||
if (hands == 1) return "cover@idles@1h@" + altitude + "@_a";
|
||||
if (hands == 2 || hands == 5) return "cover@idles@2h@" + altitude + "@_a";
|
||||
if (hands == 3 || hands == 4 || hands == 0) return "cover@idles@unarmed@" + altitude + "@_a";
|
||||
return "";
|
||||
}
|
||||
|
||||
internal int GetWeaponHandsHeld(uint weapon)
|
||||
{
|
||||
switch (weapon)
|
||||
{
|
||||
case (uint)WeaponHash.Unarmed:
|
||||
return 0;
|
||||
|
||||
case (uint)WeaponHash.RPG:
|
||||
case (uint)WeaponHash.HomingLauncher:
|
||||
case (uint)WeaponHash.Firework:
|
||||
return 5;
|
||||
|
||||
case (uint)WeaponHash.Minigun:
|
||||
return 5;
|
||||
|
||||
case (uint)WeaponHash.GolfClub:
|
||||
case (uint)WeaponHash.PoolCue:
|
||||
case (uint)WeaponHash.Bat:
|
||||
return 4;
|
||||
|
||||
case (uint)WeaponHash.Knife:
|
||||
case (uint)WeaponHash.Nightstick:
|
||||
case (uint)WeaponHash.Hammer:
|
||||
case (uint)WeaponHash.Crowbar:
|
||||
case (uint)WeaponHash.Wrench:
|
||||
case (uint)WeaponHash.BattleAxe:
|
||||
case (uint)WeaponHash.Dagger:
|
||||
case (uint)WeaponHash.Hatchet:
|
||||
case (uint)WeaponHash.KnuckleDuster:
|
||||
case unchecked((uint)-581044007):
|
||||
case unchecked((uint)-102323637):
|
||||
case unchecked((uint)-538741184):
|
||||
return 3;
|
||||
|
||||
case unchecked((uint)-1357824103):
|
||||
case unchecked((uint)-1074790547):
|
||||
case 2132975508:
|
||||
case unchecked((uint)-2084633992):
|
||||
case unchecked((uint)-952879014):
|
||||
case 100416529:
|
||||
case (uint)WeaponHash.Gusenberg:
|
||||
case (uint)WeaponHash.MG:
|
||||
case (uint)WeaponHash.CombatMG:
|
||||
case (uint)WeaponHash.CombatPDW:
|
||||
case (uint)WeaponHash.AssaultSMG:
|
||||
case (uint)WeaponHash.SMG:
|
||||
case (uint)WeaponHash.HeavySniper:
|
||||
case (uint)WeaponHash.PumpShotgun:
|
||||
case (uint)WeaponHash.HeavyShotgun:
|
||||
case (uint)WeaponHash.Musket:
|
||||
case (uint)WeaponHash.AssaultShotgun:
|
||||
case (uint)WeaponHash.BullpupShotgun:
|
||||
case (uint)WeaponHash.SawnOffShotgun:
|
||||
case (uint)WeaponHash.SweeperShotgun:
|
||||
case (uint)WeaponHash.CompactRifle:
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private string LoadAnim(string anim)
|
||||
{
|
||||
if (!Call<bool>(HAS_ANIM_DICT_LOADED, anim))
|
||||
{
|
||||
Call(REQUEST_ANIM_DICT, anim);
|
||||
return null;
|
||||
}
|
||||
|
||||
return anim;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
public partial class SyncedVehicle
|
||||
{
|
||||
public Vehicle MainVehicle { get; internal set; }
|
||||
|
||||
|
||||
#region -- SYNC DATA --
|
||||
internal VehicleData VD;
|
||||
internal VehicleDataFull VDF;
|
||||
internal VehicleDataVar VDV;
|
||||
|
||||
#endregion
|
||||
|
||||
#region FLAGS
|
||||
|
||||
internal bool EngineRunning => VD.Flags.HasVehFlag(VehicleDataFlags.IsEngineRunning);
|
||||
internal bool Transformed => VD.Flags.HasVehFlag(VehicleDataFlags.IsTransformed);
|
||||
internal bool HornActive => VD.Flags.HasVehFlag(VehicleDataFlags.IsHornActive);
|
||||
internal bool LightsOn => VD.Flags.HasVehFlag(VehicleDataFlags.AreLightsOn);
|
||||
internal bool BrakeLightsOn => VD.Flags.HasVehFlag(VehicleDataFlags.AreBrakeLightsOn);
|
||||
internal bool HighBeamsOn => VD.Flags.HasVehFlag(VehicleDataFlags.AreHighBeamsOn);
|
||||
internal bool SireneActive => VD.Flags.HasVehFlag(VehicleDataFlags.IsSirenActive);
|
||||
internal bool IsDead => VD.Flags.HasVehFlag(VehicleDataFlags.IsDead);
|
||||
internal bool IsDeluxoHovering => VD.Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering);
|
||||
|
||||
#endregion
|
||||
|
||||
#region FIXED-DATA
|
||||
|
||||
internal bool IsMotorcycle;
|
||||
internal bool IsAircraft;
|
||||
internal bool HasRocketBoost;
|
||||
internal bool HasParachute;
|
||||
internal bool HasRoof;
|
||||
internal bool IsSubmarineCar;
|
||||
internal bool IsDeluxo;
|
||||
internal bool IsTrain;
|
||||
internal ushort AvalibleExtras;
|
||||
|
||||
[DebugTunable]
|
||||
static float RotCalMult = 10f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PRIVATE
|
||||
private VehicleData _lastVD;
|
||||
private VehicleDataFull _lastVDF;
|
||||
private VehicleDataVar _lastVDV;
|
||||
private Vector3 _predictedPosition;
|
||||
internal bool _lastTransformed => _lastVD.Flags.HasVehFlag(VehicleDataFlags.IsTransformed);
|
||||
internal bool _lastHornActive => _lastVD.Flags.HasVehFlag(VehicleDataFlags.IsHornActive);
|
||||
|
||||
#endregion
|
||||
|
||||
#region OUTGOING
|
||||
|
||||
internal float LastNozzleAngle { get; set; }
|
||||
|
||||
internal float LastEngineHealth { get; set; }
|
||||
internal Vector3 LastVelocity { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,336 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
using GTA.UI;
|
||||
using RageCoop.Core;
|
||||
using static ICSharpCode.SharpZipLib.Zip.ExtendedUnixData;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// A synchronized vehicle instance
|
||||
/// </summary>
|
||||
public partial class SyncedVehicle : SyncedEntity
|
||||
{
|
||||
internal override void Update()
|
||||
{
|
||||
// Check if all data available
|
||||
if (!IsReady || Owner == null) return;
|
||||
|
||||
// Check existence
|
||||
if (MainVehicle == null || !MainVehicle.Exists() || MainVehicle.Model != Model)
|
||||
if (!CreateVehicle())
|
||||
return;
|
||||
|
||||
|
||||
DisplayVehicle();
|
||||
// Skip update if no new sync message has arrived.
|
||||
if (!NeedUpdate) return;
|
||||
|
||||
if (VD.SteeringAngle != MainVehicle.SteeringAngle)
|
||||
MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * VD.SteeringAngle);
|
||||
MainVehicle.ThrottlePower = VD.ThrottlePower;
|
||||
MainVehicle.BrakePower = VD.BrakePower;
|
||||
|
||||
if (IsDead)
|
||||
{
|
||||
if (MainVehicle.IsDead) return;
|
||||
|
||||
MainVehicle.Explode();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MainVehicle.IsDead)
|
||||
WorldThread.Delay(() =>
|
||||
{
|
||||
if (MainVehicle.IsDead && !IsDead) MainVehicle.Repair();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
if (MainVehicle.IsOnFire)
|
||||
{
|
||||
if (!VD.Flags.HasVehFlag(VehicleDataFlags.IsOnFire)) Call(STOP_ENTITY_FIRE, MainVehicle);
|
||||
}
|
||||
else if (VD.Flags.HasVehFlag(VehicleDataFlags.IsOnFire))
|
||||
{
|
||||
Call(START_ENTITY_FIRE, MainVehicle);
|
||||
}
|
||||
|
||||
if (EngineRunning != MainVehicle.IsEngineRunning) MainVehicle.IsEngineRunning = EngineRunning;
|
||||
|
||||
if (LightsOn != MainVehicle.AreLightsOn) MainVehicle.AreLightsOn = LightsOn;
|
||||
|
||||
if (HighBeamsOn != MainVehicle.AreHighBeamsOn) MainVehicle.AreHighBeamsOn = HighBeamsOn;
|
||||
|
||||
if (!IsAircraft)
|
||||
{
|
||||
if (MainVehicle.HasSiren && SireneActive != MainVehicle.IsSirenActive)
|
||||
MainVehicle.IsSirenActive = SireneActive;
|
||||
|
||||
if (HornActive)
|
||||
{
|
||||
if (!_lastHornActive)
|
||||
{
|
||||
MainVehicle.SoundHorn(99999);
|
||||
}
|
||||
}
|
||||
else if (_lastVD.Flags.HasVehFlag(VehicleDataFlags.IsHornActive))
|
||||
{
|
||||
MainVehicle.SoundHorn(1);
|
||||
}
|
||||
|
||||
if (HasRocketBoost && VD.Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive) !=
|
||||
MainVehicle.IsRocketBoostActive)
|
||||
MainVehicle.IsRocketBoostActive = VD.Flags.HasVehFlag(VehicleDataFlags.IsRocketBoostActive);
|
||||
if (HasParachute && VD.Flags.HasFlag(VehicleDataFlags.IsParachuteActive) &&
|
||||
!MainVehicle.IsParachuteDeployed)
|
||||
MainVehicle.StartParachuting(false);
|
||||
if (IsSubmarineCar)
|
||||
{
|
||||
if (Transformed)
|
||||
{
|
||||
if (!_lastTransformed)
|
||||
{
|
||||
Call(TRANSFORM_TO_SUBMARINE, MainVehicle.Handle, false);
|
||||
}
|
||||
}
|
||||
else if (_lastTransformed)
|
||||
{
|
||||
Call(TRANSFORM_TO_CAR, MainVehicle.Handle, false);
|
||||
}
|
||||
}
|
||||
else if (IsDeluxo)
|
||||
{
|
||||
MainVehicle.SetDeluxoHoverState(IsDeluxoHovering);
|
||||
if (IsDeluxoHovering) MainVehicle.SetDeluxoWingRatio(VD.DeluxoWingRatio);
|
||||
}
|
||||
Call(SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn);
|
||||
MainVehicle.LockStatus = VD.LockStatus;
|
||||
}
|
||||
_lastVD = VD;
|
||||
|
||||
if (LastFullSynced >= LastUpdated)
|
||||
{
|
||||
if (IsAircraft)
|
||||
{
|
||||
if (VDF.LandingGear != (byte)MainVehicle.LandingGearState)
|
||||
MainVehicle.LandingGearState = (VehicleLandingGearState)VDF.LandingGear;
|
||||
}
|
||||
if (HasRoof && MainVehicle.RoofState != (VehicleRoofState)VDF.RoofState)
|
||||
MainVehicle.RoofState = (VehicleRoofState)VDF.RoofState;
|
||||
|
||||
if (VD.Flags.HasVehFlag(VehicleDataFlags.Repaired)) MainVehicle.Repair();
|
||||
if (VDF.Colors != _lastVDF.Colors)
|
||||
{
|
||||
Call(SET_VEHICLE_COLOURS, MainVehicle, VDF.Colors.Item1, VDF.Colors.Item2);
|
||||
}
|
||||
|
||||
MainVehicle.EngineHealth = VDF.EngineHealth;
|
||||
|
||||
if (VDF.ToggleModsMask != _lastVDF.ToggleModsMask)
|
||||
{
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
Call(TOGGLE_VEHICLE_MOD, MainVehicle.Handle, i + 17, (VDF.ToggleModsMask & (1 << i)) != 0);
|
||||
}
|
||||
}
|
||||
if (VDF.Livery != _lastVDF.Livery)
|
||||
{
|
||||
Call(SET_VEHICLE_LIVERY, MainVehicle, VDF.Livery);
|
||||
}
|
||||
|
||||
if (VDF.HeadlightColor != _lastVDF.HeadlightColor)
|
||||
{
|
||||
Call(SET_VEHICLE_XENON_LIGHT_COLOR_INDEX, MainVehicle.Handle, VDF.HeadlightColor);
|
||||
}
|
||||
|
||||
if (!CoreUtils.StructCmp(VDF.DamageModel, _lastVDF.DamageModel))
|
||||
{
|
||||
MainVehicle.SetDamageModel(VDF.DamageModel);
|
||||
}
|
||||
|
||||
if (MainVehicle.Handle == V?.Handle && Util.GetPlayerRadioIndex() != VDF.RadioStation)
|
||||
Util.SetPlayerRadioIndex(MainVehicle.Handle, VDF.RadioStation);
|
||||
|
||||
if (VDF.ExtrasMask != _lastVDF.ExtrasMask)
|
||||
{
|
||||
for (int i = 1; i < 15; i++)
|
||||
{
|
||||
var flag = (ushort)(1 << i);
|
||||
var hasExtra = (AvalibleExtras & (ushort)(1 << i)) != 0;
|
||||
if (!hasExtra)
|
||||
continue;
|
||||
|
||||
var on = (VDF.ExtrasMask & flag) != 0;
|
||||
Call(SET_VEHICLE_EXTRA, MainVehicle.Handle, i, !on);
|
||||
}
|
||||
}
|
||||
if (VDV.Mods != null && (_lastVDV.Mods == null || !VDV.Mods.SequenceEqual(_lastVDV.Mods)))
|
||||
{
|
||||
Call(SET_VEHICLE_MOD_KIT, MainVehicle, 0);
|
||||
|
||||
foreach (var mod in VDV.Mods) MainVehicle.Mods[(VehicleModType)mod.Item1].Index = mod.Item2;
|
||||
}
|
||||
if (VDV.LicensePlate != _lastVDV.LicensePlate)
|
||||
Call(SET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle, VDV.LicensePlate);
|
||||
|
||||
_lastVDF = VDF;
|
||||
_lastVDV = VDV;
|
||||
}
|
||||
|
||||
LastUpdated = Ticked;
|
||||
}
|
||||
|
||||
private void DisplayVehicle()
|
||||
{
|
||||
_predictedPosition = Predict(Position);
|
||||
var current = MainVehicle.ReadPosition();
|
||||
var distSquared = current.DistanceToSquared(_predictedPosition);
|
||||
var cali = _predictedPosition - current;
|
||||
if (!IsTrain) cali += 0.5f * (Velocity - MainVehicle.Velocity);
|
||||
if (distSquared > 10 * 10)
|
||||
{
|
||||
MainVehicle.Position = _predictedPosition;
|
||||
MainVehicle.Velocity = Velocity;
|
||||
MainVehicle.Quaternion = Quaternion;
|
||||
return;
|
||||
}
|
||||
|
||||
var stopped = Velocity == Vector3.Zero;
|
||||
|
||||
// Calibrate position
|
||||
if (distSquared > 0.03 * 0.03)
|
||||
{
|
||||
if (IsTrain || distSquared > 20 * 20) MainVehicle.Velocity = Velocity + cali;
|
||||
else MainVehicle.ApplyWorldForceCenterOfMass(cali, ForceType.InternalImpulse, true);
|
||||
}
|
||||
|
||||
if (NeedUpdate || stopped)
|
||||
{
|
||||
// Calibrate rotation
|
||||
var diff = Quaternion.Diff(MainVehicle.ReadQuaternion());
|
||||
MainVehicle.WorldRotationVelocity = diff.ToEulerAngles() * RotCalMult;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CreateVehicle()
|
||||
{
|
||||
MainVehicle?.Delete();
|
||||
MainVehicle = Util.CreateVehicle(Model, Position);
|
||||
if (!Model.IsInCdImage)
|
||||
// GTA.UI.Notification.Show($"~r~(Vehicle)Model ({CurrentVehicleModelHash}) cannot be loaded!");
|
||||
return false;
|
||||
if (MainVehicle == null)
|
||||
{
|
||||
Model.Request();
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (EntityPool.VehiclesLock)
|
||||
{
|
||||
EntityPool.Add(this);
|
||||
}
|
||||
|
||||
MainVehicle.Quaternion = Quaternion;
|
||||
if (MainVehicle.HasRoof) MainVehicle.RoofState = (VehicleRoofState)VDF.RoofState;
|
||||
foreach (var w in MainVehicle.Wheels) w.Fix();
|
||||
if (IsInvincible) MainVehicle.IsInvincible = true;
|
||||
SetUpFixedData();
|
||||
Model.MarkAsNoLongerNeeded();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region -- CONSTRUCTORS --
|
||||
|
||||
/// <summary>
|
||||
/// Create a local entity (outgoing sync)
|
||||
/// </summary>
|
||||
/// <param name="v"></param>
|
||||
internal SyncedVehicle(Vehicle v)
|
||||
{
|
||||
ID = EntityPool.RequestNewID();
|
||||
MainVehicle = v;
|
||||
MainVehicle.CanPretendOccupants = false;
|
||||
OwnerID = LocalPlayerID;
|
||||
SetUpFixedData();
|
||||
}
|
||||
|
||||
internal void SetUpFixedData()
|
||||
{
|
||||
if (MainVehicle == null) return;
|
||||
|
||||
IsAircraft = MainVehicle.IsAircraft;
|
||||
IsMotorcycle = MainVehicle.IsMotorcycle;
|
||||
HasRocketBoost = MainVehicle.HasRocketBoost;
|
||||
HasParachute = MainVehicle.HasParachute;
|
||||
HasRoof = MainVehicle.HasRoof;
|
||||
IsSubmarineCar = MainVehicle.IsSubmarineCar;
|
||||
IsDeluxo = MainVehicle.Model == 1483171323;
|
||||
IsTrain = MainVehicle.IsTrain;
|
||||
AvalibleExtras = 0;
|
||||
for (int i = 1; i < 15; i++)
|
||||
{
|
||||
if (Call<bool>(DOES_EXTRA_EXIST, MainVehicle.Handle, i))
|
||||
AvalibleExtras |= (ushort)(1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty VehicleEntity
|
||||
/// </summary>
|
||||
internal SyncedVehicle()
|
||||
{
|
||||
}
|
||||
|
||||
internal SyncedVehicle(int id)
|
||||
{
|
||||
ID = id;
|
||||
LastSynced = Ticked;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region -- PEDALING --
|
||||
|
||||
/*
|
||||
* Thanks to @oldnapalm.
|
||||
*/
|
||||
|
||||
private string PedalingAnimDict()
|
||||
{
|
||||
switch ((VehicleHash)Model)
|
||||
{
|
||||
case VehicleHash.Bmx:
|
||||
return "veh@bicycle@bmx@front@base";
|
||||
case VehicleHash.Cruiser:
|
||||
return "veh@bicycle@cruiserfront@base";
|
||||
case VehicleHash.Scorcher:
|
||||
return "veh@bicycle@mountainfront@base";
|
||||
default:
|
||||
return "veh@bicycle@roadfront@base";
|
||||
}
|
||||
}
|
||||
|
||||
private string PedalingAnimName(bool fast)
|
||||
{
|
||||
return fast ? "fast_pedal_char" : "cruise_pedal_char";
|
||||
}
|
||||
|
||||
private void StartPedalingAnim(bool fast)
|
||||
{
|
||||
MainVehicle.Driver?.Task.PlayAnimation(PedalingAnimDict(), PedalingAnimName(fast), 8.0f, -8.0f, -1,
|
||||
AnimationFlags.Loop | AnimationFlags.Secondary, 1.0f);
|
||||
}
|
||||
|
||||
private void StopPedalingAnim(bool fast)
|
||||
{
|
||||
MainVehicle.Driver.Task.ClearAnimation(PedalingAnimDict(), PedalingAnimName(fast));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
using System;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Client.Scripting;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class SyncEvents
|
||||
{
|
||||
#region TRIGGER
|
||||
|
||||
public static void TriggerPedKilled(SyncedPed victim)
|
||||
{
|
||||
Networking.SendSync(new Packets.PedKilled { VictimID = victim.ID }, ConnectionChannel.SyncEvents);
|
||||
}
|
||||
|
||||
public static void TriggerChangeOwner(int vehicleID, int newOwnerID)
|
||||
{
|
||||
Networking.SendSync(new Packets.OwnerChanged
|
||||
{
|
||||
ID = vehicleID,
|
||||
NewOwnerID = newOwnerID
|
||||
}, ConnectionChannel.SyncEvents, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
public static void TriggerNozzleTransform(int vehID, bool hover)
|
||||
{
|
||||
Networking.SendSync(new Packets.NozzleTransform { VehicleID = vehID, Hover = hover },
|
||||
ConnectionChannel.SyncEvents);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HANDLE
|
||||
|
||||
public static ParticleEffectAsset CorePFXAsset = new ParticleEffectAsset("core");
|
||||
|
||||
private static void HandlePedKilled(Packets.PedKilled p)
|
||||
{
|
||||
EntityPool.GetPedByID(p.VictimID)?.MainPed?.Kill();
|
||||
}
|
||||
|
||||
private static void HandleOwnerChanged(Packets.OwnerChanged p)
|
||||
{
|
||||
var v = EntityPool.GetVehicleByID(p.ID);
|
||||
if (v == null) return;
|
||||
v.OwnerID = p.NewOwnerID;
|
||||
v.SetLastSynced(true);
|
||||
v.Position = v.MainVehicle.Position;
|
||||
v.Quaternion = v.MainVehicle.Quaternion;
|
||||
}
|
||||
|
||||
private static void HandleNozzleTransform(Packets.NozzleTransform p)
|
||||
{
|
||||
EntityPool.GetVehicleByID(p.VehicleID)?.MainVehicle?.SetNozzleAngel(p.Hover ? 1 : 0);
|
||||
}
|
||||
|
||||
private static void HandleBulletShot(int ownerID, uint weaponHash, Vector3 end)
|
||||
{
|
||||
var c = EntityPool.GetPedByID(ownerID);
|
||||
var p = c?.MainPed;
|
||||
if (p == null)
|
||||
{
|
||||
return;
|
||||
// p = Game.Player.Character;
|
||||
// Log.Warning("Failed to find owner for bullet");
|
||||
}
|
||||
|
||||
var damage = (int)p.GetWeaponDamage(weaponHash);
|
||||
|
||||
// Some weapon hash has some firing issue, so we need to replace it with known good ones
|
||||
weaponHash = WeaponUtil.GetWeaponFix(weaponHash);
|
||||
|
||||
// Request asset for muzzle flash
|
||||
if (!CorePFXAsset.IsLoaded) CorePFXAsset.Request();
|
||||
|
||||
// Request asset for materialising the bullet
|
||||
var asset = new WeaponAsset(weaponHash);
|
||||
if (!asset.IsLoaded) asset.Request();
|
||||
|
||||
var vehWeap = p.VehicleWeapon;
|
||||
bool isVeh = vehWeap != VehicleWeaponHash.Invalid;
|
||||
var bone = isVeh ? c.MainPed.CurrentVehicle.GetMuzzleBone(vehWeap) : c.MainPed.GetMuzzleBone();
|
||||
if (bone == null)
|
||||
{
|
||||
Log.Warning($"Failed to find muzzle bone for {(isVeh ? vehWeap : (WeaponHash)weaponHash)}, {(isVeh ? p.CurrentVehicle.DisplayName : "")}");
|
||||
return;
|
||||
}
|
||||
World.ShootBullet(bone.Position, end, p, asset, damage);
|
||||
|
||||
World.CreateParticleEffectNonLooped(CorePFXAsset,
|
||||
!isVeh && p.Weapons.Current.Components.GetSuppressorComponent().Active
|
||||
? "muz_pistol_silencer"
|
||||
: ((WeaponHash)weaponHash).GetFlashFX(isVeh), bone.Position, isVeh ? bone.GetRotation() : bone.Owner.Rotation);
|
||||
}
|
||||
|
||||
public static void HandleEvent(PacketType type, NetIncomingMessage msg)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PacketType.BulletShot:
|
||||
{
|
||||
var p = msg.GetPacket<Packets.BulletShot>();
|
||||
HandleBulletShot(p.OwnerID, p.WeaponHash, p.EndPosition);
|
||||
break;
|
||||
}
|
||||
case PacketType.OwnerChanged:
|
||||
{
|
||||
HandleOwnerChanged(msg.GetPacket<Packets.OwnerChanged>());
|
||||
}
|
||||
break;
|
||||
case PacketType.PedKilled:
|
||||
{
|
||||
HandlePedKilled(msg.GetPacket<Packets.PedKilled>());
|
||||
}
|
||||
break;
|
||||
case PacketType.NozzleTransform:
|
||||
{
|
||||
HandleNozzleTransform(msg.GetPacket<Packets.NozzleTransform>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Networking.Peer.Recycle(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CHECK EVENTS
|
||||
|
||||
public static void Check(SyncedPed c)
|
||||
{
|
||||
var subject = c.MainPed;
|
||||
|
||||
// Check bullets
|
||||
if (subject.IsShooting && !subject.IsUsingProjectileWeapon())
|
||||
{
|
||||
|
||||
var i = 0;
|
||||
|
||||
// Some weapon is not instant hit, so we may need to wait a few ticks to get the impact position
|
||||
bool getBulletImpact()
|
||||
{
|
||||
var endPos = subject.LastWeaponImpactPosition;
|
||||
var vehWeap = subject.VehicleWeapon;
|
||||
if (vehWeap == VehicleWeaponHash.Invalid)
|
||||
{
|
||||
// Ped weapon sync
|
||||
var pedWeap = subject.Weapons.Current.Hash;
|
||||
if (endPos != default)
|
||||
{
|
||||
Networking.SendBullet(c.ID, (uint)pedWeap, endPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get impact in next tick
|
||||
if (++i <= 5) return false;
|
||||
|
||||
// Exceeded maximum wait of 5 ticks, return (inaccurate) aim coordinate
|
||||
endPos = subject.GetAimCoord();
|
||||
Networking.SendBullet(c.ID, (uint)pedWeap, endPos);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Veh weapon sync
|
||||
if (endPos == default)
|
||||
{
|
||||
var veh = c.MainPed.CurrentVehicle;
|
||||
var b = veh.GetMuzzleBone(vehWeap);
|
||||
if (b == null)
|
||||
{
|
||||
Log.Warning($"Failed to find muzzle bone for {vehWeap}, {veh.DisplayName}");
|
||||
return true;
|
||||
}
|
||||
endPos = b.Position + b.ForwardVector * 200;
|
||||
}
|
||||
Networking.SendBullet(c.ID, (uint)vehWeap, endPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!getBulletImpact()) API.QueueAction(getBulletImpact);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Check(SyncedVehicle v)
|
||||
{
|
||||
if (v.MainVehicle == null || !v.MainVehicle.HasNozzle()) return;
|
||||
|
||||
if (v.LastNozzleAngle == 1 && v.MainVehicle.GetNozzleAngel() != 1)
|
||||
TriggerNozzleTransform(v.ID, false);
|
||||
else if (v.LastNozzleAngle == 0 && v.MainVehicle.GetNozzleAngel() != 0) TriggerNozzleTransform(v.ID, true);
|
||||
v.LastNozzleAngle = v.MainVehicle.GetNozzleAngel();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<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>
|
@ -1,34 +0,0 @@
|
||||
global using static RageCoop.Client.PInvoke;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal partial class PInvoke
|
||||
{
|
||||
public static void ClearLastError()
|
||||
{
|
||||
SetLastErrorEx(0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check and throw if an error occurred during last winapi call in current thread
|
||||
/// </summary>
|
||||
public static void ErrorCheck32()
|
||||
{
|
||||
var error = Marshal.GetLastWin32Error();
|
||||
if (error != 0)
|
||||
{
|
||||
ClearLastError();
|
||||
throw new Win32Exception(error);
|
||||
}
|
||||
}
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
internal static partial void SetLastErrorEx(uint dwErrCode, uint dwType);
|
||||
|
||||
[LibraryImport("Kernel32.dll", SetLastError = true)]
|
||||
public static unsafe partial uint GetFinalPathNameByHandleW(IntPtr hFile, char* lpszFilePath, uint cchFilePath, uint dwFlags);
|
||||
}
|
||||
}
|
@ -1,275 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
using GTA.UI;
|
||||
using LemonUI.Elements;
|
||||
using RageCoop.Core;
|
||||
using static RageCoop.Client.Shared;
|
||||
using Font = GTA.UI.Font;
|
||||
|
||||
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static partial class Util
|
||||
{
|
||||
/// <summary>
|
||||
/// The location of the cursor on screen between 0 and 1.
|
||||
/// </summary>
|
||||
public static PointF CursorPositionRelative
|
||||
{
|
||||
get
|
||||
{
|
||||
var cursorX = Game.IsControlEnabled(Control.CursorX)
|
||||
? Game.GetControlValueNormalized(Control.CursorX)
|
||||
: Game.GetDisabledControlValueNormalized(Control.CursorX);
|
||||
var cursorY = Game.IsControlEnabled(Control.CursorY)
|
||||
? Game.GetControlValueNormalized(Control.CursorY)
|
||||
: Game.GetDisabledControlValueNormalized(Control.CursorY);
|
||||
return new PointF(cursorX, cursorY);
|
||||
}
|
||||
}
|
||||
|
||||
public static Point CursorPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
var p = CursorPositionRelative;
|
||||
var res = Screen.Resolution;
|
||||
return new Point((int)(p.X * res.Width), (int)(p.Y * res.Height));
|
||||
}
|
||||
}
|
||||
|
||||
public static SizeF ResolutionMaintainRatio
|
||||
{
|
||||
get
|
||||
{
|
||||
// Get the game width and height
|
||||
var screenw = Screen.Resolution.Width;
|
||||
var screenh = Screen.Resolution.Height;
|
||||
// Calculate the ratio
|
||||
var ratio = (float)screenw / screenh;
|
||||
// And the width with that ratio
|
||||
var width = 1080f * ratio;
|
||||
// Finally, return a SizeF
|
||||
return new SizeF(width, 1080f);
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector3 GetRotation(this EntityBone b)
|
||||
{
|
||||
return b.ForwardVector.ToEulerRotation(b.UpVector);
|
||||
}
|
||||
|
||||
public static void DrawTextFromCoord(Vector3 coord, string text, float scale = 0.5f, Point offset = default)
|
||||
{
|
||||
Point toDraw = default;
|
||||
if (WorldToScreen(coord, ref toDraw))
|
||||
{
|
||||
toDraw.X += offset.X;
|
||||
toDraw.Y += offset.Y;
|
||||
new ScaledText(toDraw, text, scale, Font.ChaletLondon)
|
||||
{
|
||||
Outline = true,
|
||||
Alignment = Alignment.Center,
|
||||
Color = Color.White
|
||||
}.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe bool WorldToScreen(Vector3 pos, ref Point screenPos)
|
||||
{
|
||||
float x, y;
|
||||
var res = ResolutionMaintainRatio;
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ClientSettings ReadSettings(string path = null)
|
||||
{
|
||||
path = path ?? SettingsPath;
|
||||
|
||||
Directory.CreateDirectory(Directory.GetParent(path).FullName);
|
||||
ClientSettings settings;
|
||||
try
|
||||
{
|
||||
settings = JsonDeserialize<ClientSettings>(File.ReadAllText(path));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log?.Error(ex);
|
||||
File.WriteAllText(path, JsonSerialize(settings = new ClientSettings()));
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public static bool SaveSettings(string path = null, ClientSettings settings = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
path = path ?? SettingsPath;
|
||||
settings = settings ?? Settings;
|
||||
Directory.CreateDirectory(Directory.GetParent(path).FullName);
|
||||
|
||||
File.WriteAllText(path, JsonSerialize(settings));
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log?.Error(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Vehicle CreateVehicle(Model model, Vector3 position, float heading = 0f)
|
||||
{
|
||||
if (!model.IsLoaded) return null;
|
||||
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(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)
|
||||
Call(START_ENTITY_FIRE, e.Handle);
|
||||
else
|
||||
Call(STOP_ENTITY_FIRE, e.Handle);
|
||||
}
|
||||
|
||||
public static void SetFrozen(this Entity e, bool toggle)
|
||||
{
|
||||
Call(FREEZE_ENTITY_POSITION, e, toggle);
|
||||
}
|
||||
|
||||
public static SyncedPed GetSyncEntity(this Ped p)
|
||||
{
|
||||
if (p == null) return null;
|
||||
var c = EntityPool.GetPedByHandle(p.Handle);
|
||||
if (c == null) EntityPool.Add(c = new SyncedPed(p));
|
||||
return c;
|
||||
}
|
||||
|
||||
public static SyncedVehicle GetSyncEntity(this Vehicle veh)
|
||||
{
|
||||
if (veh == null) return null;
|
||||
var v = EntityPool.GetVehicleByHandle(veh.Handle);
|
||||
if (v == null) EntityPool.Add(v = new SyncedVehicle(veh));
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current radio index for player, returns 255 if there's none
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static byte GetPlayerRadioIndex()
|
||||
{
|
||||
return (byte)Call<int>(GET_PLAYER_RADIO_STATION_INDEX);
|
||||
}
|
||||
|
||||
public static void SetPlayerRadioIndex(int playerVeh, int index)
|
||||
{
|
||||
if (playerVeh == 0)
|
||||
playerVeh = Call<int>(GET_VEHICLE_PED_IS_IN, P.Handle, false);
|
||||
|
||||
if (index == byte.MaxValue)
|
||||
Call(SET_VEH_RADIO_STATION, playerVeh, "OFF");
|
||||
else
|
||||
Call(SET_RADIO_TO_STATION_INDEX, index);
|
||||
}
|
||||
|
||||
public static EntityPopulationType GetPopulationType(int handle)
|
||||
=> (EntityPopulationType)Call<int>(GET_ENTITY_POPULATION_TYPE, handle);
|
||||
|
||||
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; }
|
||||
|
||||
public static unsafe void NativeMemory()
|
||||
{
|
||||
IntPtr address;
|
||||
|
||||
address = Game.FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx");
|
||||
if (address != IntPtr.Zero) _steeringAngleOffset = *(int*)(address + 6) + 8;
|
||||
}
|
||||
|
||||
public static unsafe void CustomSteeringAngle(this Vehicle veh, float value)
|
||||
{
|
||||
var address = new IntPtr((long)veh.MemoryAddress);
|
||||
if (address == IntPtr.Zero || _steeringAngleOffset == 0) return;
|
||||
|
||||
*(float*)(address + _steeringAngleOffset).ToPointer() = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MATH
|
||||
|
||||
public static Vector3 LinearVectorLerp(Vector3 start, Vector3 end, ulong currentTime, int duration)
|
||||
{
|
||||
return new Vector3
|
||||
{
|
||||
X = LinearFloatLerp(start.X, end.X, currentTime, duration),
|
||||
Y = LinearFloatLerp(start.Y, end.Y, currentTime, duration),
|
||||
Z = LinearFloatLerp(start.Z, end.Z, currentTime, duration)
|
||||
};
|
||||
}
|
||||
|
||||
public static float LinearFloatLerp(float start, float end, ulong currentTime, int duration)
|
||||
{
|
||||
return (end - start) * currentTime / duration + start;
|
||||
}
|
||||
|
||||
public static float Lerp(float from, float to, float fAlpha)
|
||||
{
|
||||
return from * (1.0f - fAlpha) + to * fAlpha; //from + (to - from) * fAlpha
|
||||
}
|
||||
|
||||
public static Vector3 RotationToDirection(Vector3 rotation)
|
||||
{
|
||||
var z = MathExtensions.DegToRad(rotation.Z);
|
||||
var x = MathExtensions.DegToRad(rotation.X);
|
||||
var num = Math.Abs(Math.Cos(x));
|
||||
|
||||
return new Vector3
|
||||
{
|
||||
X = (float)(-Math.Sin(z) * num),
|
||||
Y = (float)(Math.Cos(z) * num),
|
||||
Z = (float)Math.Sin(x)
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class VehicleExtensions
|
||||
{
|
||||
#region VEHICLE
|
||||
|
||||
public static VehicleDataFlags GetVehicleFlags(this SyncedVehicle v)
|
||||
{
|
||||
var veh = v.MainVehicle;
|
||||
VehicleDataFlags flags = 0;
|
||||
|
||||
if (veh.IsEngineRunning) flags |= VehicleDataFlags.IsEngineRunning;
|
||||
|
||||
if (veh.AreLightsOn) flags |= VehicleDataFlags.AreLightsOn;
|
||||
|
||||
if (veh.BrakePower >= 0.01f) flags |= VehicleDataFlags.AreBrakeLightsOn;
|
||||
|
||||
if (veh.AreHighBeamsOn) flags |= VehicleDataFlags.AreHighBeamsOn;
|
||||
|
||||
if (veh.IsSirenActive) flags |= VehicleDataFlags.IsSirenActive;
|
||||
|
||||
if (veh.IsDead) flags |= VehicleDataFlags.IsDead;
|
||||
|
||||
if (Call<bool>(IS_HORN_ACTIVE, veh.Handle)) flags |= VehicleDataFlags.IsHornActive;
|
||||
|
||||
if (v.IsSubmarineCar && Call<bool>(IS_VEHICLE_IN_SUBMARINE_MODE, veh.Handle))
|
||||
flags |= VehicleDataFlags.IsTransformed;
|
||||
|
||||
if (v.IsAircraft) flags |= VehicleDataFlags.IsAircraft;
|
||||
|
||||
if (v.IsDeluxo && veh.IsDeluxoHovering()) flags |= VehicleDataFlags.IsDeluxoHovering;
|
||||
|
||||
if (v.HasRoof) flags |= VehicleDataFlags.HasRoof;
|
||||
|
||||
if (v.HasRocketBoost && veh.IsRocketBoostActive) flags |= VehicleDataFlags.IsRocketBoostActive;
|
||||
|
||||
if (v.HasParachute && veh.IsParachuteDeployed) flags |= VehicleDataFlags.IsParachuteActive;
|
||||
|
||||
if (veh.IsOnFire) flags |= VehicleDataFlags.IsOnFire;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
public static (int, int)[] GetVehicleMods(this SyncedVehicle sv, out byte togglesMask)
|
||||
{
|
||||
var modsArr = sv.MainVehicle.Mods.ToArray();
|
||||
var result = new (int, int)[modsArr.Length];
|
||||
for (int i = 0; i < modsArr.Length; i++)
|
||||
{
|
||||
result[i] = ((int)modsArr[i].Type, modsArr[i].Index);
|
||||
}
|
||||
togglesMask = 0;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
if (Call<bool>(IS_TOGGLE_MOD_ON, sv.MainVehicle.Handle, i + 17))
|
||||
{
|
||||
togglesMask |= (byte)(1 << i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ushort GetVehicleExtras(this SyncedVehicle sv)
|
||||
{
|
||||
ushort result = 0;
|
||||
for (int i = 1; i < 15; i++)
|
||||
{
|
||||
var flag = (ushort)(1 << i);
|
||||
var hasExtra = (sv.AvalibleExtras & (ushort)(1 << i)) != 0;
|
||||
if (hasExtra && Call<bool>(IS_VEHICLE_EXTRA_TURNED_ON, sv.MainVehicle.Handle, i))
|
||||
result |= flag;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static VehicleDamageModel GetVehicleDamageModel(this Vehicle veh)
|
||||
{
|
||||
// Broken windows
|
||||
byte brokenWindows = 0;
|
||||
for (var i = 0; i < 8; i++)
|
||||
if (!veh.Windows[(VehicleWindowIndex)i].IsIntact)
|
||||
brokenWindows |= (byte)(1 << i);
|
||||
|
||||
// Broken doors
|
||||
byte brokenDoors = 0;
|
||||
byte openedDoors = 0;
|
||||
foreach (var door in veh.Doors)
|
||||
if (door.IsBroken)
|
||||
brokenDoors |= (byte)(1 << (byte)door.Index);
|
||||
else if (door.IsOpen) openedDoors |= (byte)(1 << (byte)door.Index);
|
||||
|
||||
// Bursted tires
|
||||
short burstedTires = 0;
|
||||
foreach (var wheel in veh.Wheels.GetAllWheels())
|
||||
if (wheel.IsBursted)
|
||||
burstedTires |= (short)(1 << (int)wheel.BoneId);
|
||||
|
||||
return new VehicleDamageModel
|
||||
{
|
||||
BrokenDoors = brokenDoors,
|
||||
OpenedDoors = openedDoors,
|
||||
BrokenWindows = brokenWindows,
|
||||
BurstedTires = burstedTires,
|
||||
LeftHeadLightBroken = (byte)(veh.IsLeftHeadLightBroken ? 1 : 0),
|
||||
RightHeadLightBroken = (byte)(veh.IsRightHeadLightBroken ? 1 : 0)
|
||||
};
|
||||
}
|
||||
|
||||
public static void SetDamageModel(this Vehicle veh, VehicleDamageModel model, bool leavedoors = true)
|
||||
{
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
var door = veh.Doors[(VehicleDoorIndex)i];
|
||||
if ((model.BrokenDoors & (byte)(1 << i)) != 0)
|
||||
{
|
||||
if (!door.IsBroken) door.Break(leavedoors);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (door.IsBroken)
|
||||
{
|
||||
// The vehicle can only fix a door if the vehicle was completely fixed
|
||||
veh.Repair();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((model.OpenedDoors & (byte)(1 << i)) != 0)
|
||||
{
|
||||
if (!door.IsOpen && !door.IsBroken) door.Open();
|
||||
}
|
||||
else if (door.IsOpen)
|
||||
{
|
||||
if (!door.IsBroken) door.Close();
|
||||
}
|
||||
|
||||
if ((model.BrokenWindows & (byte)(1 << i)) != 0)
|
||||
veh.Windows[(VehicleWindowIndex)i].Smash();
|
||||
else if (!veh.Windows[(VehicleWindowIndex)i].IsIntact) veh.Windows[(VehicleWindowIndex)i].Repair();
|
||||
}
|
||||
|
||||
foreach (var wheel in veh.Wheels)
|
||||
if ((model.BurstedTires & (short)(1 << (int)wheel.BoneId)) != 0)
|
||||
{
|
||||
if (!wheel.IsBursted)
|
||||
{
|
||||
wheel.Puncture();
|
||||
wheel.Burst();
|
||||
}
|
||||
}
|
||||
else if (wheel.IsBursted)
|
||||
{
|
||||
wheel.Fix();
|
||||
}
|
||||
|
||||
veh.IsLeftHeadLightBroken = model.LeftHeadLightBroken > 0;
|
||||
veh.IsRightHeadLightBroken = model.RightHeadLightBroken > 0;
|
||||
}
|
||||
|
||||
public static void SetDeluxoHoverState(this Vehicle deluxo, bool hover)
|
||||
{
|
||||
Call(SET_SPECIAL_FLIGHT_MODE_TARGET_RATIO, deluxo, hover ? 1f : 0f);
|
||||
}
|
||||
|
||||
public static bool IsDeluxoHovering(this Vehicle deluxo)
|
||||
{
|
||||
return Math.Abs(deluxo.Bones[27].ForwardVector.GetCosTheta(deluxo.ForwardVector) - 1) > 0.05;
|
||||
}
|
||||
|
||||
public static void SetDeluxoWingRatio(this Vehicle v, float ratio)
|
||||
{
|
||||
Call(SET_HOVER_MODE_WING_RATIO, v, ratio);
|
||||
}
|
||||
|
||||
public static float GetDeluxoWingRatio(this Vehicle v)
|
||||
{
|
||||
return v.Bones[99].Position.DistanceTo(v.Bones[92].Position) - 1.43f;
|
||||
}
|
||||
|
||||
public static float GetNozzleAngel(this Vehicle plane)
|
||||
{
|
||||
return Call<float>(GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane);
|
||||
}
|
||||
|
||||
public static bool HasNozzle(this Vehicle v)
|
||||
{
|
||||
switch (v.Model.Hash)
|
||||
{
|
||||
// Hydra
|
||||
case 970385471:
|
||||
return true;
|
||||
|
||||
// Avenger
|
||||
case -2118308144:
|
||||
return true;
|
||||
|
||||
// Tula
|
||||
case 1043222410:
|
||||
return true;
|
||||
|
||||
// Avenger
|
||||
case 408970549:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SetNozzleAngel(this Vehicle plane, float ratio)
|
||||
{
|
||||
Call(SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
using RageCoop.Client.Scripting;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal class WeaponFix
|
||||
{
|
||||
public Dictionary<uint, string> Bullet = new Dictionary<uint, string>();
|
||||
public Dictionary<uint, string> Lazer = new Dictionary<uint, string>();
|
||||
public Dictionary<uint, string> Others = new Dictionary<uint, string>();
|
||||
}
|
||||
|
||||
internal static class WeaponUtil
|
||||
{
|
||||
public static Dictionary<uint, VehicleWeaponInfo> VehicleWeapons = new Dictionary<uint, VehicleWeaponInfo>();
|
||||
public static WeaponFix WeaponFix;
|
||||
public static Dictionary<uint, WeaponInfo> Weapons;
|
||||
|
||||
static WeaponUtil()
|
||||
{
|
||||
// Parse and load to memory
|
||||
foreach (var w in JsonDeserialize<VehicleWeaponInfo[]>(
|
||||
File.ReadAllText(VehicleWeaponDataPath))) VehicleWeapons.Add(w.Hash, w);
|
||||
|
||||
Weapons = JsonDeserialize<Dictionary<uint, WeaponInfo>>(
|
||||
File.ReadAllText(WeaponInfoDataPath));
|
||||
|
||||
if (File.Exists(WeaponFixDataPath))
|
||||
WeaponFix = JsonDeserialize<WeaponFix>(File.ReadAllText(WeaponFixDataPath));
|
||||
else
|
||||
Log.Warning("Weapon fix data not found");
|
||||
}
|
||||
|
||||
public static void DumpWeaponFix(string path)
|
||||
{
|
||||
var P = Game.Player.Character;
|
||||
var pos = P.Position + Vector3.WorldUp * 3;
|
||||
var types = new HashSet<int> { 3 };
|
||||
P.IsInvincible = true;
|
||||
var fix = new WeaponFix();
|
||||
foreach (var w in Weapons)
|
||||
{
|
||||
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 (!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)
|
||||
{
|
||||
case "BULLET":
|
||||
fix.Bullet.Add(w.Key, w.Value.Name);
|
||||
break;
|
||||
case "EXPLOSIVE":
|
||||
fix.Lazer.Add(w.Key, w.Value.Name);
|
||||
break;
|
||||
default:
|
||||
fix.Others.Add(w.Key, w.Value.Name);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var p in World.GetAllProjectiles()) p.Delete();
|
||||
Script.Wait(50);
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(path, JsonSerialize(fix));
|
||||
|
||||
P.IsInvincible = false;
|
||||
}
|
||||
|
||||
public static uint GetWeaponFix(uint hash)
|
||||
{
|
||||
if (WeaponFix.Bullet.TryGetValue(hash, out _)) return 0x461DDDB0;
|
||||
if (WeaponFix.Lazer.TryGetValue(hash, out _)) return 0xE2822A29;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
public static Dictionary<uint, bool> GetWeaponComponents(this Weapon weapon)
|
||||
{
|
||||
Dictionary<uint, bool> result = null;
|
||||
|
||||
if (weapon.Components.Count > 0)
|
||||
{
|
||||
result = new Dictionary<uint, bool>();
|
||||
|
||||
foreach (var comp in weapon.Components) result.Add((uint)comp.ComponentHash, comp.Active);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static EntityBone GetMuzzleBone(this Ped p)
|
||||
{
|
||||
return p.Weapons.CurrentWeaponObject?.Bones["gun_muzzle"];
|
||||
}
|
||||
|
||||
public static float GetWeaponDamage(this Ped P, uint hash)
|
||||
{
|
||||
var comp = P.Weapons.Current.Components.GetSuppressorComponent();
|
||||
return Call<float>(GET_WEAPON_DAMAGE, hash,
|
||||
comp.Active ? comp.ComponentHash : WeaponComponentHash.Invalid);
|
||||
}
|
||||
|
||||
public static int GetMuzzleIndex(this Vehicle v, VehicleWeaponHash hash)
|
||||
{
|
||||
if (VehicleWeapons.TryGetValue((uint)v.Model.Hash, out var veh) &&
|
||||
veh.Weapons.TryGetValue((uint)hash, out var wp))
|
||||
return (int)wp.Bones[CoreUtils.RandInt(0, wp.Bones.Length)].BoneIndex;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static EntityBone GetMuzzleBone(this Vehicle v, VehicleWeaponHash hash)
|
||||
{
|
||||
if ((uint)hash == 1422046295) hash = VehicleWeaponHash.WaterCannon;
|
||||
var i = v.GetMuzzleIndex(hash);
|
||||
if (i == -1) return null;
|
||||
return v.Bones[i];
|
||||
}
|
||||
|
||||
public static bool IsUsingProjectileWeapon(this Ped p)
|
||||
{
|
||||
var vp = p.VehicleWeapon;
|
||||
return Weapons.TryGetValue(vp != VehicleWeaponHash.Invalid ? (uint)vp : (uint)p.Weapons.Current.Hash,
|
||||
out var info)
|
||||
&& info.FireType == "PROJECTILE";
|
||||
}
|
||||
|
||||
public static string GetFlashFX(this WeaponHash w, bool veh)
|
||||
{
|
||||
if (veh)
|
||||
switch ((VehicleWeaponHash)w)
|
||||
{
|
||||
case VehicleWeaponHash.Tank:
|
||||
return "muz_tank";
|
||||
default: return "muz_buzzard";
|
||||
}
|
||||
|
||||
switch (w.GetWeaponGroup())
|
||||
{
|
||||
case WeaponGroup.SMG:
|
||||
return "muz_smg";
|
||||
|
||||
case WeaponGroup.Shotgun:
|
||||
return "muz_smg";
|
||||
|
||||
case WeaponGroup.AssaultRifle:
|
||||
return "muz_assault_rifle";
|
||||
|
||||
case WeaponGroup.Pistol:
|
||||
return "muz_pistol";
|
||||
|
||||
case WeaponGroup.Stungun:
|
||||
return "muz_stungun";
|
||||
|
||||
case WeaponGroup.Heavy:
|
||||
switch (w)
|
||||
{
|
||||
case WeaponHash.Minigun:
|
||||
return "muz_minigun";
|
||||
|
||||
case WeaponHash.RPG:
|
||||
return "muz_rpg";
|
||||
|
||||
default:
|
||||
return "muz_minigun";
|
||||
}
|
||||
case WeaponGroup.Sniper:
|
||||
return "muz_alternate_star";
|
||||
|
||||
case WeaponGroup.PetrolCan:
|
||||
return "weap_petrol_can";
|
||||
|
||||
case WeaponGroup.FireExtinguisher:
|
||||
return "weap_extinguisher";
|
||||
default:
|
||||
return "muz_assault_rifle";
|
||||
}
|
||||
}
|
||||
|
||||
public static WeaponGroup GetWeaponGroup(this WeaponHash hash)
|
||||
{
|
||||
return Call<WeaponGroup>(GET_WEAPONTYPE_GROUP, hash);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
using GTA.UI;
|
||||
using RageCoop.Client.Menus;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
[ScriptAttributes(Author = "RageCoop", SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V")]
|
||||
internal class WorldThread : Script
|
||||
{
|
||||
public static Script Instance;
|
||||
private static readonly List<Func<bool>> QueuedActions = new List<Func<bool>>();
|
||||
|
||||
private static bool _trafficEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public WorldThread()
|
||||
{
|
||||
Instance = this;
|
||||
Aborted += (e) => { DoQueuedActions(); ChangeTraffic(true); };
|
||||
}
|
||||
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 (_sleeping)
|
||||
{
|
||||
Game.Pause(true);
|
||||
while (_sleeping)
|
||||
{
|
||||
// Don't wait longer than 5 seconds or the game will crash
|
||||
Thread.Sleep(4500);
|
||||
Yield();
|
||||
}
|
||||
Game.Pause(false);
|
||||
}
|
||||
|
||||
if (Game.IsLoading) return;
|
||||
|
||||
try
|
||||
{
|
||||
CoopMenu.MenuPool.Process();
|
||||
DoQueuedActions();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
}
|
||||
|
||||
if (!Networking.IsOnServer) return;
|
||||
|
||||
Game.DisableControlThisFrame(Control.FrontendPause);
|
||||
|
||||
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.
|
||||
Call(SET_DEEP_OCEAN_SCALER, 0.0f); // Works only ~200 meters around the player
|
||||
|
||||
if (Settings.ShowEntityOwnerName)
|
||||
unsafe
|
||||
{
|
||||
int handle;
|
||||
if (Call<bool>(GET_ENTITY_PLAYER_IS_FREE_AIMING_AT, 0, &handle))
|
||||
{
|
||||
var entity = Entity.FromHandle(handle);
|
||||
if (entity != null)
|
||||
{
|
||||
var owner = "invalid";
|
||||
if (entity.EntityType == EntityType.Vehicle)
|
||||
owner = (entity as Vehicle).GetSyncEntity()?.Owner?.Username ?? "unknown";
|
||||
if (entity.EntityType == EntityType.Ped)
|
||||
owner = (entity as Ped).GetSyncEntity()?.Owner?.Username ?? "unknown";
|
||||
Screen.ShowHelpTextThisFrame("Entity owner: " + owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_trafficEnabled)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Traffic(bool enable)
|
||||
{
|
||||
ChangeTraffic(enable);
|
||||
_trafficEnabled = enable;
|
||||
}
|
||||
|
||||
private static void ChangeTraffic(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
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)
|
||||
{
|
||||
Call(ADD_SCENARIO_BLOCKING_AREA, -10000.0f, -10000.0f, -1000.0f, 10000.0f, 10000.0f,
|
||||
1000.0f, 0, 1, 1, 1);
|
||||
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;
|
||||
var c = EntityPool.GetPedByHandle(ped.Handle);
|
||||
if (c == null || (c.IsLocal && ped.Handle != Game.Player.Character.Handle &&
|
||||
ped.PopulationType != EntityPopulationType.Mission))
|
||||
{
|
||||
Log.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
|
||||
ped.CurrentVehicle?.Delete();
|
||||
ped.Kill();
|
||||
ped.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var veh in World.GetAllVehicles())
|
||||
{
|
||||
var v = veh.GetSyncEntity();
|
||||
if (v.MainVehicle == Game.Player.LastVehicle ||
|
||||
v.MainVehicle == Game.Player.Character.CurrentVehicle)
|
||||
// Don't delete player's vehicle
|
||||
continue;
|
||||
if (v == null || (v.IsLocal && veh.PopulationType != EntityPopulationType.Mission))
|
||||
// Log.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
|
||||
|
||||
veh.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Delay(Action a, int time)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Thread.Sleep(time);
|
||||
QueueAction(a);
|
||||
});
|
||||
}
|
||||
|
||||
internal static void DoQueuedActions()
|
||||
{
|
||||
lock (QueuedActions)
|
||||
{
|
||||
foreach (var action in QueuedActions.ToArray())
|
||||
try
|
||||
{
|
||||
if (action()) QueuedActions.Remove(action);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
QueuedActions.Remove(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue an action to be executed on next tick, allowing you to call scripting API from another thread.
|
||||
/// </summary>
|
||||
/// <param name="a">
|
||||
/// An action to be executed with a return value indicating whether the action can be removed after
|
||||
/// execution.
|
||||
/// </param>
|
||||
internal static void QueueAction(Func<bool> a)
|
||||
{
|
||||
lock (QueuedActions)
|
||||
{
|
||||
QueuedActions.Add(a);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void QueueAction(Action a)
|
||||
{
|
||||
lock (QueuedActions)
|
||||
{
|
||||
QueuedActions.Add(() =>
|
||||
{
|
||||
a();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all queued actions
|
||||
/// </summary>
|
||||
internal static void ClearQueuedActions()
|
||||
{
|
||||
lock (QueuedActions)
|
||||
{
|
||||
QueuedActions.Clear();
|
||||
}
|
||||
}
|
||||
private static bool _sleeping;
|
||||
[ConsoleCommand("Put the game to sleep state by blocking main thread, press any key in the debug console to resume")]
|
||||
public static void Sleep()
|
||||
{
|
||||
if (_sleeping)
|
||||
throw new InvalidOperationException("Already in sleep state");
|
||||
|
||||
_sleeping = true;
|
||||
Task.Run(() =>
|
||||
{
|
||||
System.Console.WriteLine("Press any key to put the game out of sleep state");
|
||||
System.Console.ReadKey();
|
||||
System.Console.WriteLine("Game resumed");
|
||||
_sleeping = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
245
Core/Buffer.cs
245
Core/Buffer.cs
@ -1,245 +0,0 @@
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GTA.Math;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
|
||||
internal struct LQuaternion
|
||||
{
|
||||
public LQuaternion(float x, float y, float z, float w)
|
||||
{
|
||||
X = x; Y = y; Z = z; W = w;
|
||||
}
|
||||
public float X, Y, Z, W;
|
||||
public static implicit operator LQuaternion(Quaternion q) => new(q.X, q.Y, q.Z, q.W);
|
||||
public static implicit operator Quaternion(LQuaternion q) => new(q.X, q.Y, q.Z, q.W);
|
||||
}
|
||||
}
|
@ -1,967 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using GTA.Math;
|
||||
|
||||
namespace RageCoop.Core.CompactVectors
|
||||
{
|
||||
internal struct LQuaternion : IEquatable<LQuaternion>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the X component of the quaternion.
|
||||
/// </summary>
|
||||
/// <value>The X component of the quaternion.</value>
|
||||
public float X;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Y component of the quaternion.
|
||||
/// </summary>
|
||||
/// <value>The Y component of the quaternion.</value>
|
||||
public float Y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Z component of the quaternion.
|
||||
/// </summary>
|
||||
/// <value>The Z component of the quaternion.</value>
|
||||
public float Z;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the W component of the quaternion.
|
||||
/// </summary>
|
||||
/// <value>The W component of the quaternion.</value>
|
||||
public float W;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LQuaternion"/> structure.
|
||||
/// </summary>
|
||||
/// <param name="x">The X component of the quaternion.</param>
|
||||
/// <param name="y">The Y component of the quaternion.</param>
|
||||
/// <param name="z">The Z component of the quaternion.</param>
|
||||
/// <param name="w">The W component of the quaternion.</param>
|
||||
public LQuaternion(float x, float y, float z, float w) : this()
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LQuaternion"/> structure.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis of rotation.</param>
|
||||
/// <param name="angle">The angle of rotation in radians.</param>
|
||||
public LQuaternion(Vector3 axis, float angle) : this()
|
||||
{
|
||||
axis = Vector3.Normalize(axis);
|
||||
|
||||
float half = angle * 0.5f;
|
||||
float sin = (float)(System.Math.Sin((double)(half)));
|
||||
float cos = (float)(System.Math.Cos((double)(half)));
|
||||
|
||||
X = axis.X * sin;
|
||||
Y = axis.Y * sin;
|
||||
Z = axis.Z * sin;
|
||||
W = cos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="LQuaternion"/> with all of its components set to zero.
|
||||
/// </summary>
|
||||
public static LQuaternion Zero => new LQuaternion();
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="LQuaternion"/> with all of its components set to one.
|
||||
/// </summary>
|
||||
public static LQuaternion One => new LQuaternion(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The identity <see cref="LQuaternion"/> (0, 0, 0, 1).
|
||||
/// </summary>
|
||||
public static LQuaternion Identity => new LQuaternion(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the axis components of the quaternion.
|
||||
/// </summary>
|
||||
public Vector3 Axis
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Length() != 1.0f)
|
||||
{
|
||||
return Vector3.Zero;
|
||||
}
|
||||
|
||||
float length = 1.0f - (W * W);
|
||||
if (length == 0f)
|
||||
{
|
||||
return Vector3.UnitX;
|
||||
}
|
||||
|
||||
float inv = 1.0f / (float)System.Math.Sqrt(length);
|
||||
return new Vector3(X * inv, Y * inv, Z * inv);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the angle of the quaternion.
|
||||
/// </summary>
|
||||
public float Angle => ((System.Math.Abs(W) <= 1.0f) ? 2.0f * (float)(System.Math.Acos(W)) : 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the length of the quaternion.
|
||||
/// </summary>
|
||||
/// <returns>The length of the quaternion.</returns>
|
||||
public float Length() => (float)System.Math.Sqrt((X * X) + (Y * Y) + (Z * Z) + (W * W));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the quaternion.
|
||||
/// </summary>
|
||||
/// <returns>The squared length of the quaternion.</returns>
|
||||
public float LengthSquared() => (X * X) + (Y * Y) + (Z * Z) + (W * W);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the quaternion into a unit quaternion.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
float length = Length();
|
||||
if (length != 0f)
|
||||
{
|
||||
float inverse = 1.0f / length;
|
||||
X *= inverse;
|
||||
Y *= inverse;
|
||||
Z *= inverse;
|
||||
W *= inverse;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Conjugates the quaternion.
|
||||
/// </summary>
|
||||
public void Conjugate()
|
||||
{
|
||||
X = -X;
|
||||
Y = -Y;
|
||||
Z = -Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Conjugates and renormalizes the quaternion.
|
||||
/// </summary>
|
||||
public void Invert()
|
||||
{
|
||||
float lengthSq = LengthSquared();
|
||||
if (lengthSq != 0f)
|
||||
{
|
||||
lengthSq = 1.0f / lengthSq;
|
||||
|
||||
X = -X * lengthSq;
|
||||
Y = -Y * lengthSq;
|
||||
Z = -Z * lengthSq;
|
||||
W = W * lengthSq;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the direction of a given quaternion.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion to negate.</param>
|
||||
/// <returns>A quaternion facing in the opposite direction.</returns>
|
||||
public static LQuaternion Negate(LQuaternion quaternion)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
result.X = -quaternion.X;
|
||||
result.Y = -quaternion.Y;
|
||||
result.Z = -quaternion.Z;
|
||||
result.W = -quaternion.W;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two quaternions.
|
||||
/// </summary>
|
||||
/// <param name="left">The first quaternion to add.</param>
|
||||
/// <param name="right">The second quaternion to add.</param>
|
||||
/// <returns>The sum of the two quaternions.</returns>
|
||||
public static LQuaternion Add(LQuaternion left, LQuaternion right)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
result.X = left.X + right.X;
|
||||
result.Y = left.Y + right.Y;
|
||||
result.Z = left.Z + right.Z;
|
||||
result.W = left.W + right.W;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two quaternions.
|
||||
/// </summary>
|
||||
/// <param name="left">The first quaternion to subtract.</param>
|
||||
/// <param name="right">The second quaternion to subtract.</param>
|
||||
/// <returns>The difference of the two quaternions.</returns>
|
||||
public static LQuaternion Subtract(LQuaternion left, LQuaternion right)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
result.X = left.X - right.X;
|
||||
result.Y = left.Y - right.Y;
|
||||
result.Z = left.Z - right.Z;
|
||||
result.W = left.W - right.W;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two Quaternions together.
|
||||
/// </summary>
|
||||
/// <param name="left">The Quaternion on the left side of the multiplication.</param>
|
||||
/// <param name="right">The Quaternion on the right side of the multiplication.</param>
|
||||
/// <returns>The result of the multiplication.</returns>
|
||||
public static LQuaternion Multiply(LQuaternion left, LQuaternion right)
|
||||
{
|
||||
LQuaternion quaternion;
|
||||
float lx = left.X;
|
||||
float ly = left.Y;
|
||||
float lz = left.Z;
|
||||
float lw = left.W;
|
||||
float rx = right.X;
|
||||
float ry = right.Y;
|
||||
float rz = right.Z;
|
||||
float rw = right.W;
|
||||
|
||||
quaternion.X = (lx * rw + rx * lw) + (ly * rz) - (lz * ry);
|
||||
quaternion.Y = (ly * rw + ry * lw) + (lz * rx) - (lx * rz);
|
||||
quaternion.Z = (lz * rw + rz * lw) + (lx * ry) - (ly * rx);
|
||||
quaternion.W = (lw * rw) - (lx * rx + ly * ry + lz * rz);
|
||||
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales a quaternion by the given value.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the quaternion.</param>
|
||||
/// <returns>The scaled quaternion.</returns>
|
||||
public static LQuaternion Multiply(LQuaternion quaternion, float scale)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
result.X = quaternion.X * scale;
|
||||
result.Y = quaternion.Y * scale;
|
||||
result.Z = quaternion.Z * scale;
|
||||
result.W = quaternion.W * scale;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides a quaternion by another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first quaternion to divide.</param>
|
||||
/// <param name="right">The second quaternion to divide.</param>
|
||||
/// <returns>The divided quaternion.</returns>
|
||||
public static LQuaternion Divide(LQuaternion left, LQuaternion right)
|
||||
{
|
||||
return Multiply(left, Invert(right));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the quaternion into a unit quaternion.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion to normalize.</param>
|
||||
/// <returns>The normalized quaternion.</returns>
|
||||
public static LQuaternion Normalize(LQuaternion quaternion)
|
||||
{
|
||||
quaternion.Normalize();
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the conjugate of a specified Quaternion.
|
||||
/// </summary>
|
||||
/// <param name="value">The Quaternion of which to return the conjugate.</param>
|
||||
/// <returns>A new Quaternion that is the conjugate of the specified one.</returns>
|
||||
public static LQuaternion Conjugate(LQuaternion value)
|
||||
{
|
||||
LQuaternion ans;
|
||||
|
||||
ans.X = -value.X;
|
||||
ans.Y = -value.Y;
|
||||
ans.Z = -value.Z;
|
||||
ans.W = value.W;
|
||||
|
||||
return ans;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Conjugates and renormalizes the quaternion.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion to conjugate and re-normalize.</param>
|
||||
/// <returns>The conjugated and renormalized quaternion.</returns>
|
||||
public static LQuaternion Invert(LQuaternion quaternion)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
float lengthSq = 1.0f / ((quaternion.X * quaternion.X) + (quaternion.Y * quaternion.Y) + (quaternion.Z * quaternion.Z) + (quaternion.W * quaternion.W));
|
||||
|
||||
result.X = -quaternion.X * lengthSq;
|
||||
result.Y = -quaternion.Y * lengthSq;
|
||||
result.Z = -quaternion.Z * lengthSq;
|
||||
result.W = quaternion.W * lengthSq;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two quaternions.
|
||||
/// </summary>
|
||||
/// <param name="left">First source quaternion.</param>
|
||||
/// <param name="right">Second source quaternion.</param>
|
||||
/// <returns>The dot product of the two quaternions.</returns>
|
||||
public static float Dot(LQuaternion left, LQuaternion right) => (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z) + (left.W * right.W);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a linear interpolation between two quaternion.
|
||||
/// </summary>
|
||||
/// <param name="start">Start quaternion.</param>
|
||||
/// <param name="end">End quaternion.</param>
|
||||
/// <param name="amount">Value between 0 and 1 indicating the weight of <paramref name="end"/>.</param>
|
||||
/// <returns>The linear interpolation of the two quaternions.</returns>
|
||||
/// <remarks>
|
||||
/// This method performs the linear interpolation based on the following formula.
|
||||
/// <code>start + (end - start) * amount</code>
|
||||
/// Passing <paramref name="amount"/> a value of 0 will cause <paramref name="start"/> to be returned; a value of 1 will cause <paramref name="end"/> to be returned.
|
||||
/// </remarks>
|
||||
public static LQuaternion Lerp(LQuaternion start, LQuaternion end, float amount)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
float inverse = 1.0f - amount;
|
||||
float dot = (start.X * end.X) + (start.Y * end.Y) + (start.Z * end.Z) + (start.W * end.W);
|
||||
|
||||
if (dot >= 0.0f)
|
||||
{
|
||||
result.X = (inverse * start.X) + (amount * end.X);
|
||||
result.Y = (inverse * start.Y) + (amount * end.Y);
|
||||
result.Z = (inverse * start.Z) + (amount * end.Z);
|
||||
result.W = (inverse * start.W) + (amount * end.W);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.X = (inverse * start.X) - (amount * end.X);
|
||||
result.Y = (inverse * start.Y) - (amount * end.Y);
|
||||
result.Z = (inverse * start.Z) - (amount * end.Z);
|
||||
result.W = (inverse * start.W) - (amount * end.W);
|
||||
}
|
||||
|
||||
float invLength = 1.0f / result.Length();
|
||||
|
||||
result.X *= invLength;
|
||||
result.Y *= invLength;
|
||||
result.Z *= invLength;
|
||||
result.W *= invLength;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two quaternions, using spherical linear interpolation..
|
||||
/// </summary>
|
||||
/// <param name="start">Start quaternion.</param>
|
||||
/// <param name="end">End quaternion.</param>
|
||||
/// <param name="amount">Value between 0 and 1 indicating the weight of <paramref name="end"/>.</param>
|
||||
/// <returns>The spherical linear interpolation of the two quaternions.</returns>
|
||||
public static LQuaternion Slerp(LQuaternion start, LQuaternion end, float amount)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
float kEpsilon = (float)(1.192093E-07);
|
||||
float opposite;
|
||||
float inverse;
|
||||
float dot = Dot(start, end);
|
||||
|
||||
if (System.Math.Abs(dot) > (1.0f - kEpsilon))
|
||||
{
|
||||
inverse = 1.0f - amount;
|
||||
opposite = amount * System.Math.Sign(dot);
|
||||
}
|
||||
else
|
||||
{
|
||||
float acos = (float)System.Math.Acos(System.Math.Abs(dot));
|
||||
float invSin = (float)(1.0 / System.Math.Sin(acos));
|
||||
|
||||
inverse = (float)(System.Math.Sin((1.0f - amount) * acos) * invSin);
|
||||
opposite = (float)(System.Math.Sin(amount * acos) * invSin * System.Math.Sign(dot));
|
||||
}
|
||||
|
||||
result.X = (inverse * start.X) + (opposite * end.X);
|
||||
result.Y = (inverse * start.Y) + (opposite * end.Y);
|
||||
result.Z = (inverse * start.Z) + (opposite * end.Z);
|
||||
result.W = (inverse * start.W) + (opposite * end.W);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two quaternions, using spherical linear interpolation. The parameter /t/ is not clamped.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="t"></param>
|
||||
public static LQuaternion SlerpUnclamped(LQuaternion a, LQuaternion b, float t)
|
||||
{
|
||||
if (a.LengthSquared() == 0.0f)
|
||||
{
|
||||
if (b.LengthSquared() == 0.0f)
|
||||
{
|
||||
return Identity;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
else if (b.LengthSquared() == 0.0f)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
float cosHalfAngle = a.W * b.W + Vector3.Dot(a.Axis, b.Axis);
|
||||
|
||||
if (cosHalfAngle >= 1.0f || cosHalfAngle <= -1.0f)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
else if (cosHalfAngle < 0.0f)
|
||||
{
|
||||
b.X = -b.X;
|
||||
b.Y = -b.Y;
|
||||
b.Z = -b.Z;
|
||||
b.W = -b.W;
|
||||
cosHalfAngle = -cosHalfAngle;
|
||||
}
|
||||
|
||||
float blendA;
|
||||
float blendB;
|
||||
if (cosHalfAngle < 0.99f)
|
||||
{
|
||||
float halfAngle = (float)System.Math.Acos(cosHalfAngle);
|
||||
float sinHalfAngle = (float)System.Math.Sin(halfAngle);
|
||||
float oneOverSinHalfAngle = 1.0f / sinHalfAngle;
|
||||
blendA = (float)System.Math.Sin(halfAngle * (1.0f - t)) * oneOverSinHalfAngle;
|
||||
blendB = (float)System.Math.Sin(halfAngle * t) * oneOverSinHalfAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
blendA = 1.0f - t;
|
||||
blendB = t;
|
||||
}
|
||||
|
||||
LQuaternion result = new LQuaternion(blendA * a.Axis + blendB * b.Axis, blendA * a.W + blendB * b.W);
|
||||
if (result.LengthSquared() > 0.0f)
|
||||
{
|
||||
return Normalize(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Identity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a rotation with the specified <paramref name="forward"/> and <see cref="Vector3.WorldUp"/> directions.
|
||||
/// </summary>
|
||||
public static LQuaternion LookRotation(Vector3 forward) => LookRotation(forward, Vector3.WorldUp);
|
||||
/// <summary>
|
||||
/// Creates a rotation with the specified <paramref name="forward"/> and <paramref name="up"/> directions.
|
||||
/// </summary>
|
||||
public static LQuaternion LookRotation(Vector3 forward, Vector3 up) => DirectionVectors(Vector3.Cross(forward, up), forward, up);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a rotation which rotates from fromDirection to toDirection.
|
||||
/// </summary>
|
||||
public static LQuaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection)
|
||||
{
|
||||
float NormAB = (float)(System.Math.Sqrt(fromDirection.LengthSquared() * fromDirection.LengthSquared()));
|
||||
|
||||
float w = NormAB + Vector3.Dot(fromDirection, toDirection);
|
||||
LQuaternion Result;
|
||||
|
||||
if (w >= 1e-6f * NormAB)
|
||||
{
|
||||
Result = new LQuaternion(Vector3.Cross(fromDirection, toDirection), w);
|
||||
}
|
||||
else
|
||||
{
|
||||
w = 0.0f;
|
||||
Result = System.Math.Abs(fromDirection.X) > System.Math.Abs(fromDirection.Y)
|
||||
? new LQuaternion(-fromDirection.Z, 0.0f, fromDirection.X, w)
|
||||
: new LQuaternion(0.0f, -fromDirection.Z, fromDirection.Y, w);
|
||||
}
|
||||
|
||||
Result.Normalize();
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a rotation from towards to.
|
||||
/// </summary>
|
||||
/// <param name="from">From Quaternion.</param>
|
||||
/// <param name="to">To Quaternion.</param>
|
||||
/// <param name ="maxDegreesDelta"></param>
|
||||
public static LQuaternion RotateTowards(LQuaternion from, LQuaternion to, float maxDegreesDelta)
|
||||
{
|
||||
float angle = AngleBetween(from, to);
|
||||
if (angle == 0.0f)
|
||||
{
|
||||
return to;
|
||||
}
|
||||
float t = System.Math.Min(1.0f, maxDegreesDelta / angle);
|
||||
return SlerpUnclamped(from, to, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the angle in degrees between two rotations a and b.
|
||||
/// </summary>
|
||||
/// <param name="a">The first quaternion to calculate angle.</param>
|
||||
/// <param name="b">The second quaternion to calculate angle.</param>
|
||||
/// <returns>The angle in degrees between two rotations a and b.</returns>
|
||||
public static float AngleBetween(LQuaternion a, LQuaternion b)
|
||||
{
|
||||
float dot = Dot(a, b);
|
||||
return (float)((System.Math.Acos(System.Math.Min(System.Math.Abs(dot), 1.0f)) * 2.0 * (180.0f / System.Math.PI)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a rotation that rotates z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).
|
||||
/// </summary>
|
||||
/// <param name="zaxis">Z degrees.</param>
|
||||
/// <param name ="xaxis">X degrees.</param>
|
||||
/// <param name ="yaxis">Y degrees.</param>
|
||||
public static LQuaternion Euler(float zaxis, float xaxis, float yaxis)
|
||||
{
|
||||
float Deg2Rad = (float)((System.Math.PI / 180.0));
|
||||
return RotationYawPitchRoll(zaxis * Deg2Rad, xaxis * Deg2Rad, yaxis * Deg2Rad);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a rotation that rotates z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).
|
||||
/// </summary>
|
||||
/// <param name="euler">Euler angles in degrees. euler.X = around X axis, euler.Y = around Y axis, euler.Z = around Z axis</param>
|
||||
public static LQuaternion Euler(Vector3 euler)
|
||||
{
|
||||
Vector3 eulerRad = euler * (float)((System.Math.PI / 180.0));
|
||||
return RotationYawPitchRoll(eulerRad.Z, eulerRad.X, eulerRad.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a quaternion given a rotation and an axis.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis of rotation.</param>
|
||||
/// <param name="angle">The angle of rotation in radians.</param>
|
||||
/// <returns>The newly created quaternion.</returns>
|
||||
public static LQuaternion RotationAxis(Vector3 axis, float angle)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
|
||||
axis = Vector3.Normalize(axis);
|
||||
|
||||
float half = angle * 0.5f;
|
||||
float sin = (float)(System.Math.Sin((double)(half)));
|
||||
float cos = (float)(System.Math.Cos((double)(half)));
|
||||
|
||||
result.X = axis.X * sin;
|
||||
result.Y = axis.Y * sin;
|
||||
result.Z = axis.Z * sin;
|
||||
result.W = cos;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a quaternion given a rotation matrix.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The rotation matrix.</param>
|
||||
/// <returns>The newly created quaternion.</returns>
|
||||
public static LQuaternion RotationMatrix(Matrix matrix)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
float sqrt;
|
||||
float half;
|
||||
float scale = matrix.M11 + matrix.M22 + matrix.M33;
|
||||
|
||||
if (scale > 0.0f)
|
||||
{
|
||||
sqrt = (float)System.Math.Sqrt(scale + 1.0f);
|
||||
result.W = sqrt * 0.5f;
|
||||
sqrt = 0.5f / sqrt;
|
||||
|
||||
result.X = (matrix.M23 - matrix.M32) * sqrt;
|
||||
result.Y = (matrix.M31 - matrix.M13) * sqrt;
|
||||
result.Z = (matrix.M12 - matrix.M21) * sqrt;
|
||||
}
|
||||
else if ((matrix.M11 >= matrix.M22) && (matrix.M11 >= matrix.M33))
|
||||
{
|
||||
sqrt = (float)System.Math.Sqrt(1.0f + matrix.M11 - matrix.M22 - matrix.M33);
|
||||
half = 0.5f / sqrt;
|
||||
|
||||
result.X = 0.5f * sqrt;
|
||||
result.Y = (matrix.M12 + matrix.M21) * half;
|
||||
result.Z = (matrix.M13 + matrix.M31) * half;
|
||||
result.W = (matrix.M23 - matrix.M32) * half;
|
||||
}
|
||||
else if (matrix.M22 > matrix.M33)
|
||||
{
|
||||
sqrt = (float)System.Math.Sqrt(1.0f + matrix.M22 - matrix.M11 - matrix.M33);
|
||||
half = 0.5f / sqrt;
|
||||
|
||||
result.X = (matrix.M21 + matrix.M12) * half;
|
||||
result.Y = 0.5f * sqrt;
|
||||
result.Z = (matrix.M32 + matrix.M23) * half;
|
||||
result.W = (matrix.M31 - matrix.M13) * half;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqrt = (float)System.Math.Sqrt(1.0f + matrix.M33 - matrix.M11 - matrix.M22);
|
||||
half = 0.5f / sqrt;
|
||||
|
||||
result.X = (matrix.M31 + matrix.M13) * half;
|
||||
result.Y = (matrix.M32 + matrix.M23) * half;
|
||||
result.Z = 0.5f * sqrt;
|
||||
result.W = (matrix.M12 - matrix.M21) * half;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Quaternion from the given yaw, pitch, and roll, in radians.
|
||||
/// </summary>
|
||||
/// <param name="yaw">The yaw angle, in radians, around the Z-axis.</param>
|
||||
/// <param name="pitch">The pitch angle, in radians, around the X-axis.</param>
|
||||
/// <param name="roll">The roll angle, in radians, around the Y-axis.</param>
|
||||
/// <returns>The newly created quaternion.</returns>
|
||||
public static LQuaternion RotationYawPitchRoll(float yaw, float pitch, float roll)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
|
||||
float halfYaw = yaw * 0.5f;
|
||||
float sinYaw = (float)(System.Math.Sin((double)(halfYaw)));
|
||||
float cosYaw = (float)(System.Math.Cos((double)(halfYaw)));
|
||||
float halfPitch = pitch * 0.5f;
|
||||
float sinPitch = (float)(System.Math.Sin((double)(halfPitch)));
|
||||
float cosPitch = (float)(System.Math.Cos((double)(halfPitch)));
|
||||
float halfRoll = roll * 0.5f;
|
||||
float sinRoll = (float)(System.Math.Sin((double)(halfRoll)));
|
||||
float cosRoll = (float)(System.Math.Cos((double)(halfRoll)));
|
||||
|
||||
result.X = (cosRoll * sinPitch * cosYaw) + (sinRoll * cosPitch * sinYaw);
|
||||
result.Y = (sinRoll * cosPitch * cosYaw) - (cosRoll * sinPitch * sinYaw);
|
||||
result.Z = (cosRoll * cosPitch * sinYaw) - (sinRoll * sinPitch * cosYaw);
|
||||
result.W = (cosRoll * cosPitch * cosYaw) + (sinRoll * sinPitch * sinYaw);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Quaternion from the given relative x, y, z axis
|
||||
/// </summary>
|
||||
/// The Vectors need to be perpendicular to each other
|
||||
/// <param name="rightVector">Relative X axis</param>
|
||||
/// <param name="forwardVector">Relative Y axis</param>
|
||||
/// <param name="upVector">Relative Z axis</param>
|
||||
/// <returns>The newly created quaternion.</returns>
|
||||
public static LQuaternion DirectionVectors(Vector3 rightVector, Vector3 forwardVector, Vector3 upVector)
|
||||
{
|
||||
rightVector.Normalize();
|
||||
forwardVector.Normalize();
|
||||
upVector.Normalize();
|
||||
|
||||
Matrix rotationMatrix = new Matrix();
|
||||
rotationMatrix[0, 0] = rightVector.X;
|
||||
rotationMatrix[0, 1] = rightVector.Y;
|
||||
rotationMatrix[0, 2] = rightVector.Z;
|
||||
|
||||
rotationMatrix[1, 0] = forwardVector.X;
|
||||
rotationMatrix[1, 1] = forwardVector.Y;
|
||||
rotationMatrix[1, 2] = forwardVector.Z;
|
||||
|
||||
rotationMatrix[2, 0] = upVector.X;
|
||||
rotationMatrix[2, 1] = upVector.Y;
|
||||
rotationMatrix[2, 2] = upVector.Z;
|
||||
|
||||
return RotationMatrix(rotationMatrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get direction vectors from the given quaternion
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion</param>
|
||||
/// <param name="rightVector">RightVector = relative x axis</param>
|
||||
/// <param name="forwardVector">ForwardVector = relative y axis</param>
|
||||
/// <param name="upVector">UpVector = relative z axis</param>
|
||||
public static void GetDirectionVectors(LQuaternion quaternion, out Vector3 rightVector, out Vector3 forwardVector, out Vector3 upVector)
|
||||
{
|
||||
quaternion.Normalize();
|
||||
rightVector = quaternion * Vector3.WorldEast;
|
||||
forwardVector = quaternion * Vector3.WorldNorth;
|
||||
upVector = quaternion * Vector3.WorldUp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the direction of a given quaternion.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion to negate.</param>
|
||||
/// <returns>A quaternion facing in the opposite direction.</returns>
|
||||
public static LQuaternion operator -(LQuaternion quaternion)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
result.X = -quaternion.X;
|
||||
result.Y = -quaternion.Y;
|
||||
result.Z = -quaternion.Z;
|
||||
result.W = -quaternion.W;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two quaternions.
|
||||
/// </summary>
|
||||
/// <param name="left">The first quaternion to add.</param>
|
||||
/// <param name="right">The second quaternion to add.</param>
|
||||
/// <returns>The sum of the two quaternions.</returns>
|
||||
public static LQuaternion operator +(LQuaternion left, LQuaternion right)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
result.X = left.X + right.X;
|
||||
result.Y = left.Y + right.Y;
|
||||
result.Z = left.Z + right.Z;
|
||||
result.W = left.W + right.W;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two quaternions.
|
||||
/// </summary>
|
||||
/// <param name="left">The first quaternion to subtract.</param>
|
||||
/// <param name="right">The second quaternion to subtract.</param>
|
||||
/// <returns>The difference of the two quaternions.</returns>
|
||||
public static LQuaternion operator -(LQuaternion left, LQuaternion right)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
result.X = left.X - right.X;
|
||||
result.Y = left.Y - right.Y;
|
||||
result.Z = left.Z - right.Z;
|
||||
result.W = left.W - right.W;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a quaternion by another.
|
||||
/// </summary>
|
||||
/// <param name="left">The first quaternion to multiply.</param>
|
||||
/// <param name="right">The second quaternion to multiply.</param>
|
||||
/// <returns>The multiplied quaternion.</returns>
|
||||
public static LQuaternion operator *(LQuaternion left, LQuaternion right)
|
||||
{
|
||||
LQuaternion quaternion = Zero;
|
||||
float lx = left.X;
|
||||
float ly = left.Y;
|
||||
float lz = left.Z;
|
||||
float lw = left.W;
|
||||
float rx = right.X;
|
||||
float ry = right.Y;
|
||||
float rz = right.Z;
|
||||
float rw = right.W;
|
||||
|
||||
quaternion.X = (lx * rw + rx * lw) + (ly * rz) - (lz * ry);
|
||||
quaternion.Y = (ly * rw + ry * lw) + (lz * rx) - (lx * rz);
|
||||
quaternion.Z = (lz * rw + rz * lw) + (lx * ry) - (ly * rx);
|
||||
quaternion.W = (lw * rw) - (lx * rx + ly * ry + lz * rz);
|
||||
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales a quaternion by the given value.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the quaternion.</param>
|
||||
/// <returns>The scaled quaternion.</returns>
|
||||
public static LQuaternion operator *(LQuaternion quaternion, float scale)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
result.X = quaternion.X * scale;
|
||||
result.Y = quaternion.Y * scale;
|
||||
result.Z = quaternion.Z * scale;
|
||||
result.W = quaternion.W * scale;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales a quaternion by the given value.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the quaternion.</param>
|
||||
/// <returns>The scaled quaternion.</returns>
|
||||
public static LQuaternion operator *(float scale, LQuaternion quaternion)
|
||||
{
|
||||
LQuaternion result = Zero;
|
||||
result.X = quaternion.X * scale;
|
||||
result.Y = quaternion.Y * scale;
|
||||
result.Z = quaternion.Z * scale;
|
||||
result.W = quaternion.W * scale;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides a Quaternion by another Quaternion.
|
||||
/// </summary>
|
||||
/// <param name="left">The source Quaternion.</param>
|
||||
/// <param name="right">The divisor.</param>
|
||||
/// <returns>The result of the division.</returns>
|
||||
public static LQuaternion operator /(LQuaternion left, LQuaternion right)
|
||||
{
|
||||
LQuaternion quaternion = Zero;
|
||||
|
||||
float lx = left.X;
|
||||
float ly = left.Y;
|
||||
float lz = left.Z;
|
||||
float lw = left.W;
|
||||
|
||||
// Inverse part.
|
||||
float ls = right.X * right.X + right.Y * right.Y +
|
||||
right.Z * right.Z + right.W * right.W;
|
||||
float invNorm = 1.0f / ls;
|
||||
|
||||
float rx = -right.X * invNorm;
|
||||
float ry = -right.Y * invNorm;
|
||||
float rz = -right.Z * invNorm;
|
||||
float rw = right.W * invNorm;
|
||||
|
||||
// Multiply part.
|
||||
quaternion.X = (lx * rw + rx * lw) + (ly * rz) - (lz * ry);
|
||||
quaternion.Y = (ly * rw + ry * lw) + (lz * rx) - (lx * rz);
|
||||
quaternion.Z = (lz * rw + rz * lw) + (lx * ry) - (ly * rx);
|
||||
quaternion.W = (lw * rw) - (lx * rx + ly * ry + lz * rz);
|
||||
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for equality between two objects.
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns><see langword="true" /> if <paramref name="left"/> has the same value as <paramref name="right"/>; otherwise, <see langword="false" />.</returns>
|
||||
public static bool operator ==(LQuaternion left, LQuaternion right) => Equals(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Tests for inequality between two objects.
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns><see langword="true" /> if <paramref name="left"/> has a different value than <paramref name="right"/>; otherwise, <see langword="false" />.</returns>
|
||||
public static bool operator !=(LQuaternion left, LQuaternion right) => !Equals(left, right);
|
||||
|
||||
#region RotateTransformOperators
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the point with rotation.
|
||||
/// </summary>
|
||||
/// <param name="rotation">The quaternion to rotate the vector.</param>
|
||||
/// <param name="point">The vector to be rotated.</param>
|
||||
/// <returns>The vector after rotation.</returns>
|
||||
public static Vector3 operator *(LQuaternion rotation, Vector3 point)
|
||||
{
|
||||
float q0 = rotation.W;
|
||||
float q0Square = rotation.W * rotation.W;
|
||||
Vector3 q = new Vector3(rotation.X, rotation.Y, rotation.Z);
|
||||
return ((q0Square - q.LengthSquared()) * point) + (2 * Vector3.Dot(q, point) * q) + (2 * q0 * Vector3.Cross(q, point));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the point with rotation.
|
||||
/// </summary>
|
||||
/// <param name="rotation">The quaternion to rotate the vector.</param>
|
||||
/// <param name="point">The vector to be rotated.</param>
|
||||
/// <returns>The vector after rotation.</returns>
|
||||
public static Vector3 RotateTransform(LQuaternion rotation, Vector3 point) => rotation * point;
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the point with rotation.
|
||||
/// </summary>
|
||||
/// <param name="rotation">The quaternion to rotate the vector.</param>
|
||||
/// <param name="point">The vector to be rotated.</param>
|
||||
/// <param name="center">The vector representing the origin of the new coordinate system.</param>
|
||||
/// <returns>The vector after rotation in the original coordinate system.</returns>
|
||||
public static Vector3 RotateTransform(LQuaternion rotation, Vector3 point, Vector3 center)
|
||||
{
|
||||
Vector3 PointNewCenter = Vector3.Subtract(point, center);
|
||||
Vector3 TransformedPoint = RotateTransform(rotation, PointNewCenter);
|
||||
return Vector3.Add(TransformedPoint, center);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the point with rotation.
|
||||
/// </summary>
|
||||
/// <param name="point">The vector to be rotated.</param>
|
||||
/// <returns>The vector after rotation.</returns>
|
||||
public Vector3 RotateTransform(Vector3 point) => RotateTransform(this, point);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the point with rotation.
|
||||
/// </summary>
|
||||
/// <param name="point">The vector to be rotated.</param>
|
||||
/// <param name="center">The vector representing the origin of the new coordinate system.</param>
|
||||
/// <returns>The vector after rotation in the original coordinate system.</returns>
|
||||
public Vector3 RotateTransform(Vector3 point, Vector3 center) => RotateTransform(this, point, center);
|
||||
|
||||
#endregion RotateTransformOperators
|
||||
|
||||
/// <summary>
|
||||
/// Converts the value of the object to its equivalent string representation.
|
||||
/// </summary>
|
||||
/// <returns>The string representation of the value of this instance.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(), Y.ToString(), Z.ToString(), W.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the value of the object to its equivalent string representation.
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <returns>The string representation of the value of this instance.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return String.Format(CultureInfo.InvariantCulture, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format), Y.ToString(format), Z.ToString(format), W.ToString(format));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>A 32-bit signed integer hash code.</returns>
|
||||
public override int GetHashCode() => X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether the current instance is equal to a specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to make the comparison with.</param>
|
||||
/// <returns><see langword="true" /> if the current instance is equal to the specified object; <see langword="false" /> otherwise.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((LQuaternion)obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether the current instance is equal to the specified object.
|
||||
/// </summary>
|
||||
/// <param name="other">Object to make the comparison with.</param>
|
||||
/// <returns><see langword="true" /> if the current instance is equal to the specified object; <see langword="false" /> otherwise.</returns>
|
||||
public bool Equals(LQuaternion other) => (X == other.X && Y == other.Y && Z == other.Z && W == other.W);
|
||||
|
||||
public static implicit operator Quaternion(LQuaternion q) => new(q.X, q.Y, q.Z, q.W);
|
||||
public static implicit operator LQuaternion(Quaternion q) => new(q.X, q.Y, q.Z, q.W);
|
||||
}
|
||||
}
|
@ -1,512 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using GTA.Math;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal struct LVector2 : IEquatable<LVector2>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the X component of the vector.
|
||||
/// </summary>
|
||||
/// <value>The X component of the vector.</value>
|
||||
public float X;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Y component of the vector.
|
||||
/// </summary>
|
||||
/// <value>The Y component of the vector.</value>
|
||||
public float Y;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LVector2"/> class.
|
||||
/// </summary>
|
||||
/// <param name="x">Initial value for the X component of the vector.</param>
|
||||
/// <param name="y">Initial value for the Y component of the vector.</param>
|
||||
public LVector2(float x, float y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns this vector with a magnitude of 1.
|
||||
/// </summary>
|
||||
public LVector2 Normalized => Normalize(new LVector2(X, Y));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a null vector. (0,0)
|
||||
/// </summary>
|
||||
public static LVector2 Zero => new LVector2(0.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The X unit <see cref="LVector2"/> (1, 0).
|
||||
/// </summary>
|
||||
public static LVector2 UnitX => new LVector2(1.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The Y unit <see cref="LVector2"/> (0, 1).
|
||||
/// </summary>
|
||||
public static LVector2 UnitY => new LVector2(0.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the up vector. (0,1)
|
||||
/// </summary>
|
||||
public static LVector2 Up => new LVector2(0.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the down vector. (0,-1)
|
||||
/// </summary>
|
||||
public static LVector2 Down => new LVector2(0.0f, -1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the right vector. (1,0)
|
||||
/// </summary>
|
||||
public static LVector2 Right => new LVector2(1.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the left vector. (-1,0)
|
||||
/// </summary>
|
||||
public static LVector2 Left => new LVector2(-1.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the component at the specified index.
|
||||
/// </summary>
|
||||
/// <value>The value of the X or Y component, depending on the index.</value>
|
||||
/// <param name="index">The index of the component to access. Use 0 for the X component and 1 for the Y component.</param>
|
||||
/// <returns>The value of the component at the specified index.</returns>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when the <paramref name="index"/> is out of the range [0, 1].</exception>
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return X;
|
||||
case 1:
|
||||
return Y;
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException("index", "Indices for Vector2 run from 0 to 1, inclusive.");
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
X = value;
|
||||
break;
|
||||
case 1:
|
||||
Y = value;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("index", "Indices for Vector2 run from 0 to 1, inclusive.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the length of the vector.
|
||||
/// </summary>
|
||||
/// <returns>The length of the vector.</returns>
|
||||
public float Length()
|
||||
{
|
||||
return (float)System.Math.Sqrt((X * X) + (Y * Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the vector.
|
||||
/// </summary>
|
||||
/// <returns>The squared length of the vector.</returns>
|
||||
public float LengthSquared()
|
||||
{
|
||||
return (X * X) + (Y * Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the vector into a unit vector.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
float length = Length();
|
||||
if (length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float num = 1 / length;
|
||||
X *= num;
|
||||
Y *= num;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="position">The second vector to calculate the distance to.</param>
|
||||
/// <returns>The distance to the other vector.</returns>
|
||||
public float DistanceTo(LVector2 position)
|
||||
{
|
||||
return (position - this).Length();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared distance between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="position">The second vector to calculate the squared distance to.</param>
|
||||
/// <returns>The squared distance to the other vector.</returns>
|
||||
public float DistanceToSquared(LVector2 position)
|
||||
{
|
||||
return DistanceSquared(position, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="position1">The first vector to calculate the distance to the second vector.</param>
|
||||
/// <param name="position2">The second vector to calculate the distance to the first vector.</param>
|
||||
/// <returns>The distance between the two vectors.</returns>
|
||||
public static float Distance(LVector2 position1, LVector2 position2)
|
||||
{
|
||||
return (position1 - position2).Length();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared distance between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="position1">The first vector to calculate the squared distance to the second vector.</param>
|
||||
/// <param name="position2">The second vector to calculate the squared distance to the first vector.</param>
|
||||
/// <returns>The squared distance between the two vectors.</returns>
|
||||
public static float DistanceSquared(LVector2 position1, LVector2 position2)
|
||||
{
|
||||
return (position1 - position2).LengthSquared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the angle in degrees between from and to.
|
||||
/// The angle returned is always the acute angle between the two vectors.
|
||||
/// </summary>
|
||||
public static float Angle(LVector2 from, LVector2 to)
|
||||
{
|
||||
return System.Math.Abs(SignedAngle(from, to));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the signed angle in degrees between from and to.
|
||||
/// </summary>
|
||||
public static float SignedAngle(LVector2 from, LVector2 to)
|
||||
{
|
||||
return (float)((System.Math.Atan2(to.Y, to.X) - System.Math.Atan2(from.Y, from.X)) * (180.0 / System.Math.PI));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a vector to a heading.
|
||||
/// </summary>
|
||||
public float ToHeading()
|
||||
{
|
||||
return (float)((System.Math.Atan2(X, -Y) + System.Math.PI) * (180.0 / System.Math.PI));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new normalized vector with random X and Y components.
|
||||
/// </summary>
|
||||
public static LVector2 RandomXY()
|
||||
{
|
||||
LVector2 v;
|
||||
double radian = CoreUtils.SafeRandom.NextDouble() * 2 * System.Math.PI;
|
||||
v.X = (float)(System.Math.Cos(radian));
|
||||
v.Y = (float)(System.Math.Sin(radian));
|
||||
v.Normalize();
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to add.</param>
|
||||
/// <param name="right">The second vector to add.</param>
|
||||
/// <returns>The sum of the two vectors.</returns>
|
||||
public static LVector2 Add(LVector2 left, LVector2 right) => new LVector2(left.X + right.X, left.Y + right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to subtract.</param>
|
||||
/// <param name="right">The second vector to subtract.</param>
|
||||
/// <returns>The difference of the two vectors.</returns>
|
||||
public static LVector2 Subtract(LVector2 left, LVector2 right) => new LVector2(left.X - right.X, left.Y - right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector2 Multiply(LVector2 value, float scale) => new LVector2(value.X * scale, value.Y * scale);
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a vector with another by performing component-wise multiplication.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to multiply.</param>
|
||||
/// <param name="right">The second vector to multiply.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static LVector2 Multiply(LVector2 left, LVector2 right) => new LVector2(left.X * right.X, left.Y * right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector2 Divide(LVector2 value, float scale) => new LVector2(value.X / scale, value.Y / scale);
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the direction of a given vector.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to negate.</param>
|
||||
/// <returns>A vector facing in the opposite direction.</returns>
|
||||
public static LVector2 Negate(LVector2 value) => new LVector2(-value.X, -value.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Restricts a value to be within a specified range.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static LVector2 Clamp(LVector2 value, LVector2 min, LVector2 max)
|
||||
{
|
||||
float x = value.X;
|
||||
x = (x > max.X) ? max.X : x;
|
||||
x = (x < min.X) ? min.X : x;
|
||||
|
||||
float y = value.Y;
|
||||
y = (y > max.Y) ? max.Y : y;
|
||||
y = (y < min.Y) ? min.Y : y;
|
||||
|
||||
return new LVector2(x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a linear interpolation between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="start">Start vector.</param>
|
||||
/// <param name="end">End vector.</param>
|
||||
/// <param name="amount">Value between 0 and 1 indicating the weight of <paramref name="end"/>.</param>
|
||||
/// <returns>The linear interpolation of the two vectors.</returns>
|
||||
/// <remarks>
|
||||
/// This method performs the linear interpolation based on the following formula.
|
||||
/// <code>start + (end - start) * amount</code>
|
||||
/// Passing <paramref name="amount"/> a value of 0 will cause <paramref name="start"/> to be returned; a value of 1 will cause <paramref name="end"/> to be returned.
|
||||
/// </remarks>
|
||||
public static LVector2 Lerp(LVector2 start, LVector2 end, float amount)
|
||||
{
|
||||
LVector2 vector;
|
||||
|
||||
vector.X = start.X + ((end.X - start.X) * amount);
|
||||
vector.Y = start.Y + ((end.Y - start.Y) * amount);
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the vector into a unit vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to normalize.</param>
|
||||
/// <returns>The normalized vector.</returns>
|
||||
public static LVector2 Normalize(LVector2 vector)
|
||||
{
|
||||
vector.Normalize();
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">First source vector.</param>
|
||||
/// <param name="right">Second source vector.</param>
|
||||
/// <returns>The dot product of the two vectors.</returns>
|
||||
public static float Dot(LVector2 left, LVector2 right) => (left.X * right.X + left.Y * right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the reflection of a vector off a surface that has the specified normal.
|
||||
/// </summary>
|
||||
/// <param name="vector">The source vector.</param>
|
||||
/// <param name="normal">Normal of the surface.</param>
|
||||
/// <returns>The reflected vector.</returns>
|
||||
/// <remarks>Reflect only gives the direction of a reflection off a surface, it does not determine
|
||||
/// whether the original vector was close enough to the surface to hit it.</remarks>
|
||||
public static LVector2 Reflect(LVector2 vector, LVector2 normal)
|
||||
{
|
||||
LVector2 result;
|
||||
float dot = ((vector.X * normal.X) + (vector.Y * normal.Y));
|
||||
|
||||
result.X = vector.X - ((2.0f * dot) * normal.X);
|
||||
result.Y = vector.Y - ((2.0f * dot) * normal.Y);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector containing the smallest components of the specified vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first source vector.</param>
|
||||
/// <param name="right">The second source vector.</param>
|
||||
/// <returns>A vector containing the smallest components of the source vectors.</returns>
|
||||
public static LVector2 Minimize(LVector2 left, LVector2 right)
|
||||
{
|
||||
LVector2 vector;
|
||||
vector.X = (left.X < right.X) ? left.X : right.X;
|
||||
vector.Y = (left.Y < right.Y) ? left.Y : right.Y;
|
||||
return vector;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a vector containing the largest components of the specified vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first source vector.</param>
|
||||
/// <param name="right">The second source vector.</param>
|
||||
/// <returns>A vector containing the largest components of the source vectors.</returns>
|
||||
public static LVector2 Maximize(LVector2 left, LVector2 right)
|
||||
{
|
||||
LVector2 vector;
|
||||
vector.X = (left.X > right.X) ? left.X : right.X;
|
||||
vector.Y = (left.Y > right.Y) ? left.Y : right.Y;
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to add.</param>
|
||||
/// <param name="right">The second vector to add.</param>
|
||||
/// <returns>The sum of the two vectors.</returns>
|
||||
public static LVector2 operator +(LVector2 left, LVector2 right) => new LVector2(left.X + right.X, left.Y + right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to subtract.</param>
|
||||
/// <param name="right">The second vector to subtract.</param>
|
||||
/// <returns>The difference of the two vectors.</returns>
|
||||
public static LVector2 operator -(LVector2 left, LVector2 right) => new LVector2(left.X - right.X, left.Y - right.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the direction of a given vector.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to negate.</param>
|
||||
/// <returns>A vector facing in the opposite direction.</returns>
|
||||
public static LVector2 operator -(LVector2 value) => new LVector2(-value.X, -value.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector2 operator *(LVector2 vector, float scale) => new LVector2(vector.X * scale, vector.Y * scale);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector2 operator *(float scale, LVector2 vector) => new LVector2(vector.X * scale, vector.Y * scale);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector2 operator /(LVector2 vector, float scale) => new LVector2(vector.X / scale, vector.Y / scale);
|
||||
|
||||
/// <summary>
|
||||
/// Tests for equality between two objects.
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns><see langword="true" /> if <paramref name="left"/> has the same value as <paramref name="right"/>; otherwise, <see langword="false" />.</returns>
|
||||
public static bool operator ==(LVector2 left, LVector2 right) => Equals(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Tests for inequality between two objects.
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns><see langword="true" /> if <paramref name="left"/> has a different value than <paramref name="right"/>; otherwise, <see langword="false" />.</returns>
|
||||
public static bool operator !=(LVector2 left, LVector2 right) => !Equals(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Vector2 to a Vector3 implicitly.
|
||||
/// </summary>
|
||||
public static implicit operator LVector3(LVector2 vector) => new LVector3(vector.X, vector.Y, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the value of the object to its equivalent string representation.
|
||||
/// </summary>
|
||||
/// <returns>The string representation of the value of this instance.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1}", X, Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the value of the object to its equivalent string representation.
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <returns>The string representation of the value of this instance.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
if (format == null)
|
||||
{
|
||||
return ToString();
|
||||
}
|
||||
|
||||
return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1}", X.ToString(format, CultureInfo.CurrentCulture), Y.ToString(format, CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>A 32-bit signed integer hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (X.GetHashCode() * 397) ^ Y.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether the current instance is equal to a specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to make the comparison with.</param>
|
||||
/// <returns><see langword="true" /> if the current instance is equal to the specified object; otherwise, <see langword="false" />.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((LVector2)obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether the current instance is equal to the specified object.
|
||||
/// </summary>
|
||||
/// <param name="other">Object to make the comparison with.</param>
|
||||
/// <returns><see langword="true" /> if the current instance is equal to the specified object; <see langword="false" /> otherwise.</returns>
|
||||
public bool Equals(LVector2 other) => (X == other.X && Y == other.Y);
|
||||
|
||||
|
||||
public static implicit operator LVector2(Vector2 v) => new(v.X, v.Y);
|
||||
public static implicit operator Vector2(LVector2 v) => new(v.X, v.Y);
|
||||
}
|
||||
}
|
@ -1,731 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 2007-2010 SlimDX Group
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
// associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
||||
// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
using GTA.Math;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
|
||||
[Serializable]
|
||||
internal struct LVector3 : IEquatable<LVector3>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the X component of the vector.
|
||||
/// </summary>
|
||||
/// <value>The X component of the vector.</value>
|
||||
public float X;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Y component of the vector.
|
||||
/// </summary>
|
||||
/// <value>The Y component of the vector.</value>
|
||||
public float Y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Z component of the vector.
|
||||
/// </summary>
|
||||
/// <value>The Z component of the vector.</value>
|
||||
public float Z;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LVector3"/> class.
|
||||
/// </summary>
|
||||
/// <param name="x">Initial value for the X component of the vector.</param>
|
||||
/// <param name="y">Initial value for the Y component of the vector.</param>
|
||||
/// <param name="z">Initial value for the Z component of the vector.</param>
|
||||
public LVector3(float x, float y, float z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
internal LVector3(float[] values) : this(values[0], values[1], values[2])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns this vector with a magnitude of 1.
|
||||
/// </summary>
|
||||
public LVector3 Normalized => Normalize(new LVector3(X, Y, Z));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a null vector. (0,0,0)
|
||||
/// </summary>
|
||||
public static LVector3 Zero => new(0.0f, 0.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The X unit <see cref="LVector3"/> (1, 0, 0).
|
||||
/// </summary>
|
||||
public static LVector3 UnitX => new(1.0f, 0.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The Y unit <see cref="LVector3"/> (0, 1, 0).
|
||||
/// </summary>
|
||||
public static LVector3 UnitY => new(0.0f, 1.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The Z unit <see cref="LVector3"/> (0, 0, 1).
|
||||
/// </summary>
|
||||
public static LVector3 UnitZ => new(0.0f, 0.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the world Up vector. (0,0,1)
|
||||
/// </summary>
|
||||
public static LVector3 WorldUp => new(0.0f, 0.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the world Down vector. (0,0,-1)
|
||||
/// </summary>
|
||||
public static LVector3 WorldDown => new(0.0f, 0.0f, -1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the world North vector. (0,1,0)
|
||||
/// </summary>
|
||||
public static LVector3 WorldNorth => new(0.0f, 1.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the world South vector. (0,-1,0)
|
||||
/// </summary>
|
||||
public static LVector3 WorldSouth => new(0.0f, -1.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the world East vector. (1,0,0)
|
||||
/// </summary>
|
||||
public static LVector3 WorldEast => new(1.0f, 0.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the world West vector. (-1,0,0)
|
||||
/// </summary>
|
||||
public static LVector3 WorldWest => new(-1.0f, 0.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the relative Right vector. (1,0,0)
|
||||
/// </summary>
|
||||
public static LVector3 RelativeRight => new(1.0f, 0.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the relative Left vector. (-1,0,0)
|
||||
/// </summary>
|
||||
public static LVector3 RelativeLeft => new(-1.0f, 0.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the relative Front vector. (0,1,0)
|
||||
/// </summary>
|
||||
public static LVector3 RelativeFront => new(0.0f, 1.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the relative Back vector. (0,-1,0)
|
||||
/// </summary>
|
||||
public static LVector3 RelativeBack => new(0.0f, -1.0f, 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the relative Top vector. (0,0,1)
|
||||
/// </summary>
|
||||
public static LVector3 RelativeTop => new(0.0f, 0.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the relative Bottom vector as used. (0,0,-1)
|
||||
/// </summary>
|
||||
public static LVector3 RelativeBottom => new(0.0f, 0.0f, -1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the component at the specified index.
|
||||
/// </summary>
|
||||
/// <value>The value of the X, Y or Z component, depending on the index.</value>
|
||||
/// <param name="index">The index of the component to access. Use 0 for the X component, 1 for the Y component and 2 for the Z component.</param>
|
||||
/// <returns>The value of the component at the specified index.</returns>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">Thrown when the <paramref name="index"/> is out of the range [0, 2].</exception>
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return X;
|
||||
case 1:
|
||||
return Y;
|
||||
case 2:
|
||||
return Z;
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException("index", "Indices for Vector3 run from 0 to 2, inclusive.");
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
X = value;
|
||||
break;
|
||||
case 1:
|
||||
Y = value;
|
||||
break;
|
||||
case 2:
|
||||
Z = value;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("index",
|
||||
"Indices for Vector3 run from 0 to 2, inclusive.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the length of the vector.
|
||||
/// </summary>
|
||||
/// <returns>The length of the vector.</returns>
|
||||
public float Length() => (float)(System.Math.Sqrt((X * X) + (Y * Y) + (Z * Z)));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared length of the vector.
|
||||
/// </summary>
|
||||
/// <returns>The squared length of the vector.</returns>
|
||||
public float LengthSquared() => (X * X) + (Y * Y) + (Z * Z);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the vector into a unit vector.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
float length = Length();
|
||||
if (length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float num = 1 / length;
|
||||
X *= num;
|
||||
Y *= num;
|
||||
Z *= num;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="position">The second vector to calculate the distance to.</param>
|
||||
/// <returns>The distance to the other vector.</returns>
|
||||
public float DistanceTo(LVector3 position) => (position - this).Length();
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared distance between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="position">The second vector to calculate the distance to.</param>
|
||||
/// <returns>The distance to the other vector.</returns>
|
||||
public float DistanceToSquared(LVector3 position) => DistanceSquared(position, this);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two vectors, ignoring the Z-component.
|
||||
/// </summary>
|
||||
/// <param name="position">The second vector to calculate the distance to.</param>
|
||||
/// <returns>The distance to the other vector.</returns>
|
||||
public float DistanceTo2D(LVector3 position)
|
||||
{
|
||||
var lhs = new LVector3(X, Y, 0.0f);
|
||||
var rhs = new LVector3(position.X, position.Y, 0.0f);
|
||||
|
||||
return Distance(lhs, rhs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared distance between two vectors, ignoring the Z-component.
|
||||
/// </summary>
|
||||
/// <param name="position">The second vector to calculate the squared distance to.</param>
|
||||
/// <returns>The distance to the other vector.</returns>
|
||||
public float DistanceToSquared2D(LVector3 position)
|
||||
{
|
||||
var lhs = new LVector3(X, Y, 0.0f);
|
||||
var rhs = new LVector3(position.X, position.Y, 0.0f);
|
||||
|
||||
return DistanceSquared(lhs, rhs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="position1">The first vector to calculate the distance to the second vector.</param>
|
||||
/// <param name="position2">The second vector to calculate the distance to the first vector.</param>
|
||||
/// <returns>The distance between the two vectors.</returns>
|
||||
public static float Distance(LVector3 position1, LVector3 position2) => (position1 - position2).Length();
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared distance between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="position1">The first vector to calculate the squared distance to the second vector.</param>
|
||||
/// <param name="position2">The second vector to calculate the squared distance to the first vector.</param>
|
||||
/// <returns>The squared distance between the two vectors.</returns>
|
||||
public static float DistanceSquared(LVector3 position1, LVector3 position2) =>
|
||||
(position1 - position2).LengthSquared();
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between two vectors, ignoring the Z-component.
|
||||
/// </summary>
|
||||
/// <param name="position1">The first vector to calculate the distance to the second vector.</param>
|
||||
/// <param name="position2">The second vector to calculate the distance to the first vector.</param>
|
||||
/// <returns>The distance between the two vectors.</returns>
|
||||
public static float Distance2D(LVector3 position1, LVector3 position2)
|
||||
{
|
||||
var pos1 = new LVector3(position1.X, position1.Y, 0);
|
||||
var pos2 = new LVector3(position2.X, position2.Y, 0);
|
||||
|
||||
return (pos1 - pos2).Length();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the squared distance between two vectors, ignoring the Z-component.
|
||||
/// </summary>
|
||||
/// <param name="position1">The first vector to calculate the squared distance to the second vector.</param>
|
||||
/// <param name="position2">The second vector to calculate the squared distance to the first vector.</param>
|
||||
/// <returns>The squared distance between the two vectors.</returns>
|
||||
public static float DistanceSquared2D(LVector3 position1, LVector3 position2)
|
||||
{
|
||||
var pos1 = new LVector3(position1.X, position1.Y, 0);
|
||||
var pos2 = new LVector3(position2.X, position2.Y, 0);
|
||||
|
||||
return (pos1 - pos2).LengthSquared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the angle in degrees between from and to.
|
||||
/// The angle returned is always the acute angle between the two vectors.
|
||||
/// </summary>
|
||||
public static float Angle(LVector3 from, LVector3 to)
|
||||
{
|
||||
double dot = Dot(from.Normalized, to.Normalized);
|
||||
return (float)(System.Math.Acos((dot)) * (180.0 / System.Math.PI));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the signed angle in degrees between from and to.
|
||||
/// </summary>
|
||||
public static float SignedAngle(LVector3 from, LVector3 to, LVector3 planeNormal)
|
||||
{
|
||||
LVector3 perpVector = Cross(planeNormal, from);
|
||||
|
||||
double angle = Angle(from, to);
|
||||
double dot = Dot(perpVector, to);
|
||||
if (dot < 0)
|
||||
{
|
||||
angle *= -1;
|
||||
}
|
||||
|
||||
return (float)angle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a vector to a heading.
|
||||
/// </summary>
|
||||
public float ToHeading() => (float)((System.Math.Atan2(X, -Y) + System.Math.PI) * (180.0 / System.Math.PI));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a random vector inside the circle around this position.
|
||||
/// </summary>
|
||||
public LVector3 Around(float distance) => this + (RandomXY() * distance);
|
||||
|
||||
/// <summary>
|
||||
/// Rounds each float inside the vector to a select amount of decimal places (2 by default).
|
||||
/// </summary>
|
||||
/// <param name="decimalPlaces">Number of decimal places to round to</param>
|
||||
/// <returns>The vector containing rounded values</returns>
|
||||
public LVector3 Round(int decimalPlaces = 2)
|
||||
{
|
||||
return new LVector3((float)System.Math.Round(X, decimalPlaces), (float)System.Math.Round(Y, decimalPlaces),
|
||||
(float)System.Math.Round(Z, decimalPlaces));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new normalized vector with random X and Y components.
|
||||
/// </summary>
|
||||
public static LVector3 RandomXY()
|
||||
{
|
||||
LVector3 v = Zero;
|
||||
double radian = CoreUtils.SafeRandom.NextDouble() * 2 * System.Math.PI;
|
||||
|
||||
v.X = (float)(System.Math.Cos(radian));
|
||||
v.Y = (float)(System.Math.Sin(radian));
|
||||
v.Normalize();
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new normalized vector with random X, Y and Z components.
|
||||
/// </summary>
|
||||
public static LVector3 RandomXYZ()
|
||||
{
|
||||
LVector3 v = Zero;
|
||||
double radian = CoreUtils.SafeRandom.NextDouble() * 2.0 * System.Math.PI;
|
||||
double cosTheta = (CoreUtils.SafeRandom.NextDouble() * 2.0) - 1.0;
|
||||
double theta = System.Math.Acos(cosTheta);
|
||||
|
||||
v.X = (float)(System.Math.Sin(theta) * System.Math.Cos(radian));
|
||||
v.Y = (float)(System.Math.Sin(theta) * System.Math.Sin(radian));
|
||||
v.Z = (float)(System.Math.Cos(theta));
|
||||
v.Normalize();
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to add.</param>
|
||||
/// <param name="right">The second vector to add.</param>
|
||||
/// <returns>The sum of the two vectors.</returns>
|
||||
public static LVector3 Add(LVector3 left, LVector3 right) =>
|
||||
new(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to subtract.</param>
|
||||
/// <param name="right">The second vector to subtract.</param>
|
||||
/// <returns>The difference of the two vectors.</returns>
|
||||
public static LVector3 Subtract(LVector3 left, LVector3 right) =>
|
||||
new(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector3 Multiply(LVector3 value, float scale) =>
|
||||
new(value.X * scale, value.Y * scale, value.Z * scale);
|
||||
|
||||
/// <summary>
|
||||
/// Multiply a vector with another by performing component-wise multiplication.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to multiply.</param>
|
||||
/// <param name="right">The second vector to multiply.</param>
|
||||
/// <returns>The multiplied vector.</returns>
|
||||
public static LVector3 Multiply(LVector3 left, LVector3 right) =>
|
||||
new(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector3 Divide(LVector3 value, float scale) =>
|
||||
new(value.X / scale, value.Y / scale, value.Z / scale);
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the direction of a given vector.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to negate.</param>
|
||||
/// <returns>A vector facing in the opposite direction.</returns>
|
||||
public static LVector3 Negate(LVector3 value) => new(-value.X, -value.Y, -value.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Restricts a value to be within a specified range.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static LVector3 Clamp(LVector3 value, LVector3 min, LVector3 max)
|
||||
{
|
||||
float x = value.X;
|
||||
x = (x > max.X) ? max.X : x;
|
||||
x = (x < min.X) ? min.X : x;
|
||||
|
||||
float y = value.Y;
|
||||
y = (y > max.Y) ? max.Y : y;
|
||||
y = (y < min.Y) ? min.Y : y;
|
||||
|
||||
float z = value.Z;
|
||||
z = (z > max.Z) ? max.Z : z;
|
||||
z = (z < min.Z) ? min.Z : z;
|
||||
|
||||
return new LVector3(x, y, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a linear interpolation between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="start">Start vector.</param>
|
||||
/// <param name="end">End vector.</param>
|
||||
/// <param name="amount">Value between 0 and 1 indicating the weight of <paramref name="end"/>.</param>
|
||||
/// <returns>The linear interpolation of the two vectors.</returns>
|
||||
/// <remarks>
|
||||
/// This method performs the linear interpolation based on the following formula.
|
||||
/// <code>start + (end - start) * amount</code>
|
||||
/// Passing <paramref name="amount"/> a value of 0 will cause <paramref name="start"/> to be returned; a value of 1 will cause <paramref name="end"/> to be returned.
|
||||
/// </remarks>
|
||||
public static LVector3 Lerp(LVector3 start, LVector3 end, float amount)
|
||||
{
|
||||
LVector3 vector = Zero;
|
||||
|
||||
vector.X = start.X + ((end.X - start.X) * amount);
|
||||
vector.Y = start.Y + ((end.Y - start.Y) * amount);
|
||||
vector.Z = start.Z + ((end.Z - start.Z) * amount);
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the vector into a unit vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to normalize.</param>
|
||||
/// <returns>The normalized vector.</returns>
|
||||
public static LVector3 Normalize(LVector3 vector)
|
||||
{
|
||||
vector.Normalize();
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">First source vector.</param>
|
||||
/// <param name="right">Second source vector.</param>
|
||||
/// <returns>The dot product of the two vectors.</returns>
|
||||
public static float Dot(LVector3 left, LVector3 right) =>
|
||||
(left.X * right.X + left.Y * right.Y + left.Z * right.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the cross product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">First source vector.</param>
|
||||
/// <param name="right">Second source vector.</param>
|
||||
/// <returns>The cross product of the two vectors.</returns>
|
||||
public static LVector3 Cross(LVector3 left, LVector3 right)
|
||||
{
|
||||
LVector3 result = Zero;
|
||||
result.X = left.Y * right.Z - left.Z * right.Y;
|
||||
result.Y = left.Z * right.X - left.X * right.Z;
|
||||
result.Z = left.X * right.Y - left.Y * right.X;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects a vector onto another vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to project.</param>
|
||||
/// <param name="onNormal">Vector to project onto, does not assume it is normalized.</param>
|
||||
/// <returns>The projected vector.</returns>
|
||||
public static LVector3 Project(LVector3 vector, LVector3 onNormal) =>
|
||||
onNormal * Dot(vector, onNormal) / Dot(onNormal, onNormal);
|
||||
|
||||
/// <summary>
|
||||
/// Projects a vector onto a plane defined by a normal orthogonal to the plane.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to project.</param>
|
||||
/// <param name="planeNormal">Normal of the plane, does not assume it is normalized.</param>
|
||||
/// <returns>The Projection of vector onto plane.</returns>
|
||||
public static LVector3 ProjectOnPlane(LVector3 vector, LVector3 planeNormal) =>
|
||||
(vector - Project(vector, planeNormal));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the reflection of a vector off a surface that has the specified normal.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to project onto the plane.</param>
|
||||
/// <param name="normal">Normal of the surface.</param>
|
||||
/// <returns>The reflected vector.</returns>
|
||||
/// <remarks>Reflect only gives the direction of a reflection off a surface, it does not determine
|
||||
/// whether the original vector was close enough to the surface to hit it.</remarks>
|
||||
public static LVector3 Reflect(LVector3 vector, LVector3 normal)
|
||||
{
|
||||
LVector3 result = Zero;
|
||||
float dot = ((vector.X * normal.X) + (vector.Y * normal.Y)) + (vector.Z * normal.Z);
|
||||
|
||||
result.X = vector.X - ((2.0f * dot) * normal.X);
|
||||
result.Y = vector.Y - ((2.0f * dot) * normal.Y);
|
||||
result.Z = vector.Z - ((2.0f * dot) * normal.Z);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector containing the smallest components of the specified vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first source vector.</param>
|
||||
/// <param name="right">The second source vector.</param>
|
||||
/// <returns>A vector containing the smallest components of the source vectors.</returns>
|
||||
public static LVector3 Minimize(LVector3 left, LVector3 right)
|
||||
{
|
||||
LVector3 vector = Zero;
|
||||
vector.X = (left.X < right.X) ? left.X : right.X;
|
||||
vector.Y = (left.Y < right.Y) ? left.Y : right.Y;
|
||||
vector.Z = (left.Z < right.Z) ? left.Z : right.Z;
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector containing the largest components of the specified vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first source vector.</param>
|
||||
/// <param name="right">The second source vector.</param>
|
||||
/// <returns>A vector containing the largest components of the source vectors.</returns>
|
||||
public static LVector3 Maximize(LVector3 left, LVector3 right)
|
||||
{
|
||||
LVector3 vector = Zero;
|
||||
vector.X = (left.X > right.X) ? left.X : right.X;
|
||||
vector.Y = (left.Y > right.Y) ? left.Y : right.Y;
|
||||
vector.Z = (left.Z > right.Z) ? left.Z : right.Z;
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to add.</param>
|
||||
/// <param name="right">The second vector to add.</param>
|
||||
/// <returns>The sum of the two vectors.</returns>
|
||||
public static LVector3 operator +(LVector3 left, LVector3 right) =>
|
||||
new(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="left">The first vector to subtract.</param>
|
||||
/// <param name="right">The second vector to subtract.</param>
|
||||
/// <returns>The difference of the two vectors.</returns>
|
||||
public static LVector3 operator -(LVector3 left, LVector3 right) =>
|
||||
new(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the direction of a given vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to negate.</param>
|
||||
/// <returns>A vector facing in the opposite direction.</returns>
|
||||
public static LVector3 operator -(LVector3 vector) => new(-vector.X, -vector.Y, -vector.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector3 operator *(LVector3 vector, float scale) =>
|
||||
new(vector.X * scale, vector.Y * scale, vector.Z * scale);
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector3 operator *(float scale, LVector3 vector) => vector * scale;
|
||||
|
||||
/// <summary>
|
||||
/// Scales a vector by the given value.
|
||||
/// </summary>
|
||||
/// <param name="vector">The vector to scale.</param>
|
||||
/// <param name="scale">The amount by which to scale the vector.</param>
|
||||
/// <returns>The scaled vector.</returns>
|
||||
public static LVector3 operator /(LVector3 vector, float scale)
|
||||
{
|
||||
float invScale = 1.0f / scale;
|
||||
return new LVector3(vector.X * invScale, vector.Y * invScale, vector.Z * invScale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for equality between two objects.
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns><see langword="true" /> if <paramref name="left"/> has the same value as <paramref name="right"/>; otherwise, <see langword="false" />.</returns>
|
||||
public static bool operator ==(LVector3 left, LVector3 right) => Equals(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Tests for inequality between two objects.
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns><see langword="true" /> if <paramref name="left"/> has a different value than <paramref name="right"/>; otherwise, <see langword="false" />.</returns>
|
||||
public static bool operator !=(LVector3 left, LVector3 right) => !Equals(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Vector3 to a Vector2 implicitly.
|
||||
/// </summary>
|
||||
public static implicit operator LVector2(LVector3 vector) => new(vector.X, vector.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the matrix to an array of floats.
|
||||
/// </summary>
|
||||
public float[] ToArray() => new[] { X, Y, Z };
|
||||
|
||||
/// <summary>
|
||||
/// Converts the value of the object to its equivalent string representation.
|
||||
/// </summary>
|
||||
/// <returns>The string representation of the value of this instance.</returns>
|
||||
public override string ToString() => string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2}", X, Y, Z);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the value of the object to its equivalent string representation.
|
||||
/// </summary>
|
||||
/// <param name="format">The number format.</param>
|
||||
/// <returns>The string representation of the value of this instance.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
if (format == null)
|
||||
{
|
||||
return ToString();
|
||||
}
|
||||
|
||||
return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2}",
|
||||
X.ToString(format, CultureInfo.CurrentCulture),
|
||||
Y.ToString(format, CultureInfo.CurrentCulture), Z.ToString(format, CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>A 32-bit signed integer hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = X.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Y.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Z.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether the current instance is equal to a specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to make the comparison with.</param>
|
||||
/// <returns><see langword="true" /> if the current instance is equal to the specified object; <see langword="false" /> otherwise.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((LVector3)obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether the current instance is equal to the specified object.
|
||||
/// </summary>
|
||||
/// <param name="other">Object to make the comparison with.</param>
|
||||
/// <returns><see langword="true" /> if the current instance is equal to the specified object; <see langword="false" /> otherwise.</returns>
|
||||
public bool Equals(LVector3 other) => (X == other.X && Y == other.Y && Z == other.Z);
|
||||
|
||||
public static implicit operator LVector3(Vector3 v) => new(v.X, v.Y, v.Z);
|
||||
public static implicit operator Vector3(LVector3 v) => new(v.X, v.Y, v.Z);
|
||||
}
|
||||
}
|
@ -1,589 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
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.Text.Json.Serialization;
|
||||
using System.Xml;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using Lidgren.Network;
|
||||
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
|
||||
{
|
||||
internal static Random SafeRandom => _randInstance.Value;
|
||||
private static int _randSeed = Environment.TickCount;
|
||||
private static readonly ThreadLocal<Random> _randInstance
|
||||
= new(() => new Random(Interlocked.Increment(ref _randSeed)));
|
||||
|
||||
private static readonly HashSet<string> ToIgnore = new()
|
||||
{
|
||||
"RageCoop.Client",
|
||||
"RageCoop.Client.Loader",
|
||||
"RageCoop.Client.Installer",
|
||||
"RageCoop.Core",
|
||||
"RageCoop.Server",
|
||||
"ScriptHookVDotNet2",
|
||||
"ScriptHookVDotNet3",
|
||||
"ScriptHookVDotNet",
|
||||
"ScriptHookVDotNetCore"
|
||||
};
|
||||
|
||||
public static string FormatToSharpStyle(string input, int offset)
|
||||
{
|
||||
var ss = input.Substring(offset).Split("_".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
// Replace first character with upper case
|
||||
for (var i = 0; i < ss.Length; i++)
|
||||
{
|
||||
var sec = ss[i].ToLower();
|
||||
var head = sec[0];
|
||||
ss[i] = head.ToString().ToUpper() + sec.Remove(0, 1);
|
||||
}
|
||||
|
||||
return string.Join("", ss);
|
||||
}
|
||||
|
||||
public static string ToHex(this int value)
|
||||
{
|
||||
return string.Format("0x{0:X}", value);
|
||||
}
|
||||
|
||||
public static string ToHex(this uint value)
|
||||
{
|
||||
return string.Format("0x{0:X}", value);
|
||||
}
|
||||
|
||||
public static int RandInt(int start, int end)
|
||||
{
|
||||
return SafeRandom.Next(start, end);
|
||||
}
|
||||
|
||||
public static string GetTempDirectory(string dir = null)
|
||||
{
|
||||
dir = dir ?? Path.GetTempPath();
|
||||
string path;
|
||||
do
|
||||
{
|
||||
path = Path.Combine(dir, RandomString(10));
|
||||
} while (Directory.Exists(path) || File.Exists(path));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string RandomString(int length)
|
||||
{
|
||||
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
return new string(Enumerable.Repeat(chars, length)
|
||||
.Select(s => s[SafeRandom.Next(s.Length)]).ToArray());
|
||||
}
|
||||
|
||||
public static Version GetLatestVersion(string branch = "dev-nightly")
|
||||
{
|
||||
var url =
|
||||
$"https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/{branch}/RageCoop.Server/Properties/AssemblyInfo.cs";
|
||||
var versionLine = HttpHelper.DownloadString(url)
|
||||
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(x => x.Contains("[assembly: AssemblyVersion(")).First();
|
||||
var start = versionLine.IndexOf('\"') + 1;
|
||||
var end = versionLine.LastIndexOf('\"');
|
||||
return Version.Parse(versionLine.Substring(start, end - start));
|
||||
}
|
||||
|
||||
public static bool CanBeIgnored(this string 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)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var child in Assembly.Load(assembly).GetReferencedAssemblies())
|
||||
LoadAllReferencedAssemblies(child);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!assembly.Name.StartsWith("Microsoft.CodeAnalysis"))
|
||||
{
|
||||
System.Console.WriteLine("Error loading dependency: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToFullPath(this string path)
|
||||
{
|
||||
return Path.GetFullPath(path);
|
||||
}
|
||||
|
||||
public static IPEndPoint StringToEndPoint(string endpointstring)
|
||||
{
|
||||
return StringToEndPoint(endpointstring, -1);
|
||||
}
|
||||
|
||||
public static IPEndPoint StringToEndPoint(string endpointstring, int defaultport)
|
||||
{
|
||||
if (string.IsNullOrEmpty(endpointstring)
|
||||
|| endpointstring.Trim().Length == 0)
|
||||
throw new ArgumentException("Endpoint descriptor may not be empty.");
|
||||
|
||||
if (defaultport != -1 &&
|
||||
(defaultport < IPEndPoint.MinPort
|
||||
|| defaultport > IPEndPoint.MaxPort))
|
||||
throw new ArgumentException(string.Format("Invalid default port '{0}'", defaultport));
|
||||
|
||||
var values = endpointstring.Split(':');
|
||||
IPAddress ipaddy;
|
||||
var port = -1;
|
||||
|
||||
//check if we have an IPv6 or ports
|
||||
if (values.Length <= 2) // ipv4 or hostname
|
||||
{
|
||||
if (values.Length == 1)
|
||||
//no port is specified, default
|
||||
port = defaultport;
|
||||
else
|
||||
port = getPort(values[1]);
|
||||
|
||||
//try to use the address as IPv4, otherwise get hostname
|
||||
if (!IPAddress.TryParse(values[0], out ipaddy))
|
||||
ipaddy = GetIPfromHost(values[0]);
|
||||
}
|
||||
else if (values.Length > 2) //ipv6
|
||||
{
|
||||
//could [a:b:c]:d
|
||||
if (values[0].StartsWith("[") && values[values.Length - 2].EndsWith("]"))
|
||||
{
|
||||
var ipaddressstring = string.Join(":", values.Take(values.Length - 1).ToArray());
|
||||
ipaddy = IPAddress.Parse(ipaddressstring);
|
||||
port = getPort(values[values.Length - 1]);
|
||||
}
|
||||
else //[a:b:c] or a:b:c
|
||||
{
|
||||
ipaddy = IPAddress.Parse(endpointstring);
|
||||
port = defaultport;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException(string.Format("Invalid endpoint ipaddress '{0}'", endpointstring));
|
||||
}
|
||||
|
||||
if (port == -1)
|
||||
throw new ArgumentException(string.Format("No port specified: '{0}'", endpointstring));
|
||||
|
||||
return new IPEndPoint(ipaddy, port);
|
||||
}
|
||||
|
||||
private static int getPort(string p)
|
||||
{
|
||||
if (!int.TryParse(p, out var port)
|
||||
|| port < IPEndPoint.MinPort
|
||||
|| port > IPEndPoint.MaxPort)
|
||||
throw new FormatException(string.Format("Invalid end point port '{0}'", p));
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
public static IPAddress GetLocalAddress(string target = "8.8.8.8")
|
||||
{
|
||||
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
|
||||
{
|
||||
socket.Connect(target, 65530);
|
||||
var endPoint = socket.LocalEndPoint as IPEndPoint;
|
||||
return endPoint.Address;
|
||||
}
|
||||
}
|
||||
|
||||
public static IPAddress GetIPfromHost(string p)
|
||||
{
|
||||
var hosts = Dns.GetHostAddresses(p);
|
||||
|
||||
if (hosts == null || hosts.Length == 0)
|
||||
throw new ArgumentException(string.Format("Host not found: {0}", p));
|
||||
|
||||
return hosts[0];
|
||||
}
|
||||
|
||||
public static IpInfo GetIPInfo()
|
||||
{
|
||||
// TLS only
|
||||
ServicePointManager.Expect100Continue = true;
|
||||
ServicePointManager.SecurityProtocol =
|
||||
SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
|
||||
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var response = httpClient.GetAsync("https://ipinfo.io/json").GetAwaiter().GetResult();
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
throw new Exception($"IPv4 request failed! [{(int)response.StatusCode}/{response.ReasonPhrase}]");
|
||||
|
||||
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||
return JsonDeserialize<IpInfo>(content);
|
||||
}
|
||||
|
||||
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
|
||||
{
|
||||
foreach (var dir in source.GetDirectories())
|
||||
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
|
||||
foreach (var file in source.GetFiles())
|
||||
file.CopyTo(Path.Combine(target.FullName, file.Name), true);
|
||||
}
|
||||
|
||||
public static string GetInvariantRID()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
return "win-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
return "linux-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
return "osx-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get local ip addresses on all network interfaces
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static List<IPAddress> GetLocalAddress()
|
||||
{
|
||||
var addresses = new List<IPAddress>();
|
||||
foreach (var netInterface in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
var ipProps = netInterface.GetIPProperties();
|
||||
foreach (var addr in ipProps.UnicastAddresses) addresses.Add(addr.Address);
|
||||
}
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
public static StreamWriter OpenWriter(string path, FileMode mode = FileMode.Create,
|
||||
FileAccess access = FileAccess.Write, FileShare share = FileShare.ReadWrite)
|
||||
{
|
||||
return new StreamWriter(File.Open(path, mode, access, share));
|
||||
}
|
||||
|
||||
|
||||
public static float GetFloat(this XmlNode n)
|
||||
{
|
||||
return float.Parse(n.Attributes["value"].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate jenkins one-at-a-time hash from specified string (lower)
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static uint JoaatHash(string key)
|
||||
{
|
||||
var i = 0;
|
||||
uint hash = 0;
|
||||
while (i != key.Length)
|
||||
{
|
||||
hash += char.ToLowerInvariant(key[i++]);
|
||||
hash += hash << 10;
|
||||
hash ^= hash >> 6;
|
||||
}
|
||||
|
||||
hash += hash << 3;
|
||||
hash ^= hash >> 11;
|
||||
hash += hash << 15;
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static unsafe bool StructCmp<T>(ref T left, ref T right) where T : unmanaged
|
||||
{
|
||||
fixed (T* pLeft = &left, pRight = &right)
|
||||
{
|
||||
return MemCmp(pLeft, pRight, sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static unsafe bool StructCmp<T>(T left, T right) where T : unmanaged
|
||||
{
|
||||
return MemCmp(&left, &right, sizeof(T));
|
||||
}
|
||||
|
||||
static bool _simdSupported = System.Numerics.Vector<byte>.IsSupported;
|
||||
static int _simdSlots = _simdSupported ? System.Numerics.Vector<byte>.Count : 0;
|
||||
|
||||
/// <summary>
|
||||
/// SIMD-accelerated memory comparer
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static unsafe bool MemCmp(void* p1, void* p2, int cbToCompare)
|
||||
{
|
||||
int numVectors = cbToCompare / _simdSlots;
|
||||
int ceiling = numVectors * _simdSlots;
|
||||
if (numVectors > 0)
|
||||
{
|
||||
ReadOnlySpan<System.Numerics.Vector<byte>> leftVecArray = new(p1, numVectors);
|
||||
ReadOnlySpan<System.Numerics.Vector<byte>> rightVecArray = new(p2, numVectors);
|
||||
|
||||
for (int i = 0; i < numVectors; i++)
|
||||
{
|
||||
if (leftVecArray[i] != rightVecArray[i])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int numWords = cbToCompare / sizeof(IntPtr);
|
||||
var pwLeft = (IntPtr*)p1;
|
||||
var pwRight = (IntPtr*)p2;
|
||||
|
||||
for (int i = (ceiling / sizeof(IntPtr)); i < numWords; i++)
|
||||
{
|
||||
if (pwLeft[i] != pwRight[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
var pbLeft = (byte*)p1;
|
||||
var pbRight = (byte*)p2;
|
||||
|
||||
for (int i = ceiling + (numWords * sizeof(IntPtr)); i < cbToCompare; i++)
|
||||
{
|
||||
if (pbLeft[i] != pbRight[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class IpInfo
|
||||
{
|
||||
[JsonPropertyName("ip")] public string Address { get; set; }
|
||||
|
||||
[JsonPropertyName("country")] public string Country { get; set; }
|
||||
}
|
||||
|
||||
internal static class Extensions
|
||||
{
|
||||
public static byte[] GetBytes(this string s)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(s);
|
||||
}
|
||||
|
||||
public static string GetString(this byte[] data)
|
||||
{
|
||||
return Encoding.UTF8.GetString(data);
|
||||
}
|
||||
|
||||
public static T GetPacket<T>(this NetIncomingMessage msg) where T : Packet, new()
|
||||
{
|
||||
var p = new T();
|
||||
p.Deserialize(msg);
|
||||
return p;
|
||||
}
|
||||
|
||||
public static bool HasPedFlag(this PedDataFlags flags, PedDataFlags flag)
|
||||
{
|
||||
return (flags & flag) != 0;
|
||||
}
|
||||
|
||||
public static bool HasProjDataFlag(this ProjectileDataFlags flags, ProjectileDataFlags flag)
|
||||
{
|
||||
return (flags & flag) != 0;
|
||||
}
|
||||
|
||||
public static bool HasVehFlag(this VehicleDataFlags flags, VehicleDataFlags flag)
|
||||
{
|
||||
return (flags & flag) != 0;
|
||||
}
|
||||
|
||||
public static bool HasConfigFlag(this PlayerConfigFlags flags, PlayerConfigFlags flag)
|
||||
{
|
||||
return (flags & flag) != 0;
|
||||
}
|
||||
|
||||
public static bool HasEventFlag(this CustomEventFlags flags, CustomEventFlags flag)
|
||||
{
|
||||
return (flags & flag) != 0;
|
||||
}
|
||||
|
||||
public static Type GetActualType(this TypeCode code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case TypeCode.Boolean:
|
||||
return typeof(bool);
|
||||
|
||||
case TypeCode.Byte:
|
||||
return typeof(byte);
|
||||
|
||||
case TypeCode.Char:
|
||||
return typeof(char);
|
||||
|
||||
case TypeCode.DateTime:
|
||||
return typeof(DateTime);
|
||||
|
||||
case TypeCode.DBNull:
|
||||
return typeof(DBNull);
|
||||
|
||||
case TypeCode.Decimal:
|
||||
return typeof(decimal);
|
||||
|
||||
case TypeCode.Double:
|
||||
return typeof(double);
|
||||
|
||||
case TypeCode.Empty:
|
||||
return null;
|
||||
|
||||
case TypeCode.Int16:
|
||||
return typeof(short);
|
||||
|
||||
case TypeCode.Int32:
|
||||
return typeof(int);
|
||||
|
||||
case TypeCode.Int64:
|
||||
return typeof(long);
|
||||
|
||||
case TypeCode.Object:
|
||||
return typeof(object);
|
||||
|
||||
case TypeCode.SByte:
|
||||
return typeof(sbyte);
|
||||
|
||||
case TypeCode.Single:
|
||||
return typeof(float);
|
||||
|
||||
case TypeCode.String:
|
||||
return typeof(string);
|
||||
|
||||
case TypeCode.UInt16:
|
||||
return typeof(ushort);
|
||||
|
||||
case TypeCode.UInt32:
|
||||
return typeof(uint);
|
||||
|
||||
case TypeCode.UInt64:
|
||||
return typeof(ulong);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string DumpWithType(this IEnumerable<object> objects)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var obj in objects) sb.Append(obj.GetType() + ":" + obj + "\n");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string Dump<T>(this IEnumerable<T> objects)
|
||||
{
|
||||
return $"{{{string.Join(",", objects)}}}";
|
||||
}
|
||||
|
||||
public static void ForEach<T>(this IEnumerable<T> objects, Action<T> action)
|
||||
{
|
||||
foreach (var obj in objects) action(obj);
|
||||
}
|
||||
|
||||
public static byte[] ReadToEnd(this Stream stream)
|
||||
{
|
||||
if (stream is MemoryStream)
|
||||
return ((MemoryStream)stream).ToArray();
|
||||
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
stream.CopyTo(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static MemoryStream ToMemStream(this Stream stream)
|
||||
{
|
||||
var memoryStream = new MemoryStream();
|
||||
stream.CopyTo(memoryStream);
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
public static byte[] Join(this List<byte[]> arrays, int lengthPerArray = -1)
|
||||
{
|
||||
if (arrays.Count == 1) return arrays[0];
|
||||
var output = lengthPerArray == -1
|
||||
? new byte[arrays.Sum(arr => arr.Length)]
|
||||
: new byte[arrays.Count * lengthPerArray];
|
||||
var writeIdx = 0;
|
||||
foreach (var byteArr in arrays)
|
||||
{
|
||||
byteArr.CopyTo(output, writeIdx);
|
||||
writeIdx += byteArr.Length;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static bool IsScript(this Type type, Type scriptType)
|
||||
{
|
||||
return !type.IsAbstract && type.IsSubclassOf(scriptType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some extension methods provided by RageCoop
|
||||
/// </summary>
|
||||
public static class PublicExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a SHA256 hashed byte array of the input string, internally used to hash password at client side.
|
||||
/// </summary>
|
||||
/// <param name="inputString"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] GetSHA256Hash(this string inputString)
|
||||
{
|
||||
using (HashAlgorithm algorithm = SHA256.Create())
|
||||
{
|
||||
return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a byte array to hex-encoded string, internally used to trigger handshake event
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToHexString(this byte[] data)
|
||||
{
|
||||
return BitConverter.ToString(data).Replace("-", string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a string to IP address
|
||||
/// </summary>
|
||||
/// <param name="ip"></param>
|
||||
/// <returns></returns>
|
||||
public static IPAddress ToIP(this string ip)
|
||||
{
|
||||
return IPAddress.Parse(ip);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
class IPAddressConverter : JsonConverter<IPAddress>
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(IPAddress);
|
||||
}
|
||||
|
||||
public override IPAddress Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Null) return null;
|
||||
return IPAddress.Parse(reader.GetString());
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, IPAddress value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value?.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
class IPEndPointConverter : JsonConverter<IPEndPoint>
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(IPEndPoint);
|
||||
}
|
||||
public override IPEndPoint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.Null) return null;
|
||||
var jo = JsonNode.Parse(ref reader);
|
||||
return new IPEndPoint(IPAddress.Parse(jo["Address"].ToString()), int.Parse(jo["Port"].ToString()));
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, IPEndPoint value, JsonSerializerOptions options)
|
||||
{
|
||||
new JsonObject()
|
||||
{
|
||||
{ "Address", value.Address?.ToString() },
|
||||
{ "Port", value.Port }
|
||||
}.WriteTo(writer);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
203
Core/Logger.cs
203
Core/Logger.cs
@ -1,203 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public enum LogLevel
|
||||
{
|
||||
Trace = 0,
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Warning = 3,
|
||||
Error = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class Logger : MarshalByRefObject, IDisposable
|
||||
{
|
||||
public delegate void FlushDelegate(LogLine line, string fomatted);
|
||||
|
||||
private readonly ConcurrentQueue<LogLine> _queuedLines = new ConcurrentQueue<LogLine>();
|
||||
public readonly string DateTimeFormat = "HH:mm:ss";
|
||||
|
||||
private readonly Thread LoggerThread;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use UTC time for timestamping the log
|
||||
/// </summary>
|
||||
public readonly bool UseUtc = false;
|
||||
|
||||
public bool FlushImmediately = false;
|
||||
public int FlushInterval = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
|
||||
/// </summary>
|
||||
public int LogLevel = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Name of this logger
|
||||
/// </summary>
|
||||
public string Name = "Logger";
|
||||
|
||||
private bool Stopping;
|
||||
public List<StreamWriter> Writers = new() { new StreamWriter(Console.OpenStandardOutput()) };
|
||||
|
||||
internal Logger()
|
||||
{
|
||||
Name = Environment.ProcessId.ToString();
|
||||
if (!FlushImmediately)
|
||||
{
|
||||
LoggerThread = new Thread(() =>
|
||||
{
|
||||
while (!Stopping)
|
||||
{
|
||||
Flush();
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
Flush();
|
||||
});
|
||||
LoggerThread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop backdround thread and flush all pending messages.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Stopping = true;
|
||||
LoggerThread?.Join();
|
||||
}
|
||||
|
||||
public event FlushDelegate OnFlush;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Info(string message)
|
||||
{
|
||||
Enqueue(2, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Warning(string message)
|
||||
{
|
||||
Enqueue(3, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Error(string message)
|
||||
{
|
||||
Enqueue(4, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="error"></param>
|
||||
public void Error(string message, Exception error)
|
||||
{
|
||||
Enqueue(4, $"{message}:\n {error}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="ex"></param>
|
||||
public void Error(Exception ex)
|
||||
{
|
||||
Enqueue(4, ex.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Debug(string message)
|
||||
{
|
||||
Enqueue(1, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Trace(string message)
|
||||
{
|
||||
Enqueue(0, message);
|
||||
}
|
||||
|
||||
public void Enqueue(int level, string message)
|
||||
{
|
||||
if (level < LogLevel) return;
|
||||
_queuedLines.Enqueue(new LogLine
|
||||
{
|
||||
Message = message,
|
||||
TimeStamp = UseUtc ? DateTime.UtcNow : DateTime.Now,
|
||||
LogLevel = (LogLevel)level
|
||||
});
|
||||
if (FlushImmediately) Flush();
|
||||
}
|
||||
|
||||
private string Format(LogLine line)
|
||||
{
|
||||
return string.Format("[{0}][{2}] [{3}] {1}", line.TimeStamp.ToString(DateTimeFormat), line.Message, Name,
|
||||
line.LogLevel.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public void Flush()
|
||||
{
|
||||
lock (_queuedLines)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (_queuedLines.TryDequeue(out var line))
|
||||
{
|
||||
var formatted = Format(line);
|
||||
Writers?.ForEach(x =>
|
||||
{
|
||||
try
|
||||
{
|
||||
x.WriteLine(formatted);
|
||||
x.Flush();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleError(ex);
|
||||
}
|
||||
});
|
||||
OnFlush?.Invoke(line, formatted);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
void HandleError(Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Logger {this} flush error: {ex}");
|
||||
}
|
||||
public class LogLine
|
||||
{
|
||||
public LogLevel LogLevel;
|
||||
public string Message;
|
||||
public DateTime TimeStamp;
|
||||
|
||||
internal LogLine()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal class CoopPeer : NetPeer, IDisposable
|
||||
{
|
||||
private readonly Logger Log;
|
||||
private readonly Thread _receiver;
|
||||
private bool _stopping;
|
||||
public EventHandler<NetIncomingMessage> OnMessageReceived;
|
||||
|
||||
public CoopPeer(NetPeerConfiguration config,Logger logger) : base(config)
|
||||
{
|
||||
Log = logger;
|
||||
Start();
|
||||
NetIncomingMessage msg;
|
||||
_receiver = new Thread(() =>
|
||||
{
|
||||
while (!_stopping)
|
||||
{
|
||||
msg = WaitMessage(200);
|
||||
if (msg != null) OnMessageReceived?.Invoke(this, msg);
|
||||
}
|
||||
});
|
||||
_receiver.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Terminate all connections and background thread
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_stopping = true;
|
||||
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,
|
||||
NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
|
||||
{
|
||||
var outgoingMessage = CreateMessage();
|
||||
p.Pack(outgoingMessage);
|
||||
SendMessage(outgoingMessage, connection, method, (int)channel);
|
||||
}
|
||||
|
||||
public void SendTo(Packet p, IList<NetConnection> connections,
|
||||
ConnectionChannel channel = ConnectionChannel.Default,
|
||||
NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
|
||||
{
|
||||
var outgoingMessage = CreateMessage();
|
||||
p.Pack(outgoingMessage);
|
||||
SendMessage(outgoingMessage, connections, method, (int)channel);
|
||||
}
|
||||
|
||||
public void Send(Packet p, IList<NetConnection> cons, ConnectionChannel channel = ConnectionChannel.Default,
|
||||
NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
|
||||
{
|
||||
var outgoingMessage = CreateMessage();
|
||||
p.Pack(outgoingMessage);
|
||||
SendMessage(outgoingMessage, cons, method, (int)channel);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using GTA;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Core.Scripting;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal partial class Packets
|
||||
{
|
||||
internal class CustomEvent : Packet
|
||||
{
|
||||
public CustomEventFlags Flags;
|
||||
|
||||
public CustomEvent(CustomEventFlags flags = CustomEventFlags.None)
|
||||
{
|
||||
Flags = flags;
|
||||
}
|
||||
|
||||
public override PacketType Type => PacketType.CustomEvent;
|
||||
public int Hash { get; set; }
|
||||
public byte[] Payload;
|
||||
|
||||
protected override void Serialize(NetOutgoingMessage m)
|
||||
{
|
||||
m.Write((byte)Flags);
|
||||
m.Write(Hash);
|
||||
m.Write(Payload);
|
||||
}
|
||||
|
||||
public unsafe override void Deserialize(NetIncomingMessage m)
|
||||
{
|
||||
Flags = (CustomEventFlags)m.ReadByte();
|
||||
Hash = m.ReadInt32();
|
||||
Payload = m.ReadBytes(m.LengthBytes - m.PositionInBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
using GTA;
|
||||
using Lidgren.Network;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Common data for synchronizing an entity
|
||||
/// </summary>
|
||||
internal struct EntityData
|
||||
{
|
||||
public int ID;
|
||||
public int OwnerID;
|
||||
public LQuaternion Quaternion;
|
||||
public LVector3 Position;
|
||||
public LVector3 Velocity;
|
||||
public int ModelHash;
|
||||
}
|
||||
|
||||
internal struct VehicleData
|
||||
{
|
||||
public VehicleDataFlags Flags;
|
||||
public float ThrottlePower;
|
||||
public float BrakePower;
|
||||
public float SteeringAngle;
|
||||
public VehicleLockStatus LockStatus;
|
||||
public float DeluxoWingRatio;
|
||||
}
|
||||
internal struct VehicleDataFull
|
||||
{
|
||||
|
||||
public float EngineHealth;
|
||||
public (byte, byte) Colors;
|
||||
public byte ToggleModsMask;
|
||||
public VehicleDamageModel DamageModel;
|
||||
public int Livery;
|
||||
public byte HeadlightColor;
|
||||
public byte RadioStation;
|
||||
public ushort ExtrasMask;
|
||||
public byte RoofState;
|
||||
public byte LandingGear;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-fixed vehicle data
|
||||
/// </summary>
|
||||
internal struct VehicleDataVar
|
||||
{
|
||||
public string LicensePlate;
|
||||
public (int, int)[] Mods;
|
||||
public void WriteTo(NetOutgoingMessage m)
|
||||
{
|
||||
m.Write(LicensePlate);
|
||||
m.Write((byte)Mods.Length);
|
||||
for(int i = 0;i < Mods.Length; i++)
|
||||
{
|
||||
m.Write(ref Mods[i]);
|
||||
}
|
||||
}
|
||||
public void ReadFrom(NetIncomingMessage m)
|
||||
{
|
||||
LicensePlate = m.ReadString();
|
||||
Mods = new (int, int)[m.ReadByte()];
|
||||
for(int i = 0; i < Mods.Length; i++)
|
||||
{
|
||||
Mods[i] = m.Read<(int, int)>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal partial class Packets
|
||||
{
|
||||
public class VehicleSync : Packet
|
||||
{
|
||||
public override PacketType Type => PacketType.VehicleSync;
|
||||
public EntityData ED;
|
||||
public VehicleData VD;
|
||||
public VehicleDataFull VDF;
|
||||
public VehicleDataVar VDV;
|
||||
|
||||
protected override void Serialize(NetOutgoingMessage m)
|
||||
{
|
||||
m.Write(ref ED);
|
||||
m.Write(ref VD);
|
||||
if (VD.Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
|
||||
{
|
||||
m.Write(ref VDF);
|
||||
VDV.WriteTo(m);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(NetIncomingMessage m)
|
||||
{
|
||||
m.Read(out ED);
|
||||
m.Read(out VD);
|
||||
if (VD.Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
|
||||
{
|
||||
m.Read(out VDF);
|
||||
VDV.ReadFrom(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<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>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\libs\Lidgren.Network\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\libs\ScriptHookVDotNetCore\src\ScriptHookVDotNetCore\ScriptHookVDotNetCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,85 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
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
|
||||
|
||||
[JsonInclude]
|
||||
public ulong FunctionPtr { get; private set; }
|
||||
|
||||
[JsonInclude]
|
||||
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,325 +0,0 @@
|
||||
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;
|
||||
|
||||
namespace RageCoop.Core.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes how the event should be sent or processed
|
||||
/// </summary>
|
||||
public enum CustomEventFlags : byte
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Data will be encrypted and decrypted on target client
|
||||
/// </summary>
|
||||
Encrypted = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Event will be queued and fired in script thread, specify this flag if your handler will call native functions.
|
||||
/// </summary>
|
||||
Queued = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Struct to identify different event using hash
|
||||
/// </summary>
|
||||
public struct CustomEventHash
|
||||
{
|
||||
private static readonly MD5 Hasher = MD5.Create();
|
||||
private static readonly Dictionary<int, string> Hashed = new Dictionary<int, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Hash value
|
||||
/// </summary>
|
||||
public int Hash;
|
||||
|
||||
/// <summary>
|
||||
/// Create from hash
|
||||
/// </summary>
|
||||
/// <param name="hash"></param>
|
||||
public static implicit operator CustomEventHash(int hash)
|
||||
{
|
||||
return new CustomEventHash { Hash = hash };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create from string
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
public static implicit operator CustomEventHash(string name)
|
||||
{
|
||||
return new CustomEventHash { Hash = FromString(name) };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Int32 hash of a string.
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// The exception is thrown when the name did not match a previously computed one and
|
||||
/// the hash was the same.
|
||||
/// </exception>
|
||||
public static int FromString(string s)
|
||||
{
|
||||
var hash = BitConverter.ToInt32(Hasher.ComputeHash(Encoding.UTF8.GetBytes(s)), 0);
|
||||
lock (Hashed)
|
||||
{
|
||||
if (Hashed.TryGetValue(hash, out var name))
|
||||
{
|
||||
if (name != s)
|
||||
throw new ArgumentException(
|
||||
$"Hashed value has collision with another name:{name}, hashed value:{hash}");
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
Hashed.Add(hash, s);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To int
|
||||
/// </summary>
|
||||
/// <param name="h"></param>
|
||||
public static implicit operator int(CustomEventHash h)
|
||||
{
|
||||
return h.Hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Common processing for custome client\server events
|
||||
/// </summary>
|
||||
public static partial class CustomEvents
|
||||
{
|
||||
internal static readonly CustomEventHash OnPlayerDied = "RageCoop.OnPlayerDied";
|
||||
internal static readonly CustomEventHash SetWeather = "RageCoop.SetWeather";
|
||||
internal static readonly CustomEventHash OnPedDeleted = "RageCoop.OnPedDeleted";
|
||||
internal static readonly CustomEventHash OnVehicleDeleted = "RageCoop.OnVehicleDeleted";
|
||||
internal static readonly CustomEventHash SetAutoRespawn = "RageCoop.SetAutoRespawn";
|
||||
internal static readonly CustomEventHash SetDisplayNameTag = "RageCoop.SetDisplayNameTag";
|
||||
internal static readonly CustomEventHash NativeCall = "RageCoop.NativeCall";
|
||||
internal static readonly CustomEventHash NativeResponse = "RageCoop.NativeResponse";
|
||||
internal static readonly CustomEventHash AllResourcesSent = "RageCoop.AllResourcesSent";
|
||||
internal static readonly CustomEventHash ServerPropSync = "RageCoop.ServerPropSync";
|
||||
internal static readonly CustomEventHash ServerBlipSync = "RageCoop.ServerBlipSync";
|
||||
internal static readonly CustomEventHash SetEntity = "RageCoop.SetEntity";
|
||||
internal static readonly CustomEventHash DeleteServerProp = "RageCoop.DeleteServerProp";
|
||||
internal static readonly CustomEventHash UpdatePedBlip = "RageCoop.UpdatePedBlip";
|
||||
internal static readonly CustomEventHash DeleteEntity = "RageCoop.DeleteEntity";
|
||||
internal static readonly CustomEventHash DeleteServerBlip = "RageCoop.DeleteServerBlip";
|
||||
internal static readonly CustomEventHash CreateVehicle = "RageCoop.CreateVehicle";
|
||||
internal static readonly CustomEventHash WeatherTimeSync = "RageCoop.WeatherTimeSync";
|
||||
internal static readonly CustomEventHash IsHost = "RageCoop.IsHost";
|
||||
|
||||
#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)
|
||||
{
|
||||
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);
|
||||
var vec2 = (LVector2)value;
|
||||
b.Write(ref vec2);
|
||||
break;
|
||||
case LVector2 value:
|
||||
b.WriteVal(T_VEC2);
|
||||
b.Write(ref value);
|
||||
break;
|
||||
case Vector3 value:
|
||||
b.WriteVal(T_VEC3);
|
||||
var vec3 = (LVector3)value;
|
||||
b.Write(ref vec3);
|
||||
break;
|
||||
case LVector3 value:
|
||||
b.WriteVal(T_VEC3);
|
||||
b.Write(ref value);
|
||||
break;
|
||||
case Quaternion value:
|
||||
b.WriteVal(T_QUAT);
|
||||
var quat = (LQuaternion)value;
|
||||
b.Write(ref quat);
|
||||
break;
|
||||
case LQuaternion 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 LVector3 vec);
|
||||
Args[i] = (Vector3)vec;
|
||||
break;
|
||||
case T_QUAT:
|
||||
r.Read(out LQuaternion quat);
|
||||
Args[i] = (Quaternion)quat;
|
||||
break;
|
||||
case T_MODEL:
|
||||
Args[i] = r.ReadVal<Model>();
|
||||
break;
|
||||
case T_VEC2:
|
||||
r.Read(out LVector2 vec2);
|
||||
Args[i] = (Vector2)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,70 +0,0 @@
|
||||
global using static RageCoop.Core.Shared;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
|
||||
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 JsonSerializerOptions JsonSettings = new();
|
||||
static Shared()
|
||||
{
|
||||
JsonSettings.Converters.Add(new IPAddressConverter());
|
||||
JsonSettings.Converters.Add(new IPEndPointConverter());
|
||||
JsonSettings.WriteIndented = true;
|
||||
JsonSettings.IncludeFields = true;
|
||||
}
|
||||
|
||||
public static object JsonDeserialize(string text, Type type)
|
||||
{
|
||||
return JsonSerializer.Deserialize(text, JsonTypeCheck(type), JsonSettings);
|
||||
}
|
||||
|
||||
public static T JsonDeserialize<T>(string text) => (T)JsonDeserialize(text, typeof(T));
|
||||
|
||||
public static string JsonSerialize(object obj) => JsonSerializer.Serialize(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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal class AnimDic
|
||||
{
|
||||
public string[] Animations;
|
||||
public string DictionaryName;
|
||||
|
||||
public static AnimDic[] Dump(string input, string output)
|
||||
{
|
||||
Console.WriteLine("Generating " + output);
|
||||
if (!File.Exists(input))
|
||||
{
|
||||
Console.WriteLine("Downloading");
|
||||
HttpHelper.DownloadFile(
|
||||
"https://raw.githubusercontent.com/DurtyFree/gta-v-data-dumps/master/animDictsCompact.json", input);
|
||||
}
|
||||
|
||||
Console.WriteLine("Deserializing");
|
||||
var anims = JsonDeserialize<AnimDic[]>(File.ReadAllText(input));
|
||||
Console.WriteLine("Serializing");
|
||||
File.WriteAllText(output, JsonSerialize(anims));
|
||||
return anims;
|
||||
}
|
||||
}
|
||||
|
||||
internal class WeaponInfo
|
||||
{
|
||||
public string Audio;
|
||||
public float Damage;
|
||||
public string DamageType;
|
||||
public string FireType;
|
||||
public bool IsVehicleWeapon;
|
||||
public string Name;
|
||||
public float Speed;
|
||||
|
||||
public WeaponInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public WeaponInfo(XmlNode node)
|
||||
{
|
||||
if (node.Attributes["type"].Value != "CWeaponInfo") throw new Exception("Not a CWeaponInfo node");
|
||||
foreach (XmlNode info in node.ChildNodes)
|
||||
switch (info.Name)
|
||||
{
|
||||
case "Name":
|
||||
Name = info.InnerText;
|
||||
break;
|
||||
case "Audio":
|
||||
Audio = info.InnerText;
|
||||
break;
|
||||
case "FireType":
|
||||
FireType = info.InnerText;
|
||||
break;
|
||||
case "DamageType":
|
||||
DamageType = info.InnerText;
|
||||
break;
|
||||
case "Damage":
|
||||
Damage = info.GetFloat();
|
||||
break;
|
||||
case "Speed":
|
||||
Speed = info.GetFloat();
|
||||
break;
|
||||
}
|
||||
|
||||
IsVehicleWeapon = Name.StartsWith("VEHICLE_WEAPON");
|
||||
}
|
||||
}
|
||||
|
||||
internal class VehicleInfo
|
||||
{
|
||||
public VehicleBone[] Bones;
|
||||
public uint Hash;
|
||||
public string Name;
|
||||
public string[] Weapons;
|
||||
}
|
||||
|
||||
internal class VehicleBone
|
||||
{
|
||||
public uint BoneID;
|
||||
public uint BoneIndex;
|
||||
public string BoneName;
|
||||
}
|
||||
|
||||
internal class WeaponBones
|
||||
{
|
||||
public VehicleBone[] Bones;
|
||||
public string Name;
|
||||
}
|
||||
|
||||
internal class VehicleWeaponInfo
|
||||
{
|
||||
public uint Hash;
|
||||
public string Name;
|
||||
public Dictionary<uint, WeaponBones> Weapons = new();
|
||||
|
||||
public static void Dump(string input, string output)
|
||||
{
|
||||
Console.WriteLine("Generating " + output);
|
||||
if (!File.Exists(input))
|
||||
{
|
||||
Console.WriteLine("Downloading");
|
||||
HttpHelper.DownloadFile(
|
||||
"https://raw.githubusercontent.com/DurtyFree/gta-v-data-dumps/master/vehicles.json", input);
|
||||
}
|
||||
|
||||
Console.WriteLine("Deserializing");
|
||||
var infos = JsonDeserialize<VehicleInfo[]>(File.ReadAllText(input));
|
||||
Console.WriteLine("Serializing");
|
||||
File.WriteAllText(output,
|
||||
JsonSerialize(
|
||||
infos.Select(FromVehicle).Where(x => x != null)));
|
||||
}
|
||||
|
||||
public static VehicleWeaponInfo FromVehicle(VehicleInfo info)
|
||||
{
|
||||
if (info.Weapons.Length == 0) return null;
|
||||
var result = new VehicleWeaponInfo { Hash = info.Hash, Name = info.Name };
|
||||
for (var i = 0; i < info.Weapons.Length; i++)
|
||||
result.Weapons.Add(CoreUtils.JoaatHash(info.Weapons[i])
|
||||
, new WeaponBones
|
||||
{
|
||||
Name = info.Weapons[i],
|
||||
Bones = info.Bones.Where(x =>
|
||||
x.BoneName.StartsWith($"weapon_{i + 1}") && !x.BoneName.EndsWith("rot")).ToArray()
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A worker that constantly execute jobs in a background thread.
|
||||
/// </summary>
|
||||
public class Worker : IDisposable
|
||||
{
|
||||
private readonly SemaphoreSlim _semaphoreSlim;
|
||||
private readonly Thread _workerThread;
|
||||
private readonly ConcurrentQueue<Action> Jobs = new ConcurrentQueue<Action>();
|
||||
private bool _stopping;
|
||||
|
||||
internal Worker(string name, Logger logger, int maxJobs = int.MaxValue)
|
||||
{
|
||||
Name = name;
|
||||
_semaphoreSlim = new SemaphoreSlim(0, maxJobs);
|
||||
_workerThread = new Thread(() =>
|
||||
{
|
||||
while (!_stopping)
|
||||
{
|
||||
IsBusy = false;
|
||||
_semaphoreSlim.Wait();
|
||||
if (Jobs.TryDequeue(out var job))
|
||||
{
|
||||
IsBusy = true;
|
||||
try
|
||||
{
|
||||
job.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error("Error occurred when executing queued job:");
|
||||
logger.Error(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Hmm... that's unexpected.");
|
||||
}
|
||||
}
|
||||
|
||||
IsBusy = false;
|
||||
});
|
||||
_workerThread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the worker
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this worker is busy executing job(s).
|
||||
/// </summary>
|
||||
public bool IsBusy { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Finish current job and stop the worker.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
_semaphoreSlim.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a job to be executed
|
||||
/// </summary>
|
||||
/// <param name="work"></param>
|
||||
public void QueueJob(Action work)
|
||||
{
|
||||
Jobs.Enqueue(work);
|
||||
_semaphoreSlim.Release();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finish current job and stop the worker.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
_stopping = true;
|
||||
QueueJob(() => { });
|
||||
if (_workerThread.IsAlive) _workerThread.Join();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
Dockerfile
Normal file
28
Dockerfile
Normal file
@ -0,0 +1,28 @@
|
||||
# Use the official image as a parent image
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
|
||||
# Use the SDK image to build the app
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
|
||||
WORKDIR /src
|
||||
|
||||
# Copy csproj and restore as distinct layers
|
||||
COPY RageCoop.Server/*.csproj ./RageCoop.Server/
|
||||
COPY libs/*.dll ./libs/
|
||||
|
||||
# Assuming RageCoop.Core is a dependency, if not, you can comment out the next line
|
||||
COPY RageCoop.Core/*.csproj ./RageCoop.Core/
|
||||
|
||||
RUN dotnet restore RageCoop.Server/RageCoop.Server.csproj
|
||||
|
||||
# Copy everything else and build
|
||||
COPY . .
|
||||
WORKDIR /src/RageCoop.Server
|
||||
RUN dotnet publish -c Release -o /app
|
||||
|
||||
# Build runtime image
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=build-env /app .
|
||||
ENTRYPOINT ["dotnet", "RageCoop.Server.dll"]
|
@ -15,7 +15,7 @@ RAGECOOP brings multiplayer experience to the story mode, you can complete missi
|
||||
|
||||
# 👁 Requirements
|
||||
- ScriptHookV
|
||||
- ScriptHookVDotNet 3.5.1 or later
|
||||
- ScriptHookVDotNet 3.6.0 or later
|
||||
- .NET Framework 4.8 Runtime or SDK
|
||||
|
||||
# 📋 Building the project
|
||||
@ -49,7 +49,7 @@ Then run `dotnet build` in the solution directory, built binaries are in the `bi
|
||||
5. Decent compatibility with other mods, set up a private modded server to have some fun!
|
||||
6. Weaponized vehicle sync(WIP).
|
||||
7. Optimization for high-Ping condition, play with friends around the world!
|
||||
8. Powerful scripting API and resource system, easily [add multiplayer functionality to your mod](HTTPS://docs.ragecoop.online).
|
||||
8. Powerful scripting API and resource system, easily [add multiplayer functionality to your mod](HTTPS://docs.ragecoop.com).
|
||||
|
||||
# ⚠ Known issues
|
||||
|
||||
|
128
RageCoop-V.sln
128
RageCoop-V.sln
@ -3,48 +3,22 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31919.166
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Server", "Server\RageCoop.Server.csproj", "{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Server", "RageCoop.Server\RageCoop.Server.csproj", "{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "Core\RageCoop.Core.csproj", "{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "RageCoop.Core\RageCoop.Core.csproj", "{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client", "Client\Scripts\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "RageCoop.Client\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("{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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{70A1F09D-648D-4C8B-8947-E920B1A587A3}"
|
||||
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}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client.Installer", "RageCoop.Client.Installer\RageCoop.Client.Installer.csproj", "{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
API|Any CPU = API|Any CPU
|
||||
API|x64 = API|x64
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.API|Any CPU.ActiveCfg = API|Any CPU
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.API|Any CPU.Build.0 = API|Any CPU
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.API|x64.ActiveCfg = API|Any CPU
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.API|x64.Build.0 = API|Any CPU
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@ -53,10 +27,6 @@ Global
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.API|Any CPU.ActiveCfg = API|Any CPU
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.API|Any CPU.Build.0 = API|Any CPU
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.API|x64.ActiveCfg = API|Any CPU
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.API|x64.Build.0 = API|Any CPU
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@ -65,10 +35,6 @@ Global
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.API|Any CPU.ActiveCfg = API|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.API|Any CPU.Build.0 = API|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.API|x64.ActiveCfg = API|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.API|x64.Build.0 = API|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@ -77,10 +43,6 @@ Global
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.Build.0 = Release|Any CPU
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.API|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.API|Any CPU.Build.0 = Release|Any CPU
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.API|x64.ActiveCfg = Release|Any CPU
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.API|x64.Build.0 = Release|Any CPU
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@ -89,92 +51,10 @@ 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
|
||||
{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
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.API|x64.Build.0 = Debug|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{6387D897-09AF-4464-B440-80438E3BB8D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{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
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681} = {531656CF-7269-488D-B042-741BC96C3941}
|
||||
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B} = {531656CF-7269-488D-B042-741BC96C3941}
|
||||
{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}
|
||||
EndGlobalSection
|
||||
|
@ -1,3 +0,0 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Joaat/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=RAGECOOP/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
@ -1,4 +0,0 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue"><AssemblyExplorer>
|
||||
<Assembly Path="C:\Users\Sardelka\.nuget\packages\sharpziplib\1.4.0\lib\netstandard2.0\ICSharpCode.SharpZipLib.dll" />
|
||||
</AssemblyExplorer></s:String></wpf:ResourceDictionary>
|
@ -1,8 +1,9 @@
|
||||
<Application x:Class="RageCoop.Client.Installer.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:RageCoop.Client.Installer"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
</Application>
|
@ -3,9 +3,9 @@
|
||||
namespace RageCoop.Client.Installer
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
10
RageCoop.Client.Installer/AssemblyInfo.cs
Normal file
10
RageCoop.Client.Installer/AssemblyInfo.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
@ -3,13 +3,13 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:RageCoop.Client.Installer"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="376" Width="617" Background="#FFBABABA">
|
||||
<Grid>
|
||||
<Grid.Background>
|
||||
<ImageBrush ImageSource="/bg.png" Opacity="0.2" />
|
||||
<ImageBrush ImageSource="/bg.png" Opacity="0.2"/>
|
||||
</Grid.Background>
|
||||
<Label x:Name="Status" FontSize="20" Foreground="#FF232323" HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
<Label x:Name="Status" FontSize="20" Foreground="#FF232323" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
</Window>
|
@ -6,16 +6,17 @@ 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;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
|
||||
using Path = System.IO.Path;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client.Installer
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
@ -27,12 +28,13 @@ namespace RageCoop.Client.Installer
|
||||
|
||||
private void Choose()
|
||||
{
|
||||
var od = new OpenFileDialog
|
||||
var od = new OpenFileDialog()
|
||||
{
|
||||
Filter = "GTA 5 executable |GTA5.exe;PlayGTAV.exe",
|
||||
Title = "Select you GTAV executable"
|
||||
};
|
||||
if (od.ShowDialog() ?? !true)
|
||||
if (od.ShowDialog() ?? false == true)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
@ -41,118 +43,124 @@ namespace RageCoop.Client.Installer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("Installation failed: " + ex);
|
||||
MessageBox.Show("Installation failed: " + ex.ToString());
|
||||
Environment.Exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void Install(string root)
|
||||
{
|
||||
UpdateStatus("Checking requirements");
|
||||
var shvPath = Path.Combine(root, "ScriptHookV.dll");
|
||||
var shvdnPath = Path.Combine(root, "ScriptHookVDotNetCore.dll");
|
||||
var shvdnPath = Path.Combine(root, "ScriptHookVDotNet3.dll");
|
||||
var scriptsPath = Path.Combine(root, "Scripts");
|
||||
var installPath = Path.Combine(root, "RageCoop");
|
||||
var legacyPath = Path.Combine(scriptsPath, "RageCoop");
|
||||
if (Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName.StartsWith(installPath))
|
||||
throw new InvalidOperationException(
|
||||
"The installer is not meant to be run in the game folder, please extract the zip to somewhere else and run again.");
|
||||
var lemonPath = Path.Combine(scriptsPath, "LemonUI.SHVDN3.dll");
|
||||
var installPath = Path.Combine(scriptsPath, "RageCoop");
|
||||
if (Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName == installPath)
|
||||
{
|
||||
throw new InvalidOperationException("The installer is not meant to be run in the game folder, please extract the zip to somewhere else and run again.");
|
||||
}
|
||||
Directory.CreateDirectory(installPath);
|
||||
if (!File.Exists(shvPath))
|
||||
{
|
||||
MessageBox.Show("Please install ScriptHookV first!");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(installPath);
|
||||
|
||||
if (!File.Exists(shvdnPath))
|
||||
{
|
||||
MessageBox.Show("Please install ScriptHookVDotNet first!");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
var shvdnVer = GetVer(shvdnPath);
|
||||
if (shvdnVer < new Version(3, 5, 1))
|
||||
if (shvdnVer < new Version(3, 6, 0))
|
||||
{
|
||||
MessageBox.Show("Please update ScriptHookVDotNet to latest version!" +
|
||||
$"\nCurrent version is {shvdnVer}, 3.5.1 or higher is required");
|
||||
$"\nCurrent version is {shvdnVer}, 3.6.0 or higher is required");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
if (File.Exists(lemonPath))
|
||||
{
|
||||
var lemonVer = GetVer(lemonPath);
|
||||
if (lemonVer < new Version(1, 7))
|
||||
{
|
||||
UpdateStatus("Updating LemonUI");
|
||||
File.WriteAllBytes(lemonPath, getLemon());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UpdateStatus("Removing old versions");
|
||||
|
||||
foreach (var f in Directory.GetFiles(scriptsPath, "RageCoop.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (f.EndsWith("RageCoop.Client.Settings.xml")) { continue; }
|
||||
File.Delete(f);
|
||||
}
|
||||
foreach (var f in Directory.GetFiles(installPath, "*.dll", SearchOption.AllDirectories))
|
||||
{
|
||||
File.Delete(f);
|
||||
}
|
||||
|
||||
// <= 1.5 installation check
|
||||
if (Directory.Exists(legacyPath)) Directory.Delete(legacyPath, true);
|
||||
|
||||
foreach (var f in Directory.GetFiles(installPath, "*.dll", SearchOption.AllDirectories)) File.Delete(f);
|
||||
|
||||
if (File.Exists("Scripts/RageCoop.Core.dll") && File.Exists("Scripts/RageCoop.Client.dll") &&
|
||||
File.Exists("Loader/RageCoop.Client.Loader.dll"))
|
||||
if (File.Exists("RageCoop.Core.dll") && File.Exists("RageCoop.Client.dll"))
|
||||
{
|
||||
UpdateStatus("Installing...");
|
||||
CoreUtils.CopyFilesRecursively(new DirectoryInfo(Directory.GetCurrentDirectory()),
|
||||
new DirectoryInfo(installPath));
|
||||
File.Copy("Loader/RageCoop.Client.Loader.dll", Path.Combine(scriptsPath, "RageCoop.Client.Loader.dll"),
|
||||
true);
|
||||
CoreUtils.CopyFilesRecursively(new DirectoryInfo(Directory.GetCurrentDirectory()), new DirectoryInfo(installPath));
|
||||
Finish();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(
|
||||
"Required files are missing, please re-download the installer from official website");
|
||||
throw new Exception("Required files are missing, please re-download the zip from official website");
|
||||
}
|
||||
|
||||
void Finish()
|
||||
{
|
||||
checkKeys:
|
||||
|
||||
checkKeys:
|
||||
UpdateStatus("Checking conflicts");
|
||||
var menyooConfig = Path.Combine(root, @"menyooStuff\menyooConfig.ini");
|
||||
var settingsPath = Path.Combine(root, SettingsPath);
|
||||
ClientSettings settings = null;
|
||||
var settingsPath = Path.Combine(installPath, @"Data\RageCoop.Client.Settings.xml");
|
||||
Settings settings = null;
|
||||
try
|
||||
{
|
||||
settings = Util.ReadSettings(settingsPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
settings = new();
|
||||
settings = new Settings();
|
||||
}
|
||||
|
||||
if (File.Exists(menyooConfig))
|
||||
{
|
||||
var lines = File.ReadAllLines(menyooConfig)
|
||||
.Where(x => !x.StartsWith(";") && x.EndsWith(" = " + (int)settings.MenuKey));
|
||||
var lines = File.ReadAllLines(menyooConfig).Where(x => !x.StartsWith(";") && x.EndsWith(" = " + (int)settings.MenuKey));
|
||||
if (lines.Any())
|
||||
{
|
||||
if (MessageBox.Show("Following menyoo config value will conflict with RAGECOOP menu key\n" +
|
||||
string.Join("\n", lines)
|
||||
+ "\nDo you wish to change the Menu Key?", "Warning!",
|
||||
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
string.Join("\n", lines)
|
||||
+ "\nDo you wish to change the Menu Key?", "Warning!", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
var ae = new AutoResetEvent(false);
|
||||
UpdateStatus("Press the key you wish to change to");
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
KeyDown += (s, e) =>
|
||||
{
|
||||
settings.MenuKey = (GTA.Keys)KeyInterop.VirtualKeyFromKey(e.Key);
|
||||
ae.Set();
|
||||
}));
|
||||
KeyDown += (s, e) =>
|
||||
{
|
||||
settings.MenuKey = (Keys)KeyInterop.VirtualKeyFromKey(e.Key);
|
||||
ae.Set();
|
||||
}));
|
||||
ae.WaitOne();
|
||||
if (!Util.SaveSettings(settingsPath, settings))
|
||||
{
|
||||
MessageBox.Show("Error occurred when saving settings");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
MessageBox.Show("Menu key changed to " + settings.MenuKey);
|
||||
goto checkKeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStatus("Checking ZeroTier");
|
||||
@ -162,29 +170,26 @@ namespace RageCoop.Client.Installer
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (MessageBox.Show(
|
||||
"You can't join ZeroTier server unless ZeroTier is installed, do you want to download and install it?",
|
||||
"Install ZeroTier", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
if (MessageBox.Show("You can't join ZeroTier server unless ZeroTier is installed, do you want to download and install it?", "Install ZeroTier", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
var url = "https://download.zerotier.com/dist/ZeroTier%20One.msi";
|
||||
UpdateStatus("Downloading ZeroTier from " + url);
|
||||
try
|
||||
{
|
||||
HttpHelper.DownloadFile(url, "ZeroTier.msi",
|
||||
p => UpdateStatus("Downloading ZeroTier " + p + "%"));
|
||||
HttpHelper.DownloadFile(url, "ZeroTier.msi", (p) => UpdateStatus("Downloading ZeroTier " + p + "%"));
|
||||
UpdateStatus("Installing ZeroTier");
|
||||
Process.Start("ZeroTier.msi").WaitForExit();
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show("Failed to download ZeroTier, please download it from official website");
|
||||
MessageBox.Show("Failed to download ZeroTier, please download it from officail website");
|
||||
Process.Start(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStatus("Completed!");
|
||||
MessageBox.Show("Installation successful!");
|
||||
MessageBox.Show("Installation sucessful!");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
@ -198,5 +203,11 @@ namespace RageCoop.Client.Installer
|
||||
{
|
||||
return Version.Parse(FileVersionInfo.GetVersionInfo(location).FileVersion);
|
||||
}
|
||||
|
||||
private byte[] getLemon()
|
||||
{
|
||||
return (byte[])Resource.ResourceManager.GetObject("LemonUI_SHVDN3");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
36
RageCoop.Client.Installer/RageCoop.Client.Installer.csproj
Normal file
36
RageCoop.Client.Installer/RageCoop.Client.Installer.csproj
Normal file
@ -0,0 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="bg.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RageCoop.Client\RageCoop.Client.csproj" />
|
||||
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.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>
|
||||
|
||||
</Project>
|
@ -59,5 +59,15 @@ namespace RageCoop.Client.Installer {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] LemonUI_SHVDN3 {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("LemonUI_SHVDN3", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -118,4 +118,7 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="LemonUI_SHVDN3" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\LemonUI.SHVDN3.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
</root>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user