Compare commits
166 Commits
Author | SHA1 | Date | |
---|---|---|---|
1e1259d0a5 | |||
20b326d396 | |||
b7cad3b8f7 | |||
cf8b54a3b5 | |||
edca1e2b98 | |||
89410b8b46 | |||
a9397690e2 | |||
ea678ece94 | |||
f28f23f560 | |||
b2911017d0 | |||
cda4f702a1 | |||
b6a0ae7f4a | |||
b6f3508680 | |||
ad0368b4c0 | |||
b1d67bd1c4 | |||
cda3d37f37 | |||
abed0f146f | |||
bdeaeee293 | |||
b37ce4d0c1 | |||
e3ff62bdc7 | |||
eab6c79e15 | |||
260678c1bc | |||
b2d300784f | |||
8d1d4f5eb7 | |||
25a03c7e27 | |||
27ab63ae7a | |||
9567e97b31 | |||
b10a8b47d5 | |||
1bc274e2d0 | |||
dca3420071 | |||
bc3fc70a95 | |||
eee983dfd2 | |||
fbec69cd71 | |||
615c49e395 | |||
462d68df6b | |||
0224abc9bd | |||
16fe2fe1ab | |||
61c4486551 | |||
17ef088492 | |||
1111879c4d | |||
e9d0b73296 | |||
8eadbf6fee | |||
8ae491d0d5 | |||
3dd125f861 | |||
01b4753dc6 | |||
5a5d78ac27 | |||
a05eedd68e | |||
05304f9461 | |||
fff5ec4534 | |||
cf7641cee0 | |||
a77e0c1fc9 | |||
f374de2064 | |||
8e0620bedb | |||
8bb39b1bfa | |||
2450956fda | |||
56cd17401b | |||
a53b514fec | |||
de93af73f2 | |||
44f106fd0c | |||
8ac5cc40c1 | |||
8d7eebd390 | |||
0a5bb67c54 | |||
f866151cc5 | |||
08e17b1714 | |||
fa96f4c073 | |||
dc5cf2b965 | |||
f47f0570f9 | |||
e0c96cc200 | |||
17e1ae9ea9 | |||
8401d616e0 | |||
d8ac486984 | |||
335ea2ca38 | |||
c73ff96690 | |||
15b23e3a4e | |||
47f86c098d | |||
6c2c7e881f | |||
74a2265200 | |||
0b2ec1d626 | |||
8ee188cf19 | |||
eb5b23ae17 | |||
77999fe8f3 | |||
64fda51917 | |||
4165b757a5 | |||
d31799ab7b | |||
8a46bd6b68 | |||
58362c2613 | |||
7d6c5fe3bd | |||
f9b9d78b79 | |||
7670af88a2 | |||
0800361b40 | |||
cdf3f41127 | |||
552bf8d61c | |||
2f58e48f42 | |||
67035f5d2f | |||
b48206d942 | |||
87d8102a1a | |||
bd1f0e1713 | |||
04c9081851 | |||
9cf23cac7f | |||
46f75c5a21 | |||
6167417bf8 | |||
82ab9237f5 | |||
82e7cdd785 | |||
41385abc7d | |||
84171e2949 | |||
42c038d8ab | |||
fe6b42e509 | |||
8bc5c11030 | |||
76f7157749 | |||
52a242c72a | |||
8a42f7c626 | |||
6e2c0a3037 | |||
1b77234cfe | |||
a15785a02f | |||
1a5f594b36 | |||
68b6295126 | |||
17d6d86194 | |||
f82134fb12 | |||
37edef425c | |||
30f7a281b0 | |||
e1606889f7 | |||
33244b930f | |||
212c004d31 | |||
190f23240a | |||
376830d6dc | |||
9a9fb2870c | |||
2a093d1da0 | |||
1a66f14bae | |||
887e7f428d | |||
cc4f5f46ce | |||
13883242e4 | |||
09a97c43fd | |||
671b4f9f6f | |||
1ae913f0d2 | |||
45f487c9f4 | |||
7f1891236d | |||
1c981c331c | |||
0c9213936d | |||
188556991f | |||
b6450027b8 | |||
82cffea6ed | |||
734788f310 | |||
45c3ff83cf | |||
c7655573aa | |||
88a4f046d4 | |||
7ccccabe1d | |||
3d6dbd5f2a | |||
9407d80858 | |||
51eaf4f56d | |||
4b5ec37987 | |||
3f246a8f7d | |||
7f4833be81 | |||
755615a887 | |||
ef32603801 | |||
fa80b87f7a | |||
f8b7062c61 | |||
95c4c42ff0 | |||
13fa206594 | |||
a1dda2ffa4 | |||
0914afc4ed | |||
9f2cf2eb03 | |||
1607b7025c | |||
2c0b7d4d64 | |||
62dc98f4a7 | |||
d061b5ecbe | |||
546f4b16f4 |
99
.github/workflows/nightly-build.yaml
vendored
Normal file
99
.github/workflows/nightly-build.yaml
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
name: Nightly-build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
dotnet-version: ['6.0.x']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: ${{ matrix.dotnet-version }}
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build client
|
||||
run: dotnet publish RageCoop.Client/RageCoop.Client.csproj --no-restore --configuration Release -o RageCoop.Client/bin/RageCoop -f net48
|
||||
- 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 RageCoop.Server/bin/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 RageCoop.Server/bin/linux-x64 -c Release
|
||||
- name: Build server linux-arm
|
||||
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r linux-arm -o RageCoop.Server/bin/linux-arm -c Release
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: RageCoop.Client/bin
|
||||
dest: RageCoop.Client.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: RageCoop.Server/bin/win-x64
|
||||
dest: RageCoop.Server-win-x64.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: RageCoop.Server/bin/linux-x64
|
||||
dest: RageCoop.Server-linux-x64.zip
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: RageCoop.Server/bin/linux-arm
|
||||
dest: RageCoop.Server-linux-arm.zip
|
||||
|
||||
- uses: WebFreak001/deploy-nightly@v1.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
|
||||
release_id: 70603992
|
||||
asset_path: RageCoop.Client.zip
|
||||
asset_name: RageCoop.Client.zip
|
||||
asset_content_type: application/zip
|
||||
max_releases: 7
|
||||
|
||||
- uses: WebFreak001/deploy-nightly@v1.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
|
||||
release_id: 70603992
|
||||
asset_path: RageCoop.Server-win-x64.zip
|
||||
asset_name: RageCoop.Server-win-x64.zip
|
||||
asset_content_type: application/zip
|
||||
max_releases: 7
|
||||
|
||||
- uses: WebFreak001/deploy-nightly@v1.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
|
||||
release_id: 70603992
|
||||
asset_path: RageCoop.Server-linux-x64.zip
|
||||
asset_name: RageCoop.Server-linux-x64.zip
|
||||
asset_content_type: application/zip
|
||||
max_releases: 7
|
||||
|
||||
|
||||
- uses: WebFreak001/deploy-nightly@v1.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
|
||||
release_id: 70603992
|
||||
asset_path: RageCoop.Server-linux-arm.zip
|
||||
asset_name: RageCoop.Server-linux-arm.zip
|
||||
asset_content_type: application/zip
|
||||
max_releases: 7
|
||||
- uses: actions/checkout@v2
|
||||
|
@ -1,192 +0,0 @@
|
||||
#undef DEBUG
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static class COOPAPI
|
||||
{
|
||||
|
||||
#region DELEGATES
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
/// <param name="connected"></param>
|
||||
/// <param name="from">The player's id</param>
|
||||
/// <param name="reason"></param>
|
||||
public delegate void ConnectEvent(bool connected, int from, string reason = null);
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="message">The Lidgren-Network net handle</param>
|
||||
/// <param name="args"></param>
|
||||
public delegate void ChatMessage(string from, string message, CancelEventArgs args);
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
/// <param name="from">The Lidgren-Network net handle</param>
|
||||
/// <param name="mod"></param>
|
||||
/// <param name="customID"></param>
|
||||
/// <param name="bytes"></param>
|
||||
public delegate void ModEvent(long from, string mod, byte customID, byte[] bytes);
|
||||
#endregion
|
||||
|
||||
#region EVENTS
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static event ConnectEvent OnConnection;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static event ChatMessage OnChatMessage;
|
||||
|
||||
public static void Connected()
|
||||
{
|
||||
OnConnection?.Invoke(true, GetPlayerID());
|
||||
}
|
||||
|
||||
public static void Disconnected(string reason)
|
||||
{
|
||||
OnConnection?.Invoke(false, GetPlayerID(), reason);
|
||||
}
|
||||
|
||||
public static void Connected(int playerID)
|
||||
{
|
||||
OnConnection?.Invoke(true, playerID);
|
||||
}
|
||||
|
||||
public static void Disconnected(int playerID)
|
||||
{
|
||||
OnConnection?.Invoke(false, playerID);
|
||||
}
|
||||
|
||||
public static bool ChatMessageReceived(string from, string message)
|
||||
{
|
||||
CancelEventArgs args = new CancelEventArgs(false);
|
||||
OnChatMessage?.Invoke(from, message, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Send a local chat message to this player
|
||||
/// </summary>
|
||||
/// <param name="from">Username of the player who sent this message</param>
|
||||
/// <param name="message">The player's message</param>
|
||||
public static void LocalChatMessage(string from, string message)
|
||||
{
|
||||
Main.MainChat.AddMessage(from, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to any server
|
||||
/// </summary>
|
||||
/// <param name="serverAddress">The server address to connect. Example: 127.0.0.1:4499</param>
|
||||
public static void Connect(string serverAddress)
|
||||
{
|
||||
Networking.ToggleConnection(serverAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static void Disconnect()
|
||||
{
|
||||
Networking.ToggleConnection(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the player is already on a server
|
||||
/// </summary>
|
||||
public static bool IsOnServer()
|
||||
{
|
||||
return Networking.IsOnServer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the local player's ID
|
||||
/// </summary>
|
||||
/// <returns>PlayerID</returns>
|
||||
public static int GetPlayerID()
|
||||
{
|
||||
return Main.LocalPlayerID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a RAGECOOP menu is visible
|
||||
/// </summary>
|
||||
public static bool IsMenuVisible()
|
||||
{
|
||||
#if NON_INTERACTIVE
|
||||
return false;
|
||||
#else
|
||||
return Menus.CoopMenu.MenuPool.AreAnyVisible;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the RAGECOOP chat is visible
|
||||
/// </summary>
|
||||
public static bool IsChatFocused()
|
||||
{
|
||||
return Main.MainChat.Focused;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the RAGECOOP list of players is visible
|
||||
/// </summary>
|
||||
public static bool IsPlayerListVisible()
|
||||
{
|
||||
return Util.GetTickCount64() - PlayerList.Pressed < 5000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version of RAGECOOP
|
||||
/// </summary>
|
||||
public static string GetCurrentVersion()
|
||||
{
|
||||
return Main.CurrentVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get that player's local username
|
||||
/// </summary>
|
||||
public static string GetUsername()
|
||||
{
|
||||
return Main.Settings.Username;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a new username for this player
|
||||
/// </summary>
|
||||
/// <param name="username">The new username</param>
|
||||
/// <returns>false if the player already joined a server or the username is null or empty otherwise true</returns>
|
||||
public static bool SetUsername(string username)
|
||||
{
|
||||
if (IsOnServer() || string.IsNullOrEmpty(username))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Main.Settings.Username = username;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the client's settings.
|
||||
/// </summary>
|
||||
/// <returns>The client's settings, you should NEVER change settings without notifying the player.</returns>
|
||||
public static Settings Settings()
|
||||
{
|
||||
return Main.Settings;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class DownloadManager
|
||||
{
|
||||
private static readonly List<DownloadFile> _downloadFiles = new List<DownloadFile>();
|
||||
private static readonly Dictionary<byte, FileStream> _streams = new Dictionary<byte, FileStream>();
|
||||
private static readonly List<byte> _filesFinished = new List<byte>();
|
||||
public static bool DownloadComplete = false;
|
||||
|
||||
public static void AddFile(byte id, string name, long length)
|
||||
{
|
||||
string downloadFolder = $"Scripts\\RageCoop\\Resources\\{Main.Settings.LastServerAddress.Replace(":", ".")}";
|
||||
|
||||
if (!Directory.Exists(downloadFolder))
|
||||
{
|
||||
Directory.CreateDirectory(downloadFolder);
|
||||
}
|
||||
|
||||
if (FileAlreadyExists(downloadFolder, name, length))
|
||||
{
|
||||
// Send the server we are already done
|
||||
Networking.SendDownloadFinish(id);
|
||||
|
||||
Cancel(id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!new string[] { ".js", ".xml" }.Any(x => x == Path.GetExtension(name)))
|
||||
{
|
||||
Cancel(id);
|
||||
|
||||
GTA.UI.Notification.Show($"The download of a file from the server was blocked! [{name}]", true);
|
||||
Main.Logger.Error($"The download of a file from the server was blocked! [{name}]");
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_downloadFiles)
|
||||
{
|
||||
_downloadFiles.Add(new DownloadFile()
|
||||
{
|
||||
FileID = id,
|
||||
FileName = name,
|
||||
FileLength = length
|
||||
});
|
||||
}
|
||||
|
||||
lock (_streams)
|
||||
{
|
||||
_streams.Add(id, new FileStream($"{downloadFolder}\\{name}", FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the file already exists and if the size correct otherwise delete this file
|
||||
/// </summary>
|
||||
/// <param name="folder"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
private static bool FileAlreadyExists(string folder, string name, long length)
|
||||
{
|
||||
string filePath = $"{folder}\\{name}";
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
if (new FileInfo(filePath).Length == length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete the file because the length is wrong (maybe the file was updated)
|
||||
File.Delete(filePath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void RenderProgress()
|
||||
{
|
||||
if (_downloadFiles.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_downloadFiles) lock (_filesFinished)
|
||||
{
|
||||
new LemonUI.Elements.ScaledText(new System.Drawing.PointF(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width / 2, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height - 60), $"Downloading files {_filesFinished.Count()} / {_downloadFiles.Count() + _filesFinished.Count()}", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Write(byte id, byte[] chunk)
|
||||
{
|
||||
lock (_filesFinished)
|
||||
{
|
||||
if (_filesFinished.Contains(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lock (_streams)
|
||||
{
|
||||
FileStream fs = _streams.FirstOrDefault(x => x.Key == id).Value;
|
||||
if (fs == null)
|
||||
{
|
||||
Main.Logger.Error($"Stream for file {id} not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
fs.Write(chunk, 0, chunk.Length);
|
||||
|
||||
lock (_downloadFiles)
|
||||
{
|
||||
DownloadFile file = _downloadFiles.FirstOrDefault(x => x.FileID == id);
|
||||
if (file == null)
|
||||
{
|
||||
Main.Logger.Error($"File {id} couldn't be found in the list!");
|
||||
return;
|
||||
}
|
||||
|
||||
file.FileWritten += chunk.Length;
|
||||
|
||||
if (file.FileWritten >= file.FileLength)
|
||||
{
|
||||
Cancel(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cancel(byte id)
|
||||
{
|
||||
lock (_streams) lock (_downloadFiles) lock (_filesFinished)
|
||||
{
|
||||
FileStream fs = _streams.ContainsKey(id) ? _streams[id] : null;
|
||||
if (fs != null)
|
||||
{
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
|
||||
_streams.Remove(id);
|
||||
}
|
||||
|
||||
if (_downloadFiles.Any(x => x.FileID == id))
|
||||
{
|
||||
_downloadFiles.Remove(_downloadFiles.First(x => x.FileID == id));
|
||||
}
|
||||
|
||||
_filesFinished.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cleanup(bool everything)
|
||||
{
|
||||
lock (_streams) lock (_downloadFiles) lock (_filesFinished)
|
||||
{
|
||||
foreach (KeyValuePair<byte, FileStream> stream in _streams)
|
||||
{
|
||||
stream.Value.Close();
|
||||
stream.Value.Dispose();
|
||||
}
|
||||
_streams.Clear();
|
||||
_downloadFiles.Clear();
|
||||
_filesFinished.Clear();
|
||||
}
|
||||
|
||||
if (everything)
|
||||
{
|
||||
DownloadComplete = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DownloadFile
|
||||
{
|
||||
public byte FileID { get; set; } = 0;
|
||||
public string FileName { get; set; } = string.Empty;
|
||||
public long FileLength { get; set; } = 0;
|
||||
public long FileWritten { get; set; } = 0;
|
||||
}
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using RageCoop.Core;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[XmlRoot(ElementName = "Map")]
|
||||
public class CoopMap
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[XmlArray("Props")]
|
||||
[XmlArrayItem("Prop")]
|
||||
public List<CoopProp> Props { get; set; } = new List<CoopProp>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public struct CoopProp
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Vector3 Position { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Vector3 Rotation { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int Hash { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Dynamic { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int Texture { get; set; }
|
||||
}
|
||||
|
||||
public static class MapLoader
|
||||
{
|
||||
// string = file name
|
||||
private static readonly Dictionary<string, CoopMap> _maps = new Dictionary<string, CoopMap>();
|
||||
private static readonly List<int> _createdObjects = new List<int>();
|
||||
|
||||
public static void LoadAll()
|
||||
{
|
||||
string downloadFolder = $"Scripts\\RageCoop\\Resources\\{Main.Settings.LastServerAddress.Replace(":", ".")}";
|
||||
|
||||
if (!Directory.Exists(downloadFolder))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(downloadFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error(ex.Message);
|
||||
|
||||
// Without the directory we can't do the other stuff
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string[] files = Directory.GetFiles(downloadFolder, "*.xml");
|
||||
lock (_maps)
|
||||
{
|
||||
for (int i = 0; i < files.Length; i++)
|
||||
{
|
||||
string filePath = files[i];
|
||||
string fileName = Path.GetFileName(filePath);
|
||||
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(CoopMap));
|
||||
CoopMap map;
|
||||
|
||||
using (var stream = new FileStream(filePath, FileMode.Open))
|
||||
{
|
||||
try
|
||||
{
|
||||
map = (CoopMap)serializer.Deserialize(stream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error($"The map with the name \"{fileName}\" couldn't be added!");
|
||||
Main.Logger.Error($"{ex.Message}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
_maps.Add(fileName, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadMap(string name)
|
||||
{
|
||||
lock (_maps) lock (_createdObjects)
|
||||
{
|
||||
if (!_maps.ContainsKey(name) || _createdObjects.Count != 0)
|
||||
{
|
||||
GTA.UI.Notification.Show($"The map with the name \"{name}\" couldn't be loaded!");
|
||||
Main.Logger.Error($"The map with the name \"{name}\" couldn't be loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
CoopMap map = _maps[name];
|
||||
|
||||
foreach (CoopProp prop in map.Props)
|
||||
{
|
||||
Model model = prop.Hash.ModelRequest();
|
||||
if (model == null)
|
||||
{
|
||||
Main.Logger.Error($"Model for object \"{model.Hash}\" couldn't be loaded!");
|
||||
continue;
|
||||
}
|
||||
|
||||
int handle = Function.Call<int>(Hash.CREATE_OBJECT, model.Hash, prop.Position.X, prop.Position.Y, prop.Position.Z, 1, 1, prop.Dynamic);
|
||||
model.MarkAsNoLongerNeeded();
|
||||
if (handle == 0)
|
||||
{
|
||||
Main.Logger.Error($"Object \"{prop.Hash}\" couldn't be created!");
|
||||
continue;
|
||||
}
|
||||
|
||||
_createdObjects.Add(handle);
|
||||
|
||||
if (prop.Texture > 0 && prop.Texture < 16)
|
||||
{
|
||||
Function.Call(Hash._SET_OBJECT_TEXTURE_VARIATION, handle, prop.Texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool AnyMapLoaded()
|
||||
{
|
||||
lock (_createdObjects) return _createdObjects.Any();
|
||||
}
|
||||
|
||||
public static void UnloadMap()
|
||||
{
|
||||
lock (_createdObjects)
|
||||
{
|
||||
foreach (int handle in _createdObjects)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
int tmpHandle = handle;
|
||||
Function.Call(Hash.DELETE_OBJECT, &tmpHandle);
|
||||
}
|
||||
}
|
||||
|
||||
_createdObjects.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteAll()
|
||||
{
|
||||
UnloadMap();
|
||||
lock (_maps)
|
||||
{
|
||||
_maps.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,228 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Core;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static partial class Networking
|
||||
{
|
||||
public static NetClient Client;
|
||||
public static float Latency = 0;
|
||||
public static bool ShowNetworkInfo = false;
|
||||
public static int BytesReceived = 0;
|
||||
public static int BytesSend = 0;
|
||||
private static Thread ReceiveThread;
|
||||
|
||||
public static void ToggleConnection(string address)
|
||||
{
|
||||
if (IsOnServer)
|
||||
{
|
||||
Client.Disconnect("Bye!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0")
|
||||
{
|
||||
AutoFlushSendQueue = false
|
||||
};
|
||||
|
||||
config.EnableMessageType(NetIncomingMessageType.ConnectionLatencyUpdated);
|
||||
|
||||
Client = new NetClient(config);
|
||||
|
||||
Client.Start();
|
||||
|
||||
string[] ip = new string[2];
|
||||
|
||||
int idx = address.LastIndexOf(':');
|
||||
if (idx != -1)
|
||||
{
|
||||
ip[0] = address.Substring(0, idx);
|
||||
ip[1] = address.Substring(idx + 1);
|
||||
}
|
||||
|
||||
if (ip.Length != 2)
|
||||
{
|
||||
throw new Exception("Malformed URL");
|
||||
}
|
||||
|
||||
// Send HandshakePacket
|
||||
EntityPool.AddPlayer();
|
||||
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
|
||||
new Packets.Handshake()
|
||||
{
|
||||
PedID = Main.LocalPlayerID,
|
||||
Username = Main.Settings.Username,
|
||||
ModVersion = Main.CurrentVersion,
|
||||
}.Pack(outgoingMessage);
|
||||
|
||||
Client.Connect(ip[0], short.Parse(ip[1]), outgoingMessage);
|
||||
}
|
||||
}
|
||||
public static bool IsOnServer
|
||||
{
|
||||
get { return Client?.ConnectionStatus == NetConnectionStatus.Connected; }
|
||||
}
|
||||
public static void Start()
|
||||
{
|
||||
ReceiveThread=new Thread(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Client!=null)
|
||||
{
|
||||
Client.FlushSendQueue();
|
||||
ReceiveMessages();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error(ex);
|
||||
}
|
||||
Thread.Sleep(5);
|
||||
}
|
||||
});
|
||||
ReceiveThread.Start();
|
||||
}
|
||||
|
||||
#region -- GET --
|
||||
#region -- PLAYER --
|
||||
private static void PlayerConnect(Packets.PlayerConnect packet)
|
||||
{
|
||||
var p = new PlayerData
|
||||
{
|
||||
PedID = packet.PedID,
|
||||
Username= packet.Username,
|
||||
};
|
||||
GTA.UI.Notification.Show($"{p.Username} connected.");
|
||||
PlayerList.SetPlayer(packet.PedID, packet.Username);
|
||||
|
||||
Main.Logger.Debug($"player connected:{p.Username}");
|
||||
Main.DumpCharacters();
|
||||
COOPAPI.Connected(packet.PedID);
|
||||
}
|
||||
private static void PlayerDisconnect(Packets.PlayerDisconnect packet)
|
||||
{
|
||||
var name=PlayerList.GetPlayer(packet.PedID).Username;
|
||||
GTA.UI.Notification.Show($"{name} left.");
|
||||
COOPAPI.Disconnected(packet.PedID);
|
||||
PlayerList.RemovePlayer(packet.PedID);
|
||||
EntityPool.RemoveAllFromPlayer(packet.PedID);
|
||||
|
||||
|
||||
}
|
||||
private static object DecodeNativeCall(ulong hash, List<object> args, bool returnValue, byte? returnType = null)
|
||||
{
|
||||
List<InputArgument> arguments = new List<InputArgument>();
|
||||
|
||||
if (args == null || args.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (ushort i = 0; i < args.Count; i++)
|
||||
{
|
||||
object x = args.ElementAt(i);
|
||||
switch (x)
|
||||
{
|
||||
case int _:
|
||||
arguments.Add((int)x);
|
||||
break;
|
||||
case bool _:
|
||||
arguments.Add((bool)x);
|
||||
break;
|
||||
case float _:
|
||||
arguments.Add((float)x);
|
||||
break;
|
||||
case string _:
|
||||
arguments.Add((string)x);
|
||||
break;
|
||||
case Vector3 _:
|
||||
Vector3 vector = (Vector3)x;
|
||||
arguments.Add((float)vector.X);
|
||||
arguments.Add((float)vector.Y);
|
||||
arguments.Add((float)vector.Z);
|
||||
break;
|
||||
default:
|
||||
GTA.UI.Notification.Show("[DecodeNativeCall][" + hash + "]: Type of argument not found!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!returnValue)
|
||||
{
|
||||
Function.Call((Hash)hash, arguments.ToArray());
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (returnType.Value)
|
||||
{
|
||||
case 0x00: // int
|
||||
return Function.Call<int>((Hash)hash, arguments.ToArray());
|
||||
case 0x01: // bool
|
||||
return Function.Call<bool>((Hash)hash, arguments.ToArray());
|
||||
case 0x02: // float
|
||||
return Function.Call<float>((Hash)hash, arguments.ToArray());
|
||||
case 0x03: // string
|
||||
return Function.Call<string>((Hash)hash, arguments.ToArray());
|
||||
case 0x04: // vector3
|
||||
return Function.Call<Vector3>((Hash)hash, arguments.ToArray());
|
||||
default:
|
||||
GTA.UI.Notification.Show("[DecodeNativeCall][" + hash + "]: Type of return not found!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeNativeResponse(Packets.NativeResponse packet)
|
||||
{
|
||||
object result = DecodeNativeCall(packet.Hash, packet.Args, true, packet.ResultType);
|
||||
|
||||
if (Main.CheckNativeHash.ContainsKey(packet.Hash))
|
||||
{
|
||||
foreach (KeyValuePair<ulong, byte> hash in Main.CheckNativeHash)
|
||||
{
|
||||
if (hash.Key == packet.Hash)
|
||||
{
|
||||
lock (Main.ServerItems)
|
||||
{
|
||||
Main.ServerItems.Add((int)result, hash.Value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
|
||||
new Packets.NativeResponse()
|
||||
{
|
||||
Hash = 0,
|
||||
Args = new List<object>() { result },
|
||||
ID = packet.ID
|
||||
}.Pack(outgoingMessage);
|
||||
Client.SendMessage(outgoingMessage, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Native);
|
||||
Client.FlushSendQueue();
|
||||
}
|
||||
#endregion // -- PLAYER --
|
||||
|
||||
#endregion
|
||||
public static void Tick()
|
||||
{
|
||||
|
||||
|
||||
|
||||
// Sync
|
||||
EntityPool.DoSync();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,423 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Core;
|
||||
using GTA;
|
||||
using RageCoop.Client.Menus;
|
||||
using GTA.Math;
|
||||
using GTA.Native;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static partial class Networking
|
||||
{
|
||||
public static void ReceiveMessages()
|
||||
{
|
||||
if (Client == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NetIncomingMessage message;
|
||||
|
||||
while ((message = Client.ReadMessage()) != null)
|
||||
{
|
||||
BytesReceived += message.LengthBytes;
|
||||
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
|
||||
|
||||
string reason = message.ReadString();
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
#if !NON_INTERACTIVE
|
||||
CoopMenu.InitiateConnectionMenuSetting();
|
||||
#endif
|
||||
Main.QueueAction(() => { GTA.UI.Notification.Show("~y~Trying to connect..."); return true; });
|
||||
break;
|
||||
case NetConnectionStatus.Connected:
|
||||
if (message.SenderConnection.RemoteHailMessage.ReadByte() != (byte)PacketTypes.Handshake)
|
||||
{
|
||||
Client.Disconnect("Wrong packet!");
|
||||
}
|
||||
else
|
||||
{
|
||||
int len = message.SenderConnection.RemoteHailMessage.ReadInt32();
|
||||
byte[] data = message.SenderConnection.RemoteHailMessage.ReadBytes(len);
|
||||
|
||||
Packets.Handshake handshakePacket = new Packets.Handshake();
|
||||
handshakePacket.Unpack(data);
|
||||
|
||||
#if !NON_INTERACTIVE
|
||||
|
||||
#endif
|
||||
|
||||
COOPAPI.Connected();
|
||||
Main.QueueAction(() => {
|
||||
CoopMenu.ConnectedMenuSetting();
|
||||
Main.MainChat.Init();
|
||||
PlayerList.Cleanup();
|
||||
GTA.UI.Notification.Show("~g~Connected!");
|
||||
});
|
||||
|
||||
Main.Logger.Info(">> Connected <<");
|
||||
}
|
||||
break;
|
||||
case NetConnectionStatus.Disconnected:
|
||||
DownloadManager.Cleanup(true);
|
||||
|
||||
// Reset all values
|
||||
Latency = 0;
|
||||
|
||||
Main.QueueAction(() => Main.CleanUpWorld());
|
||||
|
||||
if (Main.MainChat.Focused)
|
||||
{
|
||||
Main.MainChat.Focused = false;
|
||||
}
|
||||
|
||||
Main.QueueAction(() => Main.CleanUp());
|
||||
|
||||
#if !NON_INTERACTIVE
|
||||
CoopMenu.DisconnectedMenuSetting();
|
||||
#endif
|
||||
|
||||
COOPAPI.Disconnected(reason);
|
||||
Main.QueueAction(() =>
|
||||
GTA.UI.Notification.Show("~r~Disconnected: " + reason));
|
||||
|
||||
MapLoader.DeleteAll();
|
||||
|
||||
Main.Logger.Info($">> Disconnected << reason: {reason}");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NetIncomingMessageType.Data:
|
||||
if (message.LengthBytes==0) { continue; }
|
||||
|
||||
var packetType = (PacketTypes)message.ReadByte();
|
||||
try
|
||||
{
|
||||
|
||||
int len = message.ReadInt32();
|
||||
byte[] data = message.ReadBytes(len);
|
||||
switch (packetType)
|
||||
{
|
||||
case PacketTypes.CleanUpWorld:
|
||||
{
|
||||
Main.QueueAction(() => { Main.CleanUpWorld(); return true; });
|
||||
}
|
||||
break;
|
||||
case PacketTypes.PlayerConnect:
|
||||
{
|
||||
|
||||
Packets.PlayerConnect packet = new Packets.PlayerConnect();
|
||||
packet.Unpack(data);
|
||||
|
||||
Main.QueueAction(() =>PlayerConnect(packet));
|
||||
}
|
||||
break;
|
||||
case PacketTypes.PlayerDisconnect:
|
||||
{
|
||||
|
||||
Packets.PlayerDisconnect packet = new Packets.PlayerDisconnect();
|
||||
packet.Unpack(data);
|
||||
Main.QueueAction(() =>PlayerDisconnect(packet));
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.PlayerInfoUpdate:
|
||||
{
|
||||
var packet = new Packets.PlayerInfoUpdate();
|
||||
packet.Unpack(data);
|
||||
PlayerList.SetPlayer(packet.PedID,packet.Username,packet.Latency);
|
||||
break;
|
||||
}
|
||||
#region ENTITY SYNC
|
||||
case PacketTypes.VehicleSync:
|
||||
{
|
||||
|
||||
Packets.VehicleSync packet = new Packets.VehicleSync();
|
||||
packet.Unpack(data);
|
||||
VehicleSync(packet);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.PedSync:
|
||||
{
|
||||
|
||||
Packets.PedSync packet = new Packets.PedSync();
|
||||
packet.Unpack(data);
|
||||
PedSync(packet);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.VehicleStateSync:
|
||||
{
|
||||
|
||||
Packets.VehicleStateSync packet = new Packets.VehicleStateSync();
|
||||
packet.Unpack(data);
|
||||
VehicleStateSync(packet);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.PedStateSync:
|
||||
{
|
||||
|
||||
|
||||
Packets.PedStateSync packet = new Packets.PedStateSync();
|
||||
packet.Unpack(data);
|
||||
PedStateSync(packet);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.ProjectileSync:
|
||||
{
|
||||
Packets.ProjectileSync packet = new Packets.ProjectileSync();
|
||||
packet.Unpack(data);
|
||||
ProjectileSync(packet);
|
||||
break;
|
||||
}
|
||||
#endregion
|
||||
case PacketTypes.ChatMessage:
|
||||
{
|
||||
|
||||
Packets.ChatMessage packet = new Packets.ChatMessage();
|
||||
packet.Unpack(data);
|
||||
|
||||
if (!COOPAPI.ChatMessageReceived(packet.Username, packet.Message))
|
||||
{
|
||||
Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message);return true; });
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.NativeCall:
|
||||
{
|
||||
|
||||
Packets.NativeCall packet = new Packets.NativeCall();
|
||||
packet.Unpack(data);
|
||||
|
||||
DecodeNativeCall(packet.Hash, packet.Args, false);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.NativeResponse:
|
||||
{
|
||||
|
||||
Packets.NativeResponse packet = new Packets.NativeResponse();
|
||||
packet.Unpack(data);
|
||||
|
||||
DecodeNativeResponse(packet);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.Mod:
|
||||
{
|
||||
Packets.Mod packet = new Packets.Mod();
|
||||
packet.Unpack(data);
|
||||
// Need to do some stuff here
|
||||
}
|
||||
break;
|
||||
case PacketTypes.FileTransferTick:
|
||||
{
|
||||
Packets.FileTransferTick packet = new Packets.FileTransferTick();
|
||||
packet.Unpack(data);
|
||||
|
||||
DownloadManager.Write(packet.ID, packet.FileChunk);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.FileTransferRequest:
|
||||
{
|
||||
Packets.FileTransferRequest packet = new Packets.FileTransferRequest();
|
||||
packet.Unpack(data);
|
||||
|
||||
DownloadManager.AddFile(packet.ID, packet.FileName, packet.FileLength);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.FileTransferComplete:
|
||||
{
|
||||
Packets.FileTransferComplete packet = new Packets.FileTransferComplete();
|
||||
packet.Unpack(data);
|
||||
|
||||
DownloadManager.Cleanup(false);
|
||||
DownloadManager.DownloadComplete = true;
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.ServerClientEvent:
|
||||
{
|
||||
Packets.ServerClientEvent packet = new Packets.ServerClientEvent();
|
||||
packet.Unpack(data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (packetType.IsSyncEvent())
|
||||
{
|
||||
// Dispatch to main thread
|
||||
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, data); return true; });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.QueueAction(() => {
|
||||
GTA.UI.Notification.Show("~r~~h~Packet Error");
|
||||
return true;
|
||||
});
|
||||
Main.Logger.Error($"[{packetType}] {ex.Message}");
|
||||
Main.Logger.Error(ex);
|
||||
Client.Disconnect($"Packet Error [{packetType}]");
|
||||
}
|
||||
break;
|
||||
case NetIncomingMessageType.ConnectionLatencyUpdated:
|
||||
Latency = message.ReadFloat();
|
||||
break;
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
#if DEBUG
|
||||
// TODO?
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Client.Recycle(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void PedSync(Packets.PedSync packet)
|
||||
{
|
||||
SyncedPed c = EntityPool.GetPedByID(packet.ID);
|
||||
if (c==null)
|
||||
{
|
||||
Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}");
|
||||
EntityPool.ThreadSafe.Add(c=new SyncedPed(packet.ID));
|
||||
}
|
||||
PedDataFlags flags = packet.Flag;
|
||||
c.ID=packet.ID;
|
||||
//c.OwnerID=packet.OwnerID;
|
||||
c.Health = packet.Health;
|
||||
c.Position = packet.Position;
|
||||
c.Rotation = packet.Rotation;
|
||||
c.Velocity = packet.Velocity;
|
||||
c.Speed = packet.Speed;
|
||||
c.CurrentWeaponHash = packet.CurrentWeaponHash;
|
||||
c.IsAiming = flags.HasFlag(PedDataFlags.IsAiming);
|
||||
c.IsReloading = flags.HasFlag(PedDataFlags.IsReloading);
|
||||
c.IsJumping = flags.HasFlag(PedDataFlags.IsJumping);
|
||||
c.IsRagdoll = flags.HasFlag(PedDataFlags.IsRagdoll);
|
||||
c.IsOnFire = flags.HasFlag(PedDataFlags.IsOnFire);
|
||||
c.IsInParachuteFreeFall = flags.HasFlag(PedDataFlags.IsInParachuteFreeFall);
|
||||
c.IsParachuteOpen = flags.HasFlag(PedDataFlags.IsParachuteOpen);
|
||||
c.IsOnLadder = flags.HasFlag(PedDataFlags.IsOnLadder);
|
||||
c.IsVaulting = flags.HasFlag(PedDataFlags.IsVaulting);
|
||||
c.IsInCover = flags.HasFlag(PedDataFlags.IsInCover);
|
||||
c.Heading=packet.Heading;
|
||||
c.LastSynced = Main.Ticked;
|
||||
if (c.IsAiming)
|
||||
{
|
||||
c.AimCoords = packet.AimCoords;
|
||||
}
|
||||
if (c.IsRagdoll)
|
||||
{
|
||||
c.RotationVelocity=packet.RotationVelocity;
|
||||
}
|
||||
}
|
||||
private static void PedStateSync(Packets.PedStateSync packet)
|
||||
{
|
||||
SyncedPed c = EntityPool.GetPedByID(packet.ID);
|
||||
if (c==null) { return; }
|
||||
c.ID=packet.ID;
|
||||
c.OwnerID=packet.OwnerID;
|
||||
c.Clothes=packet.Clothes;
|
||||
c.WeaponComponents=packet.WeaponComponents;
|
||||
c.ModelHash=packet.ModelHash;
|
||||
c.LastStateSynced = Main.Ticked;
|
||||
}
|
||||
private static void VehicleSync(Packets.VehicleSync packet)
|
||||
{
|
||||
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
|
||||
if (v==null)
|
||||
{
|
||||
EntityPool.ThreadSafe.Add(v=new SyncedVehicle(packet.ID));
|
||||
}
|
||||
if (v.IsMine) { return; }
|
||||
v.ID= packet.ID;
|
||||
v.Position=packet.Position;
|
||||
v.Quaternion=packet.Quaternion;
|
||||
v.SteeringAngle=packet.SteeringAngle;
|
||||
v.ThrottlePower=packet.ThrottlePower;
|
||||
v.BrakePower=packet.BrakePower;
|
||||
v.Velocity=packet.Velocity;
|
||||
v.RotationVelocity=packet.RotationVelocity;
|
||||
v.DeluxoWingRatio=packet.DeluxoWingRatio;
|
||||
v.LastSynced=Main.Ticked;
|
||||
}
|
||||
private static void VehicleStateSync(Packets.VehicleStateSync packet)
|
||||
{
|
||||
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
|
||||
if (v==null||v.IsMine) { return; }
|
||||
v.ID= packet.ID;
|
||||
v.OwnerID= packet.OwnerID;
|
||||
v.DamageModel=packet.DamageModel;
|
||||
v.EngineHealth=packet.EngineHealth;
|
||||
v.OwnerID=packet.OwnerID;
|
||||
v.Mods=packet.Mods;
|
||||
v.ModelHash=packet.ModelHash;
|
||||
v.Colors=packet.Colors;
|
||||
v.LandingGear=packet.LandingGear;
|
||||
v.EngineRunning = packet.Flag.HasFlag(VehicleDataFlags.IsEngineRunning);
|
||||
v.LightsOn = packet.Flag.HasFlag(VehicleDataFlags.AreLightsOn);
|
||||
v.BrakeLightsOn = packet.Flag.HasFlag(VehicleDataFlags.AreBrakeLightsOn);
|
||||
v.HighBeamsOn = packet.Flag.HasFlag(VehicleDataFlags.AreHighBeamsOn);
|
||||
v.SireneActive = packet.Flag.HasFlag(VehicleDataFlags.IsSirenActive);
|
||||
v.IsDead = packet.Flag.HasFlag(VehicleDataFlags.IsDead);
|
||||
v.HornActive = packet.Flag.HasFlag(VehicleDataFlags.IsHornActive);
|
||||
v.Transformed = packet.Flag.HasFlag(VehicleDataFlags.IsTransformed);
|
||||
v.Passengers=new Dictionary<VehicleSeat, SyncedPed>();
|
||||
v.LockStatus=packet.LockStatus;
|
||||
v.RadioStation=packet.RadioStation;
|
||||
v.Flags=packet.Flag;
|
||||
foreach (KeyValuePair<int, int> pair in packet.Passengers)
|
||||
{
|
||||
if (EntityPool.PedExists(pair.Value))
|
||||
{
|
||||
v.Passengers.Add((VehicleSeat)pair.Key, EntityPool.GetPedByID(pair.Value));
|
||||
}
|
||||
}
|
||||
v.LastStateSynced= Main.Ticked;
|
||||
|
||||
}
|
||||
private static void ProjectileSync(Packets.ProjectileSync packet)
|
||||
{
|
||||
var p = EntityPool.GetProjectileByID(packet.ID);
|
||||
if (p==null)
|
||||
{
|
||||
if (packet.Exploded) { return; }
|
||||
Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}");
|
||||
EntityPool.ThreadSafe.Add(p=new SyncedProjectile(packet.ID));
|
||||
}
|
||||
p.Position=packet.Position;
|
||||
p.Rotation=packet.Rotation;
|
||||
p.Velocity=packet.Velocity;
|
||||
p.Hash=(WeaponHash)packet.WeaponHash;
|
||||
p.ShooterID=packet.ShooterID;
|
||||
p.Exploded=packet.Exploded;
|
||||
p.LastSynced=Main.Ticked;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
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")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("RageCoop.Client")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2021")]
|
||||
[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("ef56d109-1f22-43e0-9dff-cfcfb94e0681")]
|
||||
|
||||
// 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("0.4.0.0")]
|
||||
[assembly: AssemblyFileVersion("0.4.0.0")]
|
@ -1,169 +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>{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>RageCoop.Client</RootNamespace>
|
||||
<AssemblyName>RageCoop.Client</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>D:\Games\Grand Theft Auto V\Scripts\RageCoop\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ClearScript.Core, Version=7.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\scripts\ClearScript.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ClearScript.V8, Version=7.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\scripts\ClearScript.V8.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="LemonUI.SHVDN3, Version=1.5.1.0, Culture=neutral, processorArchitecture=AMD64">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\scripts\LemonUI.SHVDN3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Lidgren.Network, Version=2012.1.7.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\scripts\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.6.0.0\lib\net461\Microsoft.Extensions.DependencyInjection.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.6.0.0\lib\net461\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Options, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Options.6.0.0\lib\net461\Microsoft.Extensions.Options.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Primitives, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Primitives.6.0.0\lib\net461\Microsoft.Extensions.Primitives.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\scripts\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3, Version=3.3.2.0, Culture=neutral, processorArchitecture=AMD64">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.6.0.0\lib\net461\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<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="DevTools\DevTool.cs" />
|
||||
<Compile Include="Menus\Sub\DevToolMenu.cs" />
|
||||
<Compile Include="Menus\Sub\ServersMenu.cs" />
|
||||
<Compile Include="Scripting\ClientScript.cs" />
|
||||
<Compile Include="Scripting\Engine.cs" />
|
||||
<Compile Include="Util\MathExtensions.cs" />
|
||||
<Compile Include="Util\PedConfigFlags.cs" />
|
||||
<Compile Include="Util\Util.cs" />
|
||||
<Compile Include="Util\VehicleExtensions.cs" />
|
||||
<Compile Include="Util\WeaponUtil.cs" />
|
||||
<Compile Include="Networking\Chat.cs" />
|
||||
<Compile Include="COOPAPI.cs" />
|
||||
<Compile Include="Debug.cs" />
|
||||
<Compile Include="Networking\DownloadManager.cs" />
|
||||
<Compile Include="Util\TaskType.cs" />
|
||||
<Compile Include="Menus\Sub\DebugMenu.cs" />
|
||||
<Compile Include="Networking\Receive.cs" />
|
||||
<Compile Include="Networking\Send.cs" />
|
||||
<Compile Include="Sync\Entities\SyncedPed.cs" />
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
<Compile Include="Sync\Entities\SyncedProjectile.cs" />
|
||||
<Compile Include="Sync\EntityPool.cs" />
|
||||
<Compile Include="Sync\SyncEvents.cs" />
|
||||
<Compile Include="Sync\Entities\SyncedEntity.cs" />
|
||||
<Compile Include="Sync\Entities\SyncedVehicle.cs" />
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Networking\MapLoader.cs" />
|
||||
<Compile Include="Menus\CoopMenu.cs" />
|
||||
<Compile Include="Menus\Sub\SettingsMenu.cs" />
|
||||
<Compile Include="Networking\Networking.cs" />
|
||||
<Compile Include="PlayerList.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Sync\SyncParameters.cs" />
|
||||
<Compile Include="Util\PedExtensions.cs" />
|
||||
<Compile Include="WorldThread.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\RageCoop.Core.csproj">
|
||||
<Project>{cc2e8102-e568-4524-aa1f-f8e0f1cfe58a}</Project>
|
||||
<Name>RageCoop.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Inherit from this class, constructor will be called when the script is loaded.
|
||||
/// </summary>
|
||||
public abstract class ClientScript
|
||||
{
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
internal class Engine : Core.Scripting.ScriptingEngine
|
||||
{
|
||||
public Engine() : base(typeof(ClientScript), Main.Logger)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<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>
|
@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Bcl.AsyncInterfaces" version="6.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection" version="6.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="6.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Options" version="6.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Primitives" version="6.0.0" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="6.0.0" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.5.4" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
|
||||
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
|
||||
</packages>
|
@ -1,110 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using GTA.Math;
|
||||
using System.Net;
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public class CoreUtils
|
||||
{
|
||||
|
||||
public static (byte, byte[]) GetBytesFromObject(object obj)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case byte _:
|
||||
return (0x01, BitConverter.GetBytes((byte)obj));
|
||||
case short _:
|
||||
return (0x02, BitConverter.GetBytes((short)obj));
|
||||
case ushort _:
|
||||
return (0x03, BitConverter.GetBytes((ushort)obj));
|
||||
case int _:
|
||||
return (0x04, BitConverter.GetBytes((int)obj));
|
||||
case uint _:
|
||||
return (0x05, BitConverter.GetBytes((uint)obj));
|
||||
case long _:
|
||||
return (0x06, BitConverter.GetBytes((long)obj));
|
||||
case ulong _:
|
||||
return (0x07, BitConverter.GetBytes((ulong)obj));
|
||||
case float _:
|
||||
return (0x08, BitConverter.GetBytes((float)obj));
|
||||
case bool _:
|
||||
return (0x09, BitConverter.GetBytes((bool)obj));
|
||||
default:
|
||||
return (0x0, null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public static class Extensions
|
||||
{
|
||||
public static void AddVector3(this List<byte> bytes, Vector3 vec3)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(vec3.X));
|
||||
bytes.AddRange(BitConverter.GetBytes(vec3.Y));
|
||||
bytes.AddRange(BitConverter.GetBytes(vec3.Z));
|
||||
}
|
||||
public static void AddQuaternion(this List<byte> bytes, Quaternion quat)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(quat.X));
|
||||
bytes.AddRange(BitConverter.GetBytes(quat.Y));
|
||||
bytes.AddRange(BitConverter.GetBytes(quat.Z));
|
||||
bytes.AddRange(BitConverter.GetBytes(quat.W));
|
||||
}
|
||||
public static void AddInt(this List<byte> bytes,int i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddUint(this List<byte> bytes, uint i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddLong(this List<byte> bytes, long i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddUlong(this List<byte> bytes, ulong i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddFloat(this List<byte> bytes, float i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
|
||||
public static byte ToByte(this bool[] source)
|
||||
{
|
||||
byte result = 0;
|
||||
// This assumes the array never contains more than 8 elements!
|
||||
int index = 8 - source.Length;
|
||||
|
||||
// Loop through the array
|
||||
foreach (bool b in source)
|
||||
{
|
||||
// if the element is 'true' set the bit at that position
|
||||
if (b)
|
||||
result |= (byte)(1 << (7 - index));
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public static bool[] ToBoolArray(this byte b)
|
||||
{
|
||||
bool[] result = new bool[8];
|
||||
|
||||
// check each bit in the byte. if 1 set to true, if 0 set to false
|
||||
for (int i = 0; i < 8; i++)
|
||||
result[i] = (b & (1 << i)) != 0;
|
||||
|
||||
// reverse the array
|
||||
Array.Reverse(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RageCoop.Core.Logging
|
||||
{
|
||||
public class Logger :IDisposable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
|
||||
/// </summary>
|
||||
public int LogLevel = 0;
|
||||
public string LogPath;
|
||||
public bool UseConsole = false;
|
||||
private static StreamWriter logWriter;
|
||||
|
||||
private string Buffer="";
|
||||
private Thread LoggerThread;
|
||||
private bool Stopping=false;
|
||||
|
||||
public Logger(bool overwrite=true)
|
||||
{
|
||||
if (File.Exists(LogPath)&&overwrite) { File.Delete(LogPath); }
|
||||
LoggerThread=new Thread(() =>
|
||||
{
|
||||
while (!Stopping)
|
||||
{
|
||||
Flush();
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
});
|
||||
LoggerThread.Start();
|
||||
}
|
||||
|
||||
public void Info(string message)
|
||||
{
|
||||
if (LogLevel>2) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [INF] {1}", Date(), message, Process.GetCurrentProcess().Id);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
public void Warning(string message)
|
||||
{
|
||||
if (LogLevel>3) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [WRN] {1}", Date(), message, Process.GetCurrentProcess().Id);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Error(string message)
|
||||
{
|
||||
if (LogLevel>4) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), message, Process.GetCurrentProcess().Id);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
}
|
||||
}
|
||||
public void Error(Exception ex)
|
||||
{
|
||||
if (LogLevel>4) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(),string.Join("\r\n",ex.Message,ex.StackTrace,ex.ToString()), Process.GetCurrentProcess().Id);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
public void Debug(string message)
|
||||
{
|
||||
|
||||
if (LogLevel>1) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [DBG] {1}", Date(), message,Process.GetCurrentProcess().Id);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
public void Trace(string message)
|
||||
{
|
||||
if (LogLevel>0) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [TRC] {1}", Date(), message, Process.GetCurrentProcess().Id);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
private string Date()
|
||||
{
|
||||
return DateTime.Now.ToString();
|
||||
}
|
||||
public void Flush()
|
||||
{
|
||||
lock (Buffer)
|
||||
{
|
||||
if (Buffer!="")
|
||||
{
|
||||
if (UseConsole)
|
||||
{
|
||||
Console.Write(Buffer);
|
||||
Buffer="";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
logWriter=new StreamWriter(LogPath, true, Encoding.UTF8);
|
||||
logWriter.Write(Buffer);
|
||||
logWriter.Close();
|
||||
Buffer="";
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Stopping=true;
|
||||
LoggerThread?.Join();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,699 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Lidgren.Network;
|
||||
using Newtonsoft.Json;
|
||||
using GTA.Math;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
/*
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public struct LVector3
|
||||
{
|
||||
#region CLIENT-ONLY
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Vector3 ToVector()
|
||||
{
|
||||
return new Vector3(X, Y, Z);
|
||||
}
|
||||
#endregion
|
||||
#region SERVER-ONLY
|
||||
public float Length() => (float)Math.Sqrt((X * X) + (Y * Y) + (Z * Z));
|
||||
public static LVector3 Subtract(LVector3 pos1, LVector3 pos2) { return new LVector3(pos1.X - pos2.X, pos1.Y - pos2.Y, pos1.Z - pos2.Z); }
|
||||
public static bool Equals(LVector3 value1, LVector3 value2) => value1.X == value2.X && value1.Y == value2.Y && value1.Z == value2.Z;
|
||||
public static LVector3 operator /(LVector3 value1, float value2)
|
||||
{
|
||||
float num = 1f / value2;
|
||||
return new LVector3(value1.X * num, value1.Y * num, value1.Z * num);
|
||||
}
|
||||
public static LVector3 operator -(LVector3 left, LVector3 right)
|
||||
{
|
||||
return new LVector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
|
||||
}
|
||||
public static LVector3 operator -(LVector3 value)
|
||||
{
|
||||
return default(LVector3) - value;
|
||||
}
|
||||
#endregion
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public LVector3(float X, float Y, float Z)
|
||||
{
|
||||
this.X = X;
|
||||
this.Y = Y;
|
||||
this.Z = Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public float X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public float Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public float Z { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public struct LQuaternion
|
||||
{
|
||||
#region CLIENT-ONLY
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Quaternion ToQuaternion()
|
||||
{
|
||||
return new Quaternion(X, Y, Z, W);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public LQuaternion(float X, float Y, float Z, float W)
|
||||
{
|
||||
this.X = X;
|
||||
this.Y = Y;
|
||||
this.Z = Z;
|
||||
this.W = W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public float X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public float Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public float Z { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public float W { get; set; }
|
||||
}
|
||||
*/
|
||||
public enum PacketTypes:byte
|
||||
{
|
||||
Handshake=0,
|
||||
PlayerConnect=1,
|
||||
PlayerDisconnect=2,
|
||||
PlayerInfoUpdate=3,
|
||||
|
||||
ChatMessage=10,
|
||||
NativeCall=11,
|
||||
NativeResponse=12,
|
||||
Mod=13,
|
||||
CleanUpWorld=14,
|
||||
FileTransferTick=15,
|
||||
FileTransferRequest=16,
|
||||
FileTransferComplete=17,
|
||||
ServerClientEvent=18,
|
||||
|
||||
#region Sync
|
||||
|
||||
#region INTERVAL
|
||||
VehicleSync = 20,
|
||||
VehicleStateSync = 21,
|
||||
PedSync = 22,
|
||||
PedStateSync = 23,
|
||||
ProjectileSync=24,
|
||||
#endregion
|
||||
|
||||
#region EVENT
|
||||
|
||||
PedKilled=30,
|
||||
BulletShot=31,
|
||||
EnteringVehicle=32,
|
||||
LeaveVehicle = 33,
|
||||
EnteredVehicle=34,
|
||||
OwnerChanged=35,
|
||||
VehicleBulletShot = 36,
|
||||
NozzleTransform=37,
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
public static class PacketExtensions
|
||||
{
|
||||
public static bool IsSyncEvent(this PacketTypes p)
|
||||
{
|
||||
return (30<=(byte)p)&&((byte)p<=40);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConnectionChannel
|
||||
{
|
||||
Default = 0,
|
||||
Chat = 5,
|
||||
Native = 6,
|
||||
Mod = 7,
|
||||
File = 8,
|
||||
Event = 9,
|
||||
VehicleSync=20,
|
||||
PedSync=21,
|
||||
ProjectileSync = 22,
|
||||
SyncEvents =30,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum PedDataFlags:ushort
|
||||
{
|
||||
None=0,
|
||||
IsAiming = 1 << 0,
|
||||
IsReloading = 1 << 2,
|
||||
IsJumping = 1 << 3,
|
||||
IsRagdoll = 1 << 4,
|
||||
IsOnFire = 1 << 5,
|
||||
IsInParachuteFreeFall = 1 << 6,
|
||||
IsParachuteOpen = 1 << 7,
|
||||
IsOnLadder = 1 << 8,
|
||||
IsVaulting = 1 << 9,
|
||||
IsInCover=1<< 10,
|
||||
}
|
||||
|
||||
#region ===== VEHICLE DATA =====
|
||||
public enum VehicleDataFlags:ushort
|
||||
{
|
||||
IsEngineRunning = 1 << 0,
|
||||
AreLightsOn = 1 << 1,
|
||||
AreBrakeLightsOn = 1 << 2,
|
||||
AreHighBeamsOn = 1 << 3,
|
||||
IsSirenActive = 1 << 4,
|
||||
IsDead = 1 << 5,
|
||||
IsHornActive = 1 << 6,
|
||||
IsTransformed = 1 << 7,
|
||||
RoofOpened = 1 << 8,
|
||||
OnTurretSeat = 1 << 9,
|
||||
IsAircraft = 1 << 10,
|
||||
IsDeluxoHovering=1 << 11,
|
||||
}
|
||||
|
||||
|
||||
public struct VehicleDamageModel
|
||||
{
|
||||
public byte BrokenDoors { get; set; }
|
||||
public byte OpenedDoors { get; set; }
|
||||
public byte BrokenWindows { get; set; }
|
||||
public short BurstedTires { get; set; }
|
||||
public byte LeftHeadLightBroken { get; set; }
|
||||
public byte RightHeadLightBroken { get; set; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
interface IPacket
|
||||
{
|
||||
void Pack(NetOutgoingMessage message);
|
||||
void Unpack(byte[] array);
|
||||
}
|
||||
|
||||
public abstract class Packet : IPacket
|
||||
{
|
||||
public abstract void Pack(NetOutgoingMessage message);
|
||||
public abstract void Unpack(byte[] array);
|
||||
}
|
||||
|
||||
public partial class Packets
|
||||
{
|
||||
public class Mod : Packet
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public byte CustomPacketID { get; set; }
|
||||
|
||||
public byte[] Bytes { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.Mod);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
// Write Name
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(Name);
|
||||
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
|
||||
byteArray.AddRange(nameBytes);
|
||||
|
||||
// Write CustomPacketID
|
||||
byteArray.Add(CustomPacketID);
|
||||
|
||||
// Write Bytes
|
||||
byteArray.AddRange(BitConverter.GetBytes(Bytes.Length));
|
||||
byteArray.AddRange(Bytes);
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
message.Write(result);
|
||||
#endregion
|
||||
}
|
||||
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
// Read Name
|
||||
int nameLength = reader.ReadInt();
|
||||
Name = reader.ReadString(nameLength);
|
||||
|
||||
// Read CustomPacketID
|
||||
CustomPacketID = reader.ReadByte();
|
||||
|
||||
// Read Bytes
|
||||
int bytesLength = reader.ReadInt();
|
||||
Bytes = reader.ReadByteArray(bytesLength);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public class ChatMessage : Packet
|
||||
{
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.ChatMessage);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username);
|
||||
byte[] messageBytes = Encoding.UTF8.GetBytes(Message);
|
||||
|
||||
// Write UsernameLength
|
||||
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
|
||||
|
||||
// Write Username
|
||||
byteArray.AddRange(usernameBytes);
|
||||
|
||||
// Write MessageLength
|
||||
byteArray.AddRange(BitConverter.GetBytes(messageBytes.Length));
|
||||
|
||||
// Write Message
|
||||
byteArray.AddRange(messageBytes);
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
message.Write(result);
|
||||
#endregion
|
||||
}
|
||||
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
// Read username
|
||||
int usernameLength = reader.ReadInt();
|
||||
Username = reader.ReadString(usernameLength);
|
||||
|
||||
// Read message
|
||||
int messageLength = reader.ReadInt();
|
||||
Message = reader.ReadString(messageLength);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public class ServerClientEvent : Packet
|
||||
{
|
||||
public string EventName { get; set; }
|
||||
|
||||
public List<object> Args { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.ServerClientEvent);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
byte[] eventNameBytes = Encoding.UTF8.GetBytes(EventName);
|
||||
|
||||
// Write event name
|
||||
byteArray.AddRange(BitConverter.GetBytes(eventNameBytes.Length));
|
||||
byteArray.AddRange(eventNameBytes);
|
||||
|
||||
// Write args
|
||||
byteArray.AddRange(BitConverter.GetBytes(Args.Count));
|
||||
foreach (object arg in Args)
|
||||
{
|
||||
var test = CoreUtils.GetBytesFromObject(arg);
|
||||
if (test.Item1 == 0x0)
|
||||
{
|
||||
// Logger.Write("Can't get type of an object!", Logger.LogLevel.Server);
|
||||
byteArray.Add(0x0);
|
||||
}
|
||||
else
|
||||
{
|
||||
byteArray.Add(test.Item1);
|
||||
byteArray.AddRange(test.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
message.Write(result);
|
||||
#endregion
|
||||
}
|
||||
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
// Read event name
|
||||
int eventNameBytes = reader.ReadInt();
|
||||
EventName = reader.ReadString(eventNameBytes);
|
||||
|
||||
// Read args
|
||||
Args = new List<object>();
|
||||
int argsCount = reader.ReadInt();
|
||||
for (int i = 0; i < argsCount; i++)
|
||||
{
|
||||
byte argType = reader.ReadByte();
|
||||
switch (argType)
|
||||
{
|
||||
case 0x01:
|
||||
Args.Add(reader.ReadByte());
|
||||
break;
|
||||
case 0x02:
|
||||
Args.Add(reader.ReadShort());
|
||||
break;
|
||||
case 0x03:
|
||||
Args.Add(reader.ReadUShort());
|
||||
break;
|
||||
case 0x04:
|
||||
Args.Add(reader.ReadInt());
|
||||
break;
|
||||
case 0x05:
|
||||
Args.Add(reader.ReadUInt());
|
||||
break;
|
||||
case 0x06:
|
||||
Args.Add(reader.ReadLong());
|
||||
break;
|
||||
case 0x07:
|
||||
Args.Add(reader.ReadULong());
|
||||
break;
|
||||
case 0x08:
|
||||
Args.Add(reader.ReadFloat());
|
||||
break;
|
||||
case 0x09:
|
||||
Args.Add(reader.ReadBool());
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#region ===== NATIVECALL =====
|
||||
public class NativeCall : Packet
|
||||
{
|
||||
public ulong Hash { get; set; }
|
||||
|
||||
public List<object> Args { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.NativeCall);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
// Write Hash
|
||||
byteArray.AddRange(BitConverter.GetBytes(Hash));
|
||||
|
||||
// Write Args
|
||||
byteArray.AddRange(BitConverter.GetBytes(Args.Count));
|
||||
Args.ForEach(x =>
|
||||
{
|
||||
Type type = x.GetType();
|
||||
|
||||
if (type == typeof(int))
|
||||
{
|
||||
byteArray.Add(0x00);
|
||||
byteArray.AddRange(BitConverter.GetBytes((int)x));
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
{
|
||||
byteArray.Add(0x01);
|
||||
byteArray.AddRange(BitConverter.GetBytes((bool)x));
|
||||
}
|
||||
else if (type == typeof(float))
|
||||
{
|
||||
byteArray.Add(0x02);
|
||||
byteArray.AddRange(BitConverter.GetBytes((float)x));
|
||||
}
|
||||
else if (type == typeof(string))
|
||||
{
|
||||
byteArray.Add(0x03);
|
||||
byte[] stringBytes = Encoding.UTF8.GetBytes((string)x);
|
||||
byteArray.AddRange(BitConverter.GetBytes(stringBytes.Length));
|
||||
byteArray.AddRange(stringBytes);
|
||||
}
|
||||
else if (type == typeof(Vector3))
|
||||
{
|
||||
byteArray.Add(0x04);
|
||||
Vector3 vector = (Vector3)x;
|
||||
byteArray.AddRange(BitConverter.GetBytes(vector.X));
|
||||
byteArray.AddRange(BitConverter.GetBytes(vector.Y));
|
||||
byteArray.AddRange(BitConverter.GetBytes(vector.Z));
|
||||
}
|
||||
});
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
message.Write(result);
|
||||
#endregion
|
||||
}
|
||||
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
// Read Hash
|
||||
Hash = reader.ReadULong();
|
||||
|
||||
// Read Args
|
||||
Args = new List<object>();
|
||||
int argsLength = reader.ReadInt();
|
||||
for (int i = 0; i < argsLength; i++)
|
||||
{
|
||||
byte argType = reader.ReadByte();
|
||||
switch (argType)
|
||||
{
|
||||
case 0x00:
|
||||
Args.Add(reader.ReadInt());
|
||||
break;
|
||||
case 0x01:
|
||||
Args.Add(reader.ReadBool());
|
||||
break;
|
||||
case 0x02:
|
||||
Args.Add(reader.ReadFloat());
|
||||
break;
|
||||
case 0x03:
|
||||
int stringLength = reader.ReadInt();
|
||||
Args.Add(reader.ReadString(stringLength));
|
||||
break;
|
||||
case 0x04:
|
||||
Args.Add(new Vector3()
|
||||
{
|
||||
X = reader.ReadFloat(),
|
||||
Y = reader.ReadFloat(),
|
||||
Z = reader.ReadFloat()
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public class NativeResponse : Packet
|
||||
{
|
||||
public ulong Hash { get; set; }
|
||||
|
||||
public List<object> Args { get; set; }
|
||||
|
||||
public byte? ResultType { get; set; }
|
||||
|
||||
public long ID { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.NativeResponse);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
// Write Hash
|
||||
byteArray.AddRange(BitConverter.GetBytes(Hash));
|
||||
|
||||
Type type;
|
||||
|
||||
// Write Args
|
||||
byteArray.AddRange(BitConverter.GetBytes(Args.Count));
|
||||
Args.ForEach(x =>
|
||||
{
|
||||
type = x.GetType();
|
||||
|
||||
if (type == typeof(int))
|
||||
{
|
||||
byteArray.Add(0x00);
|
||||
byteArray.AddRange(BitConverter.GetBytes((int)x));
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
{
|
||||
byteArray.Add(0x01);
|
||||
byteArray.AddRange(BitConverter.GetBytes((bool)x));
|
||||
}
|
||||
else if (type == typeof(float))
|
||||
{
|
||||
byteArray.Add(0x02);
|
||||
byteArray.AddRange(BitConverter.GetBytes((float)x));
|
||||
}
|
||||
else if (type == typeof(string))
|
||||
{
|
||||
byteArray.Add(0x03);
|
||||
byte[] stringBytes = Encoding.UTF8.GetBytes((string)x);
|
||||
byteArray.AddRange(BitConverter.GetBytes(stringBytes.Length));
|
||||
byteArray.AddRange(stringBytes);
|
||||
}
|
||||
else if (type == typeof(Vector3))
|
||||
{
|
||||
byteArray.Add(0x04);
|
||||
Vector3 vector = (Vector3)x;
|
||||
byteArray.AddRange(BitConverter.GetBytes(vector.X));
|
||||
byteArray.AddRange(BitConverter.GetBytes(vector.Y));
|
||||
byteArray.AddRange(BitConverter.GetBytes(vector.Z));
|
||||
}
|
||||
});
|
||||
|
||||
byteArray.AddRange(BitConverter.GetBytes(ID));
|
||||
|
||||
// Write type of result
|
||||
if (ResultType.HasValue)
|
||||
{
|
||||
byteArray.Add(ResultType.Value);
|
||||
}
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
message.Write(result);
|
||||
#endregion
|
||||
}
|
||||
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
// Read Hash
|
||||
Hash = reader.ReadULong();
|
||||
|
||||
// Read Args
|
||||
Args = new List<object>();
|
||||
int argsLength = reader.ReadInt();
|
||||
for (int i = 0; i < argsLength; i++)
|
||||
{
|
||||
byte argType = reader.ReadByte();
|
||||
switch (argType)
|
||||
{
|
||||
case 0x00:
|
||||
Args.Add(reader.ReadInt());
|
||||
break;
|
||||
case 0x01:
|
||||
Args.Add(reader.ReadBool());
|
||||
break;
|
||||
case 0x02:
|
||||
Args.Add(reader.ReadFloat());
|
||||
break;
|
||||
case 0x03:
|
||||
int stringLength = reader.ReadInt();
|
||||
Args.Add(reader.ReadString(stringLength));
|
||||
break;
|
||||
case 0x04:
|
||||
Args.Add(new Vector3()
|
||||
{
|
||||
X = reader.ReadFloat(),
|
||||
Y = reader.ReadFloat(),
|
||||
Z = reader.ReadFloat()
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ID = reader.ReadLong();
|
||||
|
||||
// Read type of result
|
||||
if (reader.CanRead(1))
|
||||
{
|
||||
ResultType = reader.ReadByte();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endregion // ===== NATIVECALL =====
|
||||
}
|
||||
|
||||
public static class CoopSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static byte[] Serialize(this object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string jsonString = JsonConvert.SerializeObject(obj);
|
||||
return System.Text.Encoding.UTF8.GetBytes(jsonString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static T Deserialize<T>(this byte[] bytes) where T : class
|
||||
{
|
||||
if (bytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var jsonString = System.Text.Encoding.UTF8.GetString(bytes);
|
||||
return JsonConvert.DeserializeObject<T>(jsonString);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GTA.Math;
|
||||
|
||||
[assembly: InternalsVisibleTo("RageCoop.Server")]
|
||||
[assembly: InternalsVisibleTo("RageCoop.Client")]
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public class PlayerData
|
||||
{
|
||||
public string Username { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Universal character ID.
|
||||
/// </summary>
|
||||
public int PedID
|
||||
{
|
||||
get; internal set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ID of player's last vehicle.
|
||||
/// </summary>
|
||||
public int VehicleID { get; internal set; }
|
||||
public Vector3 Position { get;internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Player Latency in second.
|
||||
/// </summary>
|
||||
public float Latency { get; internal set; }
|
||||
public int Health { get; internal set; }
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<AssemblyVersion>0.1</AssemblyVersion>
|
||||
<FileVersion>0.1</FileVersion>
|
||||
<Version>0.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net6.0|AnyCPU'">
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0|AnyCPU'">
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Lidgren.Network">
|
||||
<HintPath>..\Libs\Release\scripts\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\Libs\Release\scripts\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3">
|
||||
<HintPath>..\Libs\Release\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,153 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Linq;
|
||||
|
||||
[assembly: InternalsVisibleTo("RageCoop.Server")]
|
||||
[assembly: InternalsVisibleTo("RageCoop.Client")]
|
||||
namespace RageCoop.Core.Scripting
|
||||
{
|
||||
internal class ScriptingEngine
|
||||
{
|
||||
private Type BaseScriptType;
|
||||
public Logging.Logger Logger { get; set; }
|
||||
public ScriptingEngine(Type baseScriptType, Logging.Logger logger)
|
||||
{
|
||||
BaseScriptType = baseScriptType;
|
||||
Logger = logger;
|
||||
}
|
||||
/// <summary>
|
||||
/// Loads scripts from the specified assembly file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the assembly file to load.</param>
|
||||
/// <returns><see langword="true" /> on success, <see langword="false" /> otherwise</returns>
|
||||
private bool LoadScriptsFromAssembly(string filename)
|
||||
{
|
||||
if (!IsManagedAssembly(filename))
|
||||
return false;
|
||||
|
||||
Logger?.Debug($"Loading assembly {Path.GetFileName(filename)} ...");
|
||||
|
||||
Assembly assembly;
|
||||
|
||||
try
|
||||
{
|
||||
assembly = Assembly.LoadFrom(filename);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.Error( "Unable to load "+Path.GetFileName(filename));
|
||||
Logger?.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadScriptsFromAssembly(assembly, filename);
|
||||
}
|
||||
/// <summary>
|
||||
/// Loads scripts from the specified assembly object.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path to the file associated with this assembly.</param>
|
||||
/// <param name="assembly">The assembly to load.</param>
|
||||
/// <returns><see langword="true" /> on success, <see langword="false" /> otherwise</returns>
|
||||
private bool LoadScriptsFromAssembly(Assembly assembly, string filename)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Find all script types in the assembly
|
||||
foreach (var type in assembly.GetTypes().Where(x => IsSubclassOf(x,nameof(BaseScriptType))))
|
||||
{
|
||||
ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);
|
||||
if (constructor != null && constructor.IsPublic)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Invoke script constructor
|
||||
constructor.Invoke(null);
|
||||
count++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.Error($"Error occurred when loading script: {type.FullName}.");
|
||||
Logger?.Error(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger?.Error($"Script {type.FullName} has an invalid contructor.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
Logger?.Error($"Failed to load assembly {Path.GetFileName(filename)}: ");
|
||||
Logger?.Error(ex);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger?.Info($"Loaded {count.ToString()} script(s) in {Path.GetFileName(filename)}");
|
||||
return count != 0;
|
||||
}
|
||||
private bool IsManagedAssembly(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Stream file = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
if (file.Length < 64)
|
||||
return false;
|
||||
|
||||
using (BinaryReader bin = new BinaryReader(file))
|
||||
{
|
||||
// PE header starts at offset 0x3C (60). Its a 4 byte header.
|
||||
file.Position = 0x3C;
|
||||
uint offset = bin.ReadUInt32();
|
||||
if (offset == 0)
|
||||
offset = 0x80;
|
||||
|
||||
// Ensure there is at least enough room for the following structures:
|
||||
// 24 byte PE Signature & Header
|
||||
// 28 byte Standard Fields (24 bytes for PE32+)
|
||||
// 68 byte NT Fields (88 bytes for PE32+)
|
||||
// >= 128 byte Data Dictionary Table
|
||||
if (offset > file.Length - 256)
|
||||
return false;
|
||||
|
||||
// Check the PE signature. Should equal 'PE\0\0'.
|
||||
file.Position = offset;
|
||||
if (bin.ReadUInt32() != 0x00004550)
|
||||
return false;
|
||||
|
||||
// Read PE magic number from Standard Fields to determine format.
|
||||
file.Position += 20;
|
||||
var peFormat = bin.ReadUInt16();
|
||||
if (peFormat != 0x10b /* PE32 */ && peFormat != 0x20b /* PE32Plus */)
|
||||
return false;
|
||||
|
||||
// Read the 15th Data Dictionary RVA field which contains the CLI header RVA.
|
||||
// When this is non-zero then the file contains CLI data otherwise not.
|
||||
file.Position = offset + (peFormat == 0x10b ? 232 : 248);
|
||||
return bin.ReadUInt32() != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// This is likely not a valid assembly if any IO exceptions occur during reading
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private bool IsSubclassOf(Type type, string baseTypeName)
|
||||
{
|
||||
for (Type t = type.BaseType; t != null; t = t.BaseType)
|
||||
if (t.FullName == baseTypeName)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
BIN
Images/LOGO.png
BIN
Images/LOGO.png
Binary file not shown.
Before Width: | Height: | Size: 106 KiB |
@ -1,2 +0,0 @@
|
||||
ReloadKey=None
|
||||
ConsoleKey=F4
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
19
README.md
19
README.md
@ -7,10 +7,6 @@
|
||||
[![Issues][issues-shield]][issues-url]
|
||||
|
||||
|
||||
# Disclaimer
|
||||
The original author of this project is @EntenKoeniq.
|
||||
The project has been reworked and is currently maintained by @Sardelka9515.
|
||||
|
||||
# 🧠 That's it
|
||||
RAGECOOP is a multiplayer mod to play story mode or some mods made for RAGECOOP or just drive around with your buddy.
|
||||
|
||||
@ -28,6 +24,8 @@ _Old name: GTACOOP:R_
|
||||
- - No new features (only improvements)
|
||||
- [Newtonsoft.Json](https://www.nuget.org/packages/Newtonsoft.Json/13.0.1)
|
||||
- [ClearScript](https://github.com/microsoft/ClearScript)
|
||||
- [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
|
||||
- [DotNetCorePlugins](https://github.com/natemcmaster/DotNetCorePlugins)
|
||||
|
||||
# Features
|
||||
|
||||
@ -36,7 +34,7 @@ _Old name: GTACOOP:R_
|
||||
3. Synchronized projectiles
|
||||
4. Simple ragdoll sync
|
||||
5. Smoother vehicle/ped movement.
|
||||
6. Ownership based sync logic, carjacking is now working properly.
|
||||
6. Ownership based sync logic, carjacking is now working (sort of).
|
||||
7. Introduced SyncEvents.
|
||||
8. Code refactoring and namespace cleanup
|
||||
9. Synchronized vehicle doors, brake and throttle.
|
||||
@ -52,19 +50,14 @@ _Old name: GTACOOP:R_
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### Client
|
||||
|
||||
Download latest release, remove old version of the mod. Extract the zip and put `RageCoop` in `Grand Theft Auto V/Scripts`.
|
||||
|
||||
### Server
|
||||
|
||||
Download latest release for your OS, then extract and run.
|
||||
Refer to the [wiki](https://github.com/RAGECOOP/RAGECOOP-V/wiki)
|
||||
|
||||
# Downloads
|
||||
|
||||
Download latest release [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/latest)
|
||||
|
||||
You can also download nightly builds [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/nightly), which includes latest features and bug-fixes, but has not been thoroughly tested.
|
||||
|
||||
Please note that this is incompatible with all previous versions of ragecoop, remove old files before installing.
|
||||
|
||||
|
||||
|
@ -3,11 +3,11 @@ 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "Client\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}"
|
||||
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.Core", "Core\RageCoop.Core.csproj", "{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "RageCoop.Client\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -25,14 +25,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
|
||||
{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
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{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
|
||||
{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
|
||||
@ -41,6 +33,14 @@ 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}.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
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
@ -172,7 +172,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
}
|
||||
public enum MuzzleDir:byte
|
||||
internal enum MuzzleDir:byte
|
||||
{
|
||||
Forward=0,
|
||||
Right = 1,
|
@ -19,34 +19,36 @@ namespace RageCoop.Client
|
||||
/// </summary>
|
||||
internal class Main : Script
|
||||
{
|
||||
|
||||
|
||||
private bool _gameLoaded = false;
|
||||
private bool _lastDead = false;
|
||||
public static readonly string CurrentVersion = "V0_4";
|
||||
internal static readonly string CurrentVersion = "V0_5_0";
|
||||
|
||||
public static int LocalPlayerID=0;
|
||||
internal static int LocalPlayerID=0;
|
||||
|
||||
internal static RelationshipGroup SyncedPedsGroup;
|
||||
|
||||
public static new Settings Settings = null;
|
||||
internal static new Settings Settings = null;
|
||||
internal static Scripting.BaseScript BaseScript=new Scripting.BaseScript();
|
||||
|
||||
#if !NON_INTERACTIVE
|
||||
#endif
|
||||
public static Chat MainChat = null;
|
||||
public static Stopwatch Counter = new Stopwatch();
|
||||
public static Core.Logging.Logger Logger = null;
|
||||
|
||||
public static ulong Ticked = 0;
|
||||
internal static Chat MainChat = null;
|
||||
internal static Stopwatch Counter = new Stopwatch();
|
||||
internal static Logger Logger = null;
|
||||
|
||||
internal static ulong Ticked = 0;
|
||||
internal static Scripting.Resources Resources=null;
|
||||
private static List<Func<bool>> QueuedActions = new List<Func<bool>>();
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public Main()
|
||||
{
|
||||
Logger=new Core.Logging.Logger()
|
||||
Settings = Util.ReadSettings();
|
||||
Directory.CreateDirectory(Settings.DataDirectory);
|
||||
Logger=new Logger()
|
||||
{
|
||||
LogPath="Scripts\\RageCoop\\RageCoop.Client.log",
|
||||
LogPath=$"{Settings.DataDirectory}\\RageCoop.Client.log",
|
||||
UseConsole=false,
|
||||
#if DEBUG
|
||||
LogLevel = 0,
|
||||
@ -54,8 +56,8 @@ namespace RageCoop.Client
|
||||
LogLevel=Settings.LogLevel,
|
||||
#endif
|
||||
};
|
||||
// Required for some synchronization!
|
||||
/*if (Game.Version < GameVersion.v1_0_1290_1_Steam)
|
||||
Resources = new Scripting.Resources();
|
||||
if (Game.Version < GameVersion.v1_0_1290_1_Steam)
|
||||
{
|
||||
Tick += (object sender, EventArgs e) =>
|
||||
{
|
||||
@ -63,7 +65,6 @@ namespace RageCoop.Client
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_gameLoaded)
|
||||
{
|
||||
GTA.UI.Notification.Show("~r~Please update your GTA5 to v1.0.1290 or newer!", true);
|
||||
@ -71,17 +72,18 @@ namespace RageCoop.Client
|
||||
}
|
||||
};
|
||||
return;
|
||||
}*/
|
||||
}
|
||||
BaseScript.OnStart();
|
||||
SyncedPedsGroup=World.AddRelationshipGroup("SYNCPED");
|
||||
Game.Player.Character.RelationshipGroup.SetRelationshipBetweenGroups(SyncedPedsGroup, Relationship.Neutral, true);
|
||||
Settings = Util.ReadSettings();
|
||||
Networking.Start();
|
||||
#if !NON_INTERACTIVE
|
||||
#endif
|
||||
MainChat = new Chat();
|
||||
|
||||
Tick += OnTick;
|
||||
Tick += (s,e) => { Scripting.API.Events.InvokeTick(); };
|
||||
KeyDown += OnKeyDown;
|
||||
KeyDown+=(s, e) => { Scripting.API.Events.InvokeKeyDown(s, e); };
|
||||
KeyUp+=(s, e) => { Scripting.API.Events.InvokeKeyUp(s, e); };
|
||||
Aborted += (object sender, EventArgs e) => CleanUp();
|
||||
|
||||
Util.NativeMemory();
|
||||
@ -89,9 +91,6 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private ulong _lastDebugData;
|
||||
private int _debugBytesSend;
|
||||
private int _debugBytesReceived;
|
||||
#endif
|
||||
private void OnTick(object sender, EventArgs e)
|
||||
{
|
||||
@ -121,37 +120,22 @@ namespace RageCoop.Client
|
||||
}
|
||||
try
|
||||
{
|
||||
Networking.Tick();
|
||||
EntityPool.DoSync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error(ex);
|
||||
}
|
||||
|
||||
if (!DownloadManager.DownloadComplete)
|
||||
{
|
||||
DownloadManager.RenderProgress();
|
||||
}
|
||||
|
||||
MapLoader.LoadAll();
|
||||
|
||||
#if DEBUG
|
||||
if (Networking.ShowNetworkInfo)
|
||||
{
|
||||
ulong time = Util.GetTickCount64();
|
||||
if (time - _lastDebugData > 1000)
|
||||
{
|
||||
_lastDebugData = time;
|
||||
|
||||
_debugBytesReceived = Networking.BytesReceived;
|
||||
Networking.BytesReceived = 0;
|
||||
_debugBytesSend = Networking.BytesSend;
|
||||
Networking.BytesSend = 0;
|
||||
}
|
||||
|
||||
|
||||
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 0), $"L: {Networking.Latency * 1000:N0}ms", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
|
||||
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 30), $"R: {Lidgren.Network.NetUtility.ToHumanReadable(_debugBytesReceived)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
|
||||
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 60), $"S: {Lidgren.Network.NetUtility.ToHumanReadable(_debugBytesSend)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
|
||||
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 30), $"R: {Lidgren.Network.NetUtility.ToHumanReadable(Statistics.BytesDownPerSecond)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
|
||||
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 60), $"S: {Lidgren.Network.NetUtility.ToHumanReadable(Statistics.BytesUpPerSecond)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -159,14 +143,16 @@ namespace RageCoop.Client
|
||||
|
||||
MainChat.Tick();
|
||||
PlayerList.Tick();
|
||||
if (Settings.DisableAutoRespawn)
|
||||
if (!Scripting.API.Config.EnableAutoRespawn)
|
||||
{
|
||||
Function.Call(Hash.PAUSE_DEATH_ARREST_RESTART, true);
|
||||
Function.Call(Hash.FORCE_GAME_STATE_PLAYING);
|
||||
Function.Call(Hash.IGNORE_NEXT_RESTART, true);
|
||||
Function.Call(Hash.FORCE_GAME_STATE_PLAYING);
|
||||
Function.Call(Hash.TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, "respawn_controller");
|
||||
|
||||
var P = Game.Player.Character;
|
||||
if (P.IsDead)
|
||||
{
|
||||
Function.Call(Hash.TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, "respawn_controller");
|
||||
Function.Call(Hash.SET_FADE_OUT_AFTER_DEATH, false);
|
||||
|
||||
if (P.Health!=1)
|
||||
@ -182,13 +168,12 @@ namespace RageCoop.Client
|
||||
|
||||
Function.Call(Hash.DISPLAY_HUD, true);
|
||||
}
|
||||
_lastDead=P.IsDead;
|
||||
|
||||
}
|
||||
|
||||
Ticked++;
|
||||
}
|
||||
|
||||
|
||||
private void OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (MainChat.Focused)
|
||||
@ -274,7 +259,7 @@ namespace RageCoop.Client
|
||||
|
||||
}
|
||||
|
||||
public static readonly Dictionary<ulong, byte> CheckNativeHash = new Dictionary<ulong, byte>()
|
||||
internal static readonly Dictionary<ulong, byte> CheckNativeHash = new Dictionary<ulong, byte>()
|
||||
{
|
||||
{ 0xD49F9B0955C367DE, 1 }, // Entities
|
||||
{ 0xEF29A16337FACADB, 1 }, //
|
||||
@ -291,8 +276,8 @@ namespace RageCoop.Client
|
||||
{ 0x5A039BB0BCA604B6, 4 }, //
|
||||
{ 0x0134F0835AB6BFCB, 5 } // Checkpoints
|
||||
};
|
||||
public static Dictionary<int, byte> ServerItems = new Dictionary<int, byte>();
|
||||
public static void CleanUpWorld()
|
||||
internal static Dictionary<int, byte> ServerItems = new Dictionary<int, byte>();
|
||||
internal static void CleanUpWorld()
|
||||
{
|
||||
if (ServerItems.Count == 0)
|
||||
{
|
||||
@ -338,7 +323,6 @@ namespace RageCoop.Client
|
||||
Main.Logger.Error($"CleanUpWorld(): ~r~Item {item.Value} cannot be deleted!");
|
||||
}
|
||||
}
|
||||
|
||||
ServerItems.Clear();
|
||||
}
|
||||
}
|
||||
@ -358,7 +342,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
GTA.UI.Screen.ShowSubtitle(ex.ToString());
|
||||
Logger.Error(ex);
|
||||
QueuedActions.Remove(action);
|
||||
}
|
||||
}
|
||||
@ -369,14 +353,14 @@ namespace RageCoop.Client
|
||||
/// Queue an action to be executed on next tick, allowing you to call scripting API from another thread.
|
||||
/// </summary>
|
||||
/// <param name="a"> The action to be executed, must return a bool indicating whether the action cane be removed after execution.</param>
|
||||
public static void QueueAction(Func<bool> a)
|
||||
internal static void QueueAction(Func<bool> a)
|
||||
{
|
||||
lock (QueuedActions)
|
||||
{
|
||||
QueuedActions.Add(a);
|
||||
}
|
||||
}
|
||||
public static void QueueAction(Action a)
|
||||
internal static void QueueAction(Action a)
|
||||
{
|
||||
lock (QueuedActions)
|
||||
{
|
||||
@ -386,37 +370,11 @@ namespace RageCoop.Client
|
||||
/// <summary>
|
||||
/// Clears all queued actions
|
||||
/// </summary>
|
||||
public static void ClearQueuedActions()
|
||||
internal static void ClearQueuedActions()
|
||||
{
|
||||
lock (QueuedActions) { QueuedActions.Clear(); }
|
||||
}
|
||||
|
||||
public static string DumpCharacters()
|
||||
{
|
||||
string s = "Characters:";
|
||||
lock (EntityPool.PedsLock)
|
||||
{
|
||||
foreach (int id in EntityPool.GetPedIDs())
|
||||
{
|
||||
var c = EntityPool.GetPedByID(id);
|
||||
s+=$"\r\nID:{c.ID} Owner:{c.OwnerID} LastUpdated:{c.LastUpdated} LastSynced:{c.LastSynced} LastStateSynced:{c.LastStateSynced}";
|
||||
// s+=$"\r\n{c.IsAiming} {c.IsJumping} {c.IsOnFire} {c.IsOnLadder} {c.IsRagdoll} {c.IsReloading} {c.IsShooting} {c.Speed}";
|
||||
}
|
||||
}
|
||||
Main.Logger.Trace(s);
|
||||
return s;
|
||||
}
|
||||
public static string DumpPlayers()
|
||||
{
|
||||
string s = "Players:";
|
||||
foreach (PlayerData p in PlayerList.Players)
|
||||
{
|
||||
|
||||
s+=$"\r\nID:{p.PedID} Username:{p.Username}";
|
||||
}
|
||||
Main.Logger.Trace(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ using System.Drawing;
|
||||
|
||||
using LemonUI;
|
||||
using LemonUI.Menus;
|
||||
using LemonUI.Scaleform;
|
||||
|
||||
namespace RageCoop.Client.Menus
|
||||
{
|
||||
@ -18,11 +19,22 @@ namespace RageCoop.Client.Menus
|
||||
UseMouse = false,
|
||||
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
|
||||
};
|
||||
public static PopUp PopUp=new PopUp()
|
||||
{
|
||||
Title="",
|
||||
Prompt="",
|
||||
Subtitle = "",
|
||||
Error="",
|
||||
ShowBackground = true,
|
||||
Visible=false,
|
||||
};
|
||||
public static NativeMenu LastMenu { get; set; } = Menu;
|
||||
#region ITEMS
|
||||
private static readonly NativeItem _usernameItem = new NativeItem("Username") { AltTitle = Main.Settings.Username };
|
||||
private static readonly NativeItem _passwordItem = new NativeItem("Password") { AltTitle = new string('*',Main.Settings.Password.Length) };
|
||||
|
||||
public static readonly NativeItem ServerIpItem = new NativeItem("Server IP") { AltTitle = Main.Settings.LastServerAddress };
|
||||
private static readonly NativeItem _serverConnectItem = new NativeItem("Connect");
|
||||
internal static readonly NativeItem _serverConnectItem = new NativeItem("Connect");
|
||||
private static readonly NativeItem _aboutItem = new NativeItem("About", "~y~SOURCE~s~~n~" +
|
||||
"https://github.com/RAGECOOP~n~" +
|
||||
"~y~VERSION~s~~n~" +
|
||||
@ -36,10 +48,12 @@ namespace RageCoop.Client.Menus
|
||||
/// </summary>
|
||||
static CoopMenu()
|
||||
{
|
||||
|
||||
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
|
||||
Menu.Title.Color = Color.FromArgb(255, 165, 0);
|
||||
|
||||
_usernameItem.Activated += UsernameActivated;
|
||||
_passwordItem.Activated+=_passwordActivated;
|
||||
ServerIpItem.Activated += ServerIpActivated;
|
||||
_serverConnectItem.Activated += (sender, item) => { Networking.ToggleConnection(Main.Settings.LastServerAddress); };
|
||||
|
||||
@ -47,6 +61,7 @@ namespace RageCoop.Client.Menus
|
||||
Menu.AddSubMenu(ServersMenu.Menu);
|
||||
|
||||
Menu.Add(_usernameItem);
|
||||
Menu.Add(_passwordItem);
|
||||
Menu.Add(ServerIpItem);
|
||||
Menu.Add(_serverConnectItem);
|
||||
|
||||
@ -61,10 +76,37 @@ namespace RageCoop.Client.Menus
|
||||
MenuPool.Add(DebugMenu.Menu);
|
||||
MenuPool.Add(DebugMenu.DiagnosticMenu);
|
||||
MenuPool.Add(ServersMenu.Menu);
|
||||
MenuPool.Add(PopUp);
|
||||
|
||||
Menu.Add(_aboutItem);
|
||||
}
|
||||
|
||||
|
||||
public static bool ShowPopUp(string prompt, string title,string subtitle,string error,bool showbackground)
|
||||
{
|
||||
PopUp.Prompt=prompt;
|
||||
PopUp.Title=title;
|
||||
PopUp.Subtitle=subtitle;
|
||||
PopUp.Error=error;
|
||||
PopUp.ShowBackground=showbackground;
|
||||
PopUp.Visible=true;
|
||||
while (true)
|
||||
{
|
||||
Game.DisableAllControlsThisFrame();
|
||||
MenuPool.Process();
|
||||
if (Game.IsControlJustPressed(Control.FrontendAccept))
|
||||
{
|
||||
PopUp.Visible=false;
|
||||
return true;
|
||||
}
|
||||
else if (Game.IsControlJustPressed(Control.FrontendCancel))
|
||||
{
|
||||
PopUp.Visible = false;
|
||||
return false;
|
||||
}
|
||||
Script.Yield();
|
||||
}
|
||||
}
|
||||
public static void UsernameActivated(object a, System.EventArgs b)
|
||||
{
|
||||
string newUsername = Game.GetUserInput(WindowTitle.EnterMessage20, _usernameItem.AltTitle, 20);
|
||||
@ -77,6 +119,16 @@ namespace RageCoop.Client.Menus
|
||||
}
|
||||
}
|
||||
|
||||
private static void _passwordActivated(object sender, System.EventArgs e)
|
||||
{
|
||||
string newPass = Game.GetUserInput(WindowTitle.EnterMessage20, "", 20);
|
||||
if (!string.IsNullOrWhiteSpace(newPass))
|
||||
{
|
||||
Main.Settings.Password = newPass;
|
||||
Util.SaveSettings();
|
||||
_passwordItem.AltTitle = new string('*', newPass.Length);
|
||||
}
|
||||
}
|
||||
public static void ServerIpActivated(object a, System.EventArgs b)
|
||||
{
|
||||
string newServerIp = Game.GetUserInput(WindowTitle.EnterMessage60, ServerIpItem.AltTitle, 60);
|
@ -77,7 +77,7 @@ namespace RageCoop.Client.Menus
|
||||
private static void GetAllServers()
|
||||
{
|
||||
List<ServerListClass> serverList = null;
|
||||
var realUrl = Main.Settings.MasterServer=="[AUTO]" ? DownloadString("https://ragecoop.online/stuff/masterserver") : Main.Settings.MasterServer;
|
||||
var realUrl = Main.Settings.MasterServer;
|
||||
serverList = JsonConvert.DeserializeObject<List<ServerListClass>>(DownloadString(realUrl));
|
||||
|
||||
// Need to be processed in main thread
|
@ -118,11 +118,6 @@ namespace RageCoop.Client.Menus
|
||||
{
|
||||
Networking.ShowNetworkInfo = _showNetworkInfoItem.Checked;
|
||||
|
||||
if (!Networking.ShowNetworkInfo)
|
||||
{
|
||||
Networking.BytesReceived = 0;
|
||||
Networking.BytesSend = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
200
RageCoop.Client/Networking/DownloadManager.cs
Normal file
200
RageCoop.Client/Networking/DownloadManager.cs
Normal file
@ -0,0 +1,200 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using RageCoop.Core;
|
||||
using System;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class DownloadManager
|
||||
{
|
||||
static DownloadManager()
|
||||
{
|
||||
Networking.RequestHandlers.Add(PacketType.FileTransferRequest, (data) =>
|
||||
{
|
||||
var fr = new Packets.FileTransferRequest();
|
||||
fr.Unpack(data);
|
||||
if (fr.Name.EndsWith(".zip"))
|
||||
{
|
||||
_zips.Add(fr.Name);
|
||||
}
|
||||
return new Packets.FileTransferResponse()
|
||||
{
|
||||
ID= fr.ID,
|
||||
Response=AddFile(fr.ID,fr.Name,fr.FileLength) ? FileResponse.NeedToDownload : FileResponse.AlreadyExists
|
||||
};
|
||||
});
|
||||
Networking.RequestHandlers.Add(PacketType.FileTransferComplete, (data) =>
|
||||
{
|
||||
Packets.FileTransferComplete packet = new Packets.FileTransferComplete();
|
||||
packet.Unpack(data);
|
||||
|
||||
Main.Logger.Debug($"Finalizing download:{packet.ID}");
|
||||
Complete(packet.ID);
|
||||
|
||||
// Inform the server that the download is completed
|
||||
return new Packets.FileTransferResponse()
|
||||
{
|
||||
ID= packet.ID,
|
||||
Response=FileResponse.Completed
|
||||
};
|
||||
});
|
||||
Networking.RequestHandlers.Add(PacketType.AllResourcesSent, (data) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Main.Resources.Load(ResourceFolder,_zips.ToArray());
|
||||
return new Packets.FileTransferResponse() { ID=0, Response=FileResponse.Loaded };
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
|
||||
Main.Logger.Error("Error occurred when loading server resource:");
|
||||
Main.Logger.Error(ex);
|
||||
return new Packets.FileTransferResponse() { ID=0, Response=FileResponse.LoadFailed };
|
||||
}
|
||||
});
|
||||
}
|
||||
public static string ResourceFolder {
|
||||
get {
|
||||
return Path.Combine(Main.Settings.DataDirectory,"Resources", Main.Settings.LastServerAddress.Replace(":", "."));
|
||||
}
|
||||
}
|
||||
private static readonly Dictionary<int, DownloadFile> InProgressDownloads = new Dictionary<int, DownloadFile>();
|
||||
private static readonly List<string> _zips = new List<string>();
|
||||
public static bool AddFile(int id, string name, long length)
|
||||
{
|
||||
Main.Logger.Debug($"Downloading file to {ResourceFolder}\\{name} , id:{id}");
|
||||
if (!Directory.Exists(ResourceFolder))
|
||||
{
|
||||
Directory.CreateDirectory(ResourceFolder);
|
||||
}
|
||||
|
||||
if (FileAlreadyExists(ResourceFolder, name, length))
|
||||
{
|
||||
Main.Logger.Debug($"File already exists! canceling download:{name}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name.EndsWith(".zip"))
|
||||
{
|
||||
Main.Logger.Error($"File download blocked! [{name}]");
|
||||
return false;
|
||||
}
|
||||
lock (InProgressDownloads)
|
||||
{
|
||||
InProgressDownloads.Add(id, new DownloadFile()
|
||||
{
|
||||
FileID = id,
|
||||
FileName = name,
|
||||
FileLength = length,
|
||||
Stream = new FileStream($"{ResourceFolder}\\{name}", FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite)
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the file already exists and if the size correct otherwise delete this file
|
||||
/// </summary>
|
||||
/// <param name="folder"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
private static bool FileAlreadyExists(string folder, string name, long length)
|
||||
{
|
||||
string filePath = $"{folder}\\{name}";
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
if (new FileInfo(filePath).Length == length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete the file because the length is wrong (maybe the file was updated)
|
||||
File.Delete(filePath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void Write(int id, byte[] chunk)
|
||||
{
|
||||
lock (InProgressDownloads)
|
||||
{
|
||||
DownloadFile file;
|
||||
if (InProgressDownloads.TryGetValue(id, out file))
|
||||
{
|
||||
|
||||
file.Stream.Write(chunk, 0, chunk.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Logger.Trace($"Received unhandled file chunk:{id}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void Complete(int id)
|
||||
{
|
||||
DownloadFile f;
|
||||
|
||||
if (InProgressDownloads.TryGetValue(id, out f))
|
||||
{
|
||||
InProgressDownloads.Remove(id);
|
||||
f.Dispose();
|
||||
if (f.FileName.EndsWith(".zip"))
|
||||
{
|
||||
_zips.Add(f.FileName);
|
||||
}
|
||||
Main.Logger.Info($"Download finished:{f.FileName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Logger.Error($"Download not found! {id}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cleanup()
|
||||
{
|
||||
lock (InProgressDownloads)
|
||||
{
|
||||
foreach (var file in InProgressDownloads.Values)
|
||||
{
|
||||
file.Dispose();
|
||||
}
|
||||
InProgressDownloads.Clear();
|
||||
}
|
||||
if (Directory.Exists(ResourceFolder))
|
||||
{
|
||||
|
||||
foreach (var zip in Directory.GetDirectories(ResourceFolder, "*.zip"))
|
||||
{
|
||||
File.Delete(zip);
|
||||
}
|
||||
}
|
||||
_zips.Clear();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal class DownloadFile: System.IDisposable
|
||||
{
|
||||
public int FileID { get; set; } = 0;
|
||||
public string FileName { get; set; } = string.Empty;
|
||||
public long FileLength { get; set; } = 0;
|
||||
public long FileWritten { get; set; } = 0;
|
||||
public FileStream Stream { get; set; }
|
||||
public void Dispose()
|
||||
{
|
||||
if(Stream!= null)
|
||||
{
|
||||
Stream.Flush();
|
||||
Stream.Close();
|
||||
Stream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
153
RageCoop.Client/Networking/Networking.cs
Normal file
153
RageCoop.Client/Networking/Networking.cs
Normal file
@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Core;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static partial class Networking
|
||||
{
|
||||
public static NetClient Client;
|
||||
public static float Latency = 0;
|
||||
public static bool ShowNetworkInfo = false;
|
||||
public static Security Security;
|
||||
static Networking()
|
||||
{
|
||||
Security=new Security(Main.Logger);
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (Client!=null)
|
||||
{
|
||||
ProcessMessage(Client.WaitMessage(200));
|
||||
Client.FlushSendQueue();
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void ToggleConnection(string address,string username=null,string password=null)
|
||||
{
|
||||
if (IsOnServer)
|
||||
{
|
||||
Client.Disconnect("Bye!");
|
||||
}
|
||||
else
|
||||
{
|
||||
password = password ?? Main.Settings.Password;
|
||||
username=username ?? Main.Settings.Username;
|
||||
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0")
|
||||
{
|
||||
AutoFlushSendQueue = true
|
||||
};
|
||||
|
||||
config.EnableMessageType(NetIncomingMessageType.ConnectionLatencyUpdated);
|
||||
config.EnableMessageType(NetIncomingMessageType.UnconnectedData);
|
||||
|
||||
|
||||
string[] ip = new string[2];
|
||||
|
||||
int idx = address.LastIndexOf(':');
|
||||
if (idx != -1)
|
||||
{
|
||||
ip[0] = address.Substring(0, idx);
|
||||
ip[1] = address.Substring(idx + 1);
|
||||
}
|
||||
|
||||
if (ip.Length != 2)
|
||||
{
|
||||
throw new Exception("Malformed URL");
|
||||
}
|
||||
|
||||
EntityPool.AddPlayer();
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
DownloadManager.Cleanup();
|
||||
Client = new NetClient(config);
|
||||
Client.Start();
|
||||
Main.QueueAction(() => { GTA.UI.Notification.Show($"~y~Trying to connect..."); });
|
||||
Menus.CoopMenu._serverConnectItem.Enabled=false;
|
||||
Security.Regen();
|
||||
if(!GetServerPublicKey(address))
|
||||
{
|
||||
Menus.CoopMenu._serverConnectItem.Enabled=true;
|
||||
throw new TimeoutException("Failed to retrive server's public key");
|
||||
}
|
||||
|
||||
// Send HandshakePacket
|
||||
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
|
||||
var handshake = new Packets.Handshake()
|
||||
{
|
||||
PedID = Main.LocalPlayerID,
|
||||
Username =username,
|
||||
ModVersion = Main.CurrentVersion,
|
||||
PassHashEncrypted=Security.Encrypt(password.GetHash())
|
||||
};
|
||||
|
||||
Security.GetSymmetricKeysCrypted(out handshake.AesKeyCrypted, out handshake.AesIVCrypted);
|
||||
handshake.Pack(outgoingMessage);
|
||||
Client.Connect(ip[0], short.Parse(ip[1]), outgoingMessage);
|
||||
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Main.Logger.Error("Cannot connect to server: ", ex);
|
||||
Main.QueueAction(() => GTA.UI.Notification.Show("Cannot connect to server: "+ex.Message));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
public static bool IsOnServer
|
||||
{
|
||||
get { return Client?.ConnectionStatus == NetConnectionStatus.Connected; }
|
||||
}
|
||||
|
||||
#region -- GET --
|
||||
#region -- PLAYER --
|
||||
private static void PlayerConnect(Packets.PlayerConnect packet)
|
||||
{
|
||||
var p = new PlayerData
|
||||
{
|
||||
PedID = packet.PedID,
|
||||
Username= packet.Username,
|
||||
};
|
||||
GTA.UI.Notification.Show($"{p.Username} connected.");
|
||||
PlayerList.SetPlayer(packet.PedID, packet.Username);
|
||||
|
||||
Main.Logger.Debug($"player connected:{p.Username}");
|
||||
}
|
||||
private static void PlayerDisconnect(Packets.PlayerDisconnect packet)
|
||||
{
|
||||
var name=PlayerList.GetPlayer(packet.PedID).Username;
|
||||
GTA.UI.Notification.Show($"{name} left.");
|
||||
PlayerList.RemovePlayer(packet.PedID);
|
||||
EntityPool.RemoveAllFromPlayer(packet.PedID);
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endregion // -- PLAYER --
|
||||
|
||||
private static bool GetServerPublicKey(string address,int timeout=10000)
|
||||
{
|
||||
var msg=Client.CreateMessage();
|
||||
new Packets.PublicKeyRequest().Pack(msg);
|
||||
var adds =address.Split(':');
|
||||
Client.SendUnconnectedMessage(msg,adds[0],int.Parse(adds[1]));
|
||||
return PublicKeyReceived.WaitOne(timeout);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
429
RageCoop.Client/Networking/Receive.cs
Normal file
429
RageCoop.Client/Networking/Receive.cs
Normal file
@ -0,0 +1,429 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Core;
|
||||
using GTA;
|
||||
using RageCoop.Client.Menus;
|
||||
using System.Threading;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static partial class Networking
|
||||
{
|
||||
private static Func<byte, BitReader, object> _resolveHandle = (t, reader) =>
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case 50:
|
||||
return EntityPool.ServerProps[reader.ReadInt()].MainProp?.Handle;
|
||||
case 51:
|
||||
return EntityPool.GetPedByID(reader.ReadInt())?.MainPed?.Handle;
|
||||
case 52:
|
||||
return EntityPool.GetVehicleByID(reader.ReadInt())?.MainVehicle?.Handle;
|
||||
case 60:
|
||||
return EntityPool.ServerBlips[reader.ReadInt()].Handle;
|
||||
default:
|
||||
throw new ArgumentException("Cannot resolve server side argument: "+t);
|
||||
|
||||
}
|
||||
};
|
||||
private static AutoResetEvent PublicKeyReceived=new AutoResetEvent(false);
|
||||
|
||||
private static Dictionary<int, Action<PacketType, byte[]>> PendingResponses = new Dictionary<int, Action<PacketType, byte[]>>();
|
||||
internal static Dictionary<PacketType, Func< byte[], Packet>> RequestHandlers = new Dictionary<PacketType, Func< byte[], Packet>>();
|
||||
public static void ProcessMessage(NetIncomingMessage message)
|
||||
{
|
||||
if(message == null) { return; }
|
||||
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
|
||||
|
||||
string reason = message.ReadString();
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
#if !NON_INTERACTIVE
|
||||
CoopMenu.InitiateConnectionMenuSetting();
|
||||
#endif
|
||||
break;
|
||||
case NetConnectionStatus.Connected:
|
||||
Main.QueueAction(() => {
|
||||
CoopMenu.ConnectedMenuSetting();
|
||||
Main.MainChat.Init();
|
||||
PlayerList.Cleanup();
|
||||
GTA.UI.Notification.Show("~g~Connected!");
|
||||
});
|
||||
|
||||
Main.Logger.Info(">> Connected <<");
|
||||
break;
|
||||
case NetConnectionStatus.Disconnected:
|
||||
DownloadManager.Cleanup();
|
||||
|
||||
// Reset all values
|
||||
Latency = 0;
|
||||
|
||||
Main.QueueAction(() => Main.CleanUpWorld());
|
||||
|
||||
if (Main.MainChat.Focused)
|
||||
{
|
||||
Main.MainChat.Focused = false;
|
||||
}
|
||||
|
||||
Main.QueueAction(() => Main.CleanUp());
|
||||
|
||||
#if !NON_INTERACTIVE
|
||||
CoopMenu.DisconnectedMenuSetting();
|
||||
#endif
|
||||
|
||||
Main.QueueAction(() =>
|
||||
GTA.UI.Notification.Show("~r~Disconnected: " + reason));
|
||||
|
||||
Main.Resources.Unload();
|
||||
Main.Logger.Info($">> Disconnected << reason: {reason}");
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NetIncomingMessageType.Data:
|
||||
{
|
||||
|
||||
if (message.LengthBytes==0) { break; }
|
||||
var packetType = PacketType.Unknown;
|
||||
try
|
||||
{
|
||||
|
||||
// Get packet type
|
||||
packetType = (PacketType)message.ReadByte();
|
||||
switch (packetType)
|
||||
{
|
||||
case PacketType.Response:
|
||||
{
|
||||
int id = message.ReadInt32();
|
||||
if (PendingResponses.TryGetValue(id, out var callback))
|
||||
{
|
||||
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32()));
|
||||
PendingResponses.Remove(id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PacketType.Request:
|
||||
{
|
||||
int id = message.ReadInt32();
|
||||
var realType = (PacketType)message.ReadByte();
|
||||
int len = message.ReadInt32();
|
||||
Main.Logger.Debug($"{id},{realType},{len}");
|
||||
if (RequestHandlers.TryGetValue(realType, out var handler))
|
||||
{
|
||||
var response = Client.CreateMessage();
|
||||
response.Write((byte)PacketType.Response);
|
||||
response.Write(id);
|
||||
handler(message.ReadBytes(len)).Pack(response);
|
||||
Client.SendMessage(response, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
byte[] data = message.ReadBytes(message.ReadInt32());
|
||||
|
||||
HandlePacket(packetType, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.QueueAction(() => {
|
||||
GTA.UI.Notification.Show("~r~~h~Packet Error");
|
||||
return true;
|
||||
});
|
||||
Main.Logger.Error($"[{packetType}] {ex.Message}");
|
||||
Main.Logger.Error(ex);
|
||||
Client.Disconnect($"Packet Error [{packetType}]");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.ConnectionLatencyUpdated:
|
||||
Latency = message.ReadFloat();
|
||||
break;
|
||||
case NetIncomingMessageType.UnconnectedData:
|
||||
{
|
||||
var packetType = (PacketType)message.ReadByte();
|
||||
int len = message.ReadInt32();
|
||||
byte[] data = message.ReadBytes(len);
|
||||
if (packetType==PacketType.PublicKeyResponse)
|
||||
{
|
||||
var packet=new Packets.PublicKeyResponse();
|
||||
packet.Unpack(data);
|
||||
Security.SetServerPublicKey(packet.Modulus,packet.Exponent);
|
||||
PublicKeyReceived.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Client.Recycle(message);
|
||||
}
|
||||
private static void HandlePacket(PacketType packetType, byte[] data)
|
||||
{
|
||||
|
||||
switch (packetType)
|
||||
{
|
||||
case PacketType.PlayerConnect:
|
||||
{
|
||||
|
||||
Packets.PlayerConnect packet = new Packets.PlayerConnect();
|
||||
packet.Unpack(data);
|
||||
|
||||
Main.QueueAction(() => PlayerConnect(packet));
|
||||
}
|
||||
break;
|
||||
case PacketType.PlayerDisconnect:
|
||||
{
|
||||
|
||||
Packets.PlayerDisconnect packet = new Packets.PlayerDisconnect();
|
||||
packet.Unpack(data);
|
||||
Main.QueueAction(() => PlayerDisconnect(packet));
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketType.PlayerInfoUpdate:
|
||||
{
|
||||
var packet = new Packets.PlayerInfoUpdate();
|
||||
packet.Unpack(data);
|
||||
PlayerList.UpdatePlayer(packet);
|
||||
break;
|
||||
}
|
||||
#region ENTITY SYNC
|
||||
case PacketType.VehicleSync:
|
||||
{
|
||||
|
||||
Packets.VehicleSync packet = new Packets.VehicleSync();
|
||||
packet.Unpack(data);
|
||||
VehicleSync(packet);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketType.PedSync:
|
||||
{
|
||||
|
||||
Packets.PedSync packet = new Packets.PedSync();
|
||||
packet.Unpack(data);
|
||||
PedSync(packet);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketType.VehicleStateSync:
|
||||
{
|
||||
|
||||
Packets.VehicleStateSync packet = new Packets.VehicleStateSync();
|
||||
packet.Unpack(data);
|
||||
VehicleStateSync(packet);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketType.PedStateSync:
|
||||
{
|
||||
|
||||
|
||||
Packets.PedStateSync packet = new Packets.PedStateSync();
|
||||
packet.Unpack(data);
|
||||
PedStateSync(packet);
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketType.ProjectileSync:
|
||||
{
|
||||
Packets.ProjectileSync packet = new Packets.ProjectileSync();
|
||||
packet.Unpack(data);
|
||||
ProjectileSync(packet);
|
||||
break;
|
||||
}
|
||||
#endregion
|
||||
case PacketType.ChatMessage:
|
||||
{
|
||||
|
||||
Packets.ChatMessage packet = new Packets.ChatMessage();
|
||||
packet.Unpack(data);
|
||||
|
||||
Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; });
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketType.CustomEvent:
|
||||
{
|
||||
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
|
||||
packet.Unpack(data);
|
||||
Scripting.API.Events.InvokeCustomEventReceived(packet);
|
||||
}
|
||||
break;
|
||||
case PacketType.CustomEventQueued:
|
||||
{
|
||||
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
|
||||
Main.QueueAction(() =>
|
||||
{
|
||||
packet.Unpack(data);
|
||||
Scripting.API.Events.InvokeCustomEventReceived(packet);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PacketType.FileTransferChunk:
|
||||
{
|
||||
Packets.FileTransferChunk packet = new Packets.FileTransferChunk();
|
||||
packet.Unpack(data);
|
||||
DownloadManager.Write(packet.ID, packet.FileChunk);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (packetType.IsSyncEvent())
|
||||
{
|
||||
// Dispatch to main thread
|
||||
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, data); return true; });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void PedSync(Packets.PedSync packet)
|
||||
{
|
||||
SyncedPed c = EntityPool.GetPedByID(packet.ID);
|
||||
if (c==null)
|
||||
{
|
||||
// Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}");
|
||||
EntityPool.ThreadSafe.Add(c=new SyncedPed(packet.ID));
|
||||
}
|
||||
PedDataFlags flags = packet.Flag;
|
||||
c.ID=packet.ID;
|
||||
//c.OwnerID=packet.OwnerID;
|
||||
c.Health = packet.Health;
|
||||
c.Position = packet.Position;
|
||||
c.Rotation = packet.Rotation;
|
||||
c.Velocity = packet.Velocity;
|
||||
c.Speed = packet.Speed;
|
||||
c.CurrentWeaponHash = packet.CurrentWeaponHash;
|
||||
c.IsAiming = flags.HasPedFlag(PedDataFlags.IsAiming);
|
||||
c.IsReloading = flags.HasPedFlag(PedDataFlags.IsReloading);
|
||||
c.IsJumping = flags.HasPedFlag(PedDataFlags.IsJumping);
|
||||
c.IsRagdoll = flags.HasPedFlag(PedDataFlags.IsRagdoll);
|
||||
c.IsOnFire = flags.HasPedFlag(PedDataFlags.IsOnFire);
|
||||
c.IsInParachuteFreeFall = flags.HasPedFlag(PedDataFlags.IsInParachuteFreeFall);
|
||||
c.IsParachuteOpen = flags.HasPedFlag(PedDataFlags.IsParachuteOpen);
|
||||
c.IsOnLadder = flags.HasPedFlag(PedDataFlags.IsOnLadder);
|
||||
c.IsVaulting = flags.HasPedFlag(PedDataFlags.IsVaulting);
|
||||
c.IsInCover = flags.HasPedFlag(PedDataFlags.IsInCover);
|
||||
c.IsInStealthMode = flags.HasPedFlag(PedDataFlags.IsInStealthMode);
|
||||
c.Heading=packet.Heading;
|
||||
c.LastSynced = Main.Ticked;
|
||||
if (c.IsAiming)
|
||||
{
|
||||
c.AimCoords = packet.AimCoords;
|
||||
}
|
||||
if (c.IsRagdoll)
|
||||
{
|
||||
c.RotationVelocity=packet.RotationVelocity;
|
||||
}
|
||||
}
|
||||
private static void PedStateSync(Packets.PedStateSync packet)
|
||||
{
|
||||
SyncedPed c = EntityPool.GetPedByID(packet.ID);
|
||||
if (c==null) { return; }
|
||||
c.ID=packet.ID;
|
||||
c.OwnerID=packet.OwnerID;
|
||||
c.Clothes=packet.Clothes;
|
||||
c.WeaponComponents=packet.WeaponComponents;
|
||||
c.WeaponTint=packet.WeaponTint;
|
||||
c.ModelHash=packet.ModelHash;
|
||||
c.LastStateSynced = Main.Ticked;
|
||||
c.BlipColor=packet.BlipColor;
|
||||
c.BlipSprite=packet.BlipSprite;
|
||||
c.BlipScale=packet.BlipScale;
|
||||
}
|
||||
private static void VehicleSync(Packets.VehicleSync packet)
|
||||
{
|
||||
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
|
||||
if (v==null)
|
||||
{
|
||||
EntityPool.ThreadSafe.Add(v=new SyncedVehicle(packet.ID));
|
||||
}
|
||||
if (v.IsLocal) { return; }
|
||||
v.ID= packet.ID;
|
||||
v.Position=packet.Position;
|
||||
v.Quaternion=packet.Quaternion;
|
||||
v.SteeringAngle=packet.SteeringAngle;
|
||||
v.ThrottlePower=packet.ThrottlePower;
|
||||
v.BrakePower=packet.BrakePower;
|
||||
v.Velocity=packet.Velocity;
|
||||
v.RotationVelocity=packet.RotationVelocity;
|
||||
v.DeluxoWingRatio=packet.DeluxoWingRatio;
|
||||
v.LastSynced=Main.Ticked;
|
||||
}
|
||||
private static void VehicleStateSync(Packets.VehicleStateSync packet)
|
||||
{
|
||||
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
|
||||
if (v==null||v.IsLocal) { return; }
|
||||
v.ID= packet.ID;
|
||||
v.OwnerID= packet.OwnerID;
|
||||
v.DamageModel=packet.DamageModel;
|
||||
v.EngineHealth=packet.EngineHealth;
|
||||
v.OwnerID=packet.OwnerID;
|
||||
v.Mods=packet.Mods;
|
||||
v.ModelHash=packet.ModelHash;
|
||||
v.Colors=packet.Colors;
|
||||
v.LandingGear=packet.LandingGear;
|
||||
v.RoofState=(VehicleRoofState)packet.RoofState;
|
||||
v.EngineRunning = packet.Flag.HasVehFlag(VehicleDataFlags.IsEngineRunning);
|
||||
v.LightsOn = packet.Flag.HasVehFlag(VehicleDataFlags.AreLightsOn);
|
||||
v.BrakeLightsOn = packet.Flag.HasVehFlag(VehicleDataFlags.AreBrakeLightsOn);
|
||||
v.HighBeamsOn = packet.Flag.HasVehFlag(VehicleDataFlags.AreHighBeamsOn);
|
||||
v.SireneActive = packet.Flag.HasVehFlag(VehicleDataFlags.IsSirenActive);
|
||||
v.IsDead = packet.Flag.HasVehFlag(VehicleDataFlags.IsDead);
|
||||
v.HornActive = packet.Flag.HasVehFlag(VehicleDataFlags.IsHornActive);
|
||||
v.Transformed = packet.Flag.HasVehFlag(VehicleDataFlags.IsTransformed);
|
||||
v.Passengers=new Dictionary<VehicleSeat, SyncedPed>();
|
||||
v.LockStatus=packet.LockStatus;
|
||||
v.RadioStation=packet.RadioStation;
|
||||
v.LicensePlate=packet.LicensePlate;
|
||||
v.Livery=packet.Livery;
|
||||
v.Flags=packet.Flag;
|
||||
foreach (KeyValuePair<int, int> pair in packet.Passengers)
|
||||
{
|
||||
if (EntityPool.PedExists(pair.Value))
|
||||
{
|
||||
v.Passengers.Add((VehicleSeat)pair.Key, EntityPool.GetPedByID(pair.Value));
|
||||
}
|
||||
}
|
||||
v.LastStateSynced= Main.Ticked;
|
||||
|
||||
}
|
||||
private static void ProjectileSync(Packets.ProjectileSync packet)
|
||||
{
|
||||
var p = EntityPool.GetProjectileByID(packet.ID);
|
||||
if (p==null)
|
||||
{
|
||||
if (packet.Exploded) { return; }
|
||||
// Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}");
|
||||
EntityPool.ThreadSafe.Add(p=new SyncedProjectile(packet.ID));
|
||||
}
|
||||
p.Position=packet.Position;
|
||||
p.Rotation=packet.Rotation;
|
||||
p.Velocity=packet.Velocity;
|
||||
p.Hash=(WeaponHash)packet.WeaponHash;
|
||||
p.ShooterID=packet.ShooterID;
|
||||
p.Exploded=packet.Exploded;
|
||||
p.LastSynced=Main.Ticked;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Lidgren.Network;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Core;
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
@ -44,11 +39,11 @@ namespace RageCoop.Client
|
||||
Flag = p.GetPedFlags(),
|
||||
Heading=p.Heading,
|
||||
};
|
||||
if (packet.Flag.HasFlag(PedDataFlags.IsAiming))
|
||||
if (packet.Flag.HasPedFlag(PedDataFlags.IsAiming))
|
||||
{
|
||||
packet.AimCoords = p.GetAimCoord();
|
||||
}
|
||||
if (packet.Flag.HasFlag(PedDataFlags.IsRagdoll))
|
||||
if (packet.Flag.HasPedFlag(PedDataFlags.IsRagdoll))
|
||||
{
|
||||
packet.RotationVelocity=p.RotationVelocity;
|
||||
}
|
||||
@ -58,15 +53,32 @@ namespace RageCoop.Client
|
||||
{
|
||||
Ped p = c.MainPed;
|
||||
|
||||
var packet=new Packets.PedStateSync()
|
||||
var packet = new Packets.PedStateSync()
|
||||
{
|
||||
ID = c.ID,
|
||||
OwnerID=c.OwnerID,
|
||||
Clothes=p.GetPedClothes(),
|
||||
ModelHash=p.Model.Hash,
|
||||
WeaponComponents=p.Weapons.Current.GetWeaponComponents(),
|
||||
WeaponTint=(byte)Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, p, p.Weapons.Current.Hash),
|
||||
};
|
||||
Blip b;
|
||||
if (c.IsPlayer)
|
||||
{
|
||||
packet.BlipColor=Scripting.API.Config.BlipColor;
|
||||
packet.BlipSprite=Scripting.API.Config.BlipSprite;
|
||||
packet.BlipScale=Scripting.API.Config.BlipScale;
|
||||
}
|
||||
else if ((b = p.AttachedBlip) !=null)
|
||||
{
|
||||
packet.BlipColor=b.Color;
|
||||
packet.BlipSprite=b.Sprite;
|
||||
|
||||
if (packet.BlipSprite==BlipSprite.PoliceOfficer || packet.BlipSprite==BlipSprite.PoliceOfficer2)
|
||||
{
|
||||
packet.BlipScale=0.5f;
|
||||
}
|
||||
}
|
||||
Send(packet, ConnectionChannel.PedSync);
|
||||
}
|
||||
public static void SendVehicle(SyncedVehicle v)
|
||||
@ -96,7 +108,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
Function.Call<byte>(Hash.GET_VEHICLE_COLOURS, veh, &primaryColor, &secondaryColor);
|
||||
}
|
||||
var packet=new Packets.VehicleStateSync()
|
||||
var packet = new Packets.VehicleStateSync()
|
||||
{
|
||||
ID =v.ID,
|
||||
OwnerID = v.OwnerID,
|
||||
@ -104,11 +116,14 @@ namespace RageCoop.Client
|
||||
Colors=new byte[] { primaryColor, secondaryColor },
|
||||
DamageModel=veh.GetVehicleDamageModel(),
|
||||
LandingGear = veh.IsAircraft ? (byte)veh.LandingGearState : (byte)0,
|
||||
RoofState=(byte)veh.RoofState,
|
||||
Mods = veh.Mods.GetVehicleMods(),
|
||||
ModelHash=veh.Model.Hash,
|
||||
EngineHealth=veh.EngineHealth,
|
||||
Passengers=veh.GetPassengers(),
|
||||
LockStatus=veh.LockStatus,
|
||||
LicensePlate=Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh),
|
||||
Livery=Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh)
|
||||
};
|
||||
if (v.MainVehicle==Game.Player.LastVehicle)
|
||||
{
|
||||
@ -156,26 +171,6 @@ namespace RageCoop.Client
|
||||
Client.FlushSendQueue();
|
||||
|
||||
#if DEBUG
|
||||
if (ShowNetworkInfo)
|
||||
{
|
||||
BytesSend += outgoingMessage.LengthBytes;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
public static void SendDownloadFinish(byte id)
|
||||
{
|
||||
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
|
||||
|
||||
new Packets.FileTransferComplete() { ID = id }.Pack(outgoingMessage);
|
||||
|
||||
Client.SendMessage(outgoingMessage, NetDeliveryMethod.ReliableUnordered, (byte)ConnectionChannel.File);
|
||||
Client.FlushSendQueue();
|
||||
|
||||
#if DEBUG
|
||||
if (ShowNetworkInfo)
|
||||
{
|
||||
BytesSend += outgoingMessage.LengthBytes;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endregion
|
29
RageCoop.Client/Networking/Statistics.cs
Normal file
29
RageCoop.Client/Networking/Statistics.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static class Statistics
|
||||
{
|
||||
public static int BytesDownPerSecond{ get; private set; }
|
||||
public static int BytesUpPerSecond { get; private set; }
|
||||
static Statistics()
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var bu=Networking.Client.Statistics.SentBytes;
|
||||
var bd = Networking.Client.Statistics.ReceivedBytes;
|
||||
Thread.Sleep(1000);
|
||||
BytesUpPerSecond=Networking.Client.Statistics.SentBytes-bu;
|
||||
BytesDownPerSecond=Networking.Client.Statistics.ReceivedBytes-bd;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RageCoop.Core;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using RageCoop.Core;
|
||||
using GTA.Native;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
public static class PlayerList
|
||||
internal static class PlayerList
|
||||
{
|
||||
private const float LEFT_POSITION = 0.122f;
|
||||
private const float RIGHT_POSITION = 0.9f;
|
||||
@ -15,7 +16,7 @@ namespace RageCoop.Client
|
||||
public static ulong Pressed { get; set; }
|
||||
|
||||
public static bool LeftAlign = true;
|
||||
public static List<PlayerData> Players=new List<PlayerData> { };
|
||||
public static Dictionary<int,PlayerData> Players=new Dictionary<int, PlayerData> { };
|
||||
public static void Tick()
|
||||
{
|
||||
if (!Networking.IsOnServer)
|
||||
@ -52,7 +53,7 @@ namespace RageCoop.Client
|
||||
|
||||
foreach (var player in Players)
|
||||
{
|
||||
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Latency * 1000:N0}ms", player.Username, 116, 0, i - 1, "", "", 2, "", "", ' ');
|
||||
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Value.Latency * 1000:N0}ms", player.Value.Username, 116, 0, i - 1, "", "", 2, "", "", ' ');
|
||||
}
|
||||
|
||||
_mainScaleform.CallFunction("SET_TITLE", "Player list", $"{Players.Count+1} players");
|
||||
@ -61,36 +62,73 @@ namespace RageCoop.Client
|
||||
public static void SetPlayer(int id, string username,float latency=0)
|
||||
{
|
||||
|
||||
var toset = Players.Where(x => x.PedID==id);
|
||||
if (toset.Any())
|
||||
PlayerData p;
|
||||
if (Players.TryGetValue(id,out p))
|
||||
{
|
||||
var p=toset.First();
|
||||
p.Username=username;
|
||||
p.PedID=id;
|
||||
p.Latency=latency;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerData p = new PlayerData { PedID=id, Username=username,Latency=latency };
|
||||
Players.Add(p);
|
||||
p = new PlayerData { PedID=id, Username=username,Latency=latency };
|
||||
Players.Add(id,p);
|
||||
}
|
||||
}
|
||||
public static void UpdatePlayer(Packets.PlayerInfoUpdate packet)
|
||||
{
|
||||
var p = GetPlayer(packet.PedID);
|
||||
if(p?.Character != null)
|
||||
{
|
||||
p.Latency= packet.Latency;
|
||||
}
|
||||
}
|
||||
public static PlayerData GetPlayer(int id)
|
||||
{
|
||||
return Players.Find(x => x.PedID==id);
|
||||
PlayerData p;
|
||||
Players.TryGetValue(id, out p);
|
||||
return p;
|
||||
}
|
||||
public static PlayerData GetPlayer(SyncedPed p)
|
||||
{
|
||||
var player = GetPlayer(p.ID);
|
||||
if (player!=null)
|
||||
{
|
||||
player.Character=p;
|
||||
}
|
||||
return player;
|
||||
}
|
||||
public static void RemovePlayer(int id)
|
||||
{
|
||||
var p = Players.Where(x => x.PedID==id);
|
||||
if (p.Any())
|
||||
if (Players.ContainsKey(id))
|
||||
{
|
||||
Players.Remove(p.First());
|
||||
Players.Remove(id);
|
||||
}
|
||||
}
|
||||
public static void Cleanup()
|
||||
{
|
||||
Players=new List<PlayerData> { };
|
||||
Players=new Dictionary<int, PlayerData>{ };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal class PlayerData
|
||||
{
|
||||
public string Username { get; internal set; }
|
||||
/// <summary>
|
||||
/// Universal character ID.
|
||||
/// </summary>
|
||||
public int PedID
|
||||
{
|
||||
get; internal set;
|
||||
}
|
||||
public SyncedPed Character { get; set; }
|
||||
/// <summary>
|
||||
/// Player Latency in second.
|
||||
/// </summary>
|
||||
public float Latency { get; set; }
|
||||
|
||||
public bool DisplayNameTag { get; set; } = true;
|
||||
}
|
||||
}
|
63
RageCoop.Client/RageCoop.Client.csproj
Normal file
63
RageCoop.Client/RageCoop.Client.csproj
Normal file
@ -0,0 +1,63 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<UseWindowsForms>True</UseWindowsForms>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<DocumentationFile></DocumentationFile>
|
||||
<DebugType>portable</DebugType>
|
||||
<AssemblyVersion>0.5.0</AssemblyVersion>
|
||||
<FileVersion>0.5.0</FileVersion>
|
||||
<Version>0.5.0</Version>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<Authors>RAGECOOP</Authors>
|
||||
<Description>An API reference for developing client-side resource for RAGECOOP</Description>
|
||||
<PackageProjectUrl>https://ragecoop.online/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/RAGECOOP/RAGECOOP-V</RepositoryUrl>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<DefineConstants>SHVDN3</DefineConstants>
|
||||
<TargetFrameworks>net48</TargetFrameworks>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<OutDir>..\bin\Debug\Client</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<OutDir>..\bin\Release\Client</OutDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="icon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="LemonUI.SHVDN3">
|
||||
<HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Lidgren.Network">
|
||||
<HintPath>..\libs\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\libs\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet">
|
||||
<HintPath>..\libs\ScriptHookVDotNet.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3">
|
||||
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
6
RageCoop.Client/RageCoop.Client.csproj.user
Normal file
6
RageCoop.Client/RageCoop.Client.csproj.user
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_LastSelectedProfileId>M:\SandBox-Shared\repo\RageCoop\RageCoop-V\RageCoop.Client\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
|
||||
</PropertyGroup>
|
||||
</Project>
|
275
RageCoop.Client/Scripting/API.cs
Normal file
275
RageCoop.Client/Scripting/API.cs
Normal file
@ -0,0 +1,275 @@
|
||||
#undef DEBUG
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using RageCoop.Core;
|
||||
using System.Windows.Forms;
|
||||
using GTA;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class CustomEventReceivedArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The event hash
|
||||
/// </summary>
|
||||
public int Hash { get; set; }
|
||||
/// <summary>
|
||||
/// Supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string, Vector3, Quaternion
|
||||
/// </summary>
|
||||
public object[] Args { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Provides vital functionality to interact with RAGECOOP
|
||||
/// </summary>
|
||||
public static class API
|
||||
{
|
||||
#region INTERNAL
|
||||
internal static Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new Dictionary<int, List<Action<CustomEventReceivedArgs>>>();
|
||||
#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 { return Main.Settings.Username; }
|
||||
set
|
||||
{
|
||||
if (Networking.IsOnServer || string.IsNullOrEmpty(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Main.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;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Base events for RageCoop
|
||||
/// </summary>
|
||||
public static class Events
|
||||
{
|
||||
#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
|
||||
/// <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;
|
||||
|
||||
/// <summary>
|
||||
/// This is equivalent of <see cref="GTA.Script.Tick"/>.
|
||||
/// </summary>
|
||||
public static event EmptyEvent OnTick;
|
||||
|
||||
/// <summary>
|
||||
/// This is equivalent of <see cref="Script.KeyDown"/>
|
||||
/// </summary>
|
||||
public static KeyEventHandler OnKeyDown;
|
||||
|
||||
/// <summary>
|
||||
/// This is equivalent of <see cref="Script.KeyUp"/>
|
||||
/// </summary>
|
||||
public static KeyEventHandler OnKeyUp;
|
||||
|
||||
#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 InvokeTick() { OnTick?.Invoke(); }
|
||||
|
||||
internal static void InvokeKeyDown(object s,KeyEventArgs e) { OnKeyDown?.Invoke(s,e); }
|
||||
|
||||
internal static void InvokeKeyUp(object s, KeyEventArgs e) { OnKeyUp?.Invoke(s, e); }
|
||||
|
||||
internal static void InvokeCustomEventReceived(Packets.CustomEvent p)
|
||||
{
|
||||
var args = new CustomEventReceivedArgs() { Hash=p.Hash, Args=p.Args};
|
||||
|
||||
// Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
|
||||
|
||||
List<Action<CustomEventReceivedArgs>> handlers;
|
||||
if (CustomEventHandlers.TryGetValue(p.Hash, out handlers))
|
||||
{
|
||||
handlers.ForEach((x) => { x.Invoke(args); });
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region PROPERTIES
|
||||
|
||||
/// <summary>
|
||||
/// Get the local player's ID
|
||||
/// </summary>
|
||||
/// <returns>PlayerID</returns>
|
||||
public static int LocalPlayerID
|
||||
{
|
||||
get { return Main.LocalPlayerID; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a RAGECOOP menu is visible
|
||||
/// </summary>
|
||||
public static bool IsMenuVisible
|
||||
{
|
||||
get { return Menus.CoopMenu.MenuPool.AreAnyVisible; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the RAGECOOP chat is visible
|
||||
/// </summary>
|
||||
public static bool IsChatFocused
|
||||
{
|
||||
get { return Main.MainChat.Focused; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the RAGECOOP list of players is visible
|
||||
/// </summary>
|
||||
public static bool IsPlayerListVisible
|
||||
{
|
||||
get { return Util.GetTickCount64() - PlayerList.Pressed < 5000; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version of RAGECOOP
|
||||
/// </summary>
|
||||
public static string CurrentVersion
|
||||
{
|
||||
get { return Main.CurrentVersion; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a <see cref="Core.Logger"/> that RAGECOOP is currently using.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Logger Logger
|
||||
{
|
||||
get
|
||||
{
|
||||
return Main.Logger;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region FUNCTIONS
|
||||
/// <summary>
|
||||
/// Send a local chat message to this player
|
||||
/// </summary>
|
||||
/// <param name="from">Name of the sender</param>
|
||||
/// <param name="message">The player's message</param>
|
||||
public static void LocalChatMessage(string from, string message)
|
||||
{
|
||||
Main.MainChat.AddMessage(from, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue an action to be executed on next tick.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
public static void QueueAction(Action a)
|
||||
{
|
||||
Main.QueueAction(a);
|
||||
}
|
||||
/// <summary>
|
||||
/// Disconnect from the server
|
||||
/// </summary>
|
||||
public static void Disconnect()
|
||||
{
|
||||
Networking.ToggleConnection(null);
|
||||
}
|
||||
|
||||
/// <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(int eventHash, params object[] args)
|
||||
{
|
||||
var p = new Packets.CustomEvent()
|
||||
{
|
||||
Args=args,
|
||||
Hash=eventHash
|
||||
};
|
||||
Networking.Send(p, ConnectionChannel.Event, Lidgren.Network.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, you can hash your event name with <see cref="Core.Scripting.CustomEvents.Hash(string)"/></param>
|
||||
/// <param name="handler">An handler to be invoked when the event is received from the server. </param>
|
||||
public static void RegisterCustomEventHandler(int hash, Action<CustomEventReceivedArgs> handler)
|
||||
{
|
||||
lock (CustomEventHandlers)
|
||||
{
|
||||
if (!CustomEventHandlers.TryGetValue(hash, out List<Action<CustomEventReceivedArgs>> handlers))
|
||||
{
|
||||
CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>());
|
||||
}
|
||||
handlers.Add(handler);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
305
RageCoop.Client/Scripting/BaseScript.cs
Normal file
305
RageCoop.Client/Scripting/BaseScript.cs
Normal file
@ -0,0 +1,305 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GTA.Native;
|
||||
using GTA.Math;
|
||||
using GTA;
|
||||
using RageCoop.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
internal class BaseScript : ClientScript
|
||||
{
|
||||
private bool _isHost=false;
|
||||
public override void OnStart()
|
||||
{
|
||||
API.Events.OnPedDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted, p.ID); };
|
||||
API.Events.OnVehicleDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); };
|
||||
|
||||
API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn,SetAutoRespawn);
|
||||
API.RegisterCustomEventHandler(CustomEvents.SetDisplayNameTag,SetDisplayNameTag);
|
||||
API.RegisterCustomEventHandler(CustomEvents.NativeCall,NativeCall);
|
||||
API.RegisterCustomEventHandler(CustomEvents.ServerPropSync, ServerObjectSync);
|
||||
API.RegisterCustomEventHandler(CustomEvents.DeleteServerProp, DeleteServerProp);
|
||||
API.RegisterCustomEventHandler(CustomEvents.DeleteEntity, DeleteEntity);
|
||||
API.RegisterCustomEventHandler(CustomEvents.SetDisplayNameTag, SetNameTag);
|
||||
API.RegisterCustomEventHandler(CustomEvents.ServerBlipSync, ServerBlipSync);
|
||||
API.RegisterCustomEventHandler(CustomEvents.DeleteServerBlip, DeleteServerBlip);
|
||||
API.RegisterCustomEventHandler(CustomEvents.CreateVehicle, CreateVehicle);
|
||||
API.RegisterCustomEventHandler(CustomEvents.UpdatePedBlip, UpdatePedBlip);
|
||||
API.RegisterCustomEventHandler(CustomEvents.IsHost, (e) => { _isHost=(bool)e.Args[0]; });
|
||||
API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, WeatherTimeSync);
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_isHost)
|
||||
{
|
||||
API.QueueAction(() =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var time = World.CurrentTimeOfDay;
|
||||
int weather1 = default(int);
|
||||
int weather2 = default(int);
|
||||
float percent2 = default(float);
|
||||
Function.Call(Hash._GET_WEATHER_TYPE_TRANSITION, &weather1, &weather2, &percent2);
|
||||
API.SendCustomEvent(CustomEvents.WeatherTimeSync, time.Hours, time.Minutes, time.Seconds, weather1, weather2, percent2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void WeatherTimeSync(CustomEventReceivedArgs e)
|
||||
{
|
||||
World.CurrentTimeOfDay=new TimeSpan((int)e.Args[0], (int)e.Args[1], (int)e.Args[2]);
|
||||
Function.Call(Hash._SET_WEATHER_TYPE_TRANSITION, (int)e.Args[3], (int)e.Args[4], (float)e.Args[5]);
|
||||
}
|
||||
|
||||
private void SetDisplayNameTag(CustomEventReceivedArgs e)
|
||||
{
|
||||
var p = PlayerList.GetPlayer((int)e.Args[0]);
|
||||
if(p != null) { p.DisplayNameTag=(bool)e.Args[1]; }
|
||||
}
|
||||
|
||||
private void UpdatePedBlip(CustomEventReceivedArgs e)
|
||||
{
|
||||
var p = Entity.FromHandle((int)e.Args[0]);
|
||||
if (p == null) { return; }
|
||||
if (p.Handle==Game.Player.Character.Handle)
|
||||
{
|
||||
API.Config.BlipColor=(BlipColor)(byte)e.Args[1];
|
||||
API.Config.BlipSprite=(BlipSprite)(ushort)e.Args[2];
|
||||
API.Config.BlipScale=(float)e.Args[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
var b = p.AttachedBlip;
|
||||
if (b == null) { b=p.AddBlip(); }
|
||||
b.Color=(BlipColor)(byte)e.Args[1];
|
||||
b.Sprite=(BlipSprite)(ushort)e.Args[2];
|
||||
b.Scale=(float)e.Args[3];
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateVehicle(CustomEventReceivedArgs e)
|
||||
{
|
||||
var vehicleModel = (Model)e.Args[1];
|
||||
vehicleModel.Request(1000);
|
||||
Vehicle veh= World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]);
|
||||
while (veh==null)
|
||||
{
|
||||
veh = World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]);
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
veh.CanPretendOccupants=false;
|
||||
var v = new SyncedVehicle()
|
||||
{
|
||||
ID=(int)e.Args[0],
|
||||
MainVehicle=veh,
|
||||
OwnerID=Main.LocalPlayerID,
|
||||
};
|
||||
EntityPool.Add(v);
|
||||
}
|
||||
|
||||
private void DeleteServerBlip(CustomEventReceivedArgs e)
|
||||
{
|
||||
if (EntityPool.ServerBlips.TryGetValue((int)e.Args[0], out var blip))
|
||||
{
|
||||
EntityPool.ServerBlips.Remove((int)e.Args[0]);
|
||||
blip?.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
private void ServerBlipSync(CustomEventReceivedArgs obj)
|
||||
{
|
||||
int id= (int)obj.Args[0];
|
||||
var sprite=(BlipSprite)(ushort)obj.Args[1];
|
||||
var color = (BlipColor)(byte)obj.Args[2];
|
||||
var scale=(float)obj.Args[3];
|
||||
var pos=(Vector3)obj.Args[4];
|
||||
int rot= (int)obj.Args[5];
|
||||
var name=(string)obj.Args[6];
|
||||
Blip blip;
|
||||
if (!EntityPool.ServerBlips.TryGetValue(id, out blip))
|
||||
{
|
||||
EntityPool.ServerBlips.Add(id, blip=World.CreateBlip(pos));
|
||||
}
|
||||
blip.Sprite = sprite;
|
||||
blip.Color = color;
|
||||
blip.Scale = scale;
|
||||
blip.Position = pos;
|
||||
blip.Rotation = rot;
|
||||
blip.Name = name;
|
||||
}
|
||||
|
||||
|
||||
private void DeleteEntity(CustomEventReceivedArgs e)
|
||||
{
|
||||
Entity.FromHandle((int)e.Args[0])?.Delete();
|
||||
}
|
||||
|
||||
public override void OnStop()
|
||||
{
|
||||
}
|
||||
private void SetNameTag(CustomEventReceivedArgs e)
|
||||
{
|
||||
var p =PlayerList.GetPlayer((int)e.Args[0]);
|
||||
if(p!= null)
|
||||
{
|
||||
p.DisplayNameTag=(bool)e.Args[1];
|
||||
}
|
||||
}
|
||||
private void SetAutoRespawn(CustomEventReceivedArgs args)
|
||||
{
|
||||
API.Config.EnableAutoRespawn=(bool)args.Args[0];
|
||||
}
|
||||
private void DeleteServerProp(CustomEventReceivedArgs e)
|
||||
{
|
||||
var id = (int)e.Args[0];
|
||||
if (EntityPool.ServerProps.TryGetValue(id, out var prop))
|
||||
{
|
||||
EntityPool.ServerProps.Remove(id);
|
||||
prop?.MainProp?.Delete();
|
||||
}
|
||||
|
||||
}
|
||||
private void ServerObjectSync(CustomEventReceivedArgs e)
|
||||
{
|
||||
SyncedProp prop;
|
||||
var id = (int)e.Args[0];
|
||||
lock (EntityPool.PropsLock)
|
||||
{
|
||||
if (!EntityPool.ServerProps.TryGetValue(id, out prop))
|
||||
{
|
||||
EntityPool.ServerProps.Add(id, prop=new SyncedProp(id));
|
||||
}
|
||||
}
|
||||
prop.LastSynced=Main.Ticked+1;
|
||||
prop.ModelHash= (Model)e.Args[1];
|
||||
prop.Position=(Vector3)e.Args[2];
|
||||
prop.Rotation=(Vector3)e.Args[3];
|
||||
prop.Update();
|
||||
}
|
||||
private void NativeCall(CustomEventReceivedArgs e)
|
||||
{
|
||||
List<InputArgument> arguments = new List<InputArgument>();
|
||||
int i;
|
||||
var ty = (byte)e.Args[0];
|
||||
TypeCode returnType=(TypeCode)ty;
|
||||
i = returnType==TypeCode.Empty ? 1 : 2;
|
||||
var hash = (Hash)e.Args[i++];
|
||||
for(; i<e.Args.Length;i++)
|
||||
{
|
||||
arguments.Add(GetInputArgument(e.Args[i]));
|
||||
}
|
||||
|
||||
if (returnType==TypeCode.Empty)
|
||||
{
|
||||
Function.Call(hash, arguments.ToArray());
|
||||
return;
|
||||
}
|
||||
var t = returnType;
|
||||
int id = (int)e.Args[1];
|
||||
|
||||
|
||||
switch (returnType)
|
||||
{
|
||||
case TypeCode.Boolean:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<bool>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
case TypeCode.Byte:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<byte>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
case TypeCode.Char:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<char>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
|
||||
case TypeCode.Single:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<float>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
|
||||
case TypeCode.Double:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<double>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
|
||||
case TypeCode.Int16:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<short>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
|
||||
case TypeCode.Int32:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<int>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
|
||||
case TypeCode.Int64:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<long>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
|
||||
case TypeCode.String:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<string>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
|
||||
case TypeCode.UInt16:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<ushort>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
|
||||
case TypeCode.UInt32:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<uint>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
|
||||
case TypeCode.UInt64:
|
||||
API.SendCustomEvent(CustomEvents.NativeResponse,
|
||||
new object[] { id, Function.Call<ulong>(hash, arguments.ToArray()) });
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
private InputArgument GetInputArgument(object obj)
|
||||
{
|
||||
// Implicit conversion
|
||||
switch (obj)
|
||||
{
|
||||
case byte _:
|
||||
return (byte)obj;
|
||||
case short _:
|
||||
return (short)obj;
|
||||
case ushort _:
|
||||
return (ushort)obj;
|
||||
case int _:
|
||||
return (int)obj;
|
||||
case uint _:
|
||||
return (uint)obj;
|
||||
case long _:
|
||||
return (long)obj;
|
||||
case ulong _:
|
||||
return (ulong)obj;
|
||||
case float _:
|
||||
return (float)obj;
|
||||
case bool _:
|
||||
return (bool)obj;
|
||||
case string _:
|
||||
return (obj as string);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
RageCoop.Client/Scripting/ClientScript.cs
Normal file
31
RageCoop.Client/Scripting/ClientScript.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using RageCoop.Core.Scripting;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Inherit from this class, constructor will be called automatically, but other scripts might have yet been loaded, you should use <see cref="OnStart"/>. to initiate your script.
|
||||
/// </summary>
|
||||
public abstract class ClientScript
|
||||
{
|
||||
/// <summary>
|
||||
/// This method would be called from background thread, call <see cref="API.QueueAction(System.Action)"/> to dispatch it to main thread.
|
||||
/// </summary>
|
||||
public abstract void OnStart();
|
||||
|
||||
/// <summary>
|
||||
/// This method would be called from background thread when the client disconnected from the server, you MUST terminate all background jobs/threads in this method.
|
||||
/// </summary>
|
||||
public abstract void OnStop();
|
||||
|
||||
/// <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; }
|
||||
|
||||
}
|
||||
}
|
286
RageCoop.Client/Scripting/Resources.cs
Normal file
286
RageCoop.Client/Scripting/Resources.cs
Normal file
@ -0,0 +1,286 @@
|
||||
using System.IO;
|
||||
using RageCoop.Core.Scripting;
|
||||
using RageCoop.Core;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RageCoop.Client.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class ClientResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the resource
|
||||
/// </summary>
|
||||
public string Name { get; internal set; }
|
||||
/// <summary>
|
||||
/// A resource-specific folder that can be used to store your files.
|
||||
/// </summary>
|
||||
public string DataFolder { get; internal set; }
|
||||
/// <summary>
|
||||
/// Get all <see cref="ClientScript"/> instance in this resource.
|
||||
/// </summary>
|
||||
public List<ClientScript> Scripts { get; internal set; } = new List<ClientScript>();
|
||||
/// <summary>
|
||||
/// Get the <see cref="ResourceFile"/> where this script is loaded from.
|
||||
/// </summary>
|
||||
public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>();
|
||||
}
|
||||
internal class Resources
|
||||
{
|
||||
public Resources(){
|
||||
BaseScriptType = "RageCoop.Client.Scripting.ClientScript";
|
||||
Logger = Main.Logger;
|
||||
}
|
||||
private void StartAll()
|
||||
{
|
||||
lock (LoadedResources)
|
||||
{
|
||||
foreach (var d in LoadedResources)
|
||||
{
|
||||
foreach (var s in d.Scripts)
|
||||
{
|
||||
try
|
||||
{
|
||||
s.OnStart();
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Logger.Error("Error occurred when starting script:"+s.GetType().FullName);
|
||||
Logger?.Error(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private void StopAll()
|
||||
{
|
||||
lock (LoadedResources)
|
||||
{
|
||||
foreach (var d in LoadedResources)
|
||||
{
|
||||
foreach (var s in d.Scripts)
|
||||
{
|
||||
try
|
||||
{
|
||||
s.OnStop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("Error occurred when stopping script:"+s.GetType().FullName);
|
||||
Logger?.Error(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Load(string path,string[] zips)
|
||||
{
|
||||
foreach (var zip in zips)
|
||||
{
|
||||
var zipPath=Path.Combine(path, zip);
|
||||
Logger?.Info($"Loading resource: {Path.GetFileNameWithoutExtension(zip)}");
|
||||
LoadResource(new ZipFile(zipPath),Path.Combine(path,"data"));
|
||||
}
|
||||
StartAll();
|
||||
}
|
||||
public void Unload()
|
||||
{
|
||||
StopAll();
|
||||
if (LoadedResources.Count > 0)
|
||||
{
|
||||
API.QueueAction(()=>Util.Reload());
|
||||
}
|
||||
LoadedResources.Clear();
|
||||
}
|
||||
private List<string> ToIgnore = new List<string>
|
||||
{
|
||||
"RageCoop.Client.dll",
|
||||
"RageCoop.Core.dll",
|
||||
"RageCoop.Server.dll",
|
||||
"ScriptHookVDotNet3.dll"
|
||||
};
|
||||
private List<ClientResource> LoadedResources = new List<ClientResource>();
|
||||
private string BaseScriptType;
|
||||
public Logger Logger { get; set; }
|
||||
|
||||
private void LoadResource(ZipFile file, string dataFolderRoot)
|
||||
{
|
||||
var r = new ClientResource()
|
||||
{
|
||||
Scripts = new List<ClientScript>(),
|
||||
Name=Path.GetFileNameWithoutExtension(file.Name),
|
||||
DataFolder=Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name))
|
||||
};
|
||||
Directory.CreateDirectory(r.DataFolder);
|
||||
|
||||
foreach (ZipEntry entry in file)
|
||||
{
|
||||
ResourceFile rFile;
|
||||
r.Files.Add(entry.Name, rFile=new ResourceFile()
|
||||
{
|
||||
Name=entry.Name,
|
||||
IsDirectory=entry.IsDirectory,
|
||||
});
|
||||
if (!entry.IsDirectory)
|
||||
{
|
||||
rFile.GetStream=() => { return file.GetInputStream(entry); };
|
||||
if (entry.Name.EndsWith(".dll"))
|
||||
{
|
||||
var tmp = Path.GetTempFileName();
|
||||
var f = File.OpenWrite(tmp);
|
||||
rFile.GetStream().CopyTo(f);
|
||||
f.Close();
|
||||
LoadScriptsFromAssembly(rFile, tmp, r, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
LoadedResources.Add(r);
|
||||
file.Close();
|
||||
}
|
||||
private bool LoadScriptsFromAssembly(ResourceFile file, string path, ClientResource resource, bool shadowCopy = true)
|
||||
{
|
||||
lock (LoadedResources)
|
||||
{
|
||||
if (!IsManagedAssembly(path)) { return false; }
|
||||
if (ToIgnore.Contains(file.Name)) { try { File.Delete(path); } catch { }; return false; }
|
||||
|
||||
Logger?.Debug($"Loading assembly {file.Name} ...");
|
||||
|
||||
Assembly assembly;
|
||||
|
||||
try
|
||||
{
|
||||
if (shadowCopy)
|
||||
{
|
||||
var temp = Path.GetTempFileName();
|
||||
File.Copy(path, temp, true);
|
||||
assembly = Assembly.LoadFrom(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
assembly = Assembly.LoadFrom(path);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.Error("Unable to load "+file.Name);
|
||||
Logger?.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadScriptsFromAssembly(file, assembly, path, resource);
|
||||
}
|
||||
}
|
||||
private bool LoadScriptsFromAssembly(ResourceFile rfile, Assembly assembly, string filename, ClientResource toload)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Find all script types in the assembly
|
||||
foreach (var type in assembly.GetTypes().Where(x => IsSubclassOf(x, BaseScriptType)))
|
||||
{
|
||||
ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);
|
||||
if (constructor != null && constructor.IsPublic)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Invoke script constructor
|
||||
var script = constructor.Invoke(null) as ClientScript;
|
||||
// script.CurrentResource = toload;
|
||||
script.CurrentFile=rfile;
|
||||
script.CurrentResource=toload;
|
||||
toload.Scripts.Add(script);
|
||||
count++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.Error($"Error occurred when loading script: {type.FullName}.");
|
||||
Logger?.Error(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger?.Error($"Script {type.FullName} has an invalid contructor.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
Logger?.Error($"Failed to load assembly {rfile.Name}: ");
|
||||
Logger?.Error(ex);
|
||||
foreach (var e in ex.LoaderExceptions)
|
||||
{
|
||||
Logger?.Error(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger?.Info($"Loaded {count} script(s) in {rfile.Name}");
|
||||
return count != 0;
|
||||
}
|
||||
private bool IsManagedAssembly(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Stream file = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
if (file.Length < 64)
|
||||
return false;
|
||||
|
||||
using (BinaryReader bin = new BinaryReader(file))
|
||||
{
|
||||
// PE header starts at offset 0x3C (60). Its a 4 byte header.
|
||||
file.Position = 0x3C;
|
||||
uint offset = bin.ReadUInt32();
|
||||
if (offset == 0)
|
||||
offset = 0x80;
|
||||
|
||||
// Ensure there is at least enough room for the following structures:
|
||||
// 24 byte PE Signature & Header
|
||||
// 28 byte Standard Fields (24 bytes for PE32+)
|
||||
// 68 byte NT Fields (88 bytes for PE32+)
|
||||
// >= 128 byte Data Dictionary Table
|
||||
if (offset > file.Length - 256)
|
||||
return false;
|
||||
|
||||
// Check the PE signature. Should equal 'PE\0\0'.
|
||||
file.Position = offset;
|
||||
if (bin.ReadUInt32() != 0x00004550)
|
||||
return false;
|
||||
|
||||
// Read PE magic number from Standard Fields to determine format.
|
||||
file.Position += 20;
|
||||
var peFormat = bin.ReadUInt16();
|
||||
if (peFormat != 0x10b /* PE32 */ && peFormat != 0x20b /* PE32Plus */)
|
||||
return false;
|
||||
|
||||
// Read the 15th Data Dictionary RVA field which contains the CLI header RVA.
|
||||
// When this is non-zero then the file contains CLI data otherwise not.
|
||||
file.Position = offset + (peFormat == 0x10b ? 232 : 248);
|
||||
return bin.ReadUInt32() != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// This is likely not a valid assembly if any IO exceptions occur during reading
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private bool IsSubclassOf(Type type, string baseTypeName)
|
||||
{
|
||||
for (Type t = type.BaseType; t != null; t = t.BaseType)
|
||||
if (t.FullName == baseTypeName)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
46
RageCoop.Client/Security.cs
Normal file
46
RageCoop.Client/Security.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Security.Cryptography;
|
||||
using System.IO;
|
||||
using RageCoop.Core;
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal class Security
|
||||
{
|
||||
public RSA ServerRSA { get; set; }
|
||||
public Aes ClientAes { get; set; }=Aes.Create();
|
||||
private Logger Logger;
|
||||
public Security(Logger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
ClientAes.GenerateKey();
|
||||
ClientAes.GenerateIV();
|
||||
}
|
||||
public void GetSymmetricKeysCrypted(out byte[] cryptedKey,out byte[] cryptedIV)
|
||||
{
|
||||
// Logger?.Debug($"Aes.Key:{ClientAes.Key.Dump()}, Aes.IV:{ClientAes.IV.Dump()}");
|
||||
cryptedKey =ServerRSA.Encrypt(ClientAes.Key, RSAEncryptionPadding.Pkcs1);
|
||||
cryptedIV =ServerRSA.Encrypt(ClientAes.IV,RSAEncryptionPadding.Pkcs1);
|
||||
}
|
||||
public byte[] Encrypt(byte[] data)
|
||||
{
|
||||
return new CryptoStream(new MemoryStream(data), ClientAes.CreateEncryptor(), CryptoStreamMode.Read).ReadToEnd();
|
||||
}
|
||||
public void SetServerPublicKey(byte[] modulus,byte[] exponent)
|
||||
{
|
||||
var para = new RSAParameters();
|
||||
para.Modulus = modulus;
|
||||
para.Exponent = exponent;
|
||||
ServerRSA=RSA.Create(para);
|
||||
}
|
||||
public void Regen()
|
||||
{
|
||||
ClientAes=Aes.Create();
|
||||
ClientAes.GenerateKey();
|
||||
ClientAes.GenerateIV();
|
||||
}
|
||||
}
|
||||
}
|
@ -12,13 +12,17 @@ namespace RageCoop.Client
|
||||
/// </summary>
|
||||
public string Username { get; set; } = "Player";
|
||||
/// <summary>
|
||||
/// The password used to authenticate when connecting to a server.
|
||||
/// </summary>
|
||||
public string Password { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public string LastServerAddress { get; set; } = "127.0.0.1:4499";
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public string MasterServer { get; set; } = "[AUTO]";
|
||||
public string MasterServer { get; set; } = "https://masterserver.ragecoop.online/";
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
@ -54,10 +58,14 @@ namespace RageCoop.Client
|
||||
/// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
|
||||
/// </summary>
|
||||
public int WorldVehicleSoftLimit { get; set; } = 35;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Disable automatic respawn.
|
||||
/// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
|
||||
/// </summary>
|
||||
public bool DisableAutoRespawn { get; set; } = false;
|
||||
public int WorldPedSoftLimit { get; set; } = 50;
|
||||
/// <summary>
|
||||
/// The directory where log and resources downloaded from server will be placed.
|
||||
/// </summary>
|
||||
public string DataDirectory { get; set; } = "Scripts\\RageCoop\\Data";
|
||||
}
|
||||
}
|
@ -8,13 +8,16 @@ using GTA.Math;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal abstract class SyncedEntity
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract class SyncedEntity
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current player is responsible for syncing this entity.
|
||||
/// </summary>
|
||||
public bool IsMine
|
||||
public bool IsLocal
|
||||
{
|
||||
get
|
||||
{
|
||||
@ -22,20 +25,30 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
public int ID { get; set; }
|
||||
public int OwnerID { get;set; }
|
||||
/// <summary>
|
||||
/// Network ID for this entity
|
||||
/// </summary>
|
||||
public int ID { get;internal set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int OwnerID { get; internal set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsOutOfSync
|
||||
{
|
||||
get
|
||||
{
|
||||
return Main.Ticked-LastSynced>200;
|
||||
return Main.Ticked-LastSynced>200 && ID!=0;
|
||||
}
|
||||
}
|
||||
public bool IsReady
|
||||
internal bool IsReady
|
||||
{
|
||||
get {return !(LastSynced==0||LastStateSynced==0);}
|
||||
get {return (LastSynced>0||LastStateSynced==0);}
|
||||
}
|
||||
public bool NeedUpdate
|
||||
internal bool IsInvincible { get; set; } = false;
|
||||
internal bool NeedUpdate
|
||||
{
|
||||
get { return LastSynced>LastUpdated; }
|
||||
}
|
||||
@ -54,12 +67,17 @@ namespace RageCoop.Client
|
||||
public ulong LastUpdated { get; set; } = 0;
|
||||
#endregion
|
||||
|
||||
public Vector3 Position { get; set; }
|
||||
public Vector3 Rotation { get; set; }
|
||||
public Quaternion Quaternion { get; set; }
|
||||
public Vector3 Velocity { get; set; }
|
||||
public abstract void Update();
|
||||
public void PauseUpdate(ulong frames)
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
internal protected bool _lastFrozen=false;
|
||||
internal int ModelHash { get; set; }
|
||||
internal Vector3 Position { get; set; }
|
||||
internal Vector3 Rotation { get; set; }
|
||||
internal Quaternion Quaternion { get; set; }
|
||||
internal Vector3 Velocity { get; set; }
|
||||
internal abstract void Update();
|
||||
internal void PauseUpdate(ulong frames)
|
||||
{
|
||||
LastUpdated=Main.Ticked+frames;
|
||||
}
|
@ -14,7 +14,7 @@ namespace RageCoop.Client
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
internal class SyncedPed:SyncedEntity
|
||||
public class SyncedPed:SyncedEntity
|
||||
{
|
||||
#region CONSTRUCTORS
|
||||
|
||||
@ -22,7 +22,7 @@ namespace RageCoop.Client
|
||||
/// Create a local entity (outgoing sync)
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
public SyncedPed(Ped p)
|
||||
internal SyncedPed(Ped p)
|
||||
{
|
||||
ID=EntityPool.RequestNewID();
|
||||
p.CanWrithe=false;
|
||||
@ -32,22 +32,25 @@ namespace RageCoop.Client
|
||||
|
||||
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false);
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableMelee, true);
|
||||
// MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableMelee, true);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty character with ID
|
||||
/// </summary>
|
||||
public SyncedPed(int id)
|
||||
internal SyncedPed(int id)
|
||||
{
|
||||
ID=id;
|
||||
LastSynced=Main.Ticked;
|
||||
}
|
||||
#endregion
|
||||
#region PLAYER -- ONLY
|
||||
public string Username = "N/A";
|
||||
public Blip PedBlip = null;
|
||||
internal Blip PedBlip = null;
|
||||
internal BlipColor BlipColor = (BlipColor)255;
|
||||
internal BlipSprite BlipSprite = (BlipSprite)0;
|
||||
internal float BlipScale=1;
|
||||
internal PlayerData Player;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@ -58,46 +61,32 @@ namespace RageCoop.Client
|
||||
/// real entity
|
||||
/// </summary>
|
||||
public Ped MainPed { get; internal set; }
|
||||
/// <summary>
|
||||
/// The latest character health (may not have been applied yet)
|
||||
/// </summary>
|
||||
public int Health { get; internal set; }
|
||||
public bool _lastEnteringVehicle=false;
|
||||
public bool _lastSittingInVehicle=false;
|
||||
internal int Health { get; set; }
|
||||
internal bool IsInStealthMode { get; set; }
|
||||
internal byte WeaponTint { get; set; }
|
||||
internal bool _lastEnteringVehicle=false;
|
||||
internal bool _lastSittingInVehicle=false;
|
||||
private bool _lastRagdoll=false;
|
||||
private ulong _lastRagdollTime=0;
|
||||
private bool _lastInCover = false;
|
||||
/// <summary>
|
||||
/// The latest character model hash (may not have been applied yet)
|
||||
/// </summary>
|
||||
public int ModelHash
|
||||
{
|
||||
get;set;
|
||||
}
|
||||
private Dictionary<byte, short> _lastClothes = null;
|
||||
public Dictionary<byte, short> Clothes { get; set; }
|
||||
private byte[] _lastClothes = null;
|
||||
internal byte[] Clothes { get; set; }
|
||||
|
||||
public float Heading { get; set; }
|
||||
public Vector3 RotationVelocity { get; set; }
|
||||
public Vector3 AimCoords { get; set; }
|
||||
internal float Heading { get; set; }
|
||||
internal Vector3 RotationVelocity { get; set; }
|
||||
internal Vector3 AimCoords { get; set; }
|
||||
|
||||
private WeaponAsset WeaponAsset { get; set; }
|
||||
|
||||
|
||||
public override void Update()
|
||||
|
||||
internal override void Update()
|
||||
{
|
||||
if (IsPlayer && PedBlip!=null)
|
||||
if (IsPlayer)
|
||||
{
|
||||
|
||||
if (Username=="N/A")
|
||||
if (Player==null)
|
||||
{
|
||||
var p = PlayerList.GetPlayer(ID);
|
||||
if (p!=null)
|
||||
{
|
||||
Username=p.Username;
|
||||
PedBlip.Name=Username;
|
||||
}
|
||||
|
||||
Player = PlayerList.GetPlayer(this);
|
||||
return;
|
||||
}
|
||||
RenderNameTag();
|
||||
}
|
||||
@ -112,10 +101,6 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool characterExist = (MainPed != null) && MainPed.Exists();
|
||||
|
||||
if (!characterExist)
|
||||
@ -123,7 +108,27 @@ namespace RageCoop.Client
|
||||
CreateCharacter();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (((byte)BlipColor==255) && (PedBlip!=null))
|
||||
{
|
||||
PedBlip.Delete();
|
||||
PedBlip=null;
|
||||
}
|
||||
else if (((byte)BlipColor != 255) && PedBlip==null)
|
||||
{
|
||||
PedBlip=MainPed.AddBlip();
|
||||
|
||||
if (IsPlayer)
|
||||
{
|
||||
Main.Logger.Debug("blip:"+Player.Username);
|
||||
PedBlip.Name=Player.Username;
|
||||
}
|
||||
PedBlip.Color=BlipColor;
|
||||
PedBlip.Sprite=BlipSprite;
|
||||
PedBlip.Scale=BlipScale;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Need to update state
|
||||
@ -136,10 +141,23 @@ namespace RageCoop.Client
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Clothes.Compare(_lastClothes))
|
||||
if (!Clothes.SequenceEqual(_lastClothes))
|
||||
{
|
||||
SetClothes();
|
||||
}
|
||||
var b = MainPed.AttachedBlip;
|
||||
if (b==null || b.Color!=BlipColor || b.Sprite!=BlipSprite)
|
||||
{
|
||||
PedBlip?.Delete();
|
||||
PedBlip=MainPed.AddBlip();
|
||||
PedBlip.Color=BlipColor;
|
||||
PedBlip.Sprite =BlipSprite;
|
||||
if (IsPlayer)
|
||||
{
|
||||
Main.Logger.Debug("blip:"+Player.Username);
|
||||
b.Name=Player.Username;
|
||||
}
|
||||
}
|
||||
|
||||
CheckCurrentWeapon();
|
||||
}
|
||||
@ -167,7 +185,6 @@ namespace RageCoop.Client
|
||||
if (Health <= 0 && !MainPed.IsDead)
|
||||
{
|
||||
MainPed.IsInvincible = false;
|
||||
Main.Logger.Debug($"Killing ped {ID}. Reason:PedDied");
|
||||
MainPed.Kill();
|
||||
return;
|
||||
}
|
||||
@ -188,12 +205,12 @@ namespace RageCoop.Client
|
||||
|
||||
private void RenderNameTag()
|
||||
{
|
||||
if (!IsPlayer || !MainPed.IsVisible || !MainPed.IsInRange(Game.Player.Character.Position, 20f))
|
||||
if (!Player.DisplayNameTag || (MainPed==null) || !MainPed.IsVisible || !MainPed.IsInRange(Game.Player.Character.Position, 20f))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string renderText = IsOutOfSync ? "~r~AFK" : Username;
|
||||
string renderText = IsOutOfSync ? "~r~AFK" : Player.Username;
|
||||
Vector3 targetPos = MainPed.Bones[Bone.IKHead].Position + new Vector3(0, 0, 0.35f);
|
||||
|
||||
Function.Call(Hash.SET_DRAW_ORIGIN, targetPos.X, targetPos.Y, targetPos.Z, 0);
|
||||
@ -245,14 +262,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
|
||||
Function.Call(Hash.SET_PED_CAN_EVASIVE_DIVE, MainPed.Handle, false);
|
||||
Function.Call(Hash.SET_PED_DROPS_WEAPONS_WHEN_DEAD, MainPed.Handle, false);
|
||||
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED, MainPed.Handle, true);
|
||||
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, true);
|
||||
Function.Call(Hash.SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false);
|
||||
Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true);
|
||||
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false);
|
||||
|
||||
|
||||
MainPed.BlockPermanentEvents = true;
|
||||
MainPed.CanWrithe=false;
|
||||
MainPed.CanBeDraggedOutOfVehicle = true;
|
||||
@ -261,27 +271,29 @@ namespace RageCoop.Client
|
||||
MainPed.IsFireProof=false;
|
||||
MainPed.IsExplosionProof=false;
|
||||
|
||||
Function.Call(Hash.SET_PED_DROPS_WEAPONS_WHEN_DEAD, MainPed.Handle, false);
|
||||
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED, MainPed.Handle, true);
|
||||
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, true);
|
||||
Function.Call(Hash.SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false);
|
||||
Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true);
|
||||
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false);
|
||||
Function.Call(Hash.SET_PED_CAN_EVASIVE_DIVE, MainPed.Handle, false);
|
||||
|
||||
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DrownsInWater,false);
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableMelee, true);
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableExplosionReactions, true);
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_AvoidTearGas, false);
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableShockingEvents, true);
|
||||
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
|
||||
|
||||
|
||||
SetClothes();
|
||||
|
||||
if (IsPlayer)
|
||||
{
|
||||
// Add a new blip for the ped
|
||||
PedBlip=MainPed.AddBlip();
|
||||
MainPed.AttachedBlip.Color = BlipColor.White;
|
||||
MainPed.AttachedBlip.Scale = 0.8f;
|
||||
MainPed.AttachedBlip.Name =Username;
|
||||
|
||||
MainPed.IsInvincible=true;
|
||||
}
|
||||
if (IsInvincible) { MainPed.IsInvincible=true; }
|
||||
|
||||
// Add to EntityPool so this Character can be accessed by handle.
|
||||
EntityPool.Add(this);
|
||||
@ -289,33 +301,33 @@ namespace RageCoop.Client
|
||||
|
||||
private void SetClothes()
|
||||
{
|
||||
foreach (KeyValuePair<byte, short> cloth in Clothes)
|
||||
for (byte i = 0; i < 12; i++)
|
||||
{
|
||||
Function.Call(Hash.SET_PED_COMPONENT_VARIATION, MainPed.Handle, cloth.Key, cloth.Value, 0, 0);
|
||||
Function.Call(Hash.SET_PED_COMPONENT_VARIATION, MainPed.Handle, i, (int)Clothes[i], (int)Clothes[i+12], (int)Clothes[i+24]);
|
||||
}
|
||||
_lastClothes = Clothes;
|
||||
}
|
||||
#region Onfoot
|
||||
#region ONFOOT
|
||||
#region -- VARIABLES --
|
||||
/// <summary>
|
||||
/// The latest character rotation (may not have been applied yet)
|
||||
/// </summary>
|
||||
public byte Speed { get; set; }
|
||||
private bool _lastIsJumping = false;
|
||||
public bool IsJumping { get; set; }
|
||||
public bool IsOnLadder { get; set; }
|
||||
public bool IsVaulting { get; set; }
|
||||
public bool IsInParachuteFreeFall { get; set; }
|
||||
public bool IsParachuteOpen { get; set; }
|
||||
public Prop ParachuteProp { get; set; } = null;
|
||||
public bool IsRagdoll { get; set; }
|
||||
public bool IsOnFire { get; set; }
|
||||
public bool IsAiming { get; set; }
|
||||
public bool IsReloading { get; set; }
|
||||
public bool IsInCover { get; set; }
|
||||
public uint CurrentWeaponHash { get; set; }
|
||||
internal bool IsJumping { get; set; }
|
||||
internal bool IsOnLadder { get; set; }
|
||||
internal bool IsVaulting { get; set; }
|
||||
internal bool IsInParachuteFreeFall { get; set; }
|
||||
internal bool IsParachuteOpen { get; set; }
|
||||
internal Prop ParachuteProp { get; set; } = null;
|
||||
internal bool IsRagdoll { get; set; }
|
||||
internal bool IsOnFire { get; set; }
|
||||
internal bool IsAiming { get; set; }
|
||||
internal bool IsReloading { get; set; }
|
||||
internal bool IsInCover { get; set; }
|
||||
internal uint CurrentWeaponHash { get; set; }
|
||||
private Dictionary<uint, bool> _lastWeaponComponents = null;
|
||||
public Dictionary<uint, bool> WeaponComponents { get; set; } = null;
|
||||
internal Dictionary<uint, bool> WeaponComponents { get; set; } = null;
|
||||
private int _lastWeaponObj = 0;
|
||||
#endregion
|
||||
|
||||
@ -567,14 +579,6 @@ namespace RageCoop.Client
|
||||
WalkTo();
|
||||
}
|
||||
}
|
||||
private void DisplayInVehicle()
|
||||
{
|
||||
if (MainPed.IsOnTurretSeat())
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_TURRET_SPEED_THIS_FRAME, MainPed.CurrentVehicle, 100);
|
||||
Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z);
|
||||
}
|
||||
}
|
||||
|
||||
#region WEAPON
|
||||
private void CheckCurrentWeapon()
|
||||
@ -605,9 +609,12 @@ namespace RageCoop.Client
|
||||
Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, _lastWeaponObj, MainPed.Handle);
|
||||
|
||||
}
|
||||
|
||||
_lastWeaponComponents = WeaponComponents;
|
||||
}
|
||||
if (Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX,MainPed,CurrentWeaponHash)!=WeaponTint)
|
||||
{
|
||||
Function.Call<int>(Hash.SET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeaponHash, WeaponTint);
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayAiming()
|
||||
@ -716,5 +723,19 @@ namespace RageCoop.Client
|
||||
return anim;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void DisplayInVehicle()
|
||||
{
|
||||
if (MainPed.IsOnTurretSeat())
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_TURRET_SPEED_THIS_FRAME, MainPed.CurrentVehicle, 100);
|
||||
Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z);
|
||||
}
|
||||
/*
|
||||
Function.Call(Hash.TASK_SWEEP_AIM_ENTITY,P, "random@paparazzi@pap_anims", "sweep_low", "sweep_med", "sweep_high", -1,V, 1.57f, 0.25f);
|
||||
Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, P,true, 0);
|
||||
return Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, P);
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
@ -13,35 +14,41 @@ namespace RageCoop.Client
|
||||
public SyncedProjectile(Projectile p)
|
||||
{
|
||||
ID=EntityPool.RequestNewID();
|
||||
IsMine=true;
|
||||
MainProjectile = p;
|
||||
Origin=p.Position;
|
||||
var shooter = EntityPool.GetPedByHandle((p.Owner?.Handle).GetValueOrDefault());
|
||||
if(shooter != null)
|
||||
{
|
||||
ShooterID=shooter.ID;
|
||||
}
|
||||
else
|
||||
if (shooter==null)
|
||||
{
|
||||
// Owner will be the vehicle if projectile is shot with a vehicle
|
||||
var shooterVeh = EntityPool.GetVehicleByHandle((p.Owner?.Handle).GetValueOrDefault());
|
||||
if (shooterVeh!=null && shooterVeh.MainVehicle.Driver!=null)
|
||||
{
|
||||
ShooterID=shooterVeh.MainVehicle.Driver.GetSyncEntity().ID;
|
||||
shooter=shooterVeh.MainVehicle.Driver?.GetSyncEntity();
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Logger.Warning($"Could not find owner for projectile:{Hash}");
|
||||
}
|
||||
}
|
||||
|
||||
if(shooter != null)
|
||||
{
|
||||
if (shooter.MainPed!=null && (p.AttachedEntity==shooter.MainPed.Weapons.CurrentWeaponObject || p.AttachedEntity== shooter.MainPed))
|
||||
{
|
||||
// Reloading
|
||||
IsValid=false;
|
||||
}
|
||||
ShooterID=shooter.ID;
|
||||
IsLocal=shooter.IsLocal;
|
||||
}
|
||||
|
||||
}
|
||||
public SyncedProjectile(int id)
|
||||
{
|
||||
ID= id;
|
||||
IsMine=false;
|
||||
IsLocal=false;
|
||||
}
|
||||
public new bool IsMine { get; private set; }
|
||||
public bool IsValid { get; private set; } = true;
|
||||
public new bool IsLocal { get; private set; } = false;
|
||||
public bool Exploded { get; set; } = false;
|
||||
public Projectile MainProjectile { get; set; }
|
||||
public int ShooterID { get; set; }
|
||||
@ -54,7 +61,7 @@ namespace RageCoop.Client
|
||||
private new int OwnerID{ set { } }
|
||||
public WeaponHash Hash { get; set; }
|
||||
private WeaponAsset Asset { get; set; }
|
||||
public override void Update()
|
||||
internal override void Update()
|
||||
{
|
||||
|
||||
// Skip update if no new sync message has arrived.
|
||||
@ -65,8 +72,7 @@ namespace RageCoop.Client
|
||||
CreateProjectile();
|
||||
return;
|
||||
}
|
||||
MainProjectile.PositionNoOffset=Position+Velocity*Networking.Latency;
|
||||
MainProjectile.Velocity=Velocity;
|
||||
MainProjectile.Velocity=Velocity+(Position+Networking.Latency*Velocity-MainProjectile.Position);
|
||||
MainProjectile.Rotation=Rotation;
|
||||
LastUpdated=Main.Ticked;
|
||||
}
|
44
RageCoop.Client/Sync/Entities/SyncedProp.cs
Normal file
44
RageCoop.Client/Sync/Entities/SyncedProp.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using GTA.Native;
|
||||
using GTA;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Synchronized prop, mostly owned by server
|
||||
/// </summary>
|
||||
public class SyncedProp : SyncedEntity
|
||||
{
|
||||
internal SyncedProp(int id)
|
||||
{
|
||||
ID= id;
|
||||
}
|
||||
/// <summary>
|
||||
/// The real entity
|
||||
/// </summary>
|
||||
public Prop MainProp { get; set; }
|
||||
internal new int OwnerID { get
|
||||
{
|
||||
// alwayse owned by server
|
||||
return 0;
|
||||
} }
|
||||
internal override void Update()
|
||||
{
|
||||
|
||||
if (!NeedUpdate) { return; }
|
||||
if (MainProp== null || !MainProp.Exists())
|
||||
{
|
||||
MainProp=World.CreateProp(ModelHash,Position,Rotation,false,false);
|
||||
MainProp.IsInvincible=true;
|
||||
}
|
||||
MainProp.Position=Position;
|
||||
MainProp.Rotation=Rotation;
|
||||
MainProp.SetFrozen(true);
|
||||
LastUpdated=Main.Ticked;
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,10 @@ using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal class SyncedVehicle : SyncedEntity
|
||||
/// <summary>
|
||||
/// A synchronized vehicle instance
|
||||
/// </summary>
|
||||
public class SyncedVehicle : SyncedEntity
|
||||
{
|
||||
|
||||
#region -- CONSTRUCTORS --
|
||||
@ -19,8 +22,8 @@ namespace RageCoop.Client
|
||||
/// <summary>
|
||||
/// Create a local entity (outgoing sync)
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
public SyncedVehicle(Vehicle v)
|
||||
/// <param name="v"></param>
|
||||
internal SyncedVehicle(Vehicle v)
|
||||
{
|
||||
|
||||
ID=EntityPool.RequestNewID();
|
||||
@ -33,11 +36,11 @@ namespace RageCoop.Client
|
||||
/// <summary>
|
||||
/// Create an empty VehicleEntity
|
||||
/// </summary>
|
||||
public SyncedVehicle()
|
||||
internal SyncedVehicle()
|
||||
{
|
||||
|
||||
}
|
||||
public SyncedVehicle(int id)
|
||||
internal SyncedVehicle(int id)
|
||||
{
|
||||
ID=id;
|
||||
LastSynced=Main.Ticked;
|
||||
@ -46,56 +49,65 @@ namespace RageCoop.Client
|
||||
/// <summary>
|
||||
/// VehicleSeat,ID
|
||||
/// </summary>
|
||||
public Vehicle MainVehicle { get; set; }
|
||||
public Vehicle MainVehicle { get;internal set; }
|
||||
|
||||
|
||||
#region LAST STATE STORE
|
||||
#region LAST STATE
|
||||
|
||||
|
||||
private byte[] _lastVehicleColors = new byte[] { 0, 0 };
|
||||
private Dictionary<int, int> _lastVehicleMods = new Dictionary<int, int>();
|
||||
private byte _lastRadioIndex=255;
|
||||
|
||||
#endregion
|
||||
|
||||
#region -- CRITICAL STUFF --
|
||||
public Vector3 RotationVelocity { get; set; }
|
||||
public float SteeringAngle { get; set; }
|
||||
public float ThrottlePower { get; set; }
|
||||
public float BrakePower { get; set; }
|
||||
public float DeluxoWingRatio { get; set; } = -1;
|
||||
internal Vector3 RotationVelocity { get; set; }
|
||||
internal float SteeringAngle { get; set; }
|
||||
internal float ThrottlePower { get; set; }
|
||||
internal float BrakePower { get; set; }
|
||||
internal float DeluxoWingRatio { get; set; } = -1;
|
||||
internal bool IsFlipped
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Quaternion*Vector3.RelativeTop).Z <(Quaternion*Vector3.RelativeBottom).Z;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region -- VEHICLE STATE --
|
||||
public VehicleDataFlags Flags { get; set; }
|
||||
public bool EngineRunning { get; set; }
|
||||
internal VehicleDataFlags Flags { get; set; }
|
||||
internal bool EngineRunning { get; set; }
|
||||
private bool _lastTransformed = false;
|
||||
public bool Transformed { get; set; }
|
||||
internal bool Transformed { get; set; }
|
||||
private bool _lastHornActive = false;
|
||||
public bool HornActive { get; set; }
|
||||
public bool LightsOn { get; set; }
|
||||
public bool BrakeLightsOn { get; set; } = false;
|
||||
public bool HighBeamsOn { get; set; }
|
||||
public byte LandingGear { get; set; }
|
||||
public bool RoofOpened { get; set; }
|
||||
public bool SireneActive { get; set; }
|
||||
public VehicleDamageModel DamageModel { get; set; }
|
||||
public int ModelHash { get; set; }
|
||||
public byte[] Colors { get; set; }
|
||||
public Dictionary<int, int> Mods { get; set; }
|
||||
public bool IsDead { get; set; }
|
||||
public float EngineHealth { get; set; }
|
||||
public VehicleLockStatus LockStatus{get;set;}
|
||||
internal bool HornActive { get; set; }
|
||||
internal bool LightsOn { get; set; }
|
||||
internal bool BrakeLightsOn { get; set; } = false;
|
||||
internal bool HighBeamsOn { get; set; }
|
||||
internal byte LandingGear { get; set; }
|
||||
internal VehicleRoofState RoofState { get; set; }
|
||||
internal bool SireneActive { get; set; }
|
||||
internal VehicleDamageModel DamageModel { get; set; }
|
||||
internal byte[] Colors { get; set; }
|
||||
internal Dictionary<int, int> Mods { get; set; }
|
||||
internal bool IsDead { get; set; }
|
||||
internal float EngineHealth { get; set; }
|
||||
internal VehicleLockStatus LockStatus{get;set;}
|
||||
/// <summary>
|
||||
/// VehicleSeat,PedID
|
||||
/// </summary>
|
||||
public Dictionary<VehicleSeat, SyncedPed> Passengers { get; set; }
|
||||
public byte RadioStation = 255;
|
||||
private long _lastPositionCalibrated { get; set; }
|
||||
internal Dictionary<VehicleSeat, SyncedPed> Passengers { get; set; }
|
||||
internal byte RadioStation = 255;
|
||||
internal string LicensePlate { get; set; }
|
||||
internal int _lastLivery = -1;
|
||||
internal int Livery { get; set; } = -1;
|
||||
internal bool _checkSeat { get; set; } = true;
|
||||
|
||||
#endregion
|
||||
public override void Update()
|
||||
internal override void Update()
|
||||
{
|
||||
|
||||
|
||||
#region -- INITIAL CHECK --
|
||||
|
||||
// Check if all data avalible
|
||||
@ -110,26 +122,35 @@ namespace RageCoop.Client
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region -- SYNC CRITICAL --
|
||||
|
||||
|
||||
if (SteeringAngle != MainVehicle.SteeringAngle)
|
||||
{
|
||||
MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * SteeringAngle);
|
||||
}
|
||||
if (MainVehicle.ThrottlePower!=ThrottlePower)
|
||||
{
|
||||
MainVehicle.ThrottlePower=ThrottlePower;
|
||||
}
|
||||
if (MainVehicle.BrakePower!=BrakePower)
|
||||
{
|
||||
MainVehicle.BrakePower=BrakePower;
|
||||
}
|
||||
MainVehicle.ThrottlePower=ThrottlePower;
|
||||
MainVehicle.BrakePower=BrakePower;
|
||||
|
||||
if (MainVehicle.Position.DistanceTo(Position)<5)
|
||||
{
|
||||
MainVehicle.Velocity = Velocity+5*(Position+Velocity*SyncParameters.PositioinPredictionDefault - MainVehicle.Position);
|
||||
MainVehicle.Quaternion=Quaternion.Slerp(MainVehicle.Quaternion, Quaternion, 0.35f);
|
||||
if (IsFlipped)
|
||||
{
|
||||
MainVehicle.Quaternion=Quaternion.Slerp(MainVehicle.Quaternion, Quaternion, 0.5f);
|
||||
MainVehicle.RotationVelocity=RotationVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 cali = GetCalibrationRotation();
|
||||
if (cali.Length()<50)
|
||||
{
|
||||
MainVehicle.RotationVelocity = RotationVelocity+cali*0.2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
MainVehicle.Quaternion=Quaternion;
|
||||
MainVehicle.RotationVelocity=RotationVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -137,8 +158,6 @@ namespace RageCoop.Client
|
||||
MainVehicle.Velocity=Velocity;
|
||||
MainVehicle.Quaternion=Quaternion;
|
||||
}
|
||||
// Vector3 r = GetCalibrationRotation();
|
||||
MainVehicle.RotationVelocity = RotationVelocity;
|
||||
if (DeluxoWingRatio!=-1)
|
||||
{
|
||||
MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio);
|
||||
@ -150,36 +169,33 @@ namespace RageCoop.Client
|
||||
#region -- PASSENGER SYNC --
|
||||
|
||||
// check passengers (and driver).
|
||||
|
||||
var currentPassengers = MainVehicle.GetPassengers();
|
||||
|
||||
lock (Passengers)
|
||||
if (_checkSeat)
|
||||
{
|
||||
for (int i = -1; i<MainVehicle.PassengerCapacity; i++)
|
||||
{
|
||||
VehicleSeat seat = (VehicleSeat)i;
|
||||
if (Passengers.ContainsKey(seat))
|
||||
{
|
||||
|
||||
SyncedPed c = Passengers[seat];
|
||||
if (c?.ID==Main.LocalPlayerID && (RadioStation!=_lastRadioIndex))
|
||||
{
|
||||
Util.SetPlayerRadioIndex(RadioStation);
|
||||
}
|
||||
if (c?.MainPed!=null&&(!currentPassengers.ContainsKey(i))&&(!c.MainPed.IsBeingJacked)&&(!c.MainPed.IsTaskActive(TaskType.CTaskExitVehicleSeat))) {
|
||||
Passengers[seat].MainPed.SetIntoVehicle(MainVehicle, seat);
|
||||
}
|
||||
}
|
||||
else if (!MainVehicle.IsSeatFree(seat))
|
||||
var currentPassengers = MainVehicle.GetPassengers();
|
||||
|
||||
lock (Passengers)
|
||||
{
|
||||
for (int i = -1; i<MainVehicle.PassengerCapacity; i++)
|
||||
{
|
||||
if (seat==VehicleSeat.Driver &&MainVehicle.Driver.IsSittingInVehicle())
|
||||
VehicleSeat seat = (VehicleSeat)i;
|
||||
if (Passengers.ContainsKey(seat))
|
||||
{
|
||||
MainVehicle.Driver.Task.WarpOutOfVehicle(MainVehicle);
|
||||
|
||||
SyncedPed c = Passengers[seat];
|
||||
if (c?.ID==Main.LocalPlayerID && (RadioStation!=Function.Call<int>(Hash.GET_PLAYER_RADIO_STATION_INDEX)))
|
||||
{
|
||||
Util.SetPlayerRadioIndex(RadioStation);
|
||||
}
|
||||
if (c?.MainPed!=null&&(!currentPassengers.ContainsKey(i))&&(!c.MainPed.IsBeingJacked)&&(!c.MainPed.IsTaskActive(TaskType.CTaskExitVehicleSeat)))
|
||||
{
|
||||
Passengers[seat].MainPed.SetIntoVehicle(MainVehicle, seat);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!MainVehicle.IsSeatFree(seat))
|
||||
{
|
||||
var p = MainVehicle.Passengers.Where(x => x.SeatIndex==seat).FirstOrDefault();
|
||||
if ((p!=null)&&p.IsSittingInVehicle())
|
||||
var p = MainVehicle.Occupants.Where(x => x.SeatIndex==seat).FirstOrDefault();
|
||||
if ((p!=null)&& !p.IsTaskActive(TaskType.CTaskLeaveAnyCar))
|
||||
{
|
||||
p.Task.WarpOutOfVehicle(MainVehicle);
|
||||
}
|
||||
@ -287,13 +303,9 @@ namespace RageCoop.Client
|
||||
MainVehicle.SoundHorn(1);
|
||||
}
|
||||
|
||||
if (MainVehicle.HasRoof)
|
||||
if (MainVehicle.HasRoof && MainVehicle.RoofState!=RoofState)
|
||||
{
|
||||
bool roofOpened = MainVehicle.RoofState == VehicleRoofState.Opened || MainVehicle.RoofState == VehicleRoofState.Opening;
|
||||
if (roofOpened != RoofOpened)
|
||||
{
|
||||
MainVehicle.RoofState = RoofOpened ? VehicleRoofState.Opening : VehicleRoofState.Closing;
|
||||
}
|
||||
MainVehicle.RoofState=RoofState;
|
||||
}
|
||||
|
||||
Function.Call(Hash.SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn);
|
||||
@ -301,7 +313,7 @@ namespace RageCoop.Client
|
||||
|
||||
}
|
||||
MainVehicle.LockStatus=LockStatus;
|
||||
if (Flags.HasFlag(VehicleDataFlags.IsDeluxoHovering))
|
||||
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering))
|
||||
{
|
||||
if (!MainVehicle.IsDeluxoHovering())
|
||||
{
|
||||
@ -316,15 +328,26 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
if (Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle)!=LicensePlate)
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_NUMBER_PLATE_TEXT,MainVehicle,LicensePlate);
|
||||
}
|
||||
|
||||
if (_lastLivery!=Livery)
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, Livery);
|
||||
_lastLivery=Livery;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
LastUpdated=Main.Ticked;
|
||||
}
|
||||
private Vector3 GetCalibrationRotation()
|
||||
{
|
||||
return (Quaternion-MainVehicle.Quaternion).ToEulerAngles().ToDegree();
|
||||
/*
|
||||
var r = Rotation-MainVehicle.Rotation;
|
||||
var rot=Quaternion.LookRotation(Quaternion*Vector3.RelativeFront, Quaternion*Vector3.RelativeTop).ToEulerAngles();
|
||||
var curRot=Quaternion.LookRotation(MainVehicle.Quaternion*Vector3.RelativeFront, MainVehicle.Quaternion*Vector3.RelativeTop).ToEulerAngles();
|
||||
|
||||
var r = (rot-curRot).ToDegree();
|
||||
if (r.X>180) { r.X=r.X-360; }
|
||||
else if(r.X<-180) { r.X=360+r.X; }
|
||||
|
||||
@ -334,7 +357,7 @@ namespace RageCoop.Client
|
||||
if (r.Z>180) { r.Z=r.Z-360; }
|
||||
else if (r.Z<-180) { r.Z=360+r.Z; }
|
||||
return r;
|
||||
*/
|
||||
|
||||
}
|
||||
private void CreateVehicle()
|
||||
{
|
||||
@ -351,15 +374,11 @@ namespace RageCoop.Client
|
||||
EntityPool.Add( this);
|
||||
}
|
||||
MainVehicle.Quaternion = Quaternion;
|
||||
|
||||
if (MainVehicle.HasRoof)
|
||||
{
|
||||
bool roofOpened = MainVehicle.RoofState == VehicleRoofState.Opened || MainVehicle.RoofState == VehicleRoofState.Opening;
|
||||
if (roofOpened != RoofOpened)
|
||||
{
|
||||
MainVehicle.RoofState = RoofOpened ? VehicleRoofState.Opened : VehicleRoofState.Closed;
|
||||
}
|
||||
MainVehicle.RoofState=RoofState;
|
||||
}
|
||||
if (IsInvincible) { MainVehicle.IsInvincible=true; }
|
||||
vehicleModel.MarkAsNoLongerNeeded();
|
||||
}
|
||||
#region -- PEDALING --
|
||||
@ -400,7 +419,7 @@ namespace RageCoop.Client
|
||||
#endregion
|
||||
|
||||
#region OUTGOING
|
||||
public float LastNozzleAngle { get; set; }
|
||||
internal float LastNozzleAngle { get; set; }
|
||||
#endregion
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using GTA.Native;
|
||||
using RageCoop.Core;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RageCoop.Client.Scripting;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
@ -14,6 +15,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
internal class EntityPool
|
||||
{
|
||||
private static bool _trafficSpawning=true;
|
||||
public static object PedsLock = new object();
|
||||
private static Dictionary<int, SyncedPed> ID_Peds = new Dictionary<int, SyncedPed>();
|
||||
public static int CharactersCount { get { return ID_Peds.Count; } }
|
||||
@ -35,6 +37,12 @@ namespace RageCoop.Client
|
||||
private static Dictionary<int, SyncedProjectile> ID_Projectiles = new Dictionary<int, SyncedProjectile>();
|
||||
private static Dictionary<int, SyncedProjectile> Handle_Projectiles = new Dictionary<int, SyncedProjectile>();
|
||||
|
||||
public static object PropsLock=new object();
|
||||
public static Dictionary<int,SyncedProp> ServerProps=new Dictionary<int,SyncedProp>();
|
||||
|
||||
public static object BlipsLock = new object();
|
||||
public static Dictionary<int, Blip> ServerBlips = new Dictionary<int, Blip>();
|
||||
|
||||
|
||||
public static void Cleanup(bool keepPlayer=true,bool keepMine=true)
|
||||
{
|
||||
@ -64,6 +72,21 @@ namespace RageCoop.Client
|
||||
}
|
||||
ID_Projectiles.Clear();
|
||||
Handle_Projectiles.Clear();
|
||||
|
||||
foreach(var p in ServerProps.Values)
|
||||
{
|
||||
p?.MainProp?.Delete();
|
||||
}
|
||||
ServerProps.Clear();
|
||||
|
||||
foreach(var b in ServerBlips.Values)
|
||||
{
|
||||
if (b.Exists())
|
||||
{
|
||||
b.Delete();
|
||||
}
|
||||
}
|
||||
ServerBlips.Clear();
|
||||
}
|
||||
|
||||
#region PEDS
|
||||
@ -112,7 +135,7 @@ namespace RageCoop.Client
|
||||
SyncedPed c = new SyncedPed(p);
|
||||
Main.LocalPlayerID=c.OwnerID=c.ID;
|
||||
Add(c);
|
||||
Main.Logger.Debug($"My player ID is:{c.ID}");
|
||||
Main.Logger.Debug($"Local player ID is:{c.ID}");
|
||||
PlayerList.SetPlayer(c.ID, Main.Settings.Username );
|
||||
return true;
|
||||
}
|
||||
@ -137,6 +160,10 @@ namespace RageCoop.Client
|
||||
{
|
||||
Handle_Peds.Add(c.MainPed.Handle, c);
|
||||
}
|
||||
if (c.IsLocal)
|
||||
{
|
||||
API.Events.InvokePedSpawned(c);
|
||||
}
|
||||
}
|
||||
public static void RemovePed(int id,string reason="Cleanup")
|
||||
{
|
||||
@ -150,7 +177,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
Handle_Peds.Remove(p.Handle);
|
||||
}
|
||||
Main.Logger.Debug($"Removing ped {c.ID}. Reason:{reason}");
|
||||
// Main.Logger.Debug($"Removing ped {c.ID}. Reason:{reason}");
|
||||
p.AttachedBlip?.Delete();
|
||||
p.Kill();
|
||||
p.MarkAsNoLongerNeeded();
|
||||
@ -159,6 +186,10 @@ namespace RageCoop.Client
|
||||
c.PedBlip?.Delete();
|
||||
c.ParachuteProp?.Delete();
|
||||
ID_Peds.Remove(id);
|
||||
if (c.IsLocal)
|
||||
{
|
||||
API.Events.InvokePedDeleted(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@ -195,6 +226,10 @@ namespace RageCoop.Client
|
||||
{
|
||||
Handle_Vehicles.Add(v.MainVehicle.Handle, v);
|
||||
}
|
||||
if (v.IsLocal)
|
||||
{
|
||||
API.Events.InvokeVehicleSpawned(v);
|
||||
}
|
||||
}
|
||||
public static void RemoveVehicle(int id,string reason = "Cleanup")
|
||||
{
|
||||
@ -208,12 +243,13 @@ namespace RageCoop.Client
|
||||
{
|
||||
Handle_Vehicles.Remove(veh.Handle);
|
||||
}
|
||||
Main.Logger.Debug($"Removing vehicle {v.ID}. Reason:{reason}");
|
||||
// Main.Logger.Debug($"Removing vehicle {v.ID}. Reason:{reason}");
|
||||
veh.AttachedBlip?.Delete();
|
||||
veh.MarkAsNoLongerNeeded();
|
||||
veh.Delete();
|
||||
}
|
||||
ID_Vehicles.Remove(id);
|
||||
if (v.IsLocal) { API.Events.InvokeVehicleDeleted(v); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,6 +262,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
public static void Add(SyncedProjectile p)
|
||||
{
|
||||
if (!p.IsValid) { return; }
|
||||
if (ID_Projectiles.ContainsKey(p.ID))
|
||||
{
|
||||
ID_Projectiles[p.ID]=p;
|
||||
@ -276,6 +313,14 @@ namespace RageCoop.Client
|
||||
return ID_Projectiles.ContainsKey(id);
|
||||
}
|
||||
#endregion
|
||||
static int vehStateIndex;
|
||||
static int pedStateIndex;
|
||||
static int vehStatesPerFrame;
|
||||
static int pedStatesPerFrame;
|
||||
static int i;
|
||||
public static Ped[] allPeds=new Ped[0];
|
||||
public static Vehicle[] allVehicles=new Vehicle[0];
|
||||
public static Projectile[] allProjectiles=new Projectile[0];
|
||||
|
||||
public static void DoSync()
|
||||
{
|
||||
@ -283,19 +328,27 @@ namespace RageCoop.Client
|
||||
PerfCounter.Restart();
|
||||
Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks;
|
||||
#endif
|
||||
var allPeds = World.GetAllPeds();
|
||||
var allVehicles=World.GetAllVehicles();
|
||||
var allProjectiles=World.GetAllProjectiles();
|
||||
if (Main.Settings.WorldVehicleSoftLimit>-1)
|
||||
allPeds = World.GetAllPeds();
|
||||
allVehicles=World.GetAllVehicles();
|
||||
allProjectiles=World.GetAllProjectiles();
|
||||
vehStatesPerFrame=allVehicles.Length*2/(int)Game.FPS+1;
|
||||
pedStatesPerFrame=allPeds.Length*2/(int)Game.FPS+1;
|
||||
|
||||
if (Main.Ticked%50==0)
|
||||
{
|
||||
if (Main.Ticked%100==0) { if (allVehicles.Length>Main.Settings.WorldVehicleSoftLimit) { SetBudget(0); } else { SetBudget(1); } }
|
||||
bool flag1 = allVehicles.Length>Main.Settings.WorldVehicleSoftLimit && Main.Settings.WorldVehicleSoftLimit>-1;
|
||||
bool flag2 = allPeds.Length>Main.Settings.WorldPedSoftLimit && Main.Settings.WorldPedSoftLimit>-1;
|
||||
if ((flag1||flag2) && _trafficSpawning)
|
||||
{ SetBudget(0); _trafficSpawning=false; }
|
||||
else if(!_trafficSpawning)
|
||||
{ SetBudget(1); _trafficSpawning=true; }
|
||||
}
|
||||
|
||||
#if BENCHMARK
|
||||
|
||||
Debug.TimeStamps[TimeStamp.GetAllEntities]=PerfCounter.ElapsedTicks;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
lock (ProjectilesLock)
|
||||
{
|
||||
|
||||
@ -312,12 +365,12 @@ namespace RageCoop.Client
|
||||
{
|
||||
|
||||
// Outgoing sync
|
||||
if (p.IsMine)
|
||||
if (p.IsLocal)
|
||||
{
|
||||
if (p.MainProjectile.AttachedEntity==null)
|
||||
{
|
||||
|
||||
/// Prevent projectiles from exploding next to vehicle
|
||||
// Prevent projectiles from exploding next to vehicle
|
||||
if (WeaponUtil.VehicleProjectileWeapons.Contains((VehicleWeaponHash)p.MainProjectile.WeaponHash))
|
||||
{
|
||||
if (p.MainProjectile.WeaponHash!=(WeaponHash)VehicleWeaponHash.Tank && p.Origin.DistanceTo(p.MainProjectile.Position)<2)
|
||||
@ -346,6 +399,7 @@ namespace RageCoop.Client
|
||||
|
||||
}
|
||||
|
||||
i=-1;
|
||||
|
||||
lock (PedsLock)
|
||||
{
|
||||
@ -356,7 +410,7 @@ namespace RageCoop.Client
|
||||
SyncedPed c = EntityPool.GetPedByHandle(p.Handle);
|
||||
if (c==null && (p!=Game.Player.Character))
|
||||
{
|
||||
Main.Logger.Trace($"Creating SyncEntity for ped, handle:{p.Handle}");
|
||||
// Main.Logger.Trace($"Creating SyncEntity for ped, handle:{p.Handle}");
|
||||
c=new SyncedPed(p);
|
||||
|
||||
EntityPool.Add(c);
|
||||
@ -368,9 +422,15 @@ namespace RageCoop.Client
|
||||
|
||||
Debug.TimeStamps[TimeStamp.AddPeds]=PerfCounter.ElapsedTicks;
|
||||
#endif
|
||||
|
||||
foreach (SyncedPed c in ID_Peds.Values.ToArray())
|
||||
var ps = ID_Peds.Values.ToArray();
|
||||
pedStateIndex+=pedStatesPerFrame;
|
||||
if (pedStateIndex>=ps.Length)
|
||||
{
|
||||
pedStateIndex=0;
|
||||
}
|
||||
foreach (SyncedPed c in ps)
|
||||
{
|
||||
i++;
|
||||
if ((c.MainPed!=null)&&(!c.MainPed.Exists()))
|
||||
{
|
||||
EntityPool.RemovePed(c.ID, "non-existent");
|
||||
@ -378,7 +438,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
// Outgoing sync
|
||||
if (c.IsMine)
|
||||
if (c.IsLocal)
|
||||
{
|
||||
#if BENCHMARK
|
||||
var start = PerfCounter2.ElapsedTicks;
|
||||
@ -386,15 +446,13 @@ namespace RageCoop.Client
|
||||
// event check
|
||||
SyncEvents.Check(c);
|
||||
|
||||
if (Main.Ticked%20==0)
|
||||
Networking.SendPed(c);
|
||||
|
||||
// Send state
|
||||
if ((i-pedStateIndex)<pedStatesPerFrame)
|
||||
{
|
||||
Networking.SendPed(c);
|
||||
Networking.SendPedState(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
Networking.SendPed(c);
|
||||
}
|
||||
#if BENCHMARK
|
||||
|
||||
Debug.TimeStamps[TimeStamp.SendPed]=PerfCounter2.ElapsedTicks-start;
|
||||
@ -421,6 +479,9 @@ namespace RageCoop.Client
|
||||
Debug.TimeStamps[TimeStamp.PedTotal]=PerfCounter.ElapsedTicks;
|
||||
#endif
|
||||
}
|
||||
|
||||
i=-1;
|
||||
|
||||
lock (VehiclesLock)
|
||||
{
|
||||
|
||||
@ -428,7 +489,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
if (!Handle_Vehicles.ContainsKey(veh.Handle))
|
||||
{
|
||||
Main.Logger.Debug($"Creating SyncEntity for vehicle, handle:{veh.Handle}");
|
||||
// Main.Logger.Debug($"Creating SyncEntity for vehicle, handle:{veh.Handle}");
|
||||
|
||||
EntityPool.Add(new SyncedVehicle(veh));
|
||||
|
||||
@ -439,8 +500,15 @@ namespace RageCoop.Client
|
||||
|
||||
Debug.TimeStamps[TimeStamp.AddVehicles]=PerfCounter.ElapsedTicks;
|
||||
#endif
|
||||
foreach (SyncedVehicle v in ID_Vehicles.Values.ToArray())
|
||||
var vs = ID_Vehicles.Values.ToArray();
|
||||
vehStateIndex+=vehStatesPerFrame;
|
||||
if (vehStateIndex>=vs.Length)
|
||||
{
|
||||
vehStateIndex=0;
|
||||
}
|
||||
foreach (SyncedVehicle v in vs)
|
||||
{
|
||||
i++;
|
||||
if ((v.MainVehicle!=null)&&(!v.MainVehicle.Exists()))
|
||||
{
|
||||
EntityPool.RemoveVehicle(v.ID,"non-existent");
|
||||
@ -448,18 +516,18 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
// Outgoing sync
|
||||
if (v.IsMine)
|
||||
if (v.IsLocal)
|
||||
{
|
||||
if (!v.MainVehicle.IsVisible) { continue; }
|
||||
SyncEvents.Check(v);
|
||||
if (Main.Ticked%20==0)
|
||||
|
||||
Networking.SendVehicle(v);
|
||||
|
||||
// Send state
|
||||
if ((i-vehStateIndex)<vehStatesPerFrame)
|
||||
{
|
||||
Networking.SendVehicle(v);
|
||||
Networking.SendVehicleState(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
Networking.SendVehicle(v);
|
||||
}
|
||||
|
||||
}
|
||||
else // Incoming sync
|
||||
@ -475,6 +543,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if BENCHMARK
|
||||
Debug.TimeStamps[TimeStamp.VehicleTotal]=PerfCounter.ElapsedTicks;
|
||||
#endif
|
||||
@ -531,6 +600,10 @@ namespace RageCoop.Client
|
||||
s+="\nHandle_Peds: "+Handle_Peds.Count;
|
||||
s+="\nID_Vehicles: "+ID_Vehicles.Count;
|
||||
s+="\nHandle_Vehicles: "+Handle_Vehicles.Count;
|
||||
s+="\nID_Projectiles: "+ID_Projectiles.Count;
|
||||
s+="\nHandle_Projectiles: "+Handle_Projectiles.Count;
|
||||
s+="\npedStatesPerFrame:"+pedStatesPerFrame;
|
||||
s+="\nvehStatesPerFrame:"+vehStatesPerFrame;
|
||||
return s;
|
||||
}
|
||||
public static class ThreadSafe
|
@ -58,7 +58,7 @@ namespace RageCoop.Client {
|
||||
|
||||
public static void TriggerBulletShot(uint hash,SyncedPed owner,Vector3 impactPosition)
|
||||
{
|
||||
Main.Logger.Trace($"bullet shot:{(WeaponHash)hash}");
|
||||
// Main.Logger.Trace($"bullet shot:{(WeaponHash)hash}");
|
||||
|
||||
|
||||
var start = owner.MainPed.GetMuzzlePosition();
|
||||
@ -112,12 +112,19 @@ namespace RageCoop.Client {
|
||||
private static void HandleLeaveVehicle(Packets.LeaveVehicle p)
|
||||
{
|
||||
var ped = EntityPool.GetPedByID(p.ID);
|
||||
var veh = ped.MainPed.CurrentVehicle.GetSyncEntity();
|
||||
veh._checkSeat=false;
|
||||
var flag = LeaveVehicleFlags.None;
|
||||
if (ped.MainPed?.CurrentVehicle==null) { return; }
|
||||
// Bail out
|
||||
if (ped.MainPed.CurrentVehicle.Speed>5) { flag|=LeaveVehicleFlags.BailOut;}
|
||||
ped.PauseUpdate((ulong)Game.FPS*2);
|
||||
// ped.PauseUpdate((ulong)Game.FPS*2);
|
||||
ped.MainPed.Task.LeaveVehicle(flag) ;
|
||||
Task.Run(() =>
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
veh._checkSeat=true;
|
||||
});
|
||||
}
|
||||
private static void HandlePedKilled(Packets.PedKilled p)
|
||||
{
|
||||
@ -169,6 +176,16 @@ namespace RageCoop.Client {
|
||||
weaponHash=1176362416;
|
||||
break;
|
||||
|
||||
// Tampa3, not working for some reason
|
||||
case 3670375085:
|
||||
weaponHash=1176362416;
|
||||
break;
|
||||
|
||||
// Ruiner2, not working for some reason
|
||||
case 50118905:
|
||||
weaponHash=1176362416;
|
||||
break;
|
||||
|
||||
// SAVAGE
|
||||
case 1638077257:
|
||||
weaponHash=(uint)VehicleWeaponHash.PlayerLazer;
|
||||
@ -189,7 +206,7 @@ namespace RageCoop.Client {
|
||||
_lastWeaponHash=weaponHash;
|
||||
}
|
||||
if (!_weaponAsset.IsLoaded) { _weaponAsset.Request(); }
|
||||
World.ShootBullet(start, end, p, _weaponAsset, p.GetWeaponDamage(weaponHash));
|
||||
World.ShootBullet(start, end, p, _weaponAsset, (int)p.GetWeaponDamage(weaponHash));
|
||||
Prop w;
|
||||
if(((w = p.Weapons.CurrentWeaponObject) != null)&&(p.VehicleWeapon==VehicleWeaponHash.Invalid))
|
||||
{
|
||||
@ -211,18 +228,18 @@ namespace RageCoop.Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void HandleEvent(PacketTypes type,byte[] data)
|
||||
public static void HandleEvent(PacketType type,byte[] data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PacketTypes.BulletShot:
|
||||
case PacketType.BulletShot:
|
||||
{
|
||||
Packets.BulletShot p = new Packets.BulletShot();
|
||||
p.Unpack(data);
|
||||
HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID);
|
||||
break;
|
||||
}
|
||||
case PacketTypes.EnteringVehicle:
|
||||
case PacketType.EnteringVehicle:
|
||||
{
|
||||
Packets.EnteringVehicle p = new Packets.EnteringVehicle();
|
||||
p.Unpack(data);
|
||||
@ -231,35 +248,35 @@ namespace RageCoop.Client {
|
||||
|
||||
}
|
||||
break;
|
||||
case PacketTypes.LeaveVehicle:
|
||||
case PacketType.LeaveVehicle:
|
||||
{
|
||||
Packets.LeaveVehicle packet = new Packets.LeaveVehicle();
|
||||
packet.Unpack(data);
|
||||
HandleLeaveVehicle(packet);
|
||||
}
|
||||
break;
|
||||
case PacketTypes.OwnerChanged:
|
||||
case PacketType.OwnerChanged:
|
||||
{
|
||||
Packets.OwnerChanged packet = new Packets.OwnerChanged();
|
||||
packet.Unpack(data);
|
||||
HandleOwnerChanged(packet);
|
||||
}
|
||||
break;
|
||||
case PacketTypes.PedKilled:
|
||||
case PacketType.PedKilled:
|
||||
{
|
||||
var packet = new Packets.PedKilled();
|
||||
packet.Unpack(data);
|
||||
HandlePedKilled(packet);
|
||||
}
|
||||
break;
|
||||
case PacketTypes.EnteredVehicle:
|
||||
case PacketType.EnteredVehicle:
|
||||
{
|
||||
var packet = new Packets.EnteredVehicle();
|
||||
packet.Unpack(data);
|
||||
HandleEnteredVehicle(packet.PedID,packet.VehicleID,(VehicleSeat)packet.VehicleSeat);
|
||||
break;
|
||||
}
|
||||
case PacketTypes.NozzleTransform:
|
||||
case PacketType.NozzleTransform:
|
||||
{
|
||||
var packet = new Packets.NozzleTransform();
|
||||
packet.Unpack(data);
|
||||
@ -268,27 +285,6 @@ namespace RageCoop.Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
public static int GetWeaponDamage(this Ped p,uint hash)
|
||||
{
|
||||
if(p.IsInVehicle() && (hash!=(uint)p.Weapons.Current.Hash))
|
||||
{
|
||||
// This is a vehicle weapon
|
||||
p.VehicleWeapon=(VehicleWeaponHash)hash;
|
||||
return 100;
|
||||
}
|
||||
switch (p.Weapons.Current.Group)
|
||||
{
|
||||
case WeaponGroup.Pistol: return 30;
|
||||
case WeaponGroup.AssaultRifle: return 30;
|
||||
case WeaponGroup.SMG: return 20;
|
||||
case WeaponGroup.MG: return 40;
|
||||
case WeaponGroup.Shotgun: return 30;
|
||||
case WeaponGroup.Sniper: return 200;
|
||||
case WeaponGroup.Heavy: return 30;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
// Potential names and hash collisions included as comments
|
||||
public enum PedConfigFlags
|
||||
internal enum PedConfigFlags
|
||||
{
|
||||
_0x67D1A445 = 0,
|
||||
_0xC63DE95E = 1,
|
@ -65,13 +65,15 @@ namespace RageCoop.Client
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static Dictionary<byte, short> GetPedClothes(this Ped ped)
|
||||
// Not sure whether component will always be lesser than 255, whatever...
|
||||
public static byte[] GetPedClothes(this Ped ped)
|
||||
{
|
||||
Dictionary<byte, short> result = new Dictionary<byte, short>();
|
||||
for (byte i = 0; i < 11; i++)
|
||||
var result = new byte[36];
|
||||
for (byte i = 0; i < 12; i++)
|
||||
{
|
||||
short mod = Function.Call<short>(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i);
|
||||
result.Add(i, mod);
|
||||
result[i]=(byte)Function.Call<short>(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i);
|
||||
result[i+12]=(byte)Function.Call<short>(Hash.GET_PED_TEXTURE_VARIATION, ped.Handle, i);
|
||||
result[i+24]=(byte)Function.Call<short>(Hash.GET_PED_PALETTE_VARIATION, ped.Handle, i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -132,6 +134,12 @@ namespace RageCoop.Client
|
||||
{
|
||||
flags |=PedDataFlags.IsInCover;
|
||||
}
|
||||
|
||||
if(Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, ped))
|
||||
{
|
||||
flags |= PedDataFlags.IsInStealthMode;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
@ -355,7 +363,8 @@ namespace RageCoop.Client
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus2
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus3;
|
||||
case 0:
|
||||
return (VehicleHash)veh.Model.Hash == VehicleHash.Apc;
|
||||
return (VehicleHash)veh.Model.Hash == VehicleHash.Apc
|
||||
|| (VehicleHash)veh.Model.Hash==VehicleHash.Dune3;
|
||||
case 1:
|
||||
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2
|
@ -1,7 +1,7 @@
|
||||
// WARNING: values can change after a game update
|
||||
// if R* adds in the middle!
|
||||
// This is up-to-date for b2372
|
||||
public enum TaskType
|
||||
internal enum TaskType
|
||||
{
|
||||
CTaskHandsUp = 0,
|
||||
CTaskClimbLadder = 1,
|
@ -2,13 +2,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using GTA.Math;
|
||||
using GTA;
|
||||
using RageCoop.Core;
|
||||
using GTA.Native;
|
||||
using System.IO;
|
||||
using System.Xml.Serialization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
@ -20,7 +22,7 @@ namespace RageCoop.Client
|
||||
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)
|
||||
{
|
||||
@ -88,11 +90,13 @@ namespace RageCoop.Client
|
||||
|
||||
|
||||
#endregion
|
||||
public static string SettingsPath= "Scripts\\RageCoop\\Data\\RageCoop.Client.Settings.xml";
|
||||
public static Settings ReadSettings()
|
||||
{
|
||||
XmlSerializer ser = new XmlSerializer(typeof(Settings));
|
||||
|
||||
string path = Directory.GetCurrentDirectory() + "\\Scripts\\RageCoop\\RageCoop.Client.Settings.xml";
|
||||
string path = SettingsPath;
|
||||
Directory.CreateDirectory(Directory.GetParent(path).FullName);
|
||||
Settings settings = null;
|
||||
|
||||
if (File.Exists(path))
|
||||
@ -121,7 +125,8 @@ namespace RageCoop.Client
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = Directory.GetCurrentDirectory() + "\\Scripts\\RageCoop\\RageCoop.Client.Settings.xml";
|
||||
string path = SettingsPath ;
|
||||
Directory.CreateDirectory(Directory.GetParent(path).FullName);
|
||||
|
||||
using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite))
|
||||
{
|
||||
@ -135,8 +140,6 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern ulong GetTickCount64();
|
||||
public static Vector3 PredictPosition(this Entity e, bool applyDefault = true)
|
||||
{
|
||||
return e.Position+e.Velocity*((applyDefault ? SyncParameters.PositioinPredictionDefault : 0)+Networking.Latency);
|
||||
@ -170,6 +173,10 @@ namespace RageCoop.Client
|
||||
Function.Call(Hash.STOP_ENTITY_FIRE, e.Handle);
|
||||
}
|
||||
}
|
||||
public static void SetFrozen(this Entity e,bool toggle)
|
||||
{
|
||||
Function.Call(Hash.FREEZE_ENTITY_POSITION, e, toggle);
|
||||
}
|
||||
|
||||
public static SyncedPed GetSyncEntity(this Ped p)
|
||||
{
|
||||
@ -195,6 +202,50 @@ namespace RageCoop.Client
|
||||
{
|
||||
Function.Call(Hash.SET_RADIO_TO_STATION_INDEX, index);
|
||||
}
|
||||
public static byte[] GetHash(this string inputString)
|
||||
{
|
||||
using (HashAlgorithm algorithm = SHA256.Create())
|
||||
return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
|
||||
}
|
||||
|
||||
|
||||
const UInt32 WM_KEYDOWN = 0x0100;
|
||||
public static void Reload()
|
||||
{
|
||||
string reloadKey="None";
|
||||
var lines = File.ReadAllLines("ScriptHookVDotNet.ini");
|
||||
foreach (var l in lines)
|
||||
{
|
||||
var ss = l.Split('=');
|
||||
if(ss.Length > 0 && ss[0]=="ReloadKey")
|
||||
{
|
||||
reloadKey = ss[1];
|
||||
}
|
||||
}
|
||||
var lineList = lines.ToList();
|
||||
if (reloadKey=="None")
|
||||
{
|
||||
foreach (var l in lines)
|
||||
{
|
||||
var ss = l.Split('=');
|
||||
if (ss.Length > 0 && ss[0]=="ReloadKey")
|
||||
{
|
||||
reloadKey = ss[1];
|
||||
lineList.Remove(l);
|
||||
}
|
||||
}
|
||||
lineList.Add("ReloadKey=Insert");
|
||||
File.WriteAllLines("ScriptHookVDotNet.ini",lineList.ToArray());
|
||||
}
|
||||
Keys key = (Keys)Enum.Parse(typeof(Keys), reloadKey, true);
|
||||
PostMessage(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle, WM_KEYDOWN, (int)key, 0);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
|
||||
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern ulong GetTickCount64();
|
||||
}
|
||||
}
|
@ -72,38 +72,14 @@ namespace RageCoop.Client
|
||||
{
|
||||
flags|= VehicleDataFlags.IsDeluxoHovering;
|
||||
}
|
||||
|
||||
if (veh.HasRoof)
|
||||
{
|
||||
flags|=VehicleDataFlags.HasRoof;
|
||||
}
|
||||
|
||||
|
||||
return flags;
|
||||
}
|
||||
public static bool HasFlag(this PedDataFlags flagToCheck, PedDataFlags flag)
|
||||
{
|
||||
return (flagToCheck & flag)!=0;
|
||||
}
|
||||
|
||||
public static bool HasFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag)
|
||||
{
|
||||
return (flagToCheck & flag)!=0;
|
||||
}
|
||||
|
||||
|
||||
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 Dictionary<int, int> GetVehicleMods(this VehicleModCollection mods)
|
||||
{
|
||||
@ -163,6 +139,66 @@ namespace RageCoop.Client
|
||||
RightHeadLightBroken = (byte)(veh.IsRightHeadLightBroken ? 1 : 0)
|
||||
};
|
||||
}
|
||||
|
||||
public static void SetDamageModel(this Vehicle veh, VehicleDamageModel model, bool leavedoors = true)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
var door = veh.Doors[(VehicleDoorIndex)i];
|
||||
if ((model.BrokenDoors & (byte)(1 << i)) != 0)
|
||||
{
|
||||
if (!door.IsBroken)
|
||||
{
|
||||
door.Break(leavedoors);
|
||||
}
|
||||
}
|
||||
else 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 (VehicleWheel 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 Dictionary<int, int> GetPassengers(this Vehicle veh)
|
||||
{
|
||||
@ -231,62 +267,6 @@ namespace RageCoop.Client
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio);
|
||||
}
|
||||
public static void SetDamageModel(this Vehicle veh, VehicleDamageModel model, bool leavedoors = true)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
var door = veh.Doors[(VehicleDoorIndex)i];
|
||||
if ((model.BrokenDoors & (byte)(1 << i)) != 0)
|
||||
{
|
||||
door.Break(leavedoors);
|
||||
}
|
||||
else 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 (VehicleWheel 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;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
using GTA.Math;
|
||||
|
||||
namespace RageCoop.Client
|
||||
@ -20,6 +21,23 @@ namespace RageCoop.Client
|
||||
}
|
||||
internal static class WeaponUtil
|
||||
{
|
||||
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 Vector3 GetMuzzlePosition(this Ped p)
|
||||
{
|
||||
var w = p.Weapons.CurrentWeaponObject;
|
||||
@ -32,12 +50,79 @@ namespace RageCoop.Client
|
||||
return p.Bones[Bone.SkelRightHand].Position;
|
||||
}
|
||||
static long BulletsShot=0;
|
||||
|
||||
public static float GetWeaponDamage(this Ped P, uint hash)
|
||||
{
|
||||
var comp = P.Weapons.Current.Components.GetSuppressorComponent();
|
||||
return Function.Call<float>(Hash.GET_WEAPON_DAMAGE, hash, comp.Active ? comp.ComponentHash : WeaponComponentHash.Invalid);
|
||||
|
||||
/*
|
||||
if (P.IsInVehicle() && (hash!=(uint)P.Weapons.Current.Hash))
|
||||
{
|
||||
// This is a vehicle weapon
|
||||
P.VehicleWeapon=(VehicleWeaponHash)hash;
|
||||
return 100;
|
||||
}
|
||||
switch (P.Weapons.Current.Group)
|
||||
{
|
||||
case WeaponGroup.Pistol: return 30;
|
||||
case WeaponGroup.AssaultRifle: return 30;
|
||||
case WeaponGroup.SMG: return 20;
|
||||
case WeaponGroup.MG: return 40;
|
||||
case WeaponGroup.Shotgun: return 30;
|
||||
case WeaponGroup.Sniper: return 200;
|
||||
case WeaponGroup.Heavy: return 30;
|
||||
}
|
||||
return 0;
|
||||
*/
|
||||
}
|
||||
|
||||
public static MuzzleInfo GetMuzzleInfo(this Vehicle v)
|
||||
{
|
||||
BulletsShot++;
|
||||
int i;
|
||||
switch (v.Model.Hash)
|
||||
{
|
||||
// JB7002
|
||||
case 394110044:
|
||||
i=BulletsShot%2==0 ? 54 : 53;
|
||||
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
|
||||
|
||||
// DOMINATOR5
|
||||
case -1375060657:
|
||||
i=BulletsShot%2==0 ? 35 : 36;
|
||||
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
|
||||
|
||||
|
||||
|
||||
// IMPALER3
|
||||
case -1924800695:
|
||||
i=BulletsShot%2==0 ? 75 : 76;
|
||||
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
|
||||
|
||||
|
||||
|
||||
// IMPERATOR2
|
||||
case 1637620610:
|
||||
i=BulletsShot%2==0 ? 97 : 99;
|
||||
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
|
||||
|
||||
|
||||
|
||||
// SLAMVAN5
|
||||
case 373261600:
|
||||
i=BulletsShot%2==0 ? 51 : 53;
|
||||
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
|
||||
|
||||
// RUINER2
|
||||
case 941494461:
|
||||
i=BulletsShot%2==0 ? 65 : 66;
|
||||
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
|
||||
|
||||
// TAMPA3
|
||||
case -1210451983:
|
||||
return new MuzzleInfo(v.Bones[87].Position, v.Bones[87].ForwardVector);
|
||||
|
||||
// SCRAMJET
|
||||
case -638562243:
|
||||
i=BulletsShot%2==0 ? 44 : 45;
|
@ -111,7 +111,7 @@ namespace RageCoop.Client
|
||||
foreach (Ped ped in World.GetAllPeds())
|
||||
{
|
||||
SyncedPed c = EntityPool.GetPedByHandle(ped.Handle);
|
||||
if ((c==null) || (c.IsMine && (ped.Handle!=Game.Player.Character.Handle)&&ped.PopulationType!=EntityPopulationType.Mission))
|
||||
if ((c==null) || (c.IsLocal && (ped.Handle!=Game.Player.Character.Handle)&&ped.PopulationType!=EntityPopulationType.Mission))
|
||||
{
|
||||
if (ped.Handle==Game.Player.Character.Handle) { continue; }
|
||||
|
||||
@ -131,7 +131,7 @@ namespace RageCoop.Client
|
||||
// Don't delete player's vehicle
|
||||
continue;
|
||||
}
|
||||
if((v== null) || (v.IsMine&&veh.PopulationType!=EntityPopulationType.Mission))
|
||||
if((v== null) || (v.IsLocal&&veh.PopulationType!=EntityPopulationType.Mission))
|
||||
{
|
||||
Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
|
||||
|
BIN
RageCoop.Client/icon.ico
Normal file
BIN
RageCoop.Client/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@ -5,7 +5,7 @@ using GTA.Math;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public class BitReader
|
||||
internal class BitReader
|
||||
{
|
||||
public int CurrentIndex { get; set; }
|
||||
|
||||
@ -50,11 +50,15 @@ namespace RageCoop.Core
|
||||
|
||||
public byte[] ReadByteArray(int length)
|
||||
{
|
||||
byte[] value = ResultArray.Skip(CurrentIndex).Take(length).ToArray();
|
||||
byte[] value = new byte[length];
|
||||
Array.Copy(ResultArray, CurrentIndex,value,0,length);
|
||||
CurrentIndex += length;
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte[] ReadByteArray()
|
||||
{
|
||||
return ReadByteArray(ReadInt());
|
||||
}
|
||||
public short ReadShort()
|
||||
{
|
||||
short value = BitConverter.ToInt16(ResultArray, CurrentIndex);
|
||||
@ -103,6 +107,13 @@ namespace RageCoop.Core
|
||||
CurrentIndex += index;
|
||||
return value;
|
||||
}
|
||||
public string ReadString()
|
||||
{
|
||||
var len = ReadInt();
|
||||
string value = Encoding.UTF8.GetString(ResultArray.Skip(CurrentIndex).Take(len).ToArray());
|
||||
CurrentIndex += len;
|
||||
return value;
|
||||
}
|
||||
|
||||
public Vector3 ReadVector3()
|
||||
{
|
||||
@ -113,6 +124,14 @@ namespace RageCoop.Core
|
||||
Z = ReadFloat()
|
||||
};
|
||||
}
|
||||
public Vector2 ReadVector2()
|
||||
{
|
||||
return new Vector2()
|
||||
{
|
||||
X = ReadFloat(),
|
||||
Y = ReadFloat()
|
||||
};
|
||||
}
|
||||
public Quaternion ReadQuaternion()
|
||||
{
|
||||
return new Quaternion()
|
287
RageCoop.Core/CoreUtils.cs
Normal file
287
RageCoop.Core/CoreUtils.cs
Normal file
@ -0,0 +1,287 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using GTA.Math;
|
||||
using System.Security.Cryptography;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("RageCoop.Server")]
|
||||
[assembly: InternalsVisibleTo("RageCoop.Client")]
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal class CoreUtils
|
||||
{
|
||||
|
||||
public static (byte, byte[]) GetBytesFromObject(object obj)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case byte _:
|
||||
return (0x01, new byte[] { (byte)obj });
|
||||
case short _:
|
||||
return (0x02, BitConverter.GetBytes((short)obj));
|
||||
case ushort _:
|
||||
return (0x03, BitConverter.GetBytes((ushort)obj));
|
||||
case int _:
|
||||
return (0x04, BitConverter.GetBytes((int)obj));
|
||||
case uint _:
|
||||
return (0x05, BitConverter.GetBytes((uint)obj));
|
||||
case long _:
|
||||
return (0x06, BitConverter.GetBytes((long)obj));
|
||||
case ulong _:
|
||||
return (0x07, BitConverter.GetBytes((ulong)obj));
|
||||
case float _:
|
||||
return (0x08, BitConverter.GetBytes((float)obj));
|
||||
case bool _:
|
||||
return (0x09, BitConverter.GetBytes((bool)obj));
|
||||
case string _:
|
||||
return (0x10, ((string)obj).GetBytesWithLength());
|
||||
case Vector3 _:
|
||||
return (0x11,((Vector3)obj).GetBytes());
|
||||
case Quaternion _:
|
||||
return (0x12, ((Quaternion)obj).GetBytes());
|
||||
case GTA.Model _:
|
||||
return (0x13, BitConverter.GetBytes((GTA.Model)obj));
|
||||
case Vector2 _:
|
||||
return (0x14, ((Vector2)obj).GetBytes());
|
||||
case Tuple<byte, byte[]> _:
|
||||
var tup = (Tuple<byte, byte[]>)obj;
|
||||
return (tup.Item1, tup.Item2);
|
||||
default:
|
||||
return (0x0, null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
internal static class Extensions
|
||||
{
|
||||
public static void AddVector3(this List<byte> bytes, Vector3 vec3)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(vec3.X));
|
||||
bytes.AddRange(BitConverter.GetBytes(vec3.Y));
|
||||
bytes.AddRange(BitConverter.GetBytes(vec3.Z));
|
||||
}
|
||||
public static void AddQuaternion(this List<byte> bytes, Quaternion quat)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(quat.X));
|
||||
bytes.AddRange(BitConverter.GetBytes(quat.Y));
|
||||
bytes.AddRange(BitConverter.GetBytes(quat.Z));
|
||||
bytes.AddRange(BitConverter.GetBytes(quat.W));
|
||||
}
|
||||
public static void AddInt(this List<byte> bytes,int i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddUint(this List<byte> bytes, uint i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddShort(this List<byte> bytes, short i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddUshort(this List<byte> bytes, ushort i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddLong(this List<byte> bytes, long i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddUlong(this List<byte> bytes, ulong i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddFloat(this List<byte> bytes, float i)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(i));
|
||||
}
|
||||
public static void AddString(this List<byte> bytes, string s)
|
||||
{
|
||||
var sb = Encoding.UTF8.GetBytes(s);
|
||||
bytes.AddInt(sb.Length);
|
||||
bytes.AddRange(sb);
|
||||
}
|
||||
public static void AddArray(this List<byte> bytes, byte[] toadd)
|
||||
{
|
||||
bytes.AddInt(toadd.Length);
|
||||
bytes.AddRange(toadd);
|
||||
}
|
||||
|
||||
public static int GetHash(string s)
|
||||
{
|
||||
MD5 md5Hasher = MD5.Create();
|
||||
var hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(s));
|
||||
return BitConverter.ToInt32(hashed, 0);
|
||||
}
|
||||
public static byte[] GetBytes(this string s)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(s);
|
||||
}
|
||||
public static byte[] GetBytesWithLength(this string s)
|
||||
{
|
||||
var data = new List<byte>(100);
|
||||
var sb = Encoding.UTF8.GetBytes(s);
|
||||
data.AddInt(sb.Length);
|
||||
data.AddRange(sb);
|
||||
return data.ToArray();
|
||||
}
|
||||
public static string GetString(this byte[] data)
|
||||
{
|
||||
return Encoding.UTF8.GetString(data);
|
||||
}
|
||||
public static byte[] GetBytes(this Vector3 vec)
|
||||
{
|
||||
// 12 bytes
|
||||
return new List<byte[]>() { BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y), BitConverter.GetBytes(vec.Z) }.Join(4);
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(this Vector2 vec)
|
||||
{
|
||||
// 8 bytes
|
||||
return new List<byte[]>() { BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y) }.Join(4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="qua"></param>
|
||||
/// <returns>An array of bytes with length 16</returns>
|
||||
public static byte[] GetBytes(this Quaternion qua)
|
||||
{
|
||||
// 16 bytes
|
||||
return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4);
|
||||
}
|
||||
|
||||
public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag)
|
||||
{
|
||||
return (flagToCheck & flag)!=0;
|
||||
}
|
||||
|
||||
public static bool HasVehFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag)
|
||||
{
|
||||
return (flagToCheck & flag)!=0;
|
||||
}
|
||||
public static bool HasConfigFlag(this PlayerConfigFlags flagToCheck, PlayerConfigFlags flag)
|
||||
{
|
||||
return (flagToCheck & 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(Single);
|
||||
|
||||
case TypeCode.String:
|
||||
return typeof(string);
|
||||
|
||||
case TypeCode.UInt16:
|
||||
return typeof(UInt16);
|
||||
|
||||
case TypeCode.UInt32:
|
||||
return typeof(UInt32);
|
||||
|
||||
case TypeCode.UInt64:
|
||||
return typeof(UInt64);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static string DumpWithType(this IEnumerable<object> objects)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach(var obj in objects)
|
||||
{
|
||||
sb.Append(obj.GetType()+":"+obj.ToString()+"\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 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];
|
||||
int writeIdx = 0;
|
||||
foreach (var byteArr in arrays)
|
||||
{
|
||||
byteArr.CopyTo(output, writeIdx);
|
||||
writeIdx += byteArr.Length;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static bool IsSubclassOf(this Type type, string baseTypeName)
|
||||
{
|
||||
for (Type t = type.BaseType; t != null; t = t.BaseType)
|
||||
if (t.FullName == baseTypeName)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
239
RageCoop.Core/Logger.cs
Normal file
239
RageCoop.Core/Logger.cs
Normal file
@ -0,0 +1,239 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class Logger : IDisposable
|
||||
{
|
||||
|
||||
/// <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 { get; set; }
|
||||
/// <summary>
|
||||
/// Path to log file.
|
||||
/// </summary>
|
||||
public string LogPath;
|
||||
/// <summary>
|
||||
/// Whether to flush messages to console instead of log file
|
||||
/// </summary>
|
||||
public bool UseConsole = false;
|
||||
private StreamWriter logWriter;
|
||||
|
||||
private string Buffer = "";
|
||||
private Thread LoggerThread;
|
||||
private bool Stopping = false;
|
||||
private bool FlushImmediately;
|
||||
|
||||
internal Logger(bool flushImmediately = false, bool overwrite = true)
|
||||
{
|
||||
FlushImmediately = flushImmediately;
|
||||
if (File.Exists(LogPath)&&overwrite) { File.Delete(LogPath); }
|
||||
Name=Process.GetCurrentProcess().Id.ToString();
|
||||
if (!flushImmediately)
|
||||
{
|
||||
LoggerThread=new Thread(() =>
|
||||
{
|
||||
if (!UseConsole)
|
||||
{
|
||||
while (LogPath==default)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
if (File.Exists(LogPath)&&overwrite) { File.Delete(LogPath); }
|
||||
}
|
||||
while (!Stopping)
|
||||
{
|
||||
Flush();
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
Flush();
|
||||
});
|
||||
LoggerThread.Start();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Info(string message)
|
||||
{
|
||||
if (LogLevel>2) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [INF] {1}", Date(), message, Name);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
}
|
||||
if (FlushImmediately)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Warning(string message)
|
||||
{
|
||||
if (LogLevel>3) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [WRN] {1}", Date(), message, Name);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
|
||||
}
|
||||
if (FlushImmediately)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Error(string message)
|
||||
{
|
||||
if (LogLevel>4) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), message, Name);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
}
|
||||
if (FlushImmediately)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="error"></param>
|
||||
public void Error(string message, Exception error)
|
||||
{
|
||||
if (LogLevel>4) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [ERR] {1}:{3}", Date(), message, Name,error.Message);
|
||||
Buffer+=msg+"\r\n";
|
||||
Trace(error.ToString());
|
||||
|
||||
}
|
||||
if (FlushImmediately)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="ex"></param>
|
||||
public void Error(Exception ex)
|
||||
{
|
||||
if (LogLevel>4) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n"+ex.Message, Name);
|
||||
Buffer+=msg+"\r\n";
|
||||
Trace(ex.ToString());
|
||||
}
|
||||
if (FlushImmediately)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Debug(string message)
|
||||
{
|
||||
|
||||
if (LogLevel>1) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [DBG] {1}", Date(), message, Name);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
}
|
||||
if (FlushImmediately)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Trace(string message)
|
||||
{
|
||||
if (LogLevel>0) { return; }
|
||||
lock (Buffer)
|
||||
{
|
||||
string msg = string.Format("[{0}][{2}] [TRC] {1}", Date(), message, Name);
|
||||
|
||||
Buffer+=msg+"\r\n";
|
||||
}
|
||||
if (FlushImmediately)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private string Date()
|
||||
{
|
||||
return DateTime.Now.ToString();
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public void Flush()
|
||||
{
|
||||
lock (Buffer)
|
||||
{
|
||||
if (Buffer!="")
|
||||
{
|
||||
if (UseConsole)
|
||||
{
|
||||
Console.Write(Buffer);
|
||||
Buffer="";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
logWriter=new StreamWriter(LogPath, true, Encoding.UTF8);
|
||||
logWriter.Write(Buffer);
|
||||
logWriter.Close();
|
||||
Buffer="";
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Stop backdround thread and flush all pending messages.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Stopping=true;
|
||||
LoggerThread?.Join();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
using System;
|
||||
using GTA.Math;
|
||||
|
||||
namespace RageCoop.Client
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
|
||||
internal static class MathExtensions
|
||||
{
|
||||
public const float Deg2Rad=(float)(Math.PI* 2) / 360;
|
||||
public const float Rad2Deg = 360 / (float)(Math.PI * 2);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@ -145,5 +147,6 @@ namespace RageCoop.Client
|
||||
|
||||
return Vector3.Dot(v1, v2)/(v1.Length()*v2.Length());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
101
RageCoop.Core/Packets/CustomEvent.cs
Normal file
101
RageCoop.Core/Packets/CustomEvent.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Lidgren.Network;
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal partial class Packets
|
||||
{
|
||||
|
||||
internal class CustomEvent : Packet
|
||||
{
|
||||
public CustomEvent(Func<byte,BitReader,object> onResolve = null,bool queued=false)
|
||||
{
|
||||
_resolve= onResolve;
|
||||
_queued= queued;
|
||||
}
|
||||
private bool _queued;
|
||||
private Func<byte, BitReader, object> _resolve { get; set; }
|
||||
public int Hash { get; set; }
|
||||
public bool IsStaged { get; set; }=false;
|
||||
public object[] Args { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
Args= Args ?? new object[] { };
|
||||
message.Write(_queued ? (byte)PacketType.CustomEventQueued: (byte)PacketType.CustomEvent);
|
||||
|
||||
List<byte> result = new List<byte>();
|
||||
result.AddInt(Hash);
|
||||
result.AddInt(Args.Length);
|
||||
(byte, byte[]) tup;
|
||||
foreach (var arg in Args)
|
||||
{
|
||||
tup=CoreUtils.GetBytesFromObject(arg);
|
||||
if (tup.Item1==0||tup.Item2==null)
|
||||
{
|
||||
throw new ArgumentException($"Object of type {arg.GetType()} is not supported");
|
||||
}
|
||||
result.Add(tup.Item1);
|
||||
result.AddRange(tup.Item2);
|
||||
}
|
||||
|
||||
message.Write(result.Count);
|
||||
message.Write(result.ToArray());
|
||||
}
|
||||
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
Hash = reader.ReadInt();
|
||||
var len=reader.ReadInt();
|
||||
Args=new object[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
byte type = reader.ReadByte();
|
||||
switch (type)
|
||||
{
|
||||
case 0x01:
|
||||
Args[i]=reader.ReadByte(); break;
|
||||
case 0x02:
|
||||
Args[i]=reader.ReadShort(); break;
|
||||
case 0x03:
|
||||
Args[i]=reader.ReadUShort(); break;
|
||||
case 0x04:
|
||||
Args[i]=reader.ReadInt(); break;
|
||||
case 0x05:
|
||||
Args[i]=reader.ReadUInt(); break;
|
||||
case 0x06:
|
||||
Args[i]=reader.ReadLong(); break;
|
||||
case 0x07:
|
||||
Args[i]=reader.ReadULong(); break;
|
||||
case 0x08:
|
||||
Args[i]=reader.ReadFloat(); break;
|
||||
case 0x09:
|
||||
Args[i]=reader.ReadBool(); break;
|
||||
case 0x10:
|
||||
Args[i]=reader.ReadString(); break;
|
||||
case 0x11:
|
||||
Args[i]=reader.ReadVector3(); break;
|
||||
case 0x12:
|
||||
Args[i]=reader.ReadQuaternion(); break;
|
||||
case 0x13:
|
||||
Args[i]=(GTA.Model)reader.ReadInt(); break;
|
||||
case 0x14:
|
||||
Args[i]=reader.ReadVector2(); break;
|
||||
default:
|
||||
if (_resolve==null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unexpected type:{type}\r\n{array.Dump()}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Args[i]=_resolve(type, reader); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,28 +6,37 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal enum FileResponse:byte
|
||||
{
|
||||
public class FileTransferRequest : Packet
|
||||
NeedToDownload=0,
|
||||
AlreadyExists=1,
|
||||
Completed=2,
|
||||
Loaded=3,
|
||||
LoadFailed=4,
|
||||
}
|
||||
internal partial class Packets
|
||||
{
|
||||
internal class FileTransferRequest : Packet
|
||||
{
|
||||
public byte ID { get; set; }
|
||||
public int ID { get; set; }
|
||||
|
||||
public string FileName { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public long FileLength { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.FileTransferRequest);
|
||||
message.Write((byte)PacketType.FileTransferRequest);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
// The ID from the download
|
||||
byteArray.Add(ID);
|
||||
byteArray.AddInt(ID);
|
||||
|
||||
|
||||
// The name of the file
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(FileName);
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(Name);
|
||||
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
|
||||
byteArray.AddRange(nameBytes);
|
||||
|
||||
@ -46,29 +55,59 @@ namespace RageCoop.Core
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
ID = reader.ReadByte();
|
||||
ID = reader.ReadInt();
|
||||
int nameArrayLength = reader.ReadInt();
|
||||
FileName = reader.ReadString(nameArrayLength);
|
||||
Name = reader.ReadString(nameArrayLength);
|
||||
FileLength = reader.ReadLong();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public class FileTransferTick : Packet
|
||||
internal class FileTransferResponse : Packet
|
||||
{
|
||||
public byte ID { get; set; }
|
||||
public int ID { get; set; }
|
||||
public FileResponse Response { get; set; }
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
message.Write((byte)PacketType.FileTransferResponse);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
// The ID from the download
|
||||
byteArray.AddInt(ID);
|
||||
|
||||
byteArray.Add((byte)Response);
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
message.Write(result);
|
||||
}
|
||||
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
ID = reader.ReadInt();
|
||||
Response = (FileResponse)reader.ReadByte();
|
||||
}
|
||||
}
|
||||
|
||||
internal class FileTransferChunk : Packet
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
public byte[] FileChunk { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.FileTransferTick);
|
||||
message.Write((byte)PacketType.FileTransferChunk);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
// The ID from the download
|
||||
byteArray.Add(ID);
|
||||
byteArray.AddInt(ID);
|
||||
|
||||
// The chunk of the file
|
||||
byteArray.AddRange(BitConverter.GetBytes(FileChunk.Length));
|
||||
@ -86,26 +125,26 @@ namespace RageCoop.Core
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
ID = reader.ReadByte();
|
||||
ID = reader.ReadInt();
|
||||
int chunkLength = reader.ReadInt();
|
||||
FileChunk = reader.ReadByteArray(chunkLength);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public class FileTransferComplete : Packet
|
||||
internal class FileTransferComplete : Packet
|
||||
{
|
||||
public byte ID { get; set; }
|
||||
public int ID { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.FileTransferComplete);
|
||||
message.Write((byte)PacketType.FileTransferComplete);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
// The ID from the download
|
||||
byteArray.Add(ID);
|
||||
// The ID for the download
|
||||
byteArray.AddInt(ID);
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
@ -119,7 +158,24 @@ namespace RageCoop.Core
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
ID = reader.ReadByte();
|
||||
ID = reader.ReadInt();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
internal class AllResourcesSent : Packet
|
||||
{
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketType.AllResourcesSent);
|
||||
message.Write(0);
|
||||
#endregion
|
||||
}
|
||||
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
#endregion
|
||||
}
|
||||
}
|
235
RageCoop.Core/Packets/Packets.cs
Normal file
235
RageCoop.Core/Packets/Packets.cs
Normal file
@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Lidgren.Network;
|
||||
using Newtonsoft.Json;
|
||||
using GTA.Math;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal enum PacketType:byte
|
||||
{
|
||||
Handshake=0,
|
||||
PlayerConnect=1,
|
||||
PlayerDisconnect=2,
|
||||
PlayerInfoUpdate=3,
|
||||
PublicKeyRequest=4,
|
||||
PublicKeyResponse=5,
|
||||
Request=6,
|
||||
Response=7,
|
||||
|
||||
ChatMessage=10,
|
||||
// NativeCall=11,
|
||||
// NativeResponse=12,
|
||||
// Mod=13,
|
||||
// CleanUpWorld=14,
|
||||
|
||||
FileTransferChunk=11,
|
||||
FileTransferRequest=12,
|
||||
FileTransferResponse = 13,
|
||||
FileTransferComplete =14,
|
||||
AllResourcesSent=15,
|
||||
|
||||
CustomEvent = 16,
|
||||
CustomEventQueued = 17,
|
||||
#region Sync
|
||||
|
||||
#region INTERVAL
|
||||
VehicleSync = 20,
|
||||
VehicleStateSync = 21,
|
||||
PedSync = 22,
|
||||
PedStateSync = 23,
|
||||
ProjectileSync=24,
|
||||
#endregion
|
||||
|
||||
#region EVENT
|
||||
|
||||
PedKilled=30,
|
||||
BulletShot=31,
|
||||
EnteringVehicle=32,
|
||||
LeaveVehicle = 33,
|
||||
EnteredVehicle=34,
|
||||
OwnerChanged=35,
|
||||
VehicleBulletShot = 36,
|
||||
NozzleTransform=37,
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
Unknown=255
|
||||
}
|
||||
internal static class PacketExtensions
|
||||
{
|
||||
internal static bool IsSyncEvent(this PacketType p)
|
||||
{
|
||||
return (30<=(byte)p)&&((byte)p<=40);
|
||||
}
|
||||
}
|
||||
|
||||
internal enum ConnectionChannel
|
||||
{
|
||||
Default = 0,
|
||||
Chat = 5,
|
||||
Native = 6,
|
||||
Mod = 7,
|
||||
File = 8,
|
||||
Event = 9,
|
||||
RequestResponse=10,
|
||||
VehicleSync=20,
|
||||
PedSync=21,
|
||||
ProjectileSync = 22,
|
||||
SyncEvents =30,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum PedDataFlags:ushort
|
||||
{
|
||||
None=0,
|
||||
IsAiming = 1 << 0,
|
||||
IsInStealthMode = 1 << 1,
|
||||
IsReloading = 1 << 2,
|
||||
IsJumping = 1 << 3,
|
||||
IsRagdoll = 1 << 4,
|
||||
IsOnFire = 1 << 5,
|
||||
IsInParachuteFreeFall = 1 << 6,
|
||||
IsParachuteOpen = 1 << 7,
|
||||
IsOnLadder = 1 << 8,
|
||||
IsVaulting = 1 << 9,
|
||||
IsInCover = 1 << 10,
|
||||
}
|
||||
|
||||
#region ===== VEHICLE DATA =====
|
||||
internal enum VehicleDataFlags:ushort
|
||||
{
|
||||
None=0,
|
||||
IsEngineRunning = 1 << 0,
|
||||
AreLightsOn = 1 << 1,
|
||||
AreBrakeLightsOn = 1 << 2,
|
||||
AreHighBeamsOn = 1 << 3,
|
||||
IsSirenActive = 1 << 4,
|
||||
IsDead = 1 << 5,
|
||||
IsHornActive = 1 << 6,
|
||||
IsTransformed = 1 << 7,
|
||||
RoofOpened = 1 << 8,
|
||||
OnTurretSeat = 1 << 9,
|
||||
IsAircraft = 1 << 10,
|
||||
IsDeluxoHovering=1 << 11,
|
||||
HasRoof=1 << 12,
|
||||
}
|
||||
|
||||
internal enum PlayerConfigFlags : byte
|
||||
{
|
||||
None = 0,
|
||||
ShowBlip= 1 << 0,
|
||||
ShowNameTag= 1 << 1
|
||||
}
|
||||
|
||||
internal struct VehicleDamageModel
|
||||
{
|
||||
public byte BrokenDoors { get; set; }
|
||||
public byte OpenedDoors { get; set; }
|
||||
public byte BrokenWindows { get; set; }
|
||||
public short BurstedTires { get; set; }
|
||||
public byte LeftHeadLightBroken { get; set; }
|
||||
public byte RightHeadLightBroken { get; set; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
interface IPacket
|
||||
{
|
||||
void Pack(NetOutgoingMessage message);
|
||||
void Unpack(byte[] array);
|
||||
}
|
||||
|
||||
internal abstract class Packet : IPacket
|
||||
{
|
||||
public abstract void Pack(NetOutgoingMessage message);
|
||||
public abstract void Unpack(byte[] array);
|
||||
}
|
||||
|
||||
internal partial class Packets
|
||||
{
|
||||
|
||||
internal class ChatMessage : Packet
|
||||
{
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketType.ChatMessage);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username);
|
||||
byte[] messageBytes = Encoding.UTF8.GetBytes(Message);
|
||||
|
||||
// Write UsernameLength
|
||||
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
|
||||
|
||||
// Write Username
|
||||
byteArray.AddRange(usernameBytes);
|
||||
|
||||
// Write MessageLength
|
||||
byteArray.AddRange(BitConverter.GetBytes(messageBytes.Length));
|
||||
|
||||
// Write Message
|
||||
byteArray.AddRange(messageBytes);
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
message.Write(result);
|
||||
#endregion
|
||||
}
|
||||
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
|
||||
// Read username
|
||||
int usernameLength = reader.ReadInt();
|
||||
Username = reader.ReadString(usernameLength);
|
||||
|
||||
// Read message
|
||||
int messageLength = reader.ReadInt();
|
||||
Message = reader.ReadString(messageLength);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class CoopSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static byte[] Serialize(this object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string jsonString = JsonConvert.SerializeObject(obj);
|
||||
return System.Text.Encoding.UTF8.GetBytes(jsonString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static T Deserialize<T>(this byte[] bytes) where T : class
|
||||
{
|
||||
if (bytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var jsonString = System.Text.Encoding.UTF8.GetString(bytes);
|
||||
return JsonConvert.DeserializeObject<T>(jsonString);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,35 +2,39 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using GTA.Math;
|
||||
using GTA;
|
||||
using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// For non-critical properties, synced every 20 frames.
|
||||
/// </summary>
|
||||
public class PedStateSync : Packet
|
||||
internal class PedStateSync : Packet
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
public int ModelHash { get; set; }
|
||||
|
||||
public Dictionary<byte, short> Clothes { get; set; }
|
||||
public byte[] Clothes { get; set; }
|
||||
|
||||
public int OwnerID { get; set; }
|
||||
|
||||
public Dictionary<uint, bool> WeaponComponents { get; set; }
|
||||
|
||||
|
||||
public byte WeaponTint { get;set; }
|
||||
public BlipColor BlipColor { get; set; } = (BlipColor)255;
|
||||
|
||||
public BlipSprite BlipSprite { get; set; }= 0;
|
||||
public float BlipScale { get; set; } = 1;
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.PedStateSync);
|
||||
message.Write((byte)PacketType.PedStateSync);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
@ -40,15 +44,7 @@ namespace RageCoop.Core
|
||||
// Write model hash
|
||||
byteArray.AddInt(ModelHash);
|
||||
|
||||
// Write player clothes
|
||||
// Write the count of clothes
|
||||
byteArray.AddRange(BitConverter.GetBytes((ushort)Clothes.Count));
|
||||
// Loop the dictionary and add the values
|
||||
foreach (KeyValuePair<byte, short> cloth in Clothes)
|
||||
{
|
||||
byteArray.Add(cloth.Key);
|
||||
byteArray.AddRange(BitConverter.GetBytes(cloth.Value));
|
||||
}
|
||||
byteArray.AddRange(Clothes);
|
||||
|
||||
//Write OwnerID for this ped
|
||||
byteArray.AddRange(BitConverter.GetBytes(OwnerID));
|
||||
@ -70,6 +66,14 @@ namespace RageCoop.Core
|
||||
byteArray.Add(0x00);
|
||||
}
|
||||
|
||||
byteArray.Add(WeaponTint);
|
||||
|
||||
byteArray.Add((byte)BlipColor);
|
||||
if ((byte)BlipColor!=255)
|
||||
{
|
||||
byteArray.AddUshort((ushort)BlipSprite);
|
||||
byteArray.AddFloat(BlipScale);
|
||||
}
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
message.Write(result.Length);
|
||||
@ -89,16 +93,7 @@ namespace RageCoop.Core
|
||||
ModelHash = reader.ReadInt();
|
||||
|
||||
// Read player clothes
|
||||
// Create new Dictionary
|
||||
Clothes = new Dictionary<byte, short>();
|
||||
// Read the count of clothes
|
||||
ushort clothCount = reader.ReadUShort();
|
||||
// For clothCount
|
||||
for (ushort i = 0; i < clothCount; i++)
|
||||
{
|
||||
// Read cloth value
|
||||
Clothes.Add(reader.ReadByte(), reader.ReadShort());
|
||||
}
|
||||
Clothes =reader.ReadByteArray(36);
|
||||
|
||||
// Read ped OwnerID
|
||||
OwnerID= reader.ReadInt();
|
||||
@ -113,13 +108,21 @@ namespace RageCoop.Core
|
||||
WeaponComponents.Add(reader.ReadUInt(), reader.ReadBool());
|
||||
}
|
||||
}
|
||||
WeaponTint=reader.ReadByte();
|
||||
|
||||
BlipColor=(BlipColor)reader.ReadByte();
|
||||
|
||||
if ((byte)BlipColor!=255)
|
||||
{
|
||||
BlipSprite=(BlipSprite)reader.ReadUShort();
|
||||
BlipScale=reader.ReadFloat();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class PedSync : Packet
|
||||
internal class PedSync : Packet
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public PedDataFlags Flag { get; set; }
|
||||
@ -145,7 +148,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.PedSync);
|
||||
message.Write((byte)PacketType.PedSync);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
@ -168,7 +171,7 @@ namespace RageCoop.Core
|
||||
// Write ped velocity
|
||||
byteArray.AddVector3(Velocity);
|
||||
|
||||
if (Flag.HasFlag(PedDataFlags.IsRagdoll))
|
||||
if (Flag.HasPedFlag(PedDataFlags.IsRagdoll))
|
||||
{
|
||||
byteArray.AddVector3(RotationVelocity);
|
||||
}
|
||||
@ -179,7 +182,7 @@ namespace RageCoop.Core
|
||||
// Write ped weapon hash
|
||||
byteArray.AddRange(BitConverter.GetBytes(CurrentWeaponHash));
|
||||
|
||||
if (Flag.HasFlag(PedDataFlags.IsAiming))
|
||||
if (Flag.HasPedFlag(PedDataFlags.IsAiming))
|
||||
{
|
||||
// Write ped aim coords
|
||||
byteArray.AddVector3(AimCoords);
|
||||
@ -218,7 +221,7 @@ namespace RageCoop.Core
|
||||
Velocity = reader.ReadVector3();
|
||||
|
||||
// Read rotation velocity if in ragdoll
|
||||
if (Flag.HasFlag(PedDataFlags.IsRagdoll))
|
||||
if (Flag.HasPedFlag(PedDataFlags.IsRagdoll))
|
||||
{
|
||||
RotationVelocity=reader.ReadVector3();
|
||||
}
|
||||
@ -230,7 +233,7 @@ namespace RageCoop.Core
|
||||
CurrentWeaponHash = reader.ReadUInt();
|
||||
|
||||
// Try to read aim coords
|
||||
if (Flag.HasFlag(PedDataFlags.IsAiming))
|
||||
if (Flag.HasPedFlag(PedDataFlags.IsAiming))
|
||||
{
|
||||
// Read player aim coords
|
||||
AimCoords = reader.ReadVector3();
|
@ -6,9 +6,9 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
public class Handshake : Packet
|
||||
internal class Handshake : Packet
|
||||
{
|
||||
public int PedID { get; set; }
|
||||
|
||||
@ -16,10 +16,25 @@ namespace RageCoop.Core
|
||||
|
||||
public string ModVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The asymetrically crypted Aes key
|
||||
/// </summary>
|
||||
public byte[] AesKeyCrypted;
|
||||
|
||||
/// <summary>
|
||||
/// The asymetrically crypted Aes IV
|
||||
/// </summary>
|
||||
public byte[] AesIVCrypted;
|
||||
|
||||
/// <summary>
|
||||
/// The password hash with client Aes
|
||||
/// </summary>
|
||||
public byte[] PassHashEncrypted { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.Handshake);
|
||||
message.Write((byte)PacketType.Handshake);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
@ -36,6 +51,16 @@ namespace RageCoop.Core
|
||||
byteArray.AddRange(BitConverter.GetBytes(modVersionBytes.Length));
|
||||
byteArray.AddRange(modVersionBytes);
|
||||
|
||||
// Write AesKeyCrypted
|
||||
byteArray.AddArray(AesKeyCrypted);
|
||||
|
||||
// Write AesIVCrypted
|
||||
byteArray.AddArray(AesIVCrypted);
|
||||
|
||||
|
||||
// Write PassHash
|
||||
byteArray.AddArray(PassHashEncrypted);
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
@ -52,12 +77,17 @@ namespace RageCoop.Core
|
||||
PedID = reader.ReadInt();
|
||||
|
||||
// Read Username
|
||||
int usernameLength = reader.ReadInt();
|
||||
Username = reader.ReadString(usernameLength);
|
||||
Username = reader.ReadString(reader.ReadInt());
|
||||
|
||||
// Read ModVersion
|
||||
int modVersionLength = reader.ReadInt();
|
||||
ModVersion = reader.ReadString(modVersionLength);
|
||||
ModVersion = reader.ReadString(reader.ReadInt());
|
||||
|
||||
AesKeyCrypted=reader.ReadByteArray();
|
||||
|
||||
AesIVCrypted=reader.ReadByteArray();
|
||||
|
||||
|
||||
PassHashEncrypted=reader.ReadByteArray();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -71,7 +101,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.PlayerConnect);
|
||||
message.Write((byte)PacketType.PlayerConnect);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
@ -116,7 +146,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.PlayerDisconnect);
|
||||
message.Write((byte)PacketType.PlayerDisconnect);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
@ -138,7 +168,6 @@ namespace RageCoop.Core
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerInfoUpdate : Packet
|
||||
{
|
||||
/// <summary>
|
||||
@ -150,7 +179,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.PlayerInfoUpdate);
|
||||
message.Write((byte)PacketType.PlayerInfoUpdate);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
@ -191,5 +220,52 @@ namespace RageCoop.Core
|
||||
Latency=reader.ReadFloat();
|
||||
}
|
||||
}
|
||||
|
||||
public class PublicKeyResponse : Packet
|
||||
{
|
||||
public byte[] Modulus;
|
||||
public byte[] Exponent;
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketType.PublicKeyResponse);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
byteArray.AddArray(Modulus);
|
||||
|
||||
byteArray.AddArray(Exponent);
|
||||
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
message.Write(result);
|
||||
#endregion
|
||||
}
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
var reader=new BitReader(array);
|
||||
Modulus=reader.ReadByteArray();
|
||||
Exponent=reader.ReadByteArray();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public class PublicKeyRequest : Packet
|
||||
{
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketType.PublicKeyRequest);
|
||||
#endregion
|
||||
}
|
||||
public override void Unpack(byte[] array)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,9 +6,9 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
public class ProjectileSync : Packet
|
||||
internal class ProjectileSync : Packet
|
||||
{
|
||||
|
||||
public int ID { get; set; }
|
||||
@ -29,7 +29,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.ProjectileSync);
|
||||
message.Write((byte)PacketType.ProjectileSync);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
@ -6,10 +6,10 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
|
||||
public class BulletShot : Packet
|
||||
internal class BulletShot : Packet
|
||||
{
|
||||
public int OwnerID { get; set; }
|
||||
|
||||
@ -21,7 +21,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.BulletShot);
|
||||
message.Write((byte)PacketType.BulletShot);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
@ -6,9 +6,9 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
public class EnteredVehicle : Packet
|
||||
internal class EnteredVehicle : Packet
|
||||
{
|
||||
public int PedID { get; set; }
|
||||
|
||||
@ -19,7 +19,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.EnteredVehicle);
|
||||
message.Write((byte)PacketType.EnteredVehicle);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
@ -6,9 +6,9 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
public class EnteringVehicle : Packet
|
||||
internal class EnteringVehicle : Packet
|
||||
{
|
||||
public int PedID { get; set; }
|
||||
|
||||
@ -19,7 +19,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.EnteringVehicle);
|
||||
message.Write((byte)PacketType.EnteringVehicle);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
@ -6,10 +6,10 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
|
||||
public class LeaveVehicle : Packet
|
||||
internal class LeaveVehicle : Packet
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
@ -17,7 +17,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.LeaveVehicle);
|
||||
message.Write((byte)PacketType.LeaveVehicle);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
@ -6,9 +6,9 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
public class NozzleTransform : Packet
|
||||
internal class NozzleTransform : Packet
|
||||
{
|
||||
public int VehicleID { get; set; }
|
||||
|
||||
@ -17,7 +17,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.NozzleTransform);
|
||||
message.Write((byte)PacketType.NozzleTransform);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
@ -6,10 +6,10 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
|
||||
public class OwnerChanged : Packet
|
||||
internal class OwnerChanged : Packet
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
@ -18,7 +18,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.OwnerChanged);
|
||||
message.Write((byte)PacketType.OwnerChanged);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
@ -6,17 +6,17 @@ using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
|
||||
public class PedKilled : Packet
|
||||
internal class PedKilled : Packet
|
||||
{
|
||||
public int VictimID { get; set; }
|
||||
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.PedKilled);
|
||||
message.Write((byte)PacketType.PedKilled);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
@ -4,15 +4,16 @@ using System.Text;
|
||||
using GTA;
|
||||
using GTA.Math;
|
||||
using Lidgren.Network;
|
||||
using System.Linq;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
public partial class Packets
|
||||
internal partial class Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// Non-critical stuff, such as damage model, landing gear, health, etc..
|
||||
/// </summary>
|
||||
public class VehicleStateSync : Packet
|
||||
internal class VehicleStateSync : Packet
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
@ -30,22 +31,26 @@ namespace RageCoop.Core
|
||||
public VehicleDamageModel DamageModel { get; set; }
|
||||
|
||||
public byte LandingGear { get; set; }
|
||||
public byte RoofState { get; set; }
|
||||
|
||||
public VehicleDataFlags Flag { get; set; }
|
||||
|
||||
|
||||
public VehicleLockStatus LockStatus { get; set; }
|
||||
|
||||
public int Livery { get; set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// VehicleSeat,PedID
|
||||
/// </summary>
|
||||
public Dictionary<int, int> Passengers { get; set; }
|
||||
|
||||
public byte RadioStation { get; set; } = 255;
|
||||
public string LicensePlate { get; set; }
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.VehicleStateSync);
|
||||
message.Write((byte)PacketType.VehicleStateSync);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
||||
@ -63,10 +68,14 @@ namespace RageCoop.Core
|
||||
byteArray.AddRange(BitConverter.GetBytes(EngineHealth));
|
||||
|
||||
// Check
|
||||
if (Flag.HasFlag(VehicleDataFlags.IsAircraft))
|
||||
if (Flag.HasVehFlag(VehicleDataFlags.IsAircraft))
|
||||
{
|
||||
// Write the vehicle landing gear
|
||||
byteArray.AddRange(BitConverter.GetBytes(LandingGear));
|
||||
byteArray.Add(LandingGear);
|
||||
}
|
||||
if (Flag.HasVehFlag(VehicleDataFlags.HasRoof))
|
||||
{
|
||||
byteArray.Add(RoofState);
|
||||
}
|
||||
|
||||
// Write vehicle colors
|
||||
@ -122,6 +131,19 @@ namespace RageCoop.Core
|
||||
// Write RadioStation
|
||||
byteArray.Add(RadioStation);
|
||||
|
||||
// Write LicensePlate
|
||||
while (LicensePlate.Length<8)
|
||||
{
|
||||
LicensePlate+=" ";
|
||||
}
|
||||
if (LicensePlate.Length>8)
|
||||
{
|
||||
LicensePlate=new string(LicensePlate.Take(8).ToArray());
|
||||
}
|
||||
byteArray.AddRange(Encoding.ASCII.GetBytes(LicensePlate));
|
||||
|
||||
byteArray.Add((byte)(Livery+1));
|
||||
|
||||
byte[] result = byteArray.ToArray();
|
||||
|
||||
message.Write(result.Length);
|
||||
@ -148,10 +170,14 @@ namespace RageCoop.Core
|
||||
|
||||
|
||||
// Check
|
||||
if (Flag.HasFlag(VehicleDataFlags.IsAircraft))
|
||||
if (Flag.HasVehFlag(VehicleDataFlags.IsAircraft))
|
||||
{
|
||||
// Read vehicle landing gear
|
||||
LandingGear = (byte)reader.ReadShort();
|
||||
LandingGear = reader.ReadByte();
|
||||
}
|
||||
if (Flag.HasVehFlag(VehicleDataFlags.HasRoof))
|
||||
{
|
||||
RoofState=reader.ReadByte();
|
||||
}
|
||||
|
||||
// Read vehicle colors
|
||||
@ -207,6 +233,10 @@ namespace RageCoop.Core
|
||||
|
||||
// Read RadioStation
|
||||
RadioStation=reader.ReadByte();
|
||||
|
||||
LicensePlate=Encoding.ASCII.GetString(reader.ReadByteArray(8));
|
||||
|
||||
Livery=(int)(reader.ReadByte()-1);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -232,7 +262,7 @@ namespace RageCoop.Core
|
||||
public override void Pack(NetOutgoingMessage message)
|
||||
{
|
||||
#region PacketToNetOutGoingMessage
|
||||
message.Write((byte)PacketTypes.VehicleSync);
|
||||
message.Write((byte)PacketType.VehicleSync);
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
|
50
RageCoop.Core/RageCoop.Core.csproj
Normal file
50
RageCoop.Core/RageCoop.Core.csproj
Normal file
@ -0,0 +1,50 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<AssemblyVersion>0.1</AssemblyVersion>
|
||||
<FileVersion>0.1</FileVersion>
|
||||
<Version>0.1</Version>
|
||||
<DebugType>embedded</DebugType>
|
||||
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<OutDir>..\bin\Debug\Core</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<OutDir>..\bin\Release\Core</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net6.0|AnyCPU'">
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0|AnyCPU'">
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Lidgren.Network\**" />
|
||||
<EmbeddedResource Remove="Lidgren.Network\**" />
|
||||
<None Remove="Lidgren.Network\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Lidgren.Network">
|
||||
<HintPath>..\libs\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\libs\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3">
|
||||
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
64
RageCoop.Core/Scripting/CustomEvents.cs
Normal file
64
RageCoop.Core/Scripting/CustomEvents.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace RageCoop.Core.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class CustomEvents
|
||||
{
|
||||
static MD5 Hasher = MD5.Create();
|
||||
static Dictionary<int,string> Hashed=new Dictionary<int,string>();
|
||||
internal static readonly int SetWeather = Hash("RageCoop.SetWeather");
|
||||
internal static readonly int OnPedDeleted = Hash("RageCoop.OnPedDeleted");
|
||||
internal static readonly int OnVehicleDeleted = Hash("RageCoop.OnVehicleDeleted");
|
||||
internal static readonly int SetAutoRespawn = Hash("RageCoop.SetAutoRespawn");
|
||||
internal static readonly int SetDisplayNameTag = Hash("RageCoop.SetDisplayNameTag");
|
||||
internal static readonly int NativeCall = Hash("RageCoop.NativeCall");
|
||||
internal static readonly int NativeResponse = Hash("RageCoop.NativeResponse");
|
||||
internal static readonly int AllResourcesSent = Hash("RageCoop.AllResourcesSent");
|
||||
internal static readonly int ServerPropSync = Hash("RageCoop.ServerPropSync");
|
||||
internal static readonly int ServerBlipSync = Hash("RageCoop.ServerBlipSync");
|
||||
internal static readonly int SetEntity = Hash("RageCoop.SetEntity");
|
||||
internal static readonly int DeleteServerProp = Hash("RageCoop.DeleteServerProp");
|
||||
internal static readonly int UpdatePedBlip = Hash("RageCoop.UpdatePedBlip");
|
||||
internal static readonly int DeleteEntity = Hash("RageCoop.DeleteEntity");
|
||||
internal static readonly int DeleteServerBlip = Hash("RageCoop.DeleteServerBlip");
|
||||
internal static readonly int CreateVehicle = Hash("RageCoop.CreateVehicle");
|
||||
internal static readonly int WeatherTimeSync = Hash("RageCoop.WeatherTimeSync");
|
||||
internal static readonly int IsHost = Hash("RageCoop.IsHost");
|
||||
/// <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 Hash(string s)
|
||||
{
|
||||
var hash = BitConverter.ToInt32(Hasher.ComputeHash(Encoding.UTF8.GetBytes(s)), 0);
|
||||
string name;
|
||||
lock (Hashed)
|
||||
{
|
||||
if (Hashed.TryGetValue(hash, out name))
|
||||
{
|
||||
if (name!=s)
|
||||
{
|
||||
throw new ArgumentException($"Hashed value has collision with another name:{name}, hashed value:{hash}");
|
||||
}
|
||||
else
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Hashed.Add(hash, s);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
RageCoop.Core/Scripting/ResourceFile.cs
Normal file
25
RageCoop.Core/Scripting/ResourceFile.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace RageCoop.Core.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class ResourceFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Full name with relative path of this file
|
||||
/// </summary>
|
||||
public string Name { get; internal set; }
|
||||
/// <summary>
|
||||
/// Whether this is a directory
|
||||
/// </summary>
|
||||
public bool IsDirectory { get; internal set; }
|
||||
/// <summary>
|
||||
/// Get a stream that can be used to read file content.
|
||||
/// </summary>
|
||||
public Func<Stream> GetStream { get; internal set; }
|
||||
}
|
||||
}
|
86
RageCoop.Core/Worker.cs
Normal file
86
RageCoop.Core/Worker.cs
Normal file
@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A worker that constantly execute jobs in a background thread.
|
||||
/// </summary>
|
||||
public class Worker:IDisposable
|
||||
{
|
||||
private SemaphoreSlim _semaphoreSlim;
|
||||
private Thread _workerThread;
|
||||
private bool _stopping=false;
|
||||
/// <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; }
|
||||
internal Worker(string name,Logger logger,int maxJobs = Int32.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>
|
||||
/// 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();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Finish current job and stop the worker.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
_semaphoreSlim.Dispose();
|
||||
}
|
||||
private ConcurrentQueue<Action> Jobs=new ConcurrentQueue<Action>();
|
||||
}
|
||||
}
|
258
RageCoop.Server/Client.cs
Normal file
258
RageCoop.Server/Client.cs
Normal file
@ -0,0 +1,258 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RageCoop.Core;
|
||||
using Lidgren.Network;
|
||||
using System.Linq;
|
||||
using GTA;
|
||||
using RageCoop.Core.Scripting;
|
||||
using System.Security.Cryptography;
|
||||
using RageCoop.Server.Scripting;
|
||||
|
||||
namespace RageCoop.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent a player connected to this server.
|
||||
/// </summary>
|
||||
public class Client
|
||||
{
|
||||
private readonly Server Server;
|
||||
internal Client(Server server)
|
||||
{
|
||||
Server=server;
|
||||
}
|
||||
/// <summary>
|
||||
/// Th client's IP address and port.
|
||||
/// </summary>
|
||||
public System.Net.IPEndPoint EndPoint { get { return Connection?.RemoteEndPoint; } }
|
||||
internal long NetID = 0;
|
||||
internal NetConnection Connection { get;set; }
|
||||
/// <summary>
|
||||
/// The <see cref="ServerPed"/> instance representing the client's main character.
|
||||
/// </summary>
|
||||
public ServerPed Player { get; internal set; }
|
||||
/// <summary>
|
||||
/// The client's latency in seconds.
|
||||
/// </summary>
|
||||
public float Latency { get; internal set; }
|
||||
internal readonly Dictionary<int, Action<object>> Callbacks = new();
|
||||
internal byte[] PublicKey { get; set; }
|
||||
/// <summary>
|
||||
/// Indicates whether the client has succefully loaded all resources.
|
||||
/// </summary>
|
||||
public bool IsReady { get; internal set; }=false;
|
||||
/// <summary>
|
||||
/// The client's username.
|
||||
/// </summary>
|
||||
public string Username { get;internal set; } = "N/A";
|
||||
|
||||
|
||||
private bool _autoRespawn=true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to enable automatic respawn for this client's main ped.
|
||||
/// </summary>
|
||||
public bool EnableAutoRespawn {
|
||||
get { return _autoRespawn; }
|
||||
set {
|
||||
BaseScript.SetAutoRespawn(this,value);
|
||||
_autoRespawn=value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _displayNameTag=true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to enable automatic respawn for this client's main ped.
|
||||
/// </summary>
|
||||
public bool DisplayNameTag
|
||||
{
|
||||
get { return _displayNameTag; }
|
||||
set
|
||||
{
|
||||
Server.BaseScript.SetNameTag(this,value);
|
||||
_displayNameTag=value;
|
||||
}
|
||||
}
|
||||
#region CUSTOMDATA FUNCTIONS
|
||||
/*
|
||||
public void SetData<T>(string name, T data)
|
||||
{
|
||||
if (HasData(name))
|
||||
{
|
||||
_customData[name] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
_customData.Add(name, data);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasData(string name)
|
||||
{
|
||||
return _customData.ContainsKey(name);
|
||||
}
|
||||
|
||||
public T GetData<T>(string name)
|
||||
{
|
||||
return HasData(name) ? (T)_customData[name] : default;
|
||||
}
|
||||
|
||||
public void RemoveData(string name)
|
||||
{
|
||||
if (HasData(name))
|
||||
{
|
||||
_customData.Remove(name);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#endregion
|
||||
#region FUNCTIONS
|
||||
/// <summary>
|
||||
/// Kick this client
|
||||
/// </summary>
|
||||
/// <param name="reason"></param>
|
||||
public void Kick(string reason="You have been kicked!")
|
||||
{
|
||||
Connection?.Disconnect(reason);
|
||||
}
|
||||
/// <summary>
|
||||
/// Kick this client
|
||||
/// </summary>
|
||||
/// <param name="reasons">Reasons to kick</param>
|
||||
public void Kick(params string[] reasons)
|
||||
{
|
||||
Kick(string.Join(" ", reasons));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a chat messsage to this client, not visible to others.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="from"></param>
|
||||
public void SendChatMessage(string message, string from = "Server")
|
||||
{
|
||||
try
|
||||
{
|
||||
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID);
|
||||
if (userConnection == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Server.SendChatMessage(from, message, userConnection);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a native call to client and do a callback when the response received.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the response</typeparam>
|
||||
/// <param name="callBack"></param>
|
||||
/// <param name="hash"></param>
|
||||
/// <param name="args"></param>
|
||||
public void SendNativeCall<T>(Action<object> callBack, GTA.Native.Hash hash, params object[] args)
|
||||
{
|
||||
var argsList= new List<object>(args);
|
||||
argsList.InsertRange(0, new object[] { (byte)Type.GetTypeCode(typeof(T)), RequestNativeCallID<T>(callBack), (ulong)hash });
|
||||
|
||||
SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray());
|
||||
}
|
||||
/// <summary>
|
||||
/// Send a native call to client and ignore it's response.
|
||||
/// </summary>
|
||||
/// <param name="hash"></param>
|
||||
/// <param name="args"></param>
|
||||
public void SendNativeCall(GTA.Native.Hash hash, params object[] args)
|
||||
{
|
||||
var argsList = new List<object>(args);
|
||||
argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty,(ulong)hash });
|
||||
// Server.Logger?.Debug(argsList.DumpWithType());
|
||||
SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray());
|
||||
}
|
||||
private int RequestNativeCallID<T>(Action<object> callback)
|
||||
{
|
||||
int ID = 0;
|
||||
lock (Callbacks)
|
||||
{
|
||||
while ((ID==0)
|
||||
|| Callbacks.ContainsKey(ID))
|
||||
{
|
||||
byte[] rngBytes = new byte[4];
|
||||
|
||||
RandomNumberGenerator.Create().GetBytes(rngBytes);
|
||||
|
||||
// Convert the bytes into an integer
|
||||
ID = BitConverter.ToInt32(rngBytes, 0);
|
||||
}
|
||||
Callbacks.Add(ID, callback);
|
||||
}
|
||||
return ID;
|
||||
}
|
||||
/// <summary>
|
||||
/// Trigger a CustomEvent for this client
|
||||
/// </summary>
|
||||
/// <param name="hash">An unique identifier of the event, you can use <see cref="CustomEvents.Hash(string)"/> to get it from a string</param>
|
||||
/// <param name="args">Arguments</param>
|
||||
public void SendCustomEvent(int hash,params object[] args)
|
||||
{
|
||||
if (!IsReady)
|
||||
{
|
||||
Server.Logger?.Warning($"Player \"{Username}\" is not ready!");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
new Packets.CustomEvent()
|
||||
{
|
||||
Hash=hash,
|
||||
Args=args
|
||||
}.Pack(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Server.Logger?.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a CustomEvent that'll be queued at client side and invoked from script thread
|
||||
/// </summary>
|
||||
/// <param name="hash"></param>
|
||||
/// <param name="args"></param>
|
||||
public void SendCustomEventQueued(int hash, params object[] args)
|
||||
{
|
||||
if (!IsReady)
|
||||
{
|
||||
Server.Logger?.Warning($"Player \"{Username}\" is not ready!");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
new Packets.CustomEvent(null,true)
|
||||
{
|
||||
Hash=hash,
|
||||
Args=args
|
||||
}.Pack(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Server.Logger?.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
16
RageCoop.Server/FileTransfer.cs
Normal file
16
RageCoop.Server/FileTransfer.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RageCoop.Server
|
||||
{
|
||||
internal class FileTransfer
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public float Progress { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool Cancel { get; set; }=false;
|
||||
}
|
||||
}
|
15
RageCoop.Server/HolePunch.cs
Normal file
15
RageCoop.Server/HolePunch.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using RageCoop.Core;
|
||||
namespace RageCoop.Server
|
||||
{
|
||||
internal class HolePunch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
63
RageCoop.Server/Program.cs
Normal file
63
RageCoop.Server/Program.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using RageCoop.Core;
|
||||
namespace RageCoop.Server
|
||||
{
|
||||
class Program
|
||||
{
|
||||
private static bool Stopping = false;
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var mainLogger= new Logger()
|
||||
{
|
||||
LogPath="RageCoop.Server.log",
|
||||
UseConsole=true,
|
||||
Name="Server"
|
||||
};
|
||||
try
|
||||
{
|
||||
Console.Title = "RAGECOOP";
|
||||
var setting = Util.Read<ServerSettings>("Settings.xml");
|
||||
#if DEBUG
|
||||
setting.LogLevel=0;
|
||||
#endif
|
||||
var server = new Server(setting, mainLogger);
|
||||
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
|
||||
{
|
||||
mainLogger.Info("Initiating shutdown sequence...");
|
||||
mainLogger.Info("Press Ctrl+C again to commence an emergency shutdown.");
|
||||
if (e.SpecialKey == ConsoleSpecialKey.ControlC)
|
||||
{
|
||||
if (!Stopping)
|
||||
{
|
||||
e.Cancel = true;
|
||||
Stopping = true;
|
||||
server.Stop();
|
||||
mainLogger.Info("Server stopped.");
|
||||
mainLogger.Dispose();
|
||||
Thread.Sleep(3000);
|
||||
}
|
||||
else
|
||||
{
|
||||
mainLogger.Flush();
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
server.Start();
|
||||
mainLogger?.Info("Please use CTRL + C if you want to stop the server!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
mainLogger.Error(e);
|
||||
mainLogger.Error($"Fatal error occurred, server shutting down.");
|
||||
mainLogger.Flush();
|
||||
Thread.Sleep(5000);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
65
RageCoop.Server/RageCoop.Server.csproj
Normal file
65
RageCoop.Server/RageCoop.Server.csproj
Normal file
@ -0,0 +1,65 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AssemblyVersion>0.5</AssemblyVersion>
|
||||
<FileVersion>0.5</FileVersion>
|
||||
<RepositoryUrl>https://github.com/RAGECOOP/RAGECOOP-V</RepositoryUrl>
|
||||
<PackageProjectUrl>https://ragecoop.online/</PackageProjectUrl>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<Product>$(AssemblyName)</Product>
|
||||
<PackageId>RageCoop.Server</PackageId>
|
||||
<Authors>RAGECOOP</Authors>
|
||||
<Version>0.5</Version>
|
||||
<DebugType>embedded</DebugType>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<Description>An library for hosting a RAGECOOP server or API reference for developing a resource.</Description>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<OutDir>..\bin\Debug\Server</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<OutDir>..\bin\Release\Server</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="icon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\images\icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Lidgren.Network">
|
||||
<HintPath>..\libs\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="McMaster.NETCore.Plugins">
|
||||
<HintPath>..\libs\McMaster.NETCore.Plugins.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\libs\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3">
|
||||
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
6
RageCoop.Server/RageCoop.Server.csproj.user
Normal file
6
RageCoop.Server/RageCoop.Server.csproj.user
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_LastSelectedProfileId>M:\SandBox-Shared\repo\RageCoop\RageCoop-V\RageCoop.Server\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
|
||||
</PropertyGroup>
|
||||
</Project>
|
353
RageCoop.Server/Scripting/API.cs
Normal file
353
RageCoop.Server/Scripting/API.cs
Normal file
@ -0,0 +1,353 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Lidgren.Network;
|
||||
using RageCoop.Core;
|
||||
using RageCoop.Core.Scripting;
|
||||
using System.Reflection;
|
||||
using System.Net;
|
||||
|
||||
namespace RageCoop.Server.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class ServerEvents
|
||||
{
|
||||
private readonly Server Server;
|
||||
internal ServerEvents(Server server)
|
||||
{
|
||||
Server = server;
|
||||
}
|
||||
#region INTERNAL
|
||||
internal Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new();
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Invoked when a chat message is received.
|
||||
/// </summary>
|
||||
public event EventHandler<ChatEventArgs> OnChatMessage;
|
||||
/// <summary>
|
||||
/// Will be invoked from main thread before registered handlers
|
||||
/// </summary>
|
||||
public event EventHandler<OnCommandEventArgs> OnCommandReceived;
|
||||
/// <summary>
|
||||
/// Will be invoked from main thread when a client is attempting to connect, use <see cref="HandshakeEventArgs.Deny(string)"/> to deny the connection request.
|
||||
/// </summary>
|
||||
public event EventHandler<HandshakeEventArgs> OnPlayerHandshake;
|
||||
/// <summary>
|
||||
/// Will be invoked when a player is connected, but this player might not be ready yet(client resources not loaded), using <see cref="OnPlayerReady"/> is recommended.
|
||||
/// </summary>
|
||||
public event EventHandler<Client> OnPlayerConnected;
|
||||
/// <summary>
|
||||
/// Will be invoked after the client connected and all resources(if any) have been loaded.
|
||||
/// </summary>
|
||||
public event EventHandler<Client> OnPlayerReady;
|
||||
/// <summary>
|
||||
/// Invoked when a player disconnected, all method won't be effective in this scope.
|
||||
/// </summary>
|
||||
public event EventHandler<Client> OnPlayerDisconnected;
|
||||
/// <summary>
|
||||
/// Invoked everytime a player's main ped has been updated
|
||||
/// </summary>
|
||||
public event EventHandler<Client> OnPlayerUpdate;
|
||||
internal void ClearHandlers()
|
||||
{
|
||||
OnChatMessage=null;
|
||||
OnPlayerHandshake=null;
|
||||
OnPlayerConnected=null;
|
||||
OnPlayerReady=null;
|
||||
OnPlayerDisconnected=null;
|
||||
// OnCustomEventReceived=null;
|
||||
OnCommandReceived=null;
|
||||
OnPlayerUpdate=null;
|
||||
}
|
||||
#region INVOKE
|
||||
internal void InvokePlayerHandshake(HandshakeEventArgs args)
|
||||
{ OnPlayerHandshake?.Invoke(this, args); }
|
||||
internal void InvokeOnCommandReceived(string cmdName, string[] cmdArgs, Client sender)
|
||||
{
|
||||
var args = new OnCommandEventArgs()
|
||||
{
|
||||
Name=cmdName,
|
||||
Args=cmdArgs,
|
||||
Sender=sender
|
||||
};
|
||||
OnCommandReceived?.Invoke(this, args);
|
||||
if (args.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Server.Commands.Any(x => x.Key.Name == cmdName))
|
||||
{
|
||||
string[] argsWithoutCmd = cmdArgs.Skip(1).ToArray();
|
||||
|
||||
CommandContext ctx = new()
|
||||
{
|
||||
Client = sender,
|
||||
Args = argsWithoutCmd
|
||||
};
|
||||
|
||||
KeyValuePair<Command, Action<CommandContext>> command = Server.Commands.First(x => x.Key.Name == cmdName);
|
||||
command.Value.Invoke(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Server.SendChatMessage("Server", "Command not found!", sender.Connection);
|
||||
}
|
||||
}
|
||||
|
||||
internal void InvokeOnChatMessage(Packets.ChatMessage p, Client sender)
|
||||
{
|
||||
OnChatMessage?.Invoke(this, new ChatEventArgs()
|
||||
{
|
||||
Sender=sender,
|
||||
Message=p.Message
|
||||
});
|
||||
}
|
||||
internal void InvokePlayerConnected(Client client)
|
||||
{ OnPlayerConnected?.Invoke(this,client); }
|
||||
internal void InvokePlayerReady(Client client)
|
||||
{ OnPlayerReady?.Invoke(this, client); }
|
||||
internal void InvokePlayerDisconnected(Client client)
|
||||
{ OnPlayerDisconnected?.Invoke(this,client); }
|
||||
|
||||
internal void InvokeCustomEventReceived(Packets.CustomEvent p, Client sender)
|
||||
{
|
||||
var args = new CustomEventReceivedArgs() { Hash=p.Hash, Args=p.Args, Sender=sender };
|
||||
List<Action<CustomEventReceivedArgs>> handlers;
|
||||
if (CustomEventHandlers.TryGetValue(p.Hash, out handlers))
|
||||
{
|
||||
handlers.ForEach((x) => { x.Invoke(args); });
|
||||
}
|
||||
}
|
||||
internal void InvokePlayerUpdate(Client client)
|
||||
{
|
||||
OnPlayerUpdate?.Invoke(this, client);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
/// <summary>
|
||||
/// An class that can be used to interact with RageCoop server.
|
||||
/// </summary>
|
||||
public class API
|
||||
{
|
||||
internal readonly Server Server;
|
||||
internal API(Server server)
|
||||
{
|
||||
Server=server;
|
||||
Events=new(server);
|
||||
}
|
||||
/// <summary>
|
||||
/// Server side events
|
||||
/// </summary>
|
||||
public readonly ServerEvents Events;
|
||||
|
||||
/// <summary>
|
||||
/// All synchronized entities on this server.
|
||||
/// </summary>
|
||||
public ServerEntities Entities { get { return Server.Entities; } }
|
||||
|
||||
#region FUNCTIONS
|
||||
/// <summary>
|
||||
/// Get a list of all Clients
|
||||
/// </summary>
|
||||
/// <returns>All clients as a dictionary indexed by NetID</returns>
|
||||
public Dictionary<string, Client> GetAllClients()
|
||||
{
|
||||
return new(Server.ClientsByName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the client by its username
|
||||
/// </summary>
|
||||
/// <param name="username">The username to search for (non case-sensitive)</param>
|
||||
/// <returns>The Client from this user or null</returns>
|
||||
public Client GetClientByUsername(string username)
|
||||
{
|
||||
return Server.Clients.Values.FirstOrDefault(x => x.Username.ToLower() == username.ToLower());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a chat message to all players, use <see cref="Client.SendChatMessage(string, string)"/> to send to an individual client.
|
||||
/// </summary>
|
||||
/// <param name="targets">The clients to send message, leave it null to send to all clients</param>
|
||||
/// <param name="message">The chat message</param>
|
||||
/// <param name="username">The username which send this message (default = "Server")</param>
|
||||
public void SendChatMessage(string message, List<Client> targets = null, string username = "Server")
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Server.MainNetServer.ConnectionsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
targets ??= new(Server.Clients.Values);
|
||||
foreach(Client client in targets)
|
||||
{
|
||||
Server.SendChatMessage(username, message, client.Connection);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send CleanUpWorld to all players to delete all objects created by the server
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Register a new command chat command (Example: "/test")
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the command (Example: "test" for "/test")</param>
|
||||
/// <param name="usage">How to use this message (argsLength required!)</param>
|
||||
/// <param name="argsLength">The length of args (Example: "/message USERNAME MESSAGE" = 2) (usage required!)</param>
|
||||
/// <param name="callback">A callback to invoke when the command received.</param>
|
||||
public void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
|
||||
{
|
||||
Server.RegisterCommand(name, usage, argsLength, callback);
|
||||
}
|
||||
/// <summary>
|
||||
/// Register a new command chat command (Example: "/test")
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the command (Example: "test" for "/test")</param>
|
||||
/// <param name="callback">A callback to invoke when the command received.</param>
|
||||
public void RegisterCommand(string name, Action<CommandContext> callback)
|
||||
{
|
||||
Server.RegisterCommand(name, callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register all commands in a static class
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Your static class with commands</typeparam>
|
||||
public void RegisterCommands<T>()
|
||||
{
|
||||
Server.RegisterCommands<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register all commands inside an class instance
|
||||
/// </summary>
|
||||
/// <param name="obj">The instance of type containing the commands</param>
|
||||
public void RegisterCommands(object obj)
|
||||
{
|
||||
IEnumerable<MethodInfo> commands = obj.GetType().GetMethods().Where(method => method.GetCustomAttributes(typeof(Command), false).Any());
|
||||
|
||||
foreach (MethodInfo method in commands)
|
||||
{
|
||||
Command attribute = method.GetCustomAttribute<Command>(true);
|
||||
RegisterCommand(attribute.Name, attribute.Usage, attribute.ArgsLength,
|
||||
(ctx) => { method.Invoke(obj, new object[] { ctx }); });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send native call specified clients.
|
||||
/// </summary>
|
||||
/// <param name="hash"></param>
|
||||
/// <param name="args"></param>
|
||||
/// /// <param name="clients">Clients to send, null for all clients</param>
|
||||
public void SendNativeCall(List<Client> clients , GTA.Native.Hash hash, params object[] args)
|
||||
{
|
||||
var argsList = new List<object>(args);
|
||||
argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty, (ulong)hash });
|
||||
SendCustomEventQueued(clients, CustomEvents.NativeCall, argsList.ToArray());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Send an event and data to the specified clients. Use <see cref="Client.SendCustomEvent(int,object[])"/> if you want to send event to individual client.
|
||||
/// </summary>
|
||||
/// <param name="eventHash">An unique identifier of the event, you can use <see cref="CustomEvents.Hash(string)"/> to get it from a string</param>
|
||||
/// <param name="args">The objects conataing your data, see <see cref="Scripting.CustomEventReceivedArgs.Args"/> for supported types.</param>
|
||||
/// <param name="targets">The target clients to send. Leave it null to send to all clients</param>
|
||||
public void SendCustomEvent(List<Client> targets, int eventHash, params object[] args)
|
||||
{
|
||||
|
||||
targets ??= new(Server.Clients.Values);
|
||||
var p = new Packets.CustomEvent()
|
||||
{
|
||||
Args=args,
|
||||
Hash=eventHash
|
||||
};
|
||||
foreach (var c in targets)
|
||||
{
|
||||
Server.Send(p, c, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a CustomEvent that'll be queued at client side and invoked from script thread
|
||||
/// </summary>
|
||||
/// <param name="targets"></param>
|
||||
/// <param name="eventHash"></param>
|
||||
/// <param name="args"></param>
|
||||
public void SendCustomEventQueued(List<Client> targets, int eventHash, params object[] args)
|
||||
{
|
||||
|
||||
targets ??= new(Server.Clients.Values);
|
||||
var p = new Packets.CustomEvent(null,true)
|
||||
{
|
||||
Args=args,
|
||||
Hash=eventHash
|
||||
};
|
||||
foreach (var c in targets)
|
||||
{
|
||||
Server.Send(p, c, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Register an handler to the specifed event hash, one event can have multiple handlers.
|
||||
/// </summary>
|
||||
/// <param name="hash">An unique identifier of the event, you can hash your event name with <see cref="CustomEvents.Hash(string)"/></param>
|
||||
/// <param name="handler">An handler to be invoked when the event is received from the server.</param>
|
||||
public void RegisterCustomEventHandler(int hash,Action<CustomEventReceivedArgs> handler)
|
||||
{
|
||||
List<Action<CustomEventReceivedArgs>> handlers;
|
||||
lock (Events.CustomEventHandlers)
|
||||
{
|
||||
if (!Events.CustomEventHandlers.TryGetValue(hash,out handlers))
|
||||
{
|
||||
Events.CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>());
|
||||
}
|
||||
handlers.Add(handler);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Register an event handler for specified event name.
|
||||
/// </summary>
|
||||
/// <param name="name">This value will be hashed to an int to reduce overhead</param>
|
||||
/// <param name="handler">The handler to be invoked when the event is received</param>
|
||||
public void RegisterCustomEventHandler(string name, Action<CustomEventReceivedArgs> handler)
|
||||
{
|
||||
RegisterCustomEventHandler(CustomEvents.Hash(name), handler);
|
||||
}
|
||||
/// <summary>
|
||||
/// Get a <see cref="Core.Logger"/> that the server is currently using, you should use <see cref="ServerResource.Logger"/> to display resource-specific information.
|
||||
/// </summary>
|
||||
public Logger Logger { get { return Server.Logger; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client that is resposible for synchronizing time and weather
|
||||
/// </summary>
|
||||
public Client Host
|
||||
{
|
||||
get { return Server._hostClient; }
|
||||
set {
|
||||
if (Server._hostClient != value)
|
||||
{
|
||||
Server._hostClient?.SendCustomEvent(CustomEvents.IsHost, false);
|
||||
value.SendCustomEvent(CustomEvents.IsHost, true);
|
||||
Server._hostClient = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
92
RageCoop.Server/Scripting/BaseScript.cs
Normal file
92
RageCoop.Server/Scripting/BaseScript.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using RageCoop.Core.Scripting;
|
||||
using RageCoop.Core;
|
||||
|
||||
namespace RageCoop.Server.Scripting
|
||||
{
|
||||
internal class BaseScript:ServerScript
|
||||
{
|
||||
private readonly Server Server;
|
||||
public BaseScript(Server server) { Server=server; }
|
||||
public override void OnStart()
|
||||
{
|
||||
API.RegisterCustomEventHandler(CustomEvents.NativeResponse, NativeResponse);
|
||||
API.RegisterCustomEventHandler(CustomEvents.OnVehicleDeleted, (e) =>
|
||||
{
|
||||
API.Entities.RemoveVehicle((int)e.Args[0]);
|
||||
});
|
||||
API.RegisterCustomEventHandler(CustomEvents.OnPedDeleted, (e) =>
|
||||
{
|
||||
API.Entities.RemovePed((int)e.Args[0]);
|
||||
});
|
||||
API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, (e) =>
|
||||
{
|
||||
if (Server.Settings.WeatherTimeSync)
|
||||
{
|
||||
foreach (var c in API.GetAllClients().Values)
|
||||
{
|
||||
if (c==e.Sender)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
c.SendCustomEventQueued(CustomEvents.WeatherTimeSync, e.Args);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
public override void OnStop()
|
||||
{
|
||||
}
|
||||
public static void SetAutoRespawn(Client c,bool toggle)
|
||||
{
|
||||
c.SendCustomEvent(CustomEvents.SetAutoRespawn, toggle );
|
||||
}
|
||||
public void SetNameTag(Client c, bool toggle)
|
||||
{
|
||||
foreach(var other in API.GetAllClients().Values)
|
||||
{
|
||||
if (c==other) { continue; }
|
||||
other.SendCustomEvent(CustomEvents.SetDisplayNameTag,c.Player.ID, toggle);
|
||||
}
|
||||
}
|
||||
public void SendServerPropsTo(List<ServerProp> objects,List<Client> clients=null)
|
||||
{
|
||||
foreach(var obj in objects)
|
||||
{
|
||||
API.SendCustomEventQueued(clients, CustomEvents.ServerPropSync,obj.ID, obj.Model ,obj.Position,obj.Rotation );
|
||||
}
|
||||
}
|
||||
public void SendServerBlipsTo(List<ServerBlip> objects, List<Client> clients = null)
|
||||
{
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
API.SendCustomEventQueued(clients, CustomEvents.ServerBlipSync, obj.ID, (ushort)obj.Sprite, (byte)obj.Color, obj.Scale,obj.Position,obj.Rotation,obj.Name );
|
||||
}
|
||||
}
|
||||
void NativeResponse(CustomEventReceivedArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
int id = (int)e.Args[0];
|
||||
Action<object> callback;
|
||||
lock (e.Sender.Callbacks)
|
||||
{
|
||||
if (e.Sender.Callbacks.TryGetValue(id, out callback))
|
||||
{
|
||||
callback(e.Args[1]);
|
||||
e.Sender.Callbacks.Remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
API.Logger.Error("Failed to parse NativeResponse");
|
||||
API.Logger.Error(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
RageCoop.Server/Scripting/EventArgs/EventArgs.cs
Normal file
101
RageCoop.Server/Scripting/EventArgs/EventArgs.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
|
||||
namespace RageCoop.Server.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class ChatEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The client that sent this message
|
||||
/// </summary>
|
||||
public Client Sender { get; set; }
|
||||
/// <summary>
|
||||
/// Message
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class CustomEventReceivedArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Client"/> that triggered this event
|
||||
/// </summary>
|
||||
public Client Sender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The event hash
|
||||
/// </summary>
|
||||
public int Hash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string, Vector3, Quaternion, Vector2 <see cref="ServerObject.Handle"/>
|
||||
/// </summary>
|
||||
public object[] Args { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class OnCommandEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Client"/> that executed this command.
|
||||
/// </summary>
|
||||
public Client Sender { get; set; }
|
||||
/// <summary>
|
||||
/// The name of executed command
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Arguments
|
||||
/// </summary>
|
||||
public string[] Args { get; set; }
|
||||
/// <summary>
|
||||
/// If this value was set to true, corresponding handler registered with <see cref="API.RegisterCommand(string, Action{CommandContext})"/> will not be invoked.
|
||||
/// </summary>
|
||||
public bool Cancel { get; set; } = false;
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class HandshakeEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The player's ID
|
||||
/// </summary>
|
||||
public int ID { get; set; }
|
||||
/// <summary>
|
||||
/// The claimed username
|
||||
/// </summary>
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client password hashed with SHA256 algorithm.
|
||||
/// </summary>
|
||||
public string PasswordHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="EndPoint"/> that sent the handshake request.
|
||||
/// </summary>
|
||||
public IPEndPoint EndPoint { get; set; }
|
||||
/// <summary>
|
||||
/// Deny the connection attempt
|
||||
/// </summary>
|
||||
/// <param name="reason"></param>
|
||||
public void Deny(string reason)
|
||||
{
|
||||
DenyReason=reason;
|
||||
Cancel=true;
|
||||
}
|
||||
internal string DenyReason { get; set; }
|
||||
internal bool Cancel { get; set; }=false;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user