55 Commits

Author SHA1 Message Date
39b780e518 Merge pull request #56 from RAGECOOP/dev-nightly 2023-10-23 10:48:52 -03:00
4a54851c51 Update AssemblyInfo.cs 2023-10-23 10:36:57 -03:00
f0eefa575c Always sync player ped 2023-10-22 18:59:19 -03:00
3fc813b2d8 Fix bug disconnecting from server 2023-10-22 17:14:50 -03:00
fbff72ff14 Update LemonUI 2023-10-18 16:57:19 -03:00
4e52407591 Update nightly-build.yaml 2023-10-18 16:56:44 -03:00
4e6fde129d Add global limits 2023-10-18 10:41:34 -03:00
92e1a970a8 Show kill notification 2023-10-06 23:46:08 -03:00
d0eb0b7818 Bump version
Add kill message
2023-10-06 16:36:49 -03:00
14151d7b2c Add option to disable blip and nametag display 2023-08-14 14:08:30 -03:00
1f8d70a520 Fix player dying when switching characters in missions 2023-08-13 19:29:48 -03:00
2cb5baf5f7 Fix player dying when switching characters 2023-08-09 23:50:47 -03:00
34e33937e2 Merge pull request #49 from RAGECOOP/dev-nightly
Update README.md
2023-07-26 15:46:58 -03:00
9287aba0f9 Update README.md 2023-07-26 15:42:08 -03:00
e88e903096 Merge pull request #48 from RAGECOOP/dev-nightly
Dev nightly
2023-07-26 15:27:40 -03:00
99642fd40c Update SHVDN 2023-07-26 15:12:58 -03:00
2fbf06b504 Use Lidgren.Network release build 2023-07-24 10:40:33 -03:00
13b771ec9f Fix exception entering vehicle as passenger 2023-07-24 10:39:37 -03:00
3b987f59e0 Remove update menu 2023-07-14 09:43:00 -03:00
de96f29097 Don't delete peds in vehicle 2023-07-14 09:39:06 -03:00
6136cbfc14 Allow multiple servers on same address
with different ports
2023-07-14 09:38:02 -03:00
ed145aedd6 Update master server 2023-07-14 09:36:49 -03:00
3f3b5fd2d0 Closes #40 2022-09-30 23:40:14 +08:00
9173e9a99e Fix non-ambient peds get deleted 2022-09-30 23:27:37 +08:00
6e64c458df Merge branch 'dev-nightly' of https://github.com/RAGECOOP/RAGECOOP-V into dev-nightly 2022-09-09 11:24:02 +08:00
76f959abe9 Stuff 2022-09-09 11:23:46 +08:00
f2e85d66ab Update build-test.yaml 2022-09-09 11:15:27 +08:00
e30ef1f4bd Update actions 2022-09-08 20:09:52 -07:00
6e8f6e78f6 Add build test action 2022-09-08 20:05:26 -07:00
f28c83ccbd Merge pull request #38 from RAGECOOP/dev-nightly
Bump to 1.5.4
2022-09-07 22:44:48 -07:00
a83821b3d2 Version bump 2022-09-08 13:19:09 -07:00
3b5436064e Don't announce if already present in master server 2022-09-08 13:15:34 -07:00
c4b321324e Filter out white space for reload key parsing 2022-09-08 12:59:14 -07:00
ba8d525ddf Merge pull request #37 from RAGECOOP/dev-nightly
Rewrite packet system
2022-09-07 21:45:14 -07:00
884e2f39f0 Yet another code cleanup 2022-09-08 12:41:56 -07:00
76c529f1d1 Rewrote packet system to reduce heap allocation 2022-09-08 12:37:06 -07:00
df0064bd38 Update nightly-build.yaml 2022-09-07 21:01:22 -07:00
f1fc96bbd7 Fix weird projectile shooting 2022-09-07 11:08:25 +08:00
23e9326f5f Fix stuff, add listening address display and projectile shoot prediction 2022-09-07 09:52:40 +08:00
4621fb4987 Some code cleanup and formatting 2022-09-06 21:46:35 +08:00
6c82895fa7 code cleaned up 2022-09-05 13:02:09 +02:00
84b040766f Simplify GetBytesFromObject 2022-08-27 14:23:28 +08:00
f44558cd3b Add some client API 2022-08-27 14:17:10 +08:00
accdbbcbc6 blah 2022-08-27 13:53:03 +08:00
2d4107f35e Fix traffic 2022-08-27 12:40:47 +08:00
bac53fd769 Load scripts from main assembly only 2022-08-26 22:58:03 +08:00
8f63fee5b5 Delete temp directory 2022-08-25 22:34:54 +08:00
8d0ad0b600 Show runtime information 2022-08-25 22:32:01 +08:00
dc08c0c1f6 Added support for loading runtime-specific libraries 2022-08-25 21:51:09 +08:00
faaa856aa5 Instructional buttons for popup 2022-08-25 00:58:21 +08:00
eab64f9254 Check fixed data every 100 frames 2022-08-24 22:38:50 +08:00
6c936cb8f9 Show help in popup 2022-08-24 19:26:40 +08:00
83a37f8556 blah 2022-08-24 18:48:21 +08:00
bb4eacce26 Don't disable traffic if not on server 2022-08-24 18:47:59 +08:00
d5b71db5d4 💩 2022-08-23 23:21:35 +08:00
109 changed files with 3395 additions and 3572 deletions

48
.github/workflows/build-test.yaml vendored Normal file
View File

@ -0,0 +1,48 @@
name: Build test
on:
push:
branches:
- '*' # matches every branch that doesn't contain a '/'
- '*/*' # matches every branch containing a single '/'
- '**' # matches every branch
- '!main' # excludes main
- '!dev-nightly' # excludes nightly
pull_request:
branches: [ "main", "dev-nightly" ]
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: Restore nuget packages
run: nuget restore
- name: Build client and installer
run: dotnet build RageCoop.Client.Installer/RageCoop.Client.Installer.csproj --configuration Release -o bin/Release/Client/RageCoop
- name: Build server win-x64
run: dotnet build RageCoop.Server/RageCoop.Server.csproj -o bin/Release/Server
- name: Upload server
uses: actions/upload-artifact@v3
with:
name: RageCoop.Server
path: bin/Release/Server
- name: Upload Client
uses: actions/upload-artifact@v3
with:
name: RageCoop.Client
path: bin/Release/Client
- uses: actions/checkout@v2

View File

@ -2,9 +2,7 @@ name: Nightly-build
on: on:
push: push:
branches: [ "main" ] branches: [ "dev-nightly" ]
pull_request:
branches: [ "main" ]
jobs: jobs:
build: build:
@ -30,8 +28,6 @@ jobs:
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r win-x64 -o bin/Release/Server/win-x64 -c Release run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r win-x64 -o bin/Release/Server/win-x64 -c Release
- name: Build server linux-x64 - name: Build server linux-x64
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r linux-x64 -o bin/Release/Server/linux-x64 -c Release run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r linux-x64 -o bin/Release/Server/linux-x64 -c Release
- 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 bin/Release/Server/linux-arm -c Release
- uses: vimtor/action-zip@v1 - uses: vimtor/action-zip@v1
with: with:
files: bin/Release/Client files: bin/Release/Client
@ -47,11 +43,6 @@ jobs:
files: bin/Release/Server/linux-x64 files: bin/Release/Server/linux-x64
dest: RageCoop.Server-linux-x64.zip dest: RageCoop.Server-linux-x64.zip
- uses: vimtor/action-zip@v1
with:
files: bin/Release/Server/linux-arm
dest: RageCoop.Server-linux-arm.zip
- uses: WebFreak001/deploy-nightly@v1.1.0 - uses: WebFreak001/deploy-nightly@v1.1.0
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
@ -84,17 +75,3 @@ jobs:
asset_name: RageCoop.Server-linux-x64.zip asset_name: RageCoop.Server-linux-x64.zip
asset_content_type: application/zip asset_content_type: application/zip
max_releases: 7 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

View File

@ -15,7 +15,7 @@ RAGECOOP brings multiplayer experience to the story mode, you can complete missi
# 👁 Requirements # 👁 Requirements
- ScriptHookV - ScriptHookV
- ScriptHookVDotNet 3.5.1 or later - ScriptHookVDotNet 3.6.0 or later
- .NET Framework 4.8 Runtime or SDK - .NET Framework 4.8 Runtime or SDK
# 📋 Building the project # 📋 Building the project
@ -49,7 +49,7 @@ Then run `dotnet build` in the solution directory, built binaries are in the `bi
5. Decent compatibility with other mods, set up a private modded server to have some fun! 5. Decent compatibility with other mods, set up a private modded server to have some fun!
6. Weaponized vehicle sync(WIP). 6. Weaponized vehicle sync(WIP).
7. Optimization for high-Ping condition, play with friends around the world! 7. Optimization for high-Ping condition, play with friends around the world!
8. Powerful scripting API and resource system, easily [add multiplayer functionality to your mod](HTTPS://docs.ragecoop.online). 8. Powerful scripting API and resource system, easily [add multiplayer functionality to your mod](HTTPS://docs.ragecoop.com).
# ⚠ Known issues # ⚠ Known issues

View File

@ -1,10 +1,4 @@
using System; using System.Windows;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace RageCoop.Client.Installer namespace RageCoop.Client.Installer
{ {

View File

@ -1,6 +1,6 @@
using System.Windows; using System.Windows;
[assembly:ThemeInfo( [assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page, //(used if a resource is not found in the page,
// or application resource dictionaries) // or application resource dictionaries)

View File

@ -1,27 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Reflection;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Diagnostics;
using System.Reflection;
using RageCoop.Core;
using System.Threading;
using System.Net;
using System.Windows.Forms; using System.Windows.Forms;
using Path = System.IO.Path; using System.Windows.Input;
using MessageBox = System.Windows.MessageBox; using MessageBox = System.Windows.MessageBox;
using OpenFileDialog = Microsoft.Win32.OpenFileDialog; using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
using Path = System.IO.Path;
using RageCoop.Core;
namespace RageCoop.Client.Installer namespace RageCoop.Client.Installer
{ {
@ -41,11 +31,12 @@ namespace RageCoop.Client.Installer
var od = new OpenFileDialog() var od = new OpenFileDialog()
{ {
Filter = "GTA 5 executable |GTA5.exe;PlayGTAV.exe", Filter = "GTA 5 executable |GTA5.exe;PlayGTAV.exe",
Title="Select you GTAV executable" Title = "Select you GTAV executable"
}; };
if (od.ShowDialog() ?? false == true) if (od.ShowDialog() ?? false == true)
{ {
Task.Run(() => { Task.Run(() =>
{
try try
{ {
Install(Directory.GetParent(od.FileName).FullName); Install(Directory.GetParent(od.FileName).FullName);
@ -62,7 +53,8 @@ namespace RageCoop.Client.Installer
Environment.Exit(0); Environment.Exit(0);
} }
} }
void Install(string root)
private void Install(string root)
{ {
UpdateStatus("Checking requirements"); UpdateStatus("Checking requirements");
var shvPath = Path.Combine(root, "ScriptHookV.dll"); var shvPath = Path.Combine(root, "ScriptHookV.dll");
@ -70,7 +62,7 @@ namespace RageCoop.Client.Installer
var scriptsPath = Path.Combine(root, "Scripts"); var scriptsPath = Path.Combine(root, "Scripts");
var lemonPath = Path.Combine(scriptsPath, "LemonUI.SHVDN3.dll"); var lemonPath = Path.Combine(scriptsPath, "LemonUI.SHVDN3.dll");
var installPath = Path.Combine(scriptsPath, "RageCoop"); var installPath = Path.Combine(scriptsPath, "RageCoop");
if(Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName == installPath) if (Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName == installPath)
{ {
throw new InvalidOperationException("The installer is not meant to be run in the game folder, please extract the zip to somewhere else and run again."); throw new InvalidOperationException("The installer is not meant to be run in the game folder, please extract the zip to somewhere else and run again.");
} }
@ -86,19 +78,19 @@ namespace RageCoop.Client.Installer
Environment.Exit(1); Environment.Exit(1);
} }
var shvdnVer = GetVer(shvdnPath); var shvdnVer = GetVer(shvdnPath);
if (shvdnVer<new Version(3,5,1)) if (shvdnVer < new Version(3, 6, 0))
{ {
MessageBox.Show("Please update ScriptHookVDotNet to latest version!" + MessageBox.Show("Please update ScriptHookVDotNet to latest version!" +
$"\nCurrent version is {shvdnVer}, 3.5.1 or higher is required"); $"\nCurrent version is {shvdnVer}, 3.6.0 or higher is required");
Environment.Exit(1); Environment.Exit(1);
} }
if (File.Exists(lemonPath)) if (File.Exists(lemonPath))
{ {
var lemonVer=GetVer(lemonPath); var lemonVer = GetVer(lemonPath);
if(lemonVer<new Version(1, 7)) if (lemonVer < new Version(1, 7))
{ {
UpdateStatus("Updating LemonUI"); UpdateStatus("Updating LemonUI");
File.WriteAllBytes(lemonPath,getLemon()); File.WriteAllBytes(lemonPath, getLemon());
} }
} }
@ -129,7 +121,7 @@ namespace RageCoop.Client.Installer
void Finish() void Finish()
{ {
checkKeys: checkKeys:
UpdateStatus("Checking conflicts"); UpdateStatus("Checking conflicts");
var menyooConfig = Path.Combine(root, @"menyooStuff\menyooConfig.ini"); var menyooConfig = Path.Combine(root, @"menyooStuff\menyooConfig.ini");
var settingsPath = Path.Combine(installPath, @"Data\RageCoop.Client.Settings.xml"); var settingsPath = Path.Combine(installPath, @"Data\RageCoop.Client.Settings.xml");
@ -144,28 +136,28 @@ namespace RageCoop.Client.Installer
} }
if (File.Exists(menyooConfig)) if (File.Exists(menyooConfig))
{ {
var lines = File.ReadAllLines(menyooConfig).Where(x => !x.StartsWith(";") && x.EndsWith(" = " +(int)settings.MenuKey)); var lines = File.ReadAllLines(menyooConfig).Where(x => !x.StartsWith(";") && x.EndsWith(" = " + (int)settings.MenuKey));
if (lines.Any()) if (lines.Any())
{ {
if(MessageBox.Show("Following menyoo config value will conflict with RAGECOOP menu key\n" + if (MessageBox.Show("Following menyoo config value will conflict with RAGECOOP menu key\n" +
string.Join("\n", lines) string.Join("\n", lines)
+ "\nDo you wish to change the Menu Key?", "Warning!", MessageBoxButton.YesNo) == MessageBoxResult.Yes) + "\nDo you wish to change the Menu Key?", "Warning!", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{ {
var ae=new AutoResetEvent(false); var ae = new AutoResetEvent(false);
UpdateStatus("Press the key you wish to change to"); UpdateStatus("Press the key you wish to change to");
Dispatcher.BeginInvoke(new Action(() => Dispatcher.BeginInvoke(new Action(() =>
KeyDown += (s,e) => KeyDown += (s, e) =>
{ {
settings.MenuKey = (Keys)KeyInterop.VirtualKeyFromKey(e.Key); settings.MenuKey = (Keys)KeyInterop.VirtualKeyFromKey(e.Key);
ae.Set(); ae.Set();
})); }));
ae.WaitOne(); ae.WaitOne();
if (!Util.SaveSettings(settingsPath,settings)) if (!Util.SaveSettings(settingsPath, settings))
{ {
MessageBox.Show("Error occurred when saving settings"); MessageBox.Show("Error occurred when saving settings");
Environment.Exit(1); Environment.Exit(1);
} }
MessageBox.Show("Menu key changed to "+settings.MenuKey); MessageBox.Show("Menu key changed to " + settings.MenuKey);
goto checkKeys; goto checkKeys;
} }
} }
@ -178,10 +170,10 @@ namespace RageCoop.Client.Installer
} }
catch catch
{ {
if (MessageBox.Show("You can't join ZeroTier server unless ZeroTier is installed, do you want to download and install it?","Install ZeroTier",MessageBoxButton.YesNo)==MessageBoxResult.Yes) if (MessageBox.Show("You can't join ZeroTier server unless ZeroTier is installed, do you want to download and install it?", "Install ZeroTier", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{ {
var url = "https://download.zerotier.com/dist/ZeroTier%20One.msi"; var url = "https://download.zerotier.com/dist/ZeroTier%20One.msi";
UpdateStatus("Downloading ZeroTier from "+url); UpdateStatus("Downloading ZeroTier from " + url);
try try
{ {
HttpHelper.DownloadFile(url, "ZeroTier.msi", (p) => UpdateStatus("Downloading ZeroTier " + p + "%")); HttpHelper.DownloadFile(url, "ZeroTier.msi", (p) => UpdateStatus("Downloading ZeroTier " + p + "%"));
@ -201,15 +193,18 @@ namespace RageCoop.Client.Installer
Environment.Exit(0); Environment.Exit(0);
} }
} }
void UpdateStatus(string status)
private void UpdateStatus(string status)
{ {
Dispatcher.BeginInvoke(new Action(() => Status.Content = status)); Dispatcher.BeginInvoke(new Action(() => Status.Content = status));
} }
Version GetVer(string location)
private Version GetVer(string location)
{ {
return Version.Parse(FileVersionInfo.GetVersionInfo(location).FileVersion); return Version.Parse(FileVersionInfo.GetVersionInfo(location).FileVersion);
} }
byte[] getLemon()
private byte[] getLemon()
{ {
return (byte[])Resource.ResourceManager.GetObject("LemonUI_SHVDN3"); return (byte[])Resource.ResourceManager.GetObject("LemonUI_SHVDN3");
} }

View File

@ -0,0 +1,72 @@
<Project>
<PropertyGroup>
<AssemblyName>RageCoop.Client.Installer</AssemblyName>
<IntermediateOutputPath>obj\Debug\</IntermediateOutputPath>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<MSBuildProjectExtensionsPath>M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Client.Installer\obj\</MSBuildProjectExtensionsPath>
<_TargetAssemblyProjectName>RageCoop.Client.Installer</_TargetAssemblyProjectName>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<None Remove="bg.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RageCoop.Client\RageCoop.Client.csproj" />
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<Compile Update="Resource.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ReferencePath Include="C:\Users\Sardelka\.nuget\packages\sharpziplib\1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll" />
<ReferencePath Include="C:\Users\Sardelka\.nuget\packages\microsoft.extensions.objectpool\6.0.8\lib\net461\Microsoft.Extensions.ObjectPool.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\mscorlib.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\PresentationCore.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\PresentationFramework.dll" />
<ReferencePath Include="M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\bin\Debug\Client\RageCoop.Client.dll" />
<ReferencePath Include="M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\bin\Debug\Core\RageCoop.Core.dll" />
<ReferencePath Include="C:\Users\Sardelka\.nuget\packages\system.buffers\4.5.1\ref\net45\System.Buffers.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Core.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Data.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Drawing.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.IO.Compression.FileSystem.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Numerics.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Runtime.Serialization.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Windows.Controls.Ribbon.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Windows.Forms.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xaml.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.Linq.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationClient.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationClientsideProviders.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationProvider.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationTypes.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\WindowsBase.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\WindowsFormsIntegration.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\Facades\netstandard.dll" />
</ItemGroup>
<ItemGroup>
<Compile Include="M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Client.Installer\obj\Debug\net48\MainWindow.g.cs" />
<Compile Include="M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Client.Installer\obj\Debug\net48\App.g.cs" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>

View File

@ -36,14 +36,14 @@ namespace RageCoop.Client
string s = ""; string s = "";
foreach (KeyValuePair<TimeStamp, long> kvp in d) foreach (KeyValuePair<TimeStamp, long> kvp in d)
{ {
s+=kvp.Key+":"+kvp.Value+"\n"; s += kvp.Key + ":" + kvp.Value + "\n";
} }
return s; return s;
} }
public static void ShowTimeStamps() public static void ShowTimeStamps()
{ {
GTA.UI.Notification.Hide(_lastNfHandle); GTA.UI.Notification.Hide(_lastNfHandle);
_lastNfHandle=GTA.UI.Notification.Show(Debug.TimeStamps.Dump()); _lastNfHandle = GTA.UI.Notification.Show(Debug.TimeStamps.Dump());
} }
} }

View File

@ -16,14 +16,14 @@ namespace RageCoop.Client
public static MuzzleDir Direction = MuzzleDir.Forward; public static MuzzleDir Direction = MuzzleDir.Forward;
public DevTool() public DevTool()
{ {
Tick+=OnTick; Tick += OnTick;
KeyDown+=OnKeyDown; KeyDown += OnKeyDown;
} }
private void OnKeyDown(object sender, KeyEventArgs e) private void OnKeyDown(object sender, KeyEventArgs e)
{ {
if (ToMark==null||(!ToMark.Exists())) { return; } if (ToMark == null || (!ToMark.Exists())) { return; }
if (DevToolMenu.Menu.SelectedItem==DevToolMenu.boneIndexItem) if (DevToolMenu.Menu.SelectedItem == DevToolMenu.boneIndexItem)
{ {
switch (e.KeyCode) switch (e.KeyCode)
@ -36,7 +36,7 @@ namespace RageCoop.Client
break; break;
} }
} }
else if (DevToolMenu.Menu.SelectedItem==DevToolMenu.secondaryBoneIndexItem) else if (DevToolMenu.Menu.SelectedItem == DevToolMenu.secondaryBoneIndexItem)
{ {
switch (e.KeyCode) switch (e.KeyCode)
@ -54,24 +54,24 @@ namespace RageCoop.Client
private static void Update() private static void Update()
{ {
if (Current>ToMark.Bones.Count-1) if (Current > ToMark.Bones.Count - 1)
{ {
Current=0; Current = 0;
} }
else if (Current< 0) else if (Current < 0)
{ {
Current=ToMark.Bones.Count-1; Current = ToMark.Bones.Count - 1;
} }
DevToolMenu.boneIndexItem.AltTitle= Current.ToString(); DevToolMenu.boneIndexItem.AltTitle = Current.ToString();
if (Secondary>ToMark.Bones.Count-1) if (Secondary > ToMark.Bones.Count - 1)
{ {
Secondary=0; Secondary = 0;
} }
else if (Secondary< 0) else if (Secondary < 0)
{ {
Secondary=ToMark.Bones.Count-1; Secondary = ToMark.Bones.Count - 1;
} }
DevToolMenu.secondaryBoneIndexItem.AltTitle= Secondary.ToString(); DevToolMenu.secondaryBoneIndexItem.AltTitle = Secondary.ToString();
} }
private static void OnTick(object sender, EventArgs e) private static void OnTick(object sender, EventArgs e)
{ {
@ -87,44 +87,44 @@ namespace RageCoop.Client
private static void Draw(int boneindex) private static void Draw(int boneindex)
{ {
var bone = ToMark.Bones[boneindex]; var bone = ToMark.Bones[boneindex];
World.DrawLine(bone.Position, bone.Position+2*bone.ForwardVector, Color.Blue); World.DrawLine(bone.Position, bone.Position + 2 * bone.ForwardVector, Color.Blue);
World.DrawLine(bone.Position, bone.Position+2*bone.UpVector, Color.Green); World.DrawLine(bone.Position, bone.Position + 2 * bone.UpVector, Color.Green);
World.DrawLine(bone.Position, bone.Position+2*bone.RightVector, Color.Yellow); World.DrawLine(bone.Position, bone.Position + 2 * bone.RightVector, Color.Yellow);
Vector3 todraw = bone.ForwardVector; Vector3 todraw = bone.ForwardVector;
switch ((byte)Direction) switch ((byte)Direction)
{ {
case 0: case 0:
todraw=bone.ForwardVector; todraw = bone.ForwardVector;
break; break;
case 1: case 1:
todraw=bone.RightVector; todraw = bone.RightVector;
break; break;
case 2: case 2:
todraw=bone.UpVector; todraw = bone.UpVector;
break; break;
case 3: case 3:
todraw=bone.ForwardVector*-1; todraw = bone.ForwardVector * -1;
break; break;
case 4: case 4:
todraw=bone.RightVector*-1; todraw = bone.RightVector * -1;
break; break;
case 5: case 5:
todraw=bone.UpVector*-1; todraw = bone.UpVector * -1;
break; break;
} }
World.DrawLine(bone.Position, bone.Position+10*todraw, Color.Red); World.DrawLine(bone.Position, bone.Position + 10 * todraw, Color.Red);
} }
public static void CopyToClipboard(MuzzleDir dir) public static void CopyToClipboard(MuzzleDir dir)
{ {
if (ToMark!=null) if (ToMark != null)
{ {
string s; string s;
if (UseSecondary) if (UseSecondary)
{ {
if ((byte)dir<3) if ((byte)dir < 3)
{ {
s=$@" s = $@"
// {ToMark.DisplayName} // {ToMark.DisplayName}
case {ToMark.Model.Hash}: case {ToMark.Model.Hash}:
return BulletsShot%2==0 ? {Current} : {Secondary}; return BulletsShot%2==0 ? {Current} : {Secondary};
@ -132,7 +132,7 @@ namespace RageCoop.Client
} }
else else
{ {
s=$@" s = $@"
// {ToMark.DisplayName} // {ToMark.DisplayName}
case {ToMark.Model.Hash}: case {ToMark.Model.Hash}:
return BulletsShot%2==0 ? {Current} : {Secondary}; return BulletsShot%2==0 ? {Current} : {Secondary};
@ -141,9 +141,9 @@ namespace RageCoop.Client
} }
else else
{ {
if ((byte)dir<3) if ((byte)dir < 3)
{ {
s=$@" s = $@"
// {ToMark.DisplayName} // {ToMark.DisplayName}
case {ToMark.Model.Hash}: case {ToMark.Model.Hash}:
return {Current}; return {Current};
@ -151,7 +151,7 @@ namespace RageCoop.Client
} }
else else
{ {
s=$@" s = $@"
// {ToMark.DisplayName} // {ToMark.DisplayName}
case {ToMark.Model.Hash}: case {ToMark.Model.Hash}:
return {Current}; return {Current};

View File

@ -6,12 +6,11 @@ using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Drawing; using System.Drawing;
using System.Linq; using System.IO;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -21,7 +20,7 @@ namespace RageCoop.Client
internal class Main : Script internal class Main : Script
{ {
private bool _gameLoaded = false; private bool _gameLoaded = false;
internal static Version Version=typeof(Main).Assembly.GetName().Version; internal static Version Version = typeof(Main).Assembly.GetName().Version;
internal static int LocalPlayerID = 0; internal static int LocalPlayerID = 0;
@ -39,7 +38,7 @@ namespace RageCoop.Client
internal static ulong Ticked = 0; internal static ulong Ticked = 0;
internal static Vector3 PlayerPosition; internal static Vector3 PlayerPosition;
internal static Scripting.Resources Resources = null; internal static Scripting.Resources Resources = null;
private static List<Func<bool>> QueuedActions = new List<Func<bool>>(); private static readonly List<Func<bool>> QueuedActions = new List<Func<bool>>();
public static Worker Worker; public static Worker Worker;
/// <summary> /// <summary>
@ -47,9 +46,6 @@ namespace RageCoop.Client
/// </summary> /// </summary>
public Main() public Main()
{ {
#if DEBUG_HIGH_PING
Networking.SimulatedLatency=0.3f;
#endif
Worker = new Worker("RageCoop.Client.Main.Worker", Logger); Worker = new Worker("RageCoop.Client.Main.Worker", Logger);
try try
{ {
@ -58,14 +54,14 @@ namespace RageCoop.Client
catch catch
{ {
GTA.UI.Notification.Show("Malformed configuration, overwriting with default values..."); GTA.UI.Notification.Show("Malformed configuration, overwriting with default values...");
Settings=new Settings(); Settings = new Settings();
Util.SaveSettings(); Util.SaveSettings();
} }
Directory.CreateDirectory(Settings.DataDirectory); Directory.CreateDirectory(Settings.DataDirectory);
Logger=new Logger() Logger = new Logger()
{ {
LogPath=$"{Settings.DataDirectory}\\RageCoop.Client.log", LogPath = $"{Settings.DataDirectory}\\RageCoop.Client.log",
UseConsole=false, UseConsole = false,
#if DEBUG #if DEBUG
LogLevel = 0, LogLevel = 0,
#else #else
@ -90,7 +86,7 @@ namespace RageCoop.Client
return; return;
} }
BaseScript.OnStart(); BaseScript.OnStart();
SyncedPedsGroup=World.AddRelationshipGroup("SYNCPED"); SyncedPedsGroup = World.AddRelationshipGroup("SYNCPED");
Game.Player.Character.RelationshipGroup.SetRelationshipBetweenGroups(SyncedPedsGroup, Relationship.Neutral, true); Game.Player.Character.RelationshipGroup.SetRelationshipBetweenGroups(SyncedPedsGroup, Relationship.Neutral, true);
#if !NON_INTERACTIVE #if !NON_INTERACTIVE
#endif #endif
@ -98,9 +94,9 @@ namespace RageCoop.Client
Tick += OnTick; Tick += OnTick;
Tick += (s, e) => { Scripting.API.Events.InvokeTick(); }; Tick += (s, e) => { Scripting.API.Events.InvokeTick(); };
KeyDown += OnKeyDown; KeyDown += OnKeyDown;
KeyDown+=(s, e) => { Scripting.API.Events.InvokeKeyDown(s, e); }; KeyDown += (s, e) => { Scripting.API.Events.InvokeKeyDown(s, e); };
KeyUp+=(s, e) => { Scripting.API.Events.InvokeKeyUp(s, e); }; KeyUp += (s, e) => { Scripting.API.Events.InvokeKeyUp(s, e); };
Aborted += (object sender, EventArgs e) => CleanUp(); Aborted += (object sender, EventArgs e) => Disconnected("Abort");
Util.NativeMemory(); Util.NativeMemory();
Counter.Restart(); Counter.Restart();
@ -111,24 +107,9 @@ namespace RageCoop.Client
private bool _lastDead; private bool _lastDead;
private void OnTick(object sender, EventArgs e) private void OnTick(object sender, EventArgs e)
{ {
/* P = Game.Player.Character;
unsafe PlayerPosition = P.ReadPosition();
{ FPS = Game.FPS;
var stationName = Function.Call<string>(Hash.GET_RADIO_STATION_NAME, Game.RadioStation);
//_GET_CURRENT_RADIO_TRACK_NAME
var currentTrack = Function.Call<int>((Hash)0x34D66BC058019CE0, stationName);
Function.Call(Hash.SET_RADIO_TRACK, "RADIO_03_HIPHOP_NEW", "ARM1_RADIO_STARTS");
return currentTrack;
var h1 = Function.Call<int>(Hash._GET_CURRENT_RADIO_STATION_HASH);
return $"{h1},{h2},{s},{s1}";
}
*/
P= Game.Player.Character;
PlayerPosition=P.ReadPosition();
FPS=Game.FPS;
// World.DrawMarker(MarkerType.DebugSphere, PedExtensions.RaycastEverything(default), default, default, new Vector3(0.2f, 0.2f, 0.2f), Color.AliceBlue);
if (Game.IsLoading) if (Game.IsLoading)
{ {
return; return;
@ -137,7 +118,6 @@ namespace RageCoop.Client
{ {
#if !NON_INTERACTIVE #if !NON_INTERACTIVE
GTA.UI.Notification.Show(GTA.UI.NotificationIcon.AllPlayersConf, "RAGECOOP", "Welcome!", $"Press ~g~{Main.Settings.MenuKey}~s~ to open the menu."); GTA.UI.Notification.Show(GTA.UI.NotificationIcon.AllPlayersConf, "RAGECOOP", "Welcome!", $"Press ~g~{Main.Settings.MenuKey}~s~ to open the menu.");
WorldThread.Traffic(!Settings.DisableTraffic);
#endif #endif
} }
@ -151,9 +131,9 @@ namespace RageCoop.Client
{ {
return; return;
} }
if (Game.TimeScale!=1) if (Game.TimeScale != 1)
{ {
Game.TimeScale=1; Game.TimeScale = 1;
} }
try try
{ {
@ -161,7 +141,9 @@ namespace RageCoop.Client
} }
catch (Exception ex) catch (Exception ex)
{ {
#if DEBUG
Main.Logger.Error(ex); Main.Logger.Error(ex);
#endif
} }
if (Networking.ShowNetworkInfo) if (Networking.ShowNetworkInfo)
@ -183,12 +165,12 @@ namespace RageCoop.Client
{ {
Function.Call(Hash.SET_FADE_OUT_AFTER_DEATH, false); Function.Call(Hash.SET_FADE_OUT_AFTER_DEATH, false);
if (P.Health!=1) if (P.Health != 1)
{ {
P.Health=1; P.Health = 1;
Game.Player.WantedLevel=0; Game.Player.WantedLevel = 0;
Main.Logger.Debug("Player died."); Main.Logger.Debug("Player died.");
Scripting.API.Events.InvokePlayerDied(); Scripting.API.Events.InvokePlayerDied(KillMessage());
} }
GTA.UI.Screen.StopEffects(); GTA.UI.Screen.StopEffects();
} }
@ -199,10 +181,10 @@ namespace RageCoop.Client
} }
else if (P.IsDead && !_lastDead) else if (P.IsDead && !_lastDead)
{ {
Scripting.API.Events.InvokePlayerDied(); Scripting.API.Events.InvokePlayerDied(KillMessage());
} }
_lastDead=P.IsDead; _lastDead = P.IsDead;
Ticked++; Ticked++;
} }
private void OnKeyDown(object sender, KeyEventArgs e) private void OnKeyDown(object sender, KeyEventArgs e)
@ -233,7 +215,7 @@ namespace RageCoop.Client
Function.Call(Hash.ACTIVATE_FRONTEND_MENU, Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0); Function.Call(Hash.ACTIVATE_FRONTEND_MENU, Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0);
return; return;
} }
if (Game.IsControlPressed(GTA.Control.FrontendPauseAlternate)&&Settings.DisableAlternatePause) if (Game.IsControlPressed(GTA.Control.FrontendPauseAlternate) && Settings.DisableAlternatePause)
{ {
Function.Call(Hash.ACTIVATE_FRONTEND_MENU, Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0); Function.Call(Hash.ACTIVATE_FRONTEND_MENU, Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0);
return; return;
@ -247,8 +229,8 @@ namespace RageCoop.Client
{ {
if (x.Visible) if (x.Visible)
{ {
CoopMenu.LastMenu=x; CoopMenu.LastMenu = x;
x.Visible=false; x.Visible = false;
} }
}); });
} }
@ -273,7 +255,7 @@ namespace RageCoop.Client
PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000 ? (currentTimestamp - 6000) : currentTimestamp; PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000 ? (currentTimestamp - 6000) : currentTimestamp;
} }
} }
else if (e.KeyCode==Settings.PassengerKey) else if (e.KeyCode == Settings.PassengerKey)
{ {
var P = Game.Player.Character; var P = Game.Player.Character;
@ -287,13 +269,13 @@ namespace RageCoop.Client
{ {
var V = World.GetClosestVehicle(P.ReadPosition(), 50); var V = World.GetClosestVehicle(P.ReadPosition(), 50);
if (V!=null) if (V != null)
{ {
var seat = P.GetNearestSeat(V); var seat = P.GetNearestSeat(V);
var p = V.GetPedOnSeat(seat); var p = V.GetPedOnSeat(seat);
if (p != null && !p.IsDead) if (p != null && !p.IsDead)
{ {
for(int i = -1; i < V.PassengerCapacity; i++) for (int i = -1; i < V.PassengerCapacity; i++)
{ {
seat = (VehicleSeat)i; seat = (VehicleSeat)i;
p = V.GetPedOnSeat(seat); p = V.GetPedOnSeat(seat);
@ -303,21 +285,55 @@ namespace RageCoop.Client
} }
} }
} }
P.Task.EnterVehicle(V, seat,-1,5,EnterVehicleFlags.None); P.Task.EnterVehicle(V, seat, -1, 5, EnterVehicleFlags.None);
} }
} }
} }
} }
} }
public static void CleanUp() internal static void Connected()
{ {
MainChat.Clear(); Memory.ApplyPatches();
if (Settings.Voice && !Voice.WasInitialized())
{
Voice.Init();
}
QueueAction(() =>
{
WorldThread.Traffic(!Settings.DisableTraffic);
Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, true);
CoopMenu.ConnectedMenuSetting();
MainChat.Init();
GTA.UI.Notification.Show("~g~Connected!");
});
Logger.Info(">> Connected <<");
}
public static void Disconnected(string reason)
{
Logger.Info($">> Disconnected << reason: {reason}");
QueueAction(() =>
{
if (MainChat.Focused)
{
MainChat.Focused = false;
}
PlayerList.Cleanup();
MainChat.Clear();
EntityPool.Cleanup();
WorldThread.Traffic(true);
Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, false);
CoopMenu.DisconnectedMenuSetting();
if (reason != "Abort")
GTA.UI.Notification.Show("~r~Disconnected: " + reason);
LocalPlayerID = default;
});
Memory.RestorePatches();
DownloadManager.Cleanup();
Voice.ClearAll(); Voice.ClearAll();
EntityPool.Cleanup(); Resources.Unload();
PlayerList.Cleanup();
LocalPlayerID=default;
WorldThread.Traffic(!Settings.DisableTraffic);
Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, false);
} }
private static void DoQueuedActions() private static void DoQueuedActions()
{ {
@ -334,7 +350,9 @@ namespace RageCoop.Client
} }
catch (Exception ex) catch (Exception ex)
{ {
#if DEBUG
Logger.Error(ex); Logger.Error(ex);
#endif
QueuedActions.Remove(action); QueuedActions.Remove(action);
} }
} }
@ -375,5 +393,16 @@ namespace RageCoop.Client
QueueAction(a); QueueAction(a);
}); });
} }
private string KillMessage()
{
if (P.Killer != null)
{
var killer = EntityPool.GetPedByHandle(P.Killer.Handle);
if (killer != null && killer.ID == killer.Owner.ID)
return $"~h~{PlayerList.GetPlayer(LocalPlayerID).Username}~h~ was killed by ~h~{killer.Owner.Username}~h~ ({P.CauseOfDeath})";
}
return $"~h~{PlayerList.GetPlayer(LocalPlayerID).Username}~h~ died";
}
} }
} }

View File

@ -1,4 +1,5 @@
using GTA; using GTA;
using GTA.Native;
using LemonUI; using LemonUI;
using LemonUI.Menus; using LemonUI.Menus;
using LemonUI.Scaleform; using LemonUI.Scaleform;
@ -19,12 +20,12 @@ namespace RageCoop.Client.Menus
}; };
public static PopUp PopUp = new PopUp() public static PopUp PopUp = new PopUp()
{ {
Title="", Title = "",
Prompt="", Prompt = "",
Subtitle = "", Subtitle = "",
Error="", Error = "",
ShowBackground = true, ShowBackground = true,
Visible=false, Visible = false,
}; };
public static NativeMenu LastMenu { get; set; } = Menu; public static NativeMenu LastMenu { get; set; } = Menu;
#region ITEMS #region ITEMS
@ -49,10 +50,10 @@ namespace RageCoop.Client.Menus
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
_usernameItem.Activated += UsernameActivated; _usernameItem.Activated += UsernameActivated;
_passwordItem.Activated+=_passwordActivated; _passwordItem.Activated += _passwordActivated;
ServerIpItem.Activated += ServerIpActivated; ServerIpItem.Activated += ServerIpActivated;
_serverConnectItem.Activated += (sender, item) => { Networking.ToggleConnection(Main.Settings.LastServerAddress); }; _serverConnectItem.Activated += (sender, item) => { Networking.ToggleConnection(Main.Settings.LastServerAddress); };
@ -66,17 +67,18 @@ namespace RageCoop.Client.Menus
Menu.AddSubMenu(SettingsMenu.Menu); Menu.AddSubMenu(SettingsMenu.Menu);
Menu.AddSubMenu(DevToolMenu.Menu); Menu.AddSubMenu(DevToolMenu.Menu);
#if DEBUG
Menu.AddSubMenu(DebugMenu.Menu); Menu.AddSubMenu(DebugMenu.Menu);
Menu.AddSubMenu(UpdateMenu.Menu); #endif
MenuPool.Add(Menu); MenuPool.Add(Menu);
MenuPool.Add(SettingsMenu.Menu); MenuPool.Add(SettingsMenu.Menu);
MenuPool.Add(DevToolMenu.Menu); MenuPool.Add(DevToolMenu.Menu);
#if DEBUG
MenuPool.Add(DebugMenu.Menu); MenuPool.Add(DebugMenu.Menu);
MenuPool.Add(DebugMenu.DiagnosticMenu); MenuPool.Add(DebugMenu.DiagnosticMenu);
#endif
MenuPool.Add(ServersMenu.Menu); MenuPool.Add(ServersMenu.Menu);
MenuPool.Add(UpdateMenu.Menu);
MenuPool.Add(PopUp); MenuPool.Add(PopUp);
Menu.Add(_aboutItem); Menu.Add(_aboutItem);
@ -85,20 +87,30 @@ namespace RageCoop.Client.Menus
public static bool ShowPopUp(string prompt, string title, string subtitle, string error, bool showbackground) public static bool ShowPopUp(string prompt, string title, string subtitle, string error, bool showbackground)
{ {
PopUp.Prompt=prompt; PopUp.Prompt = prompt;
PopUp.Title=title; PopUp.Title = title;
PopUp.Subtitle=subtitle; PopUp.Subtitle = subtitle;
PopUp.Error=error; PopUp.Error = error;
PopUp.ShowBackground=showbackground; PopUp.ShowBackground = showbackground;
PopUp.Visible=true; PopUp.Visible = true;
Script.Yield(); Script.Yield();
while (true) while (true)
{ {
Game.DisableAllControlsThisFrame(); Game.DisableAllControlsThisFrame();
MenuPool.Process(); MenuPool.Process();
var scaleform = new Scaleform("instructional_buttons");
scaleform.CallFunction("CLEAR_ALL");
scaleform.CallFunction("TOGGLE_MOUSE_BUTTONS", 0);
scaleform.CallFunction("CREATE_CONTAINER");
scaleform.CallFunction("SET_DATA_SLOT", 0, Function.Call<string>((Hash)0x0499D7B09FC9B407, 2, (int)Control.FrontendAccept, 0), "Continue");
scaleform.CallFunction("SET_DATA_SLOT", 1, Function.Call<string>((Hash)0x0499D7B09FC9B407, 2, (int)Control.FrontendCancel, 0), "Cancel");
scaleform.CallFunction("DRAW_INSTRUCTIONAL_BUTTONS", -1);
scaleform.Render2D();
if (Game.IsControlJustPressed(Control.FrontendAccept)) if (Game.IsControlJustPressed(Control.FrontendAccept))
{ {
PopUp.Visible=false; PopUp.Visible = false;
return true; return true;
} }
else if (Game.IsControlJustPressed(Control.FrontendCancel)) else if (Game.IsControlJustPressed(Control.FrontendCancel))
@ -107,6 +119,8 @@ namespace RageCoop.Client.Menus
return false; return false;
} }
Script.Yield(); Script.Yield();
Game.DisableAllControlsThisFrame();
} }
} }
public static void UsernameActivated(object a, System.EventArgs b) public static void UsernameActivated(object a, System.EventArgs b)

View File

@ -1,7 +1,8 @@
using GTA; #if DEBUG
using GTA;
using LemonUI.Menus; using LemonUI.Menus;
using System.Drawing;
using System; using System;
using System.Drawing;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -24,10 +25,10 @@ namespace RageCoop.Client
static DebugMenu() static DebugMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
DiagnosticMenu.Opening+=(sender, e) => DiagnosticMenu.Opening += (sender, e) =>
{ {
DiagnosticMenu.Clear(); DiagnosticMenu.Clear();
DiagnosticMenu.Add(new NativeItem("EntityPool", EntityPool.DumpDebug())); DiagnosticMenu.Add(new NativeItem("EntityPool", EntityPool.DumpDebug()));
@ -36,13 +37,13 @@ namespace RageCoop.Client
DiagnosticMenu.Add(new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString())); DiagnosticMenu.Add(new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString()));
} }
}; };
SimulatedLatencyItem.Activated+=(s, e) => SimulatedLatencyItem.Activated += (s, e) =>
{ {
try try
{ {
SimulatedLatencyItem.AltTitle=((Networking.SimulatedLatency=int.Parse(Game.GetUserInput(SimulatedLatencyItem.AltTitle))*0.002f)*500).ToString(); SimulatedLatencyItem.AltTitle = ((Networking.SimulatedLatency = int.Parse(Game.GetUserInput(SimulatedLatencyItem.AltTitle)) * 0.002f) * 500).ToString();
} }
catch(Exception ex) { Main.Logger.Error(ex); } catch (Exception ex) { Main.Logger.Error(ex); }
}; };
ShowNetworkInfoItem.CheckboxChanged += (s, e) => { Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked; }; ShowNetworkInfoItem.CheckboxChanged += (s, e) => { Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked; };
ShowOwnerItem.CheckboxChanged += (s, e) => { Main.Settings.ShowEntityOwnerName = ShowOwnerItem.Checked; Util.SaveSettings(); }; ShowOwnerItem.CheckboxChanged += (s, e) => { Main.Settings.ShowEntityOwnerName = ShowOwnerItem.Checked; Util.SaveSettings(); };
@ -56,3 +57,4 @@ namespace RageCoop.Client
} }
} }
#endif

View File

@ -12,9 +12,9 @@ namespace RageCoop.Client
UseMouse = false, UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
}; };
private static NativeCheckboxItem enableItem = new NativeCheckboxItem("Enable"); private static readonly NativeCheckboxItem enableItem = new NativeCheckboxItem("Enable");
private static NativeCheckboxItem enableSecondaryItem = new NativeCheckboxItem("Secondary", "Enable if this vehicle have two muzzles"); private static readonly NativeCheckboxItem enableSecondaryItem = new NativeCheckboxItem("Secondary", "Enable if this vehicle have two muzzles");
public static NativeItem boneIndexItem = new NativeItem("Current bone index"); public static NativeItem boneIndexItem = new NativeItem("Current bone index");
public static NativeItem secondaryBoneIndexItem = new NativeItem("Secondary bone index"); public static NativeItem secondaryBoneIndexItem = new NativeItem("Secondary bone index");
public static NativeItem clipboardItem = new NativeItem("Copy to clipboard"); public static NativeItem clipboardItem = new NativeItem("Copy to clipboard");
@ -22,20 +22,20 @@ namespace RageCoop.Client
static DevToolMenu() static DevToolMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
enableItem.Activated+=enableItem_Activated; enableItem.Activated += enableItem_Activated;
enableItem.Checked=false; enableItem.Checked = false;
enableSecondaryItem.CheckboxChanged+=EnableSecondaryItem_Changed; enableSecondaryItem.CheckboxChanged += EnableSecondaryItem_Changed;
secondaryBoneIndexItem.Enabled=false; secondaryBoneIndexItem.Enabled = false;
clipboardItem.Activated+=ClipboardItem_Activated; clipboardItem.Activated += ClipboardItem_Activated;
dirItem.ItemChanged+=DirItem_ItemChanged; dirItem.ItemChanged += DirItem_ItemChanged;
foreach (var d in Enum.GetValues(typeof(MuzzleDir))) foreach (var d in Enum.GetValues(typeof(MuzzleDir)))
{ {
dirItem.Items.Add((MuzzleDir)d); dirItem.Items.Add((MuzzleDir)d);
} }
dirItem.SelectedIndex=0; dirItem.SelectedIndex = 0;
Menu.Add(enableItem); Menu.Add(enableItem);
Menu.Add(boneIndexItem); Menu.Add(boneIndexItem);
@ -49,19 +49,19 @@ namespace RageCoop.Client
{ {
if (enableSecondaryItem.Checked) if (enableSecondaryItem.Checked)
{ {
DevTool.UseSecondary=true; DevTool.UseSecondary = true;
secondaryBoneIndexItem.Enabled=true; secondaryBoneIndexItem.Enabled = true;
} }
else else
{ {
DevTool.UseSecondary=false; DevTool.UseSecondary = false;
secondaryBoneIndexItem.Enabled=false; secondaryBoneIndexItem.Enabled = false;
} }
} }
private static void DirItem_ItemChanged(object sender, ItemChangedEventArgs<MuzzleDir> e) private static void DirItem_ItemChanged(object sender, ItemChangedEventArgs<MuzzleDir> e)
{ {
DevTool.Direction=dirItem.SelectedItem; DevTool.Direction = dirItem.SelectedItem;
} }
private static void ClipboardItem_Activated(object sender, EventArgs e) private static void ClipboardItem_Activated(object sender, EventArgs e)
@ -73,11 +73,11 @@ namespace RageCoop.Client
{ {
if (enableItem.Checked) if (enableItem.Checked)
{ {
DevTool.ToMark=Game.Player.Character.CurrentVehicle; DevTool.ToMark = Game.Player.Character.CurrentVehicle;
} }
else else
{ {
DevTool.ToMark=null; DevTool.ToMark = null;
} }
} }
} }

View File

@ -1,12 +1,12 @@
using LemonUI.Menus; using GTA.UI;
using LemonUI.Menus;
using Newtonsoft.Json; using Newtonsoft.Json;
using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using RageCoop.Core;
using GTA.UI;
namespace RageCoop.Client.Menus namespace RageCoop.Client.Menus
{ {
@ -30,7 +30,7 @@ namespace RageCoop.Client.Menus
static ServersMenu() static ServersMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
Menu.Opening += (object sender, System.ComponentModel.CancelEventArgs e) => Menu.Opening += (object sender, System.ComponentModel.CancelEventArgs e) =>
{ {
@ -38,7 +38,7 @@ namespace RageCoop.Client.Menus
Menu.Add(ResultItem = new NativeItem("Loading...")); Menu.Add(ResultItem = new NativeItem("Loading..."));
// Prevent freezing // Prevent freezing
GetServersThread=new Thread(() => GetAllServers()); GetServersThread = new Thread(() => GetAllServers());
GetServersThread.Start(); GetServersThread.Start();
}; };
Menu.Closing += (object sender, System.ComponentModel.CancelEventArgs e) => Menu.Closing += (object sender, System.ComponentModel.CancelEventArgs e) =>
@ -76,7 +76,7 @@ namespace RageCoop.Client.Menus
foreach (ServerInfo server in serverList) foreach (ServerInfo server in serverList)
{ {
string address = $"{server.address}:{server.port}"; string address = $"{server.address}:{server.port}";
NativeItem tmpItem = new NativeItem($"[{server.country}] {server.name}", $"~b~{address}~s~~n~~g~Version {server.version}.x~s~") { AltTitle = $"[{server.players}/{server.maxPlayers}]" }; NativeItem tmpItem = new NativeItem($"[{server.country}] {server.name}", $"~b~{address}~s~~n~~g~Version {server.version}~s~") { AltTitle = $"[{server.players}/{server.maxPlayers}]" };
tmpItem.Activated += (object sender, EventArgs e) => tmpItem.Activated += (object sender, EventArgs e) =>
{ {
try try
@ -84,14 +84,14 @@ namespace RageCoop.Client.Menus
Menu.Visible = false; Menu.Visible = false;
if (server.useZT) if (server.useZT)
{ {
address=$"{server.ztAddress}:{server.port}"; address = $"{server.ztAddress}:{server.port}";
Notification.Show($"~y~Joining ZeroTier network... {server.ztID}"); Notification.Show($"~y~Joining ZeroTier network... {server.ztID}");
if (ZeroTierHelper.Join(server.ztID)==null) if (ZeroTierHelper.Join(server.ztID) == null)
{ {
throw new Exception("Failed to obtain ZeroTier network IP"); throw new Exception("Failed to obtain ZeroTier network IP");
} }
} }
Networking.ToggleConnection(address,null,null,PublicKey.FromServerInfo(server)); Networking.ToggleConnection(address, null, null, PublicKey.FromServerInfo(server));
#if !NON_INTERACTIVE #if !NON_INTERACTIVE
CoopMenu.ServerIpItem.AltTitle = address; CoopMenu.ServerIpItem.AltTitle = address;

View File

@ -17,24 +17,36 @@ namespace RageCoop.Client.Menus
private static readonly NativeCheckboxItem _disableTrafficItem = new NativeCheckboxItem("Disable Traffic (NPCs/Vehicles)", "Local traffic only", Main.Settings.DisableTraffic); private static readonly NativeCheckboxItem _disableTrafficItem = new NativeCheckboxItem("Disable Traffic (NPCs/Vehicles)", "Local traffic only", Main.Settings.DisableTraffic);
private static readonly NativeCheckboxItem _flipMenuItem = new NativeCheckboxItem("Flip menu", Main.Settings.FlipMenu); private static readonly NativeCheckboxItem _flipMenuItem = new NativeCheckboxItem("Flip menu", Main.Settings.FlipMenu);
private static readonly NativeCheckboxItem _disablePauseAlt = new NativeCheckboxItem("Disable Alternate Pause", "Don't freeze game time when Esc pressed", Main.Settings.DisableAlternatePause); private static readonly NativeCheckboxItem _disablePauseAlt = new NativeCheckboxItem("Disable Alternate Pause", "Don't freeze game time when Esc pressed", Main.Settings.DisableAlternatePause);
private static readonly NativeCheckboxItem _showBlip = new NativeCheckboxItem("Show player blip", "Show other player's blip on map", Main.Settings.ShowPlayerBlip);
private static readonly NativeCheckboxItem _showNametag = new NativeCheckboxItem("Show player nametag", "Show other player's nametag on your screen", Main.Settings.ShowPlayerNameTag);
private static readonly NativeCheckboxItem _disableVoice = new NativeCheckboxItem("Enable voice", "Check your GTA:V settings to find the right key on your keyboard for PushToTalk and talk to your friends", Main.Settings.Voice); private static readonly NativeCheckboxItem _disableVoice = new NativeCheckboxItem("Enable voice", "Check your GTA:V settings to find the right key on your keyboard for PushToTalk and talk to your friends", Main.Settings.Voice);
private static NativeItem _menuKey = new NativeItem("Menu Key", "The key to open menu", Main.Settings.MenuKey.ToString()); private static readonly NativeItem _menuKey = new NativeItem("Menu Key", "The key to open menu", Main.Settings.MenuKey.ToString());
private static NativeItem _passengerKey = new NativeItem("Passenger Key", "The key to enter a vehicle as passenger", Main.Settings.PassengerKey.ToString()); private static readonly NativeItem _passengerKey = new NativeItem("Passenger Key", "The key to enter a vehicle as passenger", Main.Settings.PassengerKey.ToString());
private static NativeItem _vehicleSoftLimit = new NativeItem("Vehicle limit (soft)", "The game won't spawn more NPC traffic if the limit is exceeded. \n-1 for unlimited (not recommended).", Main.Settings.WorldVehicleSoftLimit.ToString()); private static readonly NativeItem _vehicleSoftLimit = new NativeItem("Vehicle limit (soft)", "The game won't spawn more NPC traffic if the limit is exceeded. \n-1 for unlimited (not recommended).", Main.Settings.WorldVehicleSoftLimit.ToString());
static SettingsMenu() static SettingsMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
_disableTrafficItem.CheckboxChanged += DisableTrafficCheckboxChanged; _disableTrafficItem.CheckboxChanged += DisableTrafficCheckboxChanged;
_disablePauseAlt.CheckboxChanged+= DisablePauseAltCheckboxChanged; _disablePauseAlt.CheckboxChanged += DisablePauseAltCheckboxChanged;
_disableVoice.CheckboxChanged += DisableVoiceCheckboxChanged; _disableVoice.CheckboxChanged += DisableVoiceCheckboxChanged;
_flipMenuItem.CheckboxChanged += FlipMenuCheckboxChanged; _flipMenuItem.CheckboxChanged += FlipMenuCheckboxChanged;
_menuKey.Activated+= ChaneMenuKey; _menuKey.Activated += ChaneMenuKey;
_passengerKey.Activated+= ChangePassengerKey; _passengerKey.Activated += ChangePassengerKey;
_vehicleSoftLimit.Activated+= VehicleSoftLimitActivated; _vehicleSoftLimit.Activated += VehicleSoftLimitActivated;
_showBlip.Activated += (s, e) =>
{
Main.Settings.ShowPlayerBlip = _showBlip.Checked;
Util.SaveSettings();
};
_showNametag.Activated += (s, e) =>
{
Main.Settings.ShowPlayerNameTag = _showNametag.Checked;
Util.SaveSettings();
};
Menu.Add(_disableTrafficItem); Menu.Add(_disableTrafficItem);
Menu.Add(_disablePauseAlt); Menu.Add(_disablePauseAlt);
@ -43,6 +55,8 @@ namespace RageCoop.Client.Menus
Menu.Add(_menuKey); Menu.Add(_menuKey);
Menu.Add(_passengerKey); Menu.Add(_passengerKey);
Menu.Add(_vehicleSoftLimit); Menu.Add(_vehicleSoftLimit);
Menu.Add(_showBlip);
Menu.Add(_showNametag);
} }
private static void DisableVoiceCheckboxChanged(object sender, EventArgs e) private static void DisableVoiceCheckboxChanged(object sender, EventArgs e)
@ -53,7 +67,9 @@ namespace RageCoop.Client.Menus
{ {
Voice.Init(); Voice.Init();
} }
} else { }
else
{
Voice.ClearAll(); Voice.ClearAll();
} }
@ -63,17 +79,17 @@ namespace RageCoop.Client.Menus
private static void DisablePauseAltCheckboxChanged(object sender, EventArgs e) private static void DisablePauseAltCheckboxChanged(object sender, EventArgs e)
{ {
Main.Settings.DisableAlternatePause=_disablePauseAlt.Checked; Main.Settings.DisableAlternatePause = _disablePauseAlt.Checked;
Util.SaveSettings(); Util.SaveSettings();
} }
private static void VehicleSoftLimitActivated(object sender, EventArgs e) private static void VehicleSoftLimitActivated(object sender, EventArgs e)
{ {
try try
{ {
Main.Settings.WorldVehicleSoftLimit =int.Parse( Main.Settings.WorldVehicleSoftLimit = int.Parse(
Game.GetUserInput(WindowTitle.EnterMessage20, Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.WorldVehicleSoftLimit.ToString(), 20)); Main.Settings.WorldVehicleSoftLimit.ToString(), 20));
_menuKey.AltTitle=Main.Settings.WorldVehicleSoftLimit.ToString(); _menuKey.AltTitle = Main.Settings.WorldVehicleSoftLimit.ToString();
Util.SaveSettings(); Util.SaveSettings();
} }
catch { } catch { }
@ -82,11 +98,11 @@ namespace RageCoop.Client.Menus
{ {
try try
{ {
Main.Settings.MenuKey =(Keys)Enum.Parse( Main.Settings.MenuKey = (Keys)Enum.Parse(
typeof(Keys), typeof(Keys),
Game.GetUserInput(WindowTitle.EnterMessage20, Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.MenuKey.ToString(), 20)); Main.Settings.MenuKey.ToString(), 20));
_menuKey.AltTitle=Main.Settings.MenuKey.ToString(); _menuKey.AltTitle = Main.Settings.MenuKey.ToString();
Util.SaveSettings(); Util.SaveSettings();
} }
catch { } catch { }
@ -96,11 +112,11 @@ namespace RageCoop.Client.Menus
{ {
try try
{ {
Main.Settings.PassengerKey =(Keys)Enum.Parse( Main.Settings.PassengerKey = (Keys)Enum.Parse(
typeof(Keys), typeof(Keys),
Game.GetUserInput(WindowTitle.EnterMessage20, Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.PassengerKey.ToString(), 20)); Main.Settings.PassengerKey.ToString(), 20));
_passengerKey.AltTitle=Main.Settings.PassengerKey.ToString(); _passengerKey.AltTitle = Main.Settings.PassengerKey.ToString();
Util.SaveSettings(); Util.SaveSettings();
} }
catch { } catch { }

View File

@ -1,105 +0,0 @@
using ICSharpCode.SharpZipLib.Zip;
using LemonUI.Menus;
using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace RageCoop.Client.Menus
{
internal class UpdateMenu
{
public static bool IsUpdating { get; private set; } = false;
private static NativeItem _updatingItem = new NativeItem("Updating...");
private static NativeItem _downloadItem = new NativeItem("Download", "Download and update to latest nightly");
private static string _downloadPath = Path.Combine(Main.Settings.DataDirectory, "RageCoop.Client.zip");
public static NativeMenu Menu = new NativeMenu("Update", "Update", "Download and install latest nightly build from GitHub")
{
UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
static UpdateMenu()
{
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0);
Menu.Opening+=Opening;
_downloadItem.Activated+=StartUpdate;
}
private static void StartUpdate(object sender, EventArgs e)
{
IsUpdating=true;
Menu.Clear();
Menu.Add(_updatingItem);
Task.Run(() =>
{
try
{
if (File.Exists(_downloadPath)) { File.Delete(_downloadPath); }
WebClient client = new WebClient();
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
client.DownloadProgressChanged += (s, e1) => { Main.QueueAction(() => { _updatingItem.AltTitle=$"{e1.ProgressPercentage}%"; }); };
client.DownloadFileCompleted +=(s, e2) => { Install(); };
client.DownloadFileAsync(new Uri("https://github.com/RAGECOOP/RAGECOOP-V/releases/download/nightly/RageCoop.Client.zip"), _downloadPath);
}
catch (Exception ex)
{
Main.Logger.Error(ex);
}
});
}
private static void Install()
{
try
{
Main.QueueAction(() =>
{
_updatingItem.AltTitle="Installing...";
});
Directory.CreateDirectory(@"Scripts\RageCoop");
foreach(var f in Directory.GetFiles(@"Scripts\RageCoop", "*.dll", SearchOption.AllDirectories))
{
try { File.Delete(f); }
catch { }
}
new FastZip().ExtractZip(_downloadPath, "Scripts", FastZip.Overwrite.Always, null, null, null, true);
try { File.Delete(_downloadPath); } catch { }
try { File.Delete(Path.Combine("Scripts","RageCoop.Client.Installer.exe")); } catch { }
Main.QueueAction(() =>
{
Util.Reload();
IsUpdating=false;
});
}
catch (Exception ex)
{
Main.Logger.Error(ex);
}
}
private static void Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
Menu.Clear();
if (Networking.IsOnServer)
{
Menu.Add(new NativeItem("Disconnect from the server first"));
}
else if (IsUpdating)
{
Menu.Add(_updatingItem);
}
else
{
Menu.Add(_downloadItem);
}
}
}
}

View File

@ -16,7 +16,7 @@ namespace RageCoop.Client
private bool CurrentFocused { get; set; } private bool CurrentFocused { get; set; }
public bool Focused public bool Focused
{ {
get { return CurrentFocused; } get => CurrentFocused;
set set
{ {
if (value && Hidden) if (value && Hidden)
@ -35,7 +35,7 @@ namespace RageCoop.Client
private bool CurrentHidden { get; set; } private bool CurrentHidden { get; set; }
private bool Hidden private bool Hidden
{ {
get { return CurrentHidden; } get => CurrentHidden;
set set
{ {
if (value) if (value)

View File

@ -21,8 +21,8 @@ namespace RageCoop.Client
} }
return new Packets.FileTransferResponse() return new Packets.FileTransferResponse()
{ {
ID= fr.ID, ID = fr.ID,
Response=AddFile(fr.ID, fr.Name, fr.FileLength) ? FileResponse.NeedToDownload : FileResponse.AlreadyExists Response = AddFile(fr.ID, fr.Name, fr.FileLength) ? FileResponse.NeedToDownload : FileResponse.AlreadyExists
}; };
}); });
Networking.RequestHandlers.Add(PacketType.FileTransferComplete, (data) => Networking.RequestHandlers.Add(PacketType.FileTransferComplete, (data) =>
@ -36,8 +36,8 @@ namespace RageCoop.Client
// Inform the server that the download is completed // Inform the server that the download is completed
return new Packets.FileTransferResponse() return new Packets.FileTransferResponse()
{ {
ID= packet.ID, ID = packet.ID,
Response=FileResponse.Completed Response = FileResponse.Completed
}; };
}); });
Networking.RequestHandlers.Add(PacketType.AllResourcesSent, (data) => Networking.RequestHandlers.Add(PacketType.AllResourcesSent, (data) =>
@ -45,13 +45,13 @@ namespace RageCoop.Client
try try
{ {
Main.Resources.Load(ResourceFolder, _resources.ToArray()); Main.Resources.Load(ResourceFolder, _resources.ToArray());
return new Packets.FileTransferResponse() { ID=0, Response=FileResponse.Loaded }; return new Packets.FileTransferResponse() { ID = 0, Response = FileResponse.Loaded };
} }
catch (Exception ex) catch (Exception ex)
{ {
Main.Logger.Error("Error occurred when loading server resource:"); Main.Logger.Error("Error occurred when loading server resource:");
Main.Logger.Error(ex); Main.Logger.Error(ex);
return new Packets.FileTransferResponse() { ID=0, Response=FileResponse.LoadFailed }; return new Packets.FileTransferResponse() { ID = 0, Response = FileResponse.LoadFailed };
} }
}); });
} }
@ -122,8 +122,7 @@ namespace RageCoop.Client
{ {
lock (InProgressDownloads) lock (InProgressDownloads)
{ {
DownloadFile file; if (InProgressDownloads.TryGetValue(id, out DownloadFile file))
if (InProgressDownloads.TryGetValue(id, out file))
{ {
file.Stream.Write(chunk, 0, chunk.Length); file.Stream.Write(chunk, 0, chunk.Length);
@ -137,9 +136,8 @@ namespace RageCoop.Client
public static void Complete(int id) public static void Complete(int id)
{ {
DownloadFile f;
if (InProgressDownloads.TryGetValue(id, out f)) if (InProgressDownloads.TryGetValue(id, out DownloadFile f))
{ {
InProgressDownloads.Remove(id); InProgressDownloads.Remove(id);
f.Dispose(); f.Dispose();
@ -176,7 +174,7 @@ namespace RageCoop.Client
public FileStream Stream { get; set; } public FileStream Stream { get; set; }
public void Dispose() public void Dispose()
{ {
if (Stream!= null) if (Stream != null)
{ {
Stream.Flush(); Stream.Flush();
Stream.Close(); Stream.Close();

View File

@ -1,12 +1,10 @@
using System; using Lidgren.Network;
using RageCoop.Core;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using RageCoop.Core;
using Lidgren.Network;
using System.Net; using System.Net;
using System.Timers;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -15,9 +13,9 @@ namespace RageCoop.Client
static HolePunch() static HolePunch()
{ {
// Periodically send hole punch message as needed // Periodically send hole punch message as needed
var timer=new Timer(1000); var timer = new Timer(1000);
timer.Elapsed+=DoPunch; timer.Elapsed += DoPunch;
timer.Enabled=true; timer.Enabled = true;
} }
private static void DoPunch(object sender, ElapsedEventArgs e) private static void DoPunch(object sender, ElapsedEventArgs e)
@ -27,14 +25,14 @@ namespace RageCoop.Client
if (!Networking.IsOnServer) { return; } if (!Networking.IsOnServer) { return; }
foreach (var p in PlayerList.Players.Values.ToArray()) foreach (var p in PlayerList.Players.Values.ToArray())
{ {
if (p.InternalEndPoint!=null && p.ExternalEndPoint!=null && (p.Connection==null || p.Connection.Status==NetConnectionStatus.Disconnected)) if (p.InternalEndPoint != null && p.ExternalEndPoint != null && (p.Connection == null || p.Connection.Status == NetConnectionStatus.Disconnected))
{ {
Main.Logger.Trace($"Sending HolePunch message to {p.InternalEndPoint},{p.ExternalEndPoint}. {p.Username}:{p.PedID}"); Main.Logger.Trace($"Sending HolePunch message to {p.InternalEndPoint},{p.ExternalEndPoint}. {p.Username}:{p.ID}");
var msg = Networking.Peer.CreateMessage(); var msg = Networking.Peer.CreateMessage();
new Packets.HolePunch new Packets.HolePunch
{ {
Puncher=Main.LocalPlayerID, Puncher = Main.LocalPlayerID,
Status=p.HolePunchStatus Status = p.HolePunchStatus
}.Pack(msg); }.Pack(msg);
Networking.Peer.SendUnconnectedMessage(msg, new List<IPEndPoint> { p.InternalEndPoint, p.ExternalEndPoint }); Networking.Peer.SendUnconnectedMessage(msg, new List<IPEndPoint> { p.InternalEndPoint, p.ExternalEndPoint });
} }
@ -48,34 +46,34 @@ namespace RageCoop.Client
public static void Add(Packets.HolePunchInit p) public static void Add(Packets.HolePunchInit p)
{ {
if(PlayerList.Players.TryGetValue(p.TargetID,out var player)) if (PlayerList.Players.TryGetValue(p.TargetID, out var player))
{ {
Main.Logger.Debug($"{p.TargetID},{player.Username} added to HolePunch target"); Main.Logger.Debug($"{p.TargetID},{player.Username} added to HolePunch target");
player.InternalEndPoint = CoreUtils.StringToEndPoint(p.TargetInternal); player.InternalEndPoint = CoreUtils.StringToEndPoint(p.TargetInternal);
player.ExternalEndPoint = CoreUtils.StringToEndPoint(p.TargetExternal); player.ExternalEndPoint = CoreUtils.StringToEndPoint(p.TargetExternal);
player.ConnectWhenPunched=p.Connect; player.ConnectWhenPunched = p.Connect;
} }
else else
{ {
Main.Logger.Warning("No player with specified TargetID found for hole punching:"+p.TargetID); Main.Logger.Warning("No player with specified TargetID found for hole punching:" + p.TargetID);
} }
} }
public static void Punched(Packets.HolePunch p,IPEndPoint from) public static void Punched(Packets.HolePunch p, IPEndPoint from)
{ {
Main.Logger.Debug($"HolePunch message received from:{from}, status:{p.Status}"); Main.Logger.Debug($"HolePunch message received from:{from}, status:{p.Status}");
if(PlayerList.Players.TryGetValue(p.Puncher,out var puncher)) if (PlayerList.Players.TryGetValue(p.Puncher, out var puncher))
{ {
Main.Logger.Debug("Puncher identified as: "+puncher.Username); Main.Logger.Debug("Puncher identified as: " + puncher.Username);
puncher.HolePunchStatus=(byte)(p.Status+1); puncher.HolePunchStatus = (byte)(p.Status + 1);
if (p.Status>=3) if (p.Status >= 3)
{ {
Main.Logger.Debug("HolePunch sucess: "+from+", "+puncher.PedID); Main.Logger.Debug("HolePunch sucess: " + from + ", " + puncher.ID);
if (puncher.ConnectWhenPunched && (puncher.Connection==null || puncher.Connection.Status==NetConnectionStatus.Disconnected)) if (puncher.ConnectWhenPunched && (puncher.Connection == null || puncher.Connection.Status == NetConnectionStatus.Disconnected))
{ {
Main.Logger.Debug("Connecting to peer: "+from); Main.Logger.Debug("Connecting to peer: " + from);
var msg = Networking.Peer.CreateMessage(); var msg = Networking.Peer.CreateMessage();
new Packets.P2PConnect { ID=Main.LocalPlayerID }.Pack(msg); new Packets.P2PConnect { ID = Main.LocalPlayerID }.Pack(msg);
puncher.Connection=Networking.Peer.Connect(from,msg); puncher.Connection = Networking.Peer.Connect(from, msg);
Networking.Peer.FlushSendQueue(); Networking.Peer.FlushSendQueue();
} }
} }

View File

@ -1,44 +1,45 @@
using Lidgren.Network; using GTA.UI;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using GTA.UI;
using System.Net;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal static partial class Networking internal static partial class Networking
{ {
public static CoopPeer Peer; public static CoopPeer Peer;
public static float Latency => ServerConnection.AverageRoundtripTime/2; public static float Latency => ServerConnection.AverageRoundtripTime / 2;
public static bool ShowNetworkInfo = false; public static bool ShowNetworkInfo = false;
public static Security Security; public static Security Security;
public static NetConnection ServerConnection; public static NetConnection ServerConnection;
private static readonly Dictionary<int, Action<PacketType, byte[]>> PendingResponses = new Dictionary<int, Action<PacketType, byte[]>>(); private static readonly Dictionary<int, Action<PacketType, NetIncomingMessage>> PendingResponses = new Dictionary<int, Action<PacketType, NetIncomingMessage>>();
internal static readonly Dictionary<PacketType, Func<byte[], Packet>> RequestHandlers = new Dictionary<PacketType, Func<byte[], Packet>>(); internal static readonly Dictionary<PacketType, Func<NetIncomingMessage, Packet>> RequestHandlers = new Dictionary<PacketType, Func<NetIncomingMessage, Packet>>();
internal static float SimulatedLatency=0; internal static float SimulatedLatency = 0;
public static bool IsConnecting { get; private set; } public static bool IsConnecting { get; private set; }
public static IPEndPoint _targetServerEP; public static IPEndPoint _targetServerEP;
static Networking() static Networking()
{ {
Security=new Security(Main.Logger); Security = new Security(Main.Logger);
} }
public static void ToggleConnection(string address, string username = null, string password = null,PublicKey publicKey=null) public static void ToggleConnection(string address, string username = null, string password = null, PublicKey publicKey = null)
{ {
Menus.CoopMenu.Menu.Visible=false; Menus.CoopMenu.Menu.Visible = false;
Peer?.Shutdown("Bye"); if (IsConnecting)
if (IsOnServer)
{ {
// ?
}
else if (IsConnecting) {
_publicKeyReceived.Set(); _publicKeyReceived.Set();
IsConnecting = false; IsConnecting = false;
Notification.Show("Connection has been canceled"); Main.QueueAction(() => Notification.Show("Connection has been canceled"));
Peer?.Shutdown("Bye");
}
else if (IsOnServer)
{
Peer?.Shutdown("Bye");
} }
else else
{ {
@ -46,19 +47,20 @@ namespace RageCoop.Client
IsConnecting = true; IsConnecting = true;
password = password ?? Main.Settings.Password; password = password ?? Main.Settings.Password;
username=username ?? Main.Settings.Username; username = username ?? Main.Settings.Username;
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP // 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0") NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0")
{ {
AutoFlushSendQueue = false, AutoFlushSendQueue = false,
SimulatedMinimumLatency =SimulatedLatency,
SimulatedRandomLatency=0,
AcceptIncomingConnections = true, AcceptIncomingConnections = true,
MaximumConnections = 32, MaximumConnections = 32,
PingInterval = 5 PingInterval = 5
}; };
#if DEBUG
config.SimulatedMinimumLatency = SimulatedLatency;
config.SimulatedRandomLatency = 0;
#endif
config.EnableMessageType(NetIncomingMessageType.UnconnectedData); config.EnableMessageType(NetIncomingMessageType.UnconnectedData);
config.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess); config.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess);
@ -78,45 +80,54 @@ namespace RageCoop.Client
PlayerList.Cleanup(); PlayerList.Cleanup();
EntityPool.AddPlayer(); EntityPool.AddPlayer();
if (publicKey==null && !string.IsNullOrEmpty(password) && !Menus.CoopMenu.ShowPopUp("", "WARNING", "Server's IP can be spoofed when using direct connection, do you wish to continue?", "", true)) if (publicKey == null && !string.IsNullOrEmpty(password) && !Menus.CoopMenu.ShowPopUp("", "WARNING", "Server's IP can be spoofed when using direct connection, do you wish to continue?", "", true))
{ {
IsConnecting=false; IsConnecting = false;
return; return;
} }
Task.Run(() => Task.Run(() =>
{ {
try try
{ {
_targetServerEP=CoreUtils.StringToEndPoint(address); _targetServerEP = CoreUtils.StringToEndPoint(address);
// Ensure static constructor invocation
DownloadManager.Cleanup(); DownloadManager.Cleanup();
Peer = new CoopPeer(config); Peer = new CoopPeer(config);
Peer.OnMessageReceived+= (s, m) => Peer.OnMessageReceived += (s, m) =>
{ {
try { ProcessMessage(m); } try { ProcessMessage(m); }
catch (Exception ex) { Main.Logger.Error(ex); } catch (Exception ex)
{
#if DEBUG
Main.Logger.Error(ex);
#endif
}
}; };
Main.QueueAction(() => { Notification.Show($"~y~Trying to connect..."); }); Main.QueueAction(() => { Notification.Show($"~y~Trying to connect..."); });
Menus.CoopMenu._serverConnectItem.Enabled=false; Menus.CoopMenu._serverConnectItem.Enabled = false;
Security.Regen(); Security.Regen();
if(publicKey==null){ if (publicKey == null)
if (!GetServerPublicKey(ip[0],int.Parse(ip[1]))) {
if (!GetServerPublicKey(ip[0], int.Parse(ip[1])))
{ {
Menus.CoopMenu._serverConnectItem.Enabled=true; Menus.CoopMenu._serverConnectItem.Enabled = true;
throw new TimeoutException("Failed to retrive server's public key"); throw new TimeoutException("Failed to retrive server's public key");
} }
} }
else{ else
Security.SetServerPublicKey(publicKey.Modulus,publicKey.Exponent); {
Security.SetServerPublicKey(publicKey.Modulus, publicKey.Exponent);
} }
// Send handshake packet // Send handshake packet
NetOutgoingMessage outgoingMessage = Peer.CreateMessage(); NetOutgoingMessage outgoingMessage = Peer.CreateMessage();
var handshake = new Packets.Handshake() var handshake = new Packets.Handshake()
{ {
PedID = Main.LocalPlayerID, PedID = Main.LocalPlayerID,
Username =username, Username = username,
ModVersion = Main.Version.ToString(), ModVersion = Main.Version.ToString(),
PasswordEncrypted=Security.Encrypt(password.GetBytes()), PasswordEncrypted = Security.Encrypt(password.GetBytes()),
InternalEndPoint = new System.Net.IPEndPoint(CoreUtils.GetLocalAddress(ip[0]), Peer.Port) InternalEndPoint = new System.Net.IPEndPoint(CoreUtils.GetLocalAddress(ip[0]), Peer.Port)
}; };
@ -128,9 +139,9 @@ namespace RageCoop.Client
catch (Exception ex) catch (Exception ex)
{ {
Main.Logger.Error("Cannot connect to server: ", ex); Main.Logger.Error("Cannot connect to server: ", ex);
Main.QueueAction(() => Notification.Show("Cannot connect to server: "+ex.Message)); Main.QueueAction(() => Notification.Show("Cannot connect to server: " + ex.Message));
} }
IsConnecting=false; IsConnecting = false;
}); });
} }
} }
@ -141,8 +152,8 @@ namespace RageCoop.Client
{ {
var p = new Player var p = new Player
{ {
PedID = packet.PedID, ID = packet.PedID,
Username= packet.Username, Username = packet.Username,
}; };
PlayerList.SetPlayer(packet.PedID, packet.Username); PlayerList.SetPlayer(packet.PedID, packet.Username);
@ -153,9 +164,10 @@ namespace RageCoop.Client
private static void PlayerDisconnect(Packets.PlayerDisconnect packet) private static void PlayerDisconnect(Packets.PlayerDisconnect packet)
{ {
var player = PlayerList.GetPlayer(packet.PedID); var player = PlayerList.GetPlayer(packet.PedID);
if (player==null) { return; } if (player == null) { return; }
PlayerList.RemovePlayer(packet.PedID); PlayerList.RemovePlayer(packet.PedID);
Main.QueueAction(() => { Main.QueueAction(() =>
{
EntityPool.RemoveAllFromPlayer(packet.PedID); EntityPool.RemoveAllFromPlayer(packet.PedID);
GTA.UI.Notification.Show($"~h~{player.Username}~h~ left."); GTA.UI.Notification.Show($"~h~{player.Username}~h~ left.");
}); });
@ -163,13 +175,13 @@ namespace RageCoop.Client
#endregion // -- PLAYER -- #endregion // -- PLAYER --
#region -- GET -- #region -- GET --
private static bool GetServerPublicKey(string host,int port, int timeout = 10000) private static bool GetServerPublicKey(string host, int port, int timeout = 10000)
{ {
Security.ServerRSA=null; Security.ServerRSA = null;
var msg = Peer.CreateMessage(); var msg = Peer.CreateMessage();
new Packets.PublicKeyRequest().Pack(msg); new Packets.PublicKeyRequest().Pack(msg);
Peer.SendUnconnectedMessage(msg, host, port); Peer.SendUnconnectedMessage(msg, host, port);
return _publicKeyReceived.WaitOne(timeout) && Security.ServerRSA!=null; return _publicKeyReceived.WaitOne(timeout) && Security.ServerRSA != null;
} }
public static void GetResponse<T>(Packet request, Action<T> callback, ConnectionChannel channel = ConnectionChannel.RequestResponse) where T : Packet, new() public static void GetResponse<T>(Packet request, Action<T> callback, ConnectionChannel channel = ConnectionChannel.RequestResponse) where T : Packet, new()
@ -186,14 +198,14 @@ namespace RageCoop.Client
msg.Write((byte)PacketType.Request); msg.Write((byte)PacketType.Request);
msg.Write(id); msg.Write(id);
request.Pack(msg); request.Pack(msg);
Peer.SendMessage(msg,ServerConnection, NetDeliveryMethod.ReliableOrdered, (int)channel); Peer.SendMessage(msg, ServerConnection, NetDeliveryMethod.ReliableOrdered, (int)channel);
} }
#endregion #endregion
private static int NewRequestID() private static int NewRequestID()
{ {
int ID = 0; int ID = 0;
while ((ID==0) || PendingResponses.ContainsKey(ID)) while ((ID == 0) || PendingResponses.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];

View File

@ -3,9 +3,7 @@ using Lidgren.Network;
using RageCoop.Client.Menus; using RageCoop.Client.Menus;
using RageCoop.Core; using RageCoop.Core;
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using GTA.Native;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -15,7 +13,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Reduce GC pressure by reusing frequently used packets /// Reduce GC pressure by reusing frequently used packets
/// </summary> /// </summary>
static class ReceivedPackets private static class ReceivedPackets
{ {
public static Packets.PedSync PedPacket = new Packets.PedSync(); public static Packets.PedSync PedPacket = new Packets.PedSync();
public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync(); public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync();
@ -25,7 +23,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Used to reslove entity handle in a <see cref="Packets.CustomEvent"/> /// Used to reslove entity handle in a <see cref="Packets.CustomEvent"/>
/// </summary> /// </summary>
private static readonly Func<byte, BitReader, object> _resolveHandle = (t, reader) => private static readonly Func<byte, NetIncomingMessage, object> _resolveHandle = (t, reader) =>
{ {
switch (t) switch (t)
{ {
@ -38,14 +36,15 @@ namespace RageCoop.Client
case 60: case 60:
return EntityPool.ServerBlips[reader.ReadInt32()].Handle; return EntityPool.ServerBlips[reader.ReadInt32()].Handle;
default: default:
throw new ArgumentException("Cannot resolve server side argument: "+t); throw new ArgumentException("Cannot resolve server side argument: " + t);
} }
}; };
private static readonly AutoResetEvent _publicKeyReceived = new AutoResetEvent(false); private static readonly AutoResetEvent _publicKeyReceived = new AutoResetEvent(false);
private static bool _recycle;
public static void ProcessMessage(NetIncomingMessage message) public static void ProcessMessage(NetIncomingMessage message)
{ {
if (message == null) { return; } if (message == null) { return; }
_recycle = true;
switch (message.MessageType) switch (message.MessageType)
{ {
case NetIncomingMessageType.StatusChanged: case NetIncomingMessageType.StatusChanged:
@ -54,50 +53,36 @@ namespace RageCoop.Client
switch (status) switch (status)
{ {
case NetConnectionStatus.InitiatedConnect: case NetConnectionStatus.InitiatedConnect:
if (message.SenderConnection==ServerConnection) if (message.SenderConnection == ServerConnection)
{ {
CoopMenu.InitiateConnectionMenuSetting(); CoopMenu.InitiateConnectionMenuSetting();
} }
break; break;
case NetConnectionStatus.Connected: case NetConnectionStatus.Connected:
if (message.SenderConnection==ServerConnection) if (message.SenderConnection == ServerConnection)
{ {
Memory.ApplyPatches();
var response = message.SenderConnection.RemoteHailMessage; var response = message.SenderConnection.RemoteHailMessage;
if ((PacketType)response.ReadByte()!=PacketType.HandshakeSuccess) if ((PacketType)response.ReadByte() != PacketType.HandshakeSuccess)
{ {
throw new Exception("Invalid handshake response!"); throw new Exception("Invalid handshake response!");
} }
var p = new Packets.HandshakeSuccess(); var p = new Packets.HandshakeSuccess();
p.Deserialize(response.ReadBytes(response.ReadInt32())); p.Deserialize(response);
foreach(var player in p.Players) foreach (var player in p.Players)
{ {
PlayerList.SetPlayer(player.ID,player.Username); PlayerList.SetPlayer(player.ID, player.Username);
} }
Main.QueueAction(() => Main.Connected();
{
WorldThread.Traffic(!Main.Settings.DisableTraffic);
Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, true);
CoopMenu.ConnectedMenuSetting();
Main.MainChat.Init();
if (Main.Settings.Voice && !Voice.WasInitialized())
{
Voice.Init();
}
GTA.UI.Notification.Show("~g~Connected!");
});
Main.Logger.Info(">> Connected <<");
} }
else else
{ {
// Self-initiated connection // Self-initiated connection
if (message.SenderConnection.RemoteHailMessage==null) { return; } if (message.SenderConnection.RemoteHailMessage == null) { return; }
var p = message.SenderConnection.RemoteHailMessage.GetPacket<Packets.P2PConnect>(); var p = message.SenderConnection.RemoteHailMessage.GetPacket<Packets.P2PConnect>();
if (PlayerList.Players.TryGetValue(p.ID,out var player)) if (PlayerList.Players.TryGetValue(p.ID, out var player))
{ {
player.Connection=message.SenderConnection; player.Connection = message.SenderConnection;
Main.Logger.Debug($"Direct connection to {player.Username} established"); Main.Logger.Debug($"Direct connection to {player.Username} established");
} }
else else
@ -108,28 +93,16 @@ namespace RageCoop.Client
} }
break; break;
case NetConnectionStatus.Disconnected: case NetConnectionStatus.Disconnected:
if (message.SenderConnection==ServerConnection) if (message.SenderConnection == ServerConnection)
{ {
Memory.RestorePatches(); Main.Disconnected(reason);
DownloadManager.Cleanup();
if (Main.MainChat.Focused)
{
Main.MainChat.Focused = false;
}
Main.QueueAction(() => Main.CleanUp());
CoopMenu.DisconnectedMenuSetting();
Main.Logger.Info($">> Disconnected << reason: {reason}");
Main.QueueAction(() => GTA.UI.Notification.Show("~r~Disconnected: " + reason));
Main.Resources.Unload();
} }
break; break;
} }
break; break;
case NetIncomingMessageType.Data: case NetIncomingMessageType.Data:
{ {
if (message.LengthBytes==0) { break; } if (message.LengthBytes == 0) { break; }
var packetType = PacketType.Unknown; var packetType = PacketType.Unknown;
try try
{ {
@ -142,7 +115,7 @@ namespace RageCoop.Client
int id = message.ReadInt32(); int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback)) if (PendingResponses.TryGetValue(id, out var callback))
{ {
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32())); callback((PacketType)message.ReadByte(), message);
PendingResponses.Remove(id); PendingResponses.Remove(id);
} }
break; break;
@ -151,57 +124,60 @@ namespace RageCoop.Client
{ {
int id = message.ReadInt32(); int id = message.ReadInt32();
var realType = (PacketType)message.ReadByte(); var realType = (PacketType)message.ReadByte();
int len = message.ReadInt32();
if (RequestHandlers.TryGetValue(realType, out var handler)) if (RequestHandlers.TryGetValue(realType, out var handler))
{ {
var response = Peer.CreateMessage(); var response = Peer.CreateMessage();
response.Write((byte)PacketType.Response); response.Write((byte)PacketType.Response);
response.Write(id); response.Write(id);
handler(message.ReadBytes(len)).Pack(response); handler(message).Pack(response);
Peer.SendMessage(response,ServerConnection, NetDeliveryMethod.ReliableOrdered, message.SequenceChannel); Peer.SendMessage(response, ServerConnection, NetDeliveryMethod.ReliableOrdered, message.SequenceChannel);
Peer.FlushSendQueue(); Peer.FlushSendQueue();
} }
else
{
Main.Logger.Debug("Did not find a request handler of type: " + realType);
}
break; break;
} }
default: default:
{ {
byte[] data = message.ReadBytes(message.ReadInt32());
HandlePacket(packetType, data,message.SenderConnection); HandlePacket(packetType, message, message.SenderConnection, ref _recycle);
break; break;
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
#if DEBUG
Main.QueueAction(() => Main.QueueAction(() =>
{ {
GTA.UI.Notification.Show("~r~~h~Packet Error"); GTA.UI.Notification.Show($"~r~~h~Packet Error {ex.Message}");
return true; return true;
}); });
Main.Logger.Error($"[{packetType}] {ex.Message}"); Main.Logger.Error($"[{packetType}] {ex.Message}");
Main.Logger.Error(ex); Main.Logger.Error(ex);
Peer.Shutdown($"Packet Error [{packetType}]"); Peer.Shutdown($"Packet Error [{packetType}]");
#endif
_recycle = false;
} }
break; break;
} }
case NetIncomingMessageType.UnconnectedData: case NetIncomingMessageType.UnconnectedData:
{ {
var packetType = (PacketType)message.ReadByte(); var packetType = (PacketType)message.ReadByte();
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
switch (packetType) switch (packetType)
{ {
case PacketType.HolePunch: case PacketType.HolePunch:
{ {
HolePunch.Punched(data.GetPacket<Packets.HolePunch>(), message.SenderEndPoint); HolePunch.Punched(message.GetPacket<Packets.HolePunch>(), message.SenderEndPoint);
break; break;
} }
case PacketType.PublicKeyResponse: case PacketType.PublicKeyResponse:
{ {
if(message.SenderEndPoint.ToString()!=_targetServerEP.ToString() ||!IsConnecting){break;} if (message.SenderEndPoint.ToString() != _targetServerEP.ToString() || !IsConnecting) { break; }
var packet = data.GetPacket<Packets.PublicKeyResponse>(); var packet = message.GetPacket<Packets.PublicKeyResponse>();
Security.SetServerPublicKey(packet.Modulus, packet.Exponent); Security.SetServerPublicKey(packet.Modulus, packet.Exponent);
_publicKeyReceived.Set(); _publicKeyReceived.Set();
break; break;
@ -218,41 +194,42 @@ namespace RageCoop.Client
default: default:
break; break;
} }
if (_recycle)
Peer.Recycle(message); {
Peer.Recycle(message);
}
} }
private static void HandlePacket(PacketType packetType, byte[] data, NetConnection senderConnection) private static void HandlePacket(PacketType packetType, NetIncomingMessage msg, NetConnection senderConnection, ref bool recycle)
{ {
switch (packetType) switch (packetType)
{ {
case PacketType.HolePunchInit: case PacketType.HolePunchInit:
HolePunch.Add(data.GetPacket<Packets.HolePunchInit>()); HolePunch.Add(msg.GetPacket<Packets.HolePunchInit>());
break; break;
case PacketType.PlayerConnect: case PacketType.PlayerConnect:
PlayerConnect(data.GetPacket<Packets.PlayerConnect>()); PlayerConnect(msg.GetPacket<Packets.PlayerConnect>());
break; break;
case PacketType.PlayerDisconnect: case PacketType.PlayerDisconnect:
PlayerDisconnect(data.GetPacket<Packets.PlayerDisconnect>()); PlayerDisconnect(msg.GetPacket<Packets.PlayerDisconnect>());
break; break;
case PacketType.PlayerInfoUpdate: case PacketType.PlayerInfoUpdate:
PlayerList.UpdatePlayer(data.GetPacket<Packets.PlayerInfoUpdate>()); PlayerList.UpdatePlayer(msg.GetPacket<Packets.PlayerInfoUpdate>());
break; break;
case PacketType.VehicleSync: case PacketType.VehicleSync:
ReceivedPackets.VehicelPacket.Deserialize(data); ReceivedPackets.VehicelPacket.Deserialize(msg);
VehicleSync(ReceivedPackets.VehicelPacket); VehicleSync(ReceivedPackets.VehicelPacket);
break; break;
case PacketType.PedSync: case PacketType.PedSync:
ReceivedPackets.PedPacket.Deserialize(data); ReceivedPackets.PedPacket.Deserialize(msg);
PedSync(ReceivedPackets.PedPacket); PedSync(ReceivedPackets.PedPacket);
break; break;
case PacketType.ProjectileSync: case PacketType.ProjectileSync:
ReceivedPackets.ProjectilePacket.Deserialize(data); ReceivedPackets.ProjectilePacket.Deserialize(msg);
ProjectileSync(ReceivedPackets.ProjectilePacket); ProjectileSync(ReceivedPackets.ProjectilePacket);
break; break;
@ -260,7 +237,7 @@ namespace RageCoop.Client
{ {
Packets.ChatMessage packet = new Packets.ChatMessage((b) => Security.Decrypt(b)); Packets.ChatMessage packet = new Packets.ChatMessage((b) => Security.Decrypt(b));
packet.Deserialize(data); packet.Deserialize(msg);
Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; }); Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; });
} }
@ -271,7 +248,7 @@ namespace RageCoop.Client
if (Main.Settings.Voice) if (Main.Settings.Voice)
{ {
Packets.Voice packet = new Packets.Voice(); Packets.Voice packet = new Packets.Voice();
packet.Deserialize(data); packet.Deserialize(msg);
SyncedPed player = EntityPool.GetPedByID(packet.ID); SyncedPed player = EntityPool.GetPedByID(packet.ID);
@ -286,18 +263,20 @@ namespace RageCoop.Client
case PacketType.CustomEvent: case PacketType.CustomEvent:
{ {
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle); Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
packet.Deserialize(data); packet.Deserialize(msg);
Scripting.API.Events.InvokeCustomEventReceived(packet); Scripting.API.Events.InvokeCustomEventReceived(packet);
} }
break; break;
case PacketType.CustomEventQueued: case PacketType.CustomEventQueued:
{ {
recycle = false;
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle); Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
Main.QueueAction(() => Main.QueueAction(() =>
{ {
packet.Deserialize(data); packet.Deserialize(msg);
Scripting.API.Events.InvokeCustomEventReceived(packet); Scripting.API.Events.InvokeCustomEventReceived(packet);
Peer.Recycle(msg);
}); });
} }
break; break;
@ -305,7 +284,7 @@ namespace RageCoop.Client
case PacketType.FileTransferChunk: case PacketType.FileTransferChunk:
{ {
Packets.FileTransferChunk packet = new Packets.FileTransferChunk(); Packets.FileTransferChunk packet = new Packets.FileTransferChunk();
packet.Deserialize(data); packet.Deserialize(msg);
DownloadManager.Write(packet.ID, packet.FileChunk); DownloadManager.Write(packet.ID, packet.FileChunk);
} }
break; break;
@ -313,8 +292,9 @@ namespace RageCoop.Client
default: default:
if (packetType.IsSyncEvent()) if (packetType.IsSyncEvent())
{ {
recycle = false;
// Dispatch to script thread // Dispatch to script thread
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, data); return true; }); Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, msg); return true; });
} }
break; break;
} }
@ -323,34 +303,36 @@ namespace RageCoop.Client
private static void PedSync(Packets.PedSync packet) private static void PedSync(Packets.PedSync packet)
{ {
SyncedPed c = EntityPool.GetPedByID(packet.ID); SyncedPed c = EntityPool.GetPedByID(packet.ID);
if (c==null) if (c == null)
{ {
// Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}"); // Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}");
EntityPool.ThreadSafe.Add(c=new SyncedPed(packet.ID)); if (EntityPool.allPeds.Length < Main.Settings.GlobalPedSoftLimit || PlayerList.Players.ContainsKey(packet.ID))
EntityPool.ThreadSafe.Add(c = new SyncedPed(packet.ID));
else return;
} }
PedDataFlags flags = packet.Flags; PedDataFlags flags = packet.Flags;
c.ID=packet.ID; c.ID = packet.ID;
c.OwnerID=packet.OwnerID; c.OwnerID = packet.OwnerID;
c.Health = packet.Health; c.Health = packet.Health;
c.Rotation = packet.Rotation; c.Rotation = packet.Rotation;
c.Velocity = packet.Velocity; c.Velocity = packet.Velocity;
c.Speed = packet.Speed; c.Speed = packet.Speed;
c.Flags=packet.Flags; c.Flags = packet.Flags;
c.Heading=packet.Heading; c.Heading = packet.Heading;
c.Position = packet.Position; c.Position = packet.Position;
c.LastSyncedStopWatch.Restart(); c.LastSyncedStopWatch.Restart();
if (c.IsRagdoll) if (c.IsRagdoll)
{ {
c.HeadPosition=packet.HeadPosition; c.HeadPosition = packet.HeadPosition;
c.RightFootPosition=packet.RightFootPosition; c.RightFootPosition = packet.RightFootPosition;
c.LeftFootPosition=packet.LeftFootPosition; c.LeftFootPosition = packet.LeftFootPosition;
} }
else if (c.Speed>=4) else if (c.Speed >= 4)
{ {
c.VehicleID=packet.VehicleID; c.VehicleID = packet.VehicleID;
c.Seat=packet.Seat; c.Seat = packet.Seat;
} }
c.LastSynced = Main.Ticked; c.LastSynced = Main.Ticked;
if (c.IsAiming) if (c.IsAiming)
{ {
c.AimCoords = packet.AimCoords; c.AimCoords = packet.AimCoords;
@ -358,13 +340,13 @@ namespace RageCoop.Client
if (packet.Flags.HasPedFlag(PedDataFlags.IsFullSync)) if (packet.Flags.HasPedFlag(PedDataFlags.IsFullSync))
{ {
c.CurrentWeaponHash = packet.CurrentWeaponHash; c.CurrentWeaponHash = packet.CurrentWeaponHash;
c.Clothes=packet.Clothes; c.Clothes = packet.Clothes;
c.WeaponComponents=packet.WeaponComponents; c.WeaponComponents = packet.WeaponComponents;
c.WeaponTint=packet.WeaponTint; c.WeaponTint = packet.WeaponTint;
c.Model=packet.ModelHash; c.Model = packet.ModelHash;
c.BlipColor=packet.BlipColor; c.BlipColor = packet.BlipColor;
c.BlipSprite=packet.BlipSprite; c.BlipSprite = packet.BlipSprite;
c.BlipScale=packet.BlipScale; c.BlipScale = packet.BlipScale;
c.LastFullSynced = Main.Ticked; c.LastFullSynced = Main.Ticked;
} }
@ -372,58 +354,63 @@ namespace RageCoop.Client
private static void VehicleSync(Packets.VehicleSync packet) private static void VehicleSync(Packets.VehicleSync packet)
{ {
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID); SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
if (v==null) if (v == null)
{ {
EntityPool.ThreadSafe.Add(v=new SyncedVehicle(packet.ID)); if (EntityPool.allVehicles.Length < Main.Settings.GlobalVehicleSoftLimit)
EntityPool.ThreadSafe.Add(v = new SyncedVehicle(packet.ID));
else return;
} }
if (v.IsLocal) { return; } if (v.IsLocal) { return; }
v.ID= packet.ID; v.ID = packet.ID;
v.OwnerID= packet.OwnerID; v.OwnerID = packet.OwnerID;
v.Flags=packet.Flags; v.Flags = packet.Flags;
v.Position=packet.Position; v.Position = packet.Position;
v.Quaternion=packet.Quaternion; v.Quaternion = packet.Quaternion;
v.SteeringAngle=packet.SteeringAngle; v.SteeringAngle = packet.SteeringAngle;
v.ThrottlePower=packet.ThrottlePower; v.ThrottlePower = packet.ThrottlePower;
v.BrakePower=packet.BrakePower; v.BrakePower = packet.BrakePower;
v.Velocity=packet.Velocity; v.Velocity = packet.Velocity;
v.RotationVelocity=packet.RotationVelocity; v.RotationVelocity = packet.RotationVelocity;
v.DeluxoWingRatio=packet.DeluxoWingRatio; v.DeluxoWingRatio = packet.DeluxoWingRatio;
v.LastSynced=Main.Ticked; v.LastSynced = Main.Ticked;
v.LastSyncedStopWatch.Restart(); v.LastSyncedStopWatch.Restart();
if (packet.Flags.HasVehFlag(VehicleDataFlags.IsFullSync)) if (packet.Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{ {
v.DamageModel=packet.DamageModel; v.DamageModel = packet.DamageModel;
v.EngineHealth=packet.EngineHealth; v.EngineHealth = packet.EngineHealth;
v.Mods=packet.Mods; v.Mods = packet.Mods;
v.Model=packet.ModelHash; v.Model = packet.ModelHash;
v.Colors=packet.Colors; v.Colors = packet.Colors;
v.LandingGear=packet.LandingGear; v.LandingGear = packet.LandingGear;
v.RoofState=(VehicleRoofState)packet.RoofState; v.RoofState = (VehicleRoofState)packet.RoofState;
v.LockStatus=packet.LockStatus; v.LockStatus = packet.LockStatus;
v.RadioStation=packet.RadioStation; v.RadioStation = packet.RadioStation;
v.LicensePlate=packet.LicensePlate; v.LicensePlate = packet.LicensePlate;
v.Livery=packet.Livery; v.Livery = packet.Livery;
v.LastFullSynced= Main.Ticked; v.LastFullSynced = Main.Ticked;
} }
} }
private static void ProjectileSync(Packets.ProjectileSync packet) private static void ProjectileSync(Packets.ProjectileSync packet)
{ {
var p = EntityPool.GetProjectileByID(packet.ID); var p = EntityPool.GetProjectileByID(packet.ID);
if (p==null) if (p == null)
{ {
if (packet.Flags.HasProjDataFlag(ProjectileDataFlags.Exploded)) { return; } if (packet.Flags.HasProjDataFlag(ProjectileDataFlags.Exploded)) { return; }
// Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}"); // Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}");
EntityPool.ThreadSafe.Add(p=new SyncedProjectile(packet.ID)); if (EntityPool.allProjectiles.Length < Main.Settings.GlobalProjectileSoftLimit)
EntityPool.ThreadSafe.Add(p = new SyncedProjectile(packet.ID));
else return;
} }
p.Flags=packet.Flags; p.Flags = packet.Flags;
p.Position=packet.Position; p.Position = packet.Position;
p.Rotation=packet.Rotation; p.Rotation = packet.Rotation;
p.Velocity=packet.Velocity; p.Velocity = packet.Velocity;
p.WeaponHash=(WeaponHash)packet.WeaponHash; p.WeaponHash = (WeaponHash)packet.WeaponHash;
p.Shooter= packet.Flags.HasProjDataFlag(ProjectileDataFlags.IsShotByVehicle) ? p.Shooter = packet.Flags.HasProjDataFlag(ProjectileDataFlags.IsShotByVehicle) ?
(SyncedEntity)EntityPool.GetVehicleByID(packet.ShooterID) : EntityPool.GetPedByID(packet.ShooterID); (SyncedEntity)EntityPool.GetVehicleByID(packet.ShooterID) : EntityPool.GetPedByID(packet.ShooterID);
p.LastSynced=Main.Ticked; p.LastSynced = Main.Ticked;
p.LastSyncedStopWatch.Restart();
} }
} }
} }

View File

@ -13,7 +13,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Reduce GC pressure by reusing frequently used packets /// Reduce GC pressure by reusing frequently used packets
/// </summary> /// </summary>
static class SendPackets private static class SendPackets
{ {
public static Packets.PedSync PedPacket = new Packets.PedSync(); public static Packets.PedSync PedPacket = new Packets.PedSync();
public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync(); public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync();
@ -28,50 +28,50 @@ namespace RageCoop.Client
public static void SendPed(SyncedPed sp, bool full) public static void SendPed(SyncedPed sp, bool full)
{ {
if (sp.LastSentStopWatch.ElapsedMilliseconds<SyncInterval) if (sp.LastSentStopWatch.ElapsedMilliseconds < SyncInterval)
{ {
return; return;
} }
Ped ped = sp.MainPed; Ped ped = sp.MainPed;
var p = SendPackets.PedPacket; var p = SendPackets.PedPacket;
p.ID =sp.ID; p.ID = sp.ID;
p.OwnerID=sp.OwnerID; p.OwnerID = sp.OwnerID;
p.Health = ped.Health; p.Health = ped.Health;
p.Rotation = ped.ReadRotation(); p.Rotation = ped.ReadRotation();
p.Velocity = ped.ReadVelocity(); p.Velocity = ped.ReadVelocity();
p.Speed = ped.GetPedSpeed(); p.Speed = ped.GetPedSpeed();
p.Flags = ped.GetPedFlags(); p.Flags = ped.GetPedFlags();
p.Heading=ped.Heading; p.Heading = ped.Heading;
if (p.Flags.HasPedFlag(PedDataFlags.IsAiming)) if (p.Flags.HasPedFlag(PedDataFlags.IsAiming))
{ {
p.AimCoords = ped.GetAimCoord(); p.AimCoords = ped.GetAimCoord();
} }
if (p.Flags.HasPedFlag(PedDataFlags.IsRagdoll)) if (p.Flags.HasPedFlag(PedDataFlags.IsRagdoll))
{ {
p.HeadPosition=ped.Bones[Bone.SkelHead].Position; p.HeadPosition = ped.Bones[Bone.SkelHead].Position;
p.RightFootPosition=ped.Bones[Bone.SkelRightFoot].Position; p.RightFootPosition = ped.Bones[Bone.SkelRightFoot].Position;
p.LeftFootPosition=ped.Bones[Bone.SkelLeftFoot].Position; p.LeftFootPosition = ped.Bones[Bone.SkelLeftFoot].Position;
} }
else else
{ {
// Seat sync // Seat sync
if (p.Speed>=4) if (p.Speed >= 4)
{ {
var veh = ped.CurrentVehicle?.GetSyncEntity() ?? ped.VehicleTryingToEnter?.GetSyncEntity() ?? ped.LastVehicle?.GetSyncEntity(); var veh = ped.CurrentVehicle?.GetSyncEntity() ?? ped.VehicleTryingToEnter?.GetSyncEntity() ?? ped.LastVehicle?.GetSyncEntity();
p.VehicleID = veh?.ID ?? 0; p.VehicleID = veh?.ID ?? 0;
if (p.VehicleID==0) { Main.Logger.Error("Invalid vehicle"); } if (p.VehicleID == 0) { Main.Logger.Error("Invalid vehicle"); }
if (p.Speed==5) if (p.Speed == 5)
{ {
p.Seat=ped.GetSeatTryingToEnter(); p.Seat = ped.GetSeatTryingToEnter();
} }
else else
{ {
p.Seat=ped.SeatIndex; p.Seat = ped.SeatIndex;
} }
if (!veh.IsLocal && p.Speed==4 && p.Seat==VehicleSeat.Driver) if (!veh.IsLocal && p.Speed == 4 && p.Seat == VehicleSeat.Driver)
{ {
veh.OwnerID=Main.LocalPlayerID; veh.OwnerID = Main.LocalPlayerID;
SyncEvents.TriggerChangeOwner(veh.ID,Main.LocalPlayerID); SyncEvents.TriggerChangeOwner(veh.ID, Main.LocalPlayerID);
} }
} }
p.Position = ped.ReadPosition(); p.Position = ped.ReadPosition();
@ -80,57 +80,57 @@ namespace RageCoop.Client
if (full) if (full)
{ {
var w = ped.VehicleWeapon; var w = ped.VehicleWeapon;
p.CurrentWeaponHash = (w!=VehicleWeaponHash.Invalid)? (uint)w:(uint)ped.Weapons.Current.Hash; p.CurrentWeaponHash = (w != VehicleWeaponHash.Invalid) ? (uint)w : (uint)ped.Weapons.Current.Hash;
p.Flags |= PedDataFlags.IsFullSync; p.Flags |= PedDataFlags.IsFullSync;
p.Clothes=ped.GetPedClothes(); p.Clothes = ped.GetPedClothes();
p.ModelHash=ped.Model.Hash; p.ModelHash = ped.Model.Hash;
p.WeaponComponents=ped.Weapons.Current.GetWeaponComponents(); p.WeaponComponents = ped.Weapons.Current.GetWeaponComponents();
p.WeaponTint=(byte)Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, ped, ped.Weapons.Current.Hash); p.WeaponTint = (byte)Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, ped, ped.Weapons.Current.Hash);
Blip b; Blip b;
if (sp.IsPlayer) if (sp.IsPlayer)
{ {
p.BlipColor=Scripting.API.Config.BlipColor; p.BlipColor = Scripting.API.Config.BlipColor;
p.BlipSprite=Scripting.API.Config.BlipSprite; p.BlipSprite = Scripting.API.Config.BlipSprite;
p.BlipScale=Scripting.API.Config.BlipScale; p.BlipScale = Scripting.API.Config.BlipScale;
} }
else if ((b = ped.AttachedBlip) !=null) else if ((b = ped.AttachedBlip) != null)
{ {
p.BlipColor=b.Color; p.BlipColor = b.Color;
p.BlipSprite=b.Sprite; p.BlipSprite = b.Sprite;
if (p.BlipSprite==BlipSprite.PoliceOfficer || p.BlipSprite==BlipSprite.PoliceOfficer2) if (p.BlipSprite == BlipSprite.PoliceOfficer || p.BlipSprite == BlipSprite.PoliceOfficer2)
{ {
p.BlipScale=0.5f; p.BlipScale = 0.5f;
} }
} }
else else
{ {
p.BlipColor=(BlipColor)255; p.BlipColor = (BlipColor)255;
} }
} }
SendSync(p, ConnectionChannel.PedSync); SendSync(p, ConnectionChannel.PedSync);
} }
public static void SendVehicle(SyncedVehicle v, bool full) public static void SendVehicle(SyncedVehicle v, bool full)
{ {
if (v.LastSentStopWatch.ElapsedMilliseconds<SyncInterval) if (v.LastSentStopWatch.ElapsedMilliseconds < SyncInterval)
{ {
return; return;
} }
Vehicle veh = v.MainVehicle; Vehicle veh = v.MainVehicle;
var packet = SendPackets.VehicelPacket; var packet = SendPackets.VehicelPacket;
packet.ID =v.ID; packet.ID = v.ID;
packet.OwnerID=v.OwnerID; packet.OwnerID = v.OwnerID;
packet.Flags = v.GetVehicleFlags(); packet.Flags = v.GetVehicleFlags();
packet.SteeringAngle = veh.SteeringAngle; packet.SteeringAngle = veh.SteeringAngle;
packet.Position = veh.ReadPosition(); packet.Position = veh.ReadPosition();
packet.Velocity=veh.Velocity; packet.Velocity = veh.Velocity;
packet.Quaternion=veh.ReadQuaternion(); packet.Quaternion = veh.ReadQuaternion();
packet.RotationVelocity=veh.RotationVelocity; packet.RotationVelocity = veh.RotationVelocity;
packet.ThrottlePower = veh.ThrottlePower; packet.ThrottlePower = veh.ThrottlePower;
packet.BrakePower = veh.BrakePower; packet.BrakePower = veh.BrakePower;
v.LastSentStopWatch.Restart(); v.LastSentStopWatch.Restart();
if (packet.Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering)) { packet.DeluxoWingRatio=v.MainVehicle.GetDeluxoWingRatio(); } if (packet.Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering)) { packet.DeluxoWingRatio = v.MainVehicle.GetDeluxoWingRatio(); }
if (full) if (full)
{ {
byte primaryColor = 0; byte primaryColor = 0;
@ -141,24 +141,24 @@ namespace RageCoop.Client
} }
packet.Flags |= VehicleDataFlags.IsFullSync; packet.Flags |= VehicleDataFlags.IsFullSync;
packet.Colors = new byte[] { primaryColor, secondaryColor }; packet.Colors = new byte[] { primaryColor, secondaryColor };
packet.DamageModel=veh.GetVehicleDamageModel(); packet.DamageModel = veh.GetVehicleDamageModel();
packet.LandingGear = veh.IsAircraft ? (byte)veh.LandingGearState : (byte)0; packet.LandingGear = veh.IsAircraft ? (byte)veh.LandingGearState : (byte)0;
packet.RoofState=(byte)veh.RoofState; packet.RoofState = (byte)veh.RoofState;
packet.Mods = veh.Mods.GetVehicleMods(); packet.Mods = veh.Mods.GetVehicleMods();
packet.ModelHash=veh.Model.Hash; packet.ModelHash = veh.Model.Hash;
packet.EngineHealth=veh.EngineHealth; packet.EngineHealth = veh.EngineHealth;
packet.LockStatus=veh.LockStatus; packet.LockStatus = veh.LockStatus;
packet.LicensePlate=Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh); packet.LicensePlate = Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh);
packet.Livery=Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh); packet.Livery = Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh);
if (v.MainVehicle==Game.Player.LastVehicle) if (v.MainVehicle == Game.Player.LastVehicle)
{ {
packet.RadioStation=Util.GetPlayerRadioIndex(); packet.RadioStation = Util.GetPlayerRadioIndex();
} }
if (packet.EngineHealth>v.LastEngineHealth) if (packet.EngineHealth > v.LastEngineHealth)
{ {
packet.Flags |= VehicleDataFlags.Repaired; packet.Flags |= VehicleDataFlags.Repaired;
} }
v.LastEngineHealth=packet.EngineHealth; v.LastEngineHealth = packet.EngineHealth;
} }
SendSync(packet, ConnectionChannel.VehicleSync); SendSync(packet, ConnectionChannel.VehicleSync);
} }
@ -178,25 +178,25 @@ namespace RageCoop.Client
StartPosition = start, StartPosition = start,
EndPosition = end, EndPosition = end,
OwnerID = ownerID, OwnerID = ownerID,
WeaponHash=weapon, WeaponHash = weapon,
}, ConnectionChannel.SyncEvents); }, ConnectionChannel.SyncEvents);
} }
public static void SendVehicleBullet(uint hash,SyncedPed owner,EntityBone b) public static void SendVehicleBullet(uint hash, SyncedPed owner, EntityBone b)
{ {
SendSync(new Packets.VehicleBulletShot SendSync(new Packets.VehicleBulletShot
{ {
StartPosition = b.Position, StartPosition = b.Position,
EndPosition = b.Position+b.ForwardVector, EndPosition = b.Position + b.ForwardVector,
OwnerID=owner.ID, OwnerID = owner.ID,
Bone=(ushort)b.Index, Bone = (ushort)b.Index,
WeaponHash=hash WeaponHash = hash
}); });
} }
#endregion #endregion
public static void SendChatMessage(string message) public static void SendChatMessage(string message)
{ {
Peer.SendTo(new Packets.ChatMessage(new Func<string, byte[]>((s) => Security.Encrypt(s.GetBytes()))) Peer.SendTo(new Packets.ChatMessage(new Func<string, byte[]>((s) => Security.Encrypt(s.GetBytes())))
{ Username = Main.Settings.Username, Message = message },ServerConnection, ConnectionChannel.Chat, NetDeliveryMethod.ReliableOrdered); { Username = Main.Settings.Username, Message = message }, ServerConnection, ConnectionChannel.Chat, NetDeliveryMethod.ReliableOrdered);
Peer.FlushSendQueue(); Peer.FlushSendQueue();
} }
public static void SendVoiceMessage(byte[] buffer, int recorded) public static void SendVoiceMessage(byte[] buffer, int recorded)

View File

@ -16,8 +16,8 @@ namespace RageCoop.Client
var bu = Networking.Peer.Statistics.SentBytes; var bu = Networking.Peer.Statistics.SentBytes;
var bd = Networking.Peer.Statistics.ReceivedBytes; var bd = Networking.Peer.Statistics.ReceivedBytes;
Thread.Sleep(1000); Thread.Sleep(1000);
BytesUpPerSecond=Networking.Peer.Statistics.SentBytes-bu; BytesUpPerSecond = Networking.Peer.Statistics.SentBytes - bu;
BytesDownPerSecond=Networking.Peer.Statistics.ReceivedBytes-bd; BytesDownPerSecond = Networking.Peer.Statistics.ReceivedBytes - bd;
} }
}); });
} }

View File

@ -1,11 +1,11 @@
using GTA; using GTA;
using GTA.Math; using GTA.Math;
using GTA.Native; using GTA.Native;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using System.Collections.Generic; using System.Collections.Generic;
using Lidgren.Network;
using System.Net;
using System.Linq; using System.Linq;
using System.Net;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -50,11 +50,11 @@ namespace RageCoop.Client
_mainScaleform.CallFunction("SET_DATA_SLOT_EMPTY", 0); _mainScaleform.CallFunction("SET_DATA_SLOT_EMPTY", 0);
int i=0; int i = 0;
foreach (var player in Players.Values) foreach (var player in Players.Values)
{ {
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Ping * 1000:N0}ms", player.Username+(player.IsHost ? " (Host)" : ""), 116, 0, i - 1, "", "", 2, "", "", ' '); _mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Ping * 1000:N0}ms", player.Username + (player.IsHost ? " (Host)" : ""), 116, 0, i - 1, "", "", 2, "", "", ' ');
} }
_mainScaleform.CallFunction("SET_TITLE", "Player list", $"{Players.Count} players"); _mainScaleform.CallFunction("SET_TITLE", "Player list", $"{Players.Count} players");
@ -63,34 +63,33 @@ namespace RageCoop.Client
public static void SetPlayer(int id, string username, float latency = 0) public static void SetPlayer(int id, string username, float latency = 0)
{ {
Main.Logger.Debug($"{id},{username},{latency}"); Main.Logger.Debug($"{id},{username},{latency}");
Player p; if (Players.TryGetValue(id, out Player p))
if (Players.TryGetValue(id, out p))
{ {
p.Username=username; p.Username = username;
p.PedID=id; p.ID = id;
p._latencyToServer=latency; p._latencyToServer = latency;
} }
else else
{ {
p = new Player { PedID=id, Username=username, _latencyToServer=latency }; p = new Player { ID = id, Username = username, _latencyToServer = latency };
Players.Add(id, p); Players.Add(id, p);
} }
} }
public static void UpdatePlayer(Packets.PlayerInfoUpdate packet) public static void UpdatePlayer(Packets.PlayerInfoUpdate packet)
{ {
var p = GetPlayer(packet.PedID); var p = GetPlayer(packet.PedID);
if (p!=null) if (p != null)
{ {
p._latencyToServer = packet.Latency; p._latencyToServer = packet.Latency;
p.Position = packet.Position; p.Position = packet.Position;
p.IsHost= packet.IsHost; p.IsHost = packet.IsHost;
Main.QueueAction(() => Main.QueueAction(() =>
{ {
if (p.FakeBlip?.Exists()!=true) if (p.FakeBlip?.Exists() != true)
{ {
p.FakeBlip=World.CreateBlip(p.Position); p.FakeBlip = World.CreateBlip(p.Position);
} }
if (EntityPool.PedExists(p.PedID)) if (EntityPool.PedExists(p.ID))
{ {
p.FakeBlip.DisplayType = BlipDisplayType.NoDisplay; p.FakeBlip.DisplayType = BlipDisplayType.NoDisplay;
} }
@ -108,22 +107,21 @@ namespace RageCoop.Client
} }
public static Player GetPlayer(int id) public static Player GetPlayer(int id)
{ {
Player p; Players.TryGetValue(id, out Player p);
Players.TryGetValue(id, out p);
return p; return p;
} }
public static Player GetPlayer(SyncedPed p) public static Player GetPlayer(SyncedPed p)
{ {
var player = GetPlayer(p.ID); var player = GetPlayer(p.ID);
if (player!=null) if (player != null)
{ {
player.Character=p; player.Character = p;
} }
return player; return player;
} }
public static void RemovePlayer(int id) public static void RemovePlayer(int id)
{ {
if (Players.TryGetValue(id,out var player)) if (Players.TryGetValue(id, out var player))
{ {
Players.Remove(id); Players.Remove(id);
Main.QueueAction(() => player.FakeBlip?.Delete()); Main.QueueAction(() => player.FakeBlip?.Delete());
@ -131,40 +129,40 @@ namespace RageCoop.Client
} }
public static void Cleanup() public static void Cleanup()
{ {
foreach(var p in Players.Values.ToArray()) foreach (var p in Players.Values.ToArray())
{ {
p.FakeBlip?.Delete(); p.FakeBlip?.Delete();
} }
Players=new Dictionary<int, Player> { }; Players = new Dictionary<int, Player> { };
} }
} }
internal class Player public class Player
{ {
public byte HolePunchStatus { get; set; } = 1; public byte HolePunchStatus { get; internal set; } = 1;
public bool IsHost; public bool IsHost { get; internal set; }
public string Username { get; internal set; } public string Username { get; internal set; }
/// <summary> /// <summary>
/// Universal character ID. /// Universal ped ID.
/// </summary> /// </summary>
public int PedID public int ID
{ {
get; internal set; get; internal set;
} }
public IPEndPoint InternalEndPoint { get; set; } public IPEndPoint InternalEndPoint { get; internal set; }
public IPEndPoint ExternalEndPoint { get; set; } public IPEndPoint ExternalEndPoint { get; internal set; }
public bool ConnectWhenPunched { get; set; } internal bool ConnectWhenPunched { get; set; }
public Blip FakeBlip { get; set; } public Blip FakeBlip { get; internal set; }
public Vector3 Position { get; set; } public Vector3 Position { get; internal set; }
public SyncedPed Character { get; set; } public SyncedPed Character { get; internal set; }
/// <summary> /// <summary>
/// Player round-trip time in seconds, will be the latency to server if not using P2P connection. /// Player round-trip time in seconds, will be the rtt to server if not using P2P connection.
/// </summary> /// </summary>
public float Ping => Main.LocalPlayerID==PedID ? Networking.Latency*2 : (HasDirectConnection ? Connection.AverageRoundtripTime : _latencyToServer*2); public float Ping => Main.LocalPlayerID == ID ? Networking.Latency * 2 : (HasDirectConnection ? Connection.AverageRoundtripTime : _latencyToServer * 2);
public float PacketTravelTime => HasDirectConnection ? Connection.AverageRoundtripTime/2 : Networking.Latency+_latencyToServer; public float PacketTravelTime => HasDirectConnection ? Connection.AverageRoundtripTime / 2 : Networking.Latency + _latencyToServer;
public float _latencyToServer = 0; internal float _latencyToServer = 0;
public bool DisplayNameTag { get; set; } = true; public bool DisplayNameTag { get; set; } = true;
public NetConnection Connection { get; set; } public NetConnection Connection { get; internal set; }
public bool HasDirectConnection => Connection?.Status==NetConnectionStatus.Connected; public bool HasDirectConnection => Connection?.Status == NetConnectionStatus.Connected;
} }
} }

View File

@ -15,8 +15,8 @@ using System.Resources;
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
// Version informationr( // Version information
[assembly: AssemblyVersion("1.5.3.124")] [assembly: AssemblyVersion("1.5.4.6")]
[assembly: AssemblyFileVersion("1.5.3.124")] [assembly: AssemblyFileVersion("1.5.4.6")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )] [assembly: NeutralResourcesLanguageAttribute( "en-US" )]

View File

@ -21,6 +21,7 @@
<OutPutPath>..\bin\Debug\Client</OutPutPath> <OutPutPath>..\bin\Debug\Client</OutPutPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>DEBUG</DefineConstants> <DefineConstants>DEBUG</DefineConstants>
<WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'"> <PropertyGroup Condition="'$(Configuration)' == 'Release'">
<OutPutPath>..\bin\Release\Client</OutPutPath> <OutPutPath>..\bin\Release\Client</OutPutPath>
@ -35,7 +36,6 @@
<Compile Include="Menus\Sub\DevToolMenu.cs" /> <Compile Include="Menus\Sub\DevToolMenu.cs" />
<Compile Include="Menus\Sub\ServersMenu.cs" /> <Compile Include="Menus\Sub\ServersMenu.cs" />
<Compile Include="Menus\Sub\SettingsMenu.cs" /> <Compile Include="Menus\Sub\SettingsMenu.cs" />
<Compile Include="Menus\Sub\UpdateMenu.cs" />
<Compile Include="Networking\Chat.cs" /> <Compile Include="Networking\Chat.cs" />
<Compile Include="Networking\DownloadManager.cs" /> <Compile Include="Networking\DownloadManager.cs" />
<Compile Include="Networking\HolePunch.cs" /> <Compile Include="Networking\HolePunch.cs" />
@ -55,6 +55,7 @@
<Compile Include="Scripting\Resources.cs" /> <Compile Include="Scripting\Resources.cs" />
<Compile Include="Security.cs" /> <Compile Include="Security.cs" />
<Compile Include="Settings.cs" /> <Compile Include="Settings.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.Members.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.Animations.cs" /> <Compile Include="Sync\Entities\Ped\SyncedPed.Animations.cs" />
<Compile Include="Sync\Entities\SyncedEntity.cs" /> <Compile Include="Sync\Entities\SyncedEntity.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.cs" /> <Compile Include="Sync\Entities\Ped\SyncedPed.cs" />
@ -65,6 +66,7 @@
<Compile Include="Sync\EntityPool.cs" /> <Compile Include="Sync\EntityPool.cs" />
<Compile Include="Sync\SyncEvents.cs" /> <Compile Include="Sync\SyncEvents.cs" />
<Compile Include="Sync\Voice.cs" /> <Compile Include="Sync\Voice.cs" />
<Compile Include="Util\AddOnDataProvider.cs" />
<Compile Include="Util\Memory.cs" /> <Compile Include="Util\Memory.cs" />
<Compile Include="Util\NativeCaller.cs" /> <Compile Include="Util\NativeCaller.cs" />
<Compile Include="Util\PedConfigFlags.cs" /> <Compile Include="Util\PedConfigFlags.cs" />
@ -88,7 +90,7 @@
<Reference Include="ICSharpCode.SharpZipLib, Version=1.3.3.11, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL"> <Reference Include="ICSharpCode.SharpZipLib, Version=1.3.3.11, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath> <HintPath>..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference> </Reference>
<Reference Include="LemonUI.SHVDN3, Version=1.7.0.0, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="LemonUI.SHVDN3, Version=1.10.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath> <HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath>
</Reference> </Reference>

View File

@ -1,5 +1,6 @@
#undef DEBUG #undef DEBUG
using GTA; using GTA;
using Newtonsoft.Json;
using RageCoop.Core; using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -39,7 +40,7 @@ namespace RageCoop.Client.Scripting
/// </summary> /// </summary>
public static string Username public static string Username
{ {
get { return Main.Settings.Username; } get => Main.Settings.Username;
set set
{ {
if (Networking.IsOnServer || string.IsNullOrEmpty(value)) if (Networking.IsOnServer || string.IsNullOrEmpty(value))
@ -90,7 +91,7 @@ namespace RageCoop.Client.Scripting
/// <summary> /// <summary>
/// The local player is dead /// The local player is dead
/// </summary> /// </summary>
public static event EmptyEvent OnPlayerDied; public static event EventHandler<string> OnPlayerDied;
/// <summary> /// <summary>
/// A local vehicle is spawned /// A local vehicle is spawned
@ -132,7 +133,7 @@ namespace RageCoop.Client.Scripting
internal static void InvokeVehicleDeleted(SyncedVehicle v) { OnVehicleDeleted?.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 InvokePedSpawned(SyncedPed p) { OnPedSpawned?.Invoke(null, p); }
internal static void InvokePedDeleted(SyncedPed p) { OnPedDeleted?.Invoke(null, p); } internal static void InvokePedDeleted(SyncedPed p) { OnPedDeleted?.Invoke(null, p); }
internal static void InvokePlayerDied() { OnPlayerDied?.Invoke(); } internal static void InvokePlayerDied(string m) { OnPlayerDied?.Invoke(null, m); }
internal static void InvokeTick() { OnTick?.Invoke(); } internal static void InvokeTick() { OnTick?.Invoke(); }
internal static void InvokeKeyDown(object s, KeyEventArgs e) { OnKeyDown?.Invoke(s, e); } internal static void InvokeKeyDown(object s, KeyEventArgs e) { OnKeyDown?.Invoke(s, e); }
@ -141,12 +142,11 @@ namespace RageCoop.Client.Scripting
internal static void InvokeCustomEventReceived(Packets.CustomEvent p) internal static void InvokeCustomEventReceived(Packets.CustomEvent p)
{ {
var args = new CustomEventReceivedArgs() { Hash=p.Hash, Args=p.Args }; var args = new CustomEventReceivedArgs() { Hash = p.Hash, Args = p.Args };
// Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType()); // Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
List<Action<CustomEventReceivedArgs>> handlers; if (CustomEventHandlers.TryGetValue(p.Hash, out List<Action<CustomEventReceivedArgs>> handlers))
if (CustomEventHandlers.TryGetValue(p.Hash, out handlers))
{ {
handlers.ForEach((x) => { x.Invoke(args); }); handlers.ForEach((x) => { x.Invoke(args); });
} }
@ -160,65 +160,48 @@ namespace RageCoop.Client.Scripting
/// Get the local player's ID /// Get the local player's ID
/// </summary> /// </summary>
/// <returns>PlayerID</returns> /// <returns>PlayerID</returns>
public static int LocalPlayerID public static int LocalPlayerID => Main.LocalPlayerID;
{
get { return Main.LocalPlayerID; }
}
/// <summary> /// <summary>
/// Check if player is connected to a server /// Check if player is connected to a server
/// </summary> /// </summary>
public static bool IsOnServer { get { return Networking.IsOnServer; } } public static bool IsOnServer => Networking.IsOnServer;
/// <summary> /// <summary>
/// Get an <see cref="System.Net.IPEndPoint"/> that the player is currently connected to, or null if not connected to the server /// Get an <see cref="System.Net.IPEndPoint"/> that the player is currently connected to, or null if not connected to the server
/// </summary> /// </summary>
public static System.Net.IPEndPoint ServerEndPoint { get { return Networking.IsOnServer ? Networking.ServerConnection?.RemoteEndPoint : null; } } public static System.Net.IPEndPoint ServerEndPoint => Networking.IsOnServer ? Networking.ServerConnection?.RemoteEndPoint : null;
/// <summary> /// <summary>
/// Check if a RAGECOOP menu is visible /// Check if a RAGECOOP menu is visible
/// </summary> /// </summary>
public static bool IsMenuVisible public static bool IsMenuVisible => Menus.CoopMenu.MenuPool.AreAnyVisible;
{
get { return Menus.CoopMenu.MenuPool.AreAnyVisible; }
}
/// <summary> /// <summary>
/// Check if the RAGECOOP chat is visible /// Check if the RAGECOOP chat is visible
/// </summary> /// </summary>
public static bool IsChatFocused public static bool IsChatFocused => Main.MainChat.Focused;
{
get { return Main.MainChat.Focused; }
}
/// <summary> /// <summary>
/// Check if the RAGECOOP list of players is visible /// Check if the RAGECOOP list of players is visible
/// </summary> /// </summary>
public static bool IsPlayerListVisible public static bool IsPlayerListVisible => Util.GetTickCount64() - PlayerList.Pressed < 5000;
{
get { return Util.GetTickCount64() - PlayerList.Pressed < 5000; }
}
/// <summary> /// <summary>
/// Get the version of RAGECOOP /// Get the version of RAGECOOP
/// </summary> /// </summary>
public static Version CurrentVersion public static Version CurrentVersion => Main.Version;
{
get { return Main.Version; }
}
/// <summary> /// <summary>
/// Get a <see cref="Core.Logger"/> that RAGECOOP is currently using. /// Get a <see cref="Core.Logger"/> that RAGECOOP is currently using.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static Logger Logger public static Logger Logger => Main.Logger;
{ /// <summary>
get /// Get all players indexed by their ID
{ /// </summary>
return Main.Logger; public static Dictionary<int, Player> Players => new Dictionary<int, Player>(PlayerList.Players);
}
}
#endregion #endregion
#region FUNCTIONS #region FUNCTIONS
@ -246,6 +229,15 @@ namespace RageCoop.Client.Scripting
} }
} }
/// <summary>
/// List all servers from master server address
/// </summary>
/// <returns></returns>
public static List<ServerInfo> ListServers()
{
return JsonConvert.DeserializeObject<List<ServerInfo>>(HttpHelper.DownloadString(Main.Settings.MasterServer));
}
/// <summary> /// <summary>
/// Send a local chat message to this player /// Send a local chat message to this player
/// </summary> /// </summary>
@ -293,9 +285,9 @@ namespace RageCoop.Client.Scripting
Networking.Peer.SendTo(new Packets.CustomEvent() Networking.Peer.SendTo(new Packets.CustomEvent()
{ {
Args=args, Args = args,
Hash=eventHash Hash = eventHash
},Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered); }, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
} }
/// <summary> /// <summary>
@ -328,17 +320,17 @@ namespace RageCoop.Client.Scripting
callback(e); callback(e);
} }
}; };
DownloadManager.DownloadCompleted+=handler; DownloadManager.DownloadCompleted += handler;
Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest() Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest()
{ {
Name=name, Name = name,
}, },
(p) => (p) =>
{ {
if (p.Response != FileResponse.Loaded) if (p.Response != FileResponse.Loaded)
{ {
DownloadManager.DownloadCompleted-=handler; DownloadManager.DownloadCompleted -= handler;
throw new ArgumentException("Requested file was not found on the server: "+name); throw new ArgumentException("Requested file was not found on the server: " + name);
} }
}); });
} }

View File

@ -14,9 +14,9 @@ namespace RageCoop.Client.Scripting
private bool _isHost = false; private bool _isHost = false;
public override void OnStart() public override void OnStart()
{ {
API.Events.OnPedDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted, p.ID); }; API.Events.OnPedDeleted += (s, p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted, p.ID); };
API.Events.OnVehicleDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); }; API.Events.OnVehicleDeleted += (s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); };
API.Events.OnPlayerDied+=() => { API.SendCustomEvent(CustomEvents.OnPlayerDied); }; API.Events.OnPlayerDied += (s, m) => { API.SendCustomEvent(CustomEvents.OnPlayerDied, m); };
API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn, SetAutoRespawn); API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn, SetAutoRespawn);
API.RegisterCustomEventHandler(CustomEvents.SetDisplayNameTag, SetDisplayNameTag); API.RegisterCustomEventHandler(CustomEvents.SetDisplayNameTag, SetDisplayNameTag);
@ -29,9 +29,9 @@ namespace RageCoop.Client.Scripting
API.RegisterCustomEventHandler(CustomEvents.DeleteServerBlip, DeleteServerBlip); API.RegisterCustomEventHandler(CustomEvents.DeleteServerBlip, DeleteServerBlip);
API.RegisterCustomEventHandler(CustomEvents.CreateVehicle, CreateVehicle); API.RegisterCustomEventHandler(CustomEvents.CreateVehicle, CreateVehicle);
API.RegisterCustomEventHandler(CustomEvents.UpdatePedBlip, UpdatePedBlip); API.RegisterCustomEventHandler(CustomEvents.UpdatePedBlip, UpdatePedBlip);
API.RegisterCustomEventHandler(CustomEvents.IsHost, (e) => { _isHost=(bool)e.Args[0]; }); API.RegisterCustomEventHandler(CustomEvents.IsHost, (e) => { _isHost = (bool)e.Args[0]; });
API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, WeatherTimeSync); API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, WeatherTimeSync);
API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied, (e) => { GTA.UI.Notification.Show($"~h~{e.Args[0]}~h~ died."); }); API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied, (e) => { GTA.UI.Notification.Show((string)e.Args[0]); });
Task.Run(() => Task.Run(() =>
{ {
while (true) while (true)
@ -46,7 +46,7 @@ namespace RageCoop.Client.Scripting
int weather1 = default(int); int weather1 = default(int);
int weather2 = default(int); int weather2 = default(int);
float percent2 = default(float); float percent2 = default(float);
Function.Call(Hash._GET_WEATHER_TYPE_TRANSITION, &weather1, &weather2, &percent2); Function.Call(Hash.GET_CURR_WEATHER_STATE, &weather1, &weather2, &percent2);
API.SendCustomEvent(CustomEvents.WeatherTimeSync, time.Hours, time.Minutes, time.Seconds, weather1, weather2, percent2); API.SendCustomEvent(CustomEvents.WeatherTimeSync, time.Hours, time.Minutes, time.Seconds, weather1, weather2, percent2);
} }
}); });
@ -59,33 +59,33 @@ namespace RageCoop.Client.Scripting
private void WeatherTimeSync(CustomEventReceivedArgs e) private void WeatherTimeSync(CustomEventReceivedArgs e)
{ {
World.CurrentTimeOfDay=new TimeSpan((int)e.Args[0], (int)e.Args[1], (int)e.Args[2]); 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]); Function.Call(Hash.SET_CURR_WEATHER_STATE, (int)e.Args[3], (int)e.Args[4], (float)e.Args[5]);
} }
private void SetDisplayNameTag(CustomEventReceivedArgs e) private void SetDisplayNameTag(CustomEventReceivedArgs e)
{ {
var p = PlayerList.GetPlayer((int)e.Args[0]); var p = PlayerList.GetPlayer((int)e.Args[0]);
if (p != null) { p.DisplayNameTag=(bool)e.Args[1]; } if (p != null) { p.DisplayNameTag = (bool)e.Args[1]; }
} }
private void UpdatePedBlip(CustomEventReceivedArgs e) private void UpdatePedBlip(CustomEventReceivedArgs e)
{ {
var p = Entity.FromHandle((int)e.Args[0]); var p = Entity.FromHandle((int)e.Args[0]);
if (p == null) { return; } if (p == null) { return; }
if (p.Handle==Game.Player.Character.Handle) if (p.Handle == Game.Player.Character.Handle)
{ {
API.Config.BlipColor=(BlipColor)(byte)e.Args[1]; API.Config.BlipColor = (BlipColor)(byte)e.Args[1];
API.Config.BlipSprite=(BlipSprite)(ushort)e.Args[2]; API.Config.BlipSprite = (BlipSprite)(ushort)e.Args[2];
API.Config.BlipScale=(float)e.Args[3]; API.Config.BlipScale = (float)e.Args[3];
} }
else else
{ {
var b = p.AttachedBlip; var b = p.AttachedBlip;
if (b == null) { b=p.AddBlip(); } if (b == null) { b = p.AddBlip(); }
b.Color=(BlipColor)(byte)e.Args[1]; b.Color = (BlipColor)(byte)e.Args[1];
b.Sprite=(BlipSprite)(ushort)e.Args[2]; b.Sprite = (BlipSprite)(ushort)e.Args[2];
b.Scale=(float)e.Args[3]; b.Scale = (float)e.Args[3];
} }
} }
@ -94,17 +94,17 @@ namespace RageCoop.Client.Scripting
var vehicleModel = (Model)e.Args[1]; var vehicleModel = (Model)e.Args[1];
vehicleModel.Request(1000); vehicleModel.Request(1000);
Vehicle veh = World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]); Vehicle veh = World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]);
while (veh==null) while (veh == null)
{ {
veh = World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]); veh = World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]);
Thread.Sleep(10); Thread.Sleep(10);
} }
veh.CanPretendOccupants=false; veh.CanPretendOccupants = false;
var v = new SyncedVehicle() var v = new SyncedVehicle()
{ {
ID=(int)e.Args[0], ID = (int)e.Args[0],
MainVehicle=veh, MainVehicle = veh,
OwnerID=Main.LocalPlayerID, OwnerID = Main.LocalPlayerID,
}; };
EntityPool.Add(v); EntityPool.Add(v);
} }
@ -127,10 +127,9 @@ namespace RageCoop.Client.Scripting
var pos = (Vector3)obj.Args[4]; var pos = (Vector3)obj.Args[4];
int rot = (int)obj.Args[5]; int rot = (int)obj.Args[5];
var name = (string)obj.Args[6]; var name = (string)obj.Args[6];
Blip blip; if (!EntityPool.ServerBlips.TryGetValue(id, out Blip blip))
if (!EntityPool.ServerBlips.TryGetValue(id, out blip))
{ {
EntityPool.ServerBlips.Add(id, blip=World.CreateBlip(pos)); EntityPool.ServerBlips.Add(id, blip = World.CreateBlip(pos));
} }
blip.Sprite = sprite; blip.Sprite = sprite;
blip.Color = color; blip.Color = color;
@ -152,14 +151,14 @@ namespace RageCoop.Client.Scripting
private void SetNameTag(CustomEventReceivedArgs e) private void SetNameTag(CustomEventReceivedArgs e)
{ {
var p = PlayerList.GetPlayer((int)e.Args[0]); var p = PlayerList.GetPlayer((int)e.Args[0]);
if (p!= null) if (p != null)
{ {
p.DisplayNameTag=(bool)e.Args[1]; p.DisplayNameTag = (bool)e.Args[1];
} }
} }
private void SetAutoRespawn(CustomEventReceivedArgs args) private void SetAutoRespawn(CustomEventReceivedArgs args)
{ {
API.Config.EnableAutoRespawn=(bool)args.Args[0]; API.Config.EnableAutoRespawn = (bool)args.Args[0];
} }
private void DeleteServerProp(CustomEventReceivedArgs e) private void DeleteServerProp(CustomEventReceivedArgs e)
{ {
@ -179,13 +178,13 @@ namespace RageCoop.Client.Scripting
{ {
if (!EntityPool.ServerProps.TryGetValue(id, out prop)) if (!EntityPool.ServerProps.TryGetValue(id, out prop))
{ {
EntityPool.ServerProps.Add(id, prop=new SyncedProp(id)); EntityPool.ServerProps.Add(id, prop = new SyncedProp(id));
} }
} }
prop.LastSynced=Main.Ticked+1; prop.LastSynced = Main.Ticked + 1;
prop.Model= (Model)e.Args[1]; prop.Model = (Model)e.Args[1];
prop.Position=(Vector3)e.Args[2]; prop.Position = (Vector3)e.Args[2];
prop.Rotation=(Vector3)e.Args[3]; prop.Rotation = (Vector3)e.Args[3];
prop.Update(); prop.Update();
} }
private void NativeCall(CustomEventReceivedArgs e) private void NativeCall(CustomEventReceivedArgs e)
@ -194,14 +193,14 @@ namespace RageCoop.Client.Scripting
int i; int i;
var ty = (byte)e.Args[0]; var ty = (byte)e.Args[0];
TypeCode returnType = (TypeCode)ty; TypeCode returnType = (TypeCode)ty;
i = returnType==TypeCode.Empty ? 1 : 2; i = returnType == TypeCode.Empty ? 1 : 2;
var hash = (Hash)e.Args[i++]; var hash = (Hash)e.Args[i++];
for (; i<e.Args.Length; i++) for (; i < e.Args.Length; i++)
{ {
arguments.Add(GetInputArgument(e.Args[i])); arguments.Add(GetInputArgument(e.Args[i]));
} }
if (returnType==TypeCode.Empty) if (returnType == TypeCode.Empty)
{ {
Function.Call(hash, arguments.ToArray()); Function.Call(hash, arguments.ToArray());
return; return;

View File

@ -30,7 +30,7 @@ namespace RageCoop.Client.Scripting
/// <summary> /// <summary>
/// Eqivalent of <see cref="ClientResource.Logger"/> in <see cref="CurrentResource"/> /// Eqivalent of <see cref="ClientResource.Logger"/> in <see cref="CurrentResource"/>
/// </summary> /// </summary>
public Core.Logger Logger { get { return CurrentResource.Logger; } } public Core.Logger Logger => CurrentResource.Logger;
} }
} }

View File

@ -38,9 +38,11 @@ namespace RageCoop.Client.Scripting
} }
internal class Resources internal class Resources
{ {
private readonly List<ClientResource> LoadedResources = new List<ClientResource>();
private const string BaseScriptType = "RageCoop.Client.Scripting.ClientScript";
private Logger Logger { get; set; }
public Resources() public Resources()
{ {
BaseScriptType = "RageCoop.Client.Scripting.ClientScript";
Logger = Main.Logger; Logger = Main.Logger;
} }
private void StartAll() private void StartAll()
@ -53,12 +55,12 @@ namespace RageCoop.Client.Scripting
{ {
try try
{ {
s.CurrentResource=d; s.CurrentResource = d;
s.OnStart(); s.OnStart();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error("Error occurred when starting script:"+s.GetType().FullName); Logger.Error("Error occurred when starting script:" + s.GetType().FullName);
Logger?.Error(ex); Logger?.Error(ex);
} }
} }
@ -79,7 +81,7 @@ namespace RageCoop.Client.Scripting
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error("Error occurred when stopping script:"+s.GetType().FullName); Logger.Error("Error occurred when stopping script:" + s.GetType().FullName);
Logger?.Error(ex); Logger?.Error(ex);
} }
} }
@ -106,9 +108,6 @@ namespace RageCoop.Client.Scripting
} }
LoadedResources.Clear(); LoadedResources.Clear();
} }
private List<ClientResource> LoadedResources = new List<ClientResource>();
private string BaseScriptType;
public Logger Logger { get; set; }
private void LoadResource(ZipFile file, string dataFolderRoot) private void LoadResource(ZipFile file, string dataFolderRoot)
{ {
@ -117,22 +116,22 @@ namespace RageCoop.Client.Scripting
{ {
Logger = Main.Logger, Logger = Main.Logger,
Scripts = new List<ClientScript>(), Scripts = new List<ClientScript>(),
Name=Path.GetFileNameWithoutExtension(file.Name), Name = Path.GetFileNameWithoutExtension(file.Name),
DataFolder=Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name)) DataFolder = Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name))
}; };
Directory.CreateDirectory(r.DataFolder); Directory.CreateDirectory(r.DataFolder);
foreach (ZipEntry entry in file) foreach (ZipEntry entry in file)
{ {
ResourceFile rFile; ResourceFile rFile;
r.Files.Add(entry.Name, rFile=new ResourceFile() r.Files.Add(entry.Name, rFile = new ResourceFile()
{ {
Name=entry.Name, Name = entry.Name,
IsDirectory=entry.IsDirectory, IsDirectory = entry.IsDirectory,
}); });
if (!entry.IsDirectory) if (!entry.IsDirectory)
{ {
rFile.GetStream=() => { return file.GetInputStream(entry); }; rFile.GetStream = () => { return file.GetInputStream(entry); };
if (entry.Name.EndsWith(".dll") && !entry.Name.Contains("/")) if (entry.Name.EndsWith(".dll") && !entry.Name.Contains("/"))
{ {
// Don't load API assembly // Don't load API assembly
@ -145,12 +144,12 @@ namespace RageCoop.Client.Scripting
{ {
continue; continue;
} }
var asm=Assembly.LoadFrom(tmp); var asm = Assembly.LoadFrom(tmp);
toLoad.Add(() => LoadScriptsFromAssembly(rFile,asm, entry.Name,r)); toLoad.Add(() => LoadScriptsFromAssembly(rFile, asm, entry.Name, r));
} }
} }
} }
foreach(var a in toLoad) foreach (var a in toLoad)
{ {
a(); a();
} }
@ -174,8 +173,8 @@ namespace RageCoop.Client.Scripting
// Invoke script constructor // Invoke script constructor
var script = constructor.Invoke(null) as ClientScript; var script = constructor.Invoke(null) as ClientScript;
// script.CurrentResource = toload; // script.CurrentResource = toload;
script.CurrentFile=rfile; script.CurrentFile = rfile;
script.CurrentResource=toload; script.CurrentResource = toload;
toload.Scripts.Add(script); toload.Scripts.Add(script);
count++; count++;
} }

View File

@ -7,7 +7,7 @@ namespace RageCoop.Client
{ {
public RSA ServerRSA { get; set; } public RSA ServerRSA { get; set; }
public Aes ClientAes { get; set; } = Aes.Create(); public Aes ClientAes { get; set; } = Aes.Create();
private Logger Logger; private readonly Logger Logger;
public Security(Logger logger) public Security(Logger logger)
{ {
Logger = logger; Logger = logger;
@ -17,8 +17,8 @@ namespace RageCoop.Client
public void GetSymmetricKeysCrypted(out byte[] cryptedKey, out byte[] cryptedIV) public void GetSymmetricKeysCrypted(out byte[] cryptedKey, out byte[] cryptedIV)
{ {
// Logger?.Debug($"Aes.Key:{ClientAes.Key.Dump()}, Aes.IV:{ClientAes.IV.Dump()}"); // Logger?.Debug($"Aes.Key:{ClientAes.Key.Dump()}, Aes.IV:{ClientAes.IV.Dump()}");
cryptedKey =ServerRSA.Encrypt(ClientAes.Key, RSAEncryptionPadding.Pkcs1); cryptedKey = ServerRSA.Encrypt(ClientAes.Key, RSAEncryptionPadding.Pkcs1);
cryptedIV =ServerRSA.Encrypt(ClientAes.IV, RSAEncryptionPadding.Pkcs1); cryptedIV = ServerRSA.Encrypt(ClientAes.IV, RSAEncryptionPadding.Pkcs1);
} }
public byte[] Encrypt(byte[] data) public byte[] Encrypt(byte[] data)
{ {
@ -33,11 +33,11 @@ namespace RageCoop.Client
var para = new RSAParameters(); var para = new RSAParameters();
para.Modulus = modulus; para.Modulus = modulus;
para.Exponent = exponent; para.Exponent = exponent;
ServerRSA=RSA.Create(para); ServerRSA = RSA.Create(para);
} }
public void Regen() public void Regen()
{ {
ClientAes=Aes.Create(); ClientAes = Aes.Create();
ClientAes.GenerateKey(); ClientAes.GenerateKey();
ClientAes.GenerateIV(); ClientAes.GenerateIV();
} }

View File

@ -22,7 +22,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Don't use it! /// Don't use it!
/// </summary> /// </summary>
public string MasterServer { get; set; } = "https://masterserver.ragecoop.online/"; public string MasterServer { get; set; } = "https://masterserver.ragecoop.com/";
/// <summary> /// <summary>
/// Don't use it! /// Don't use it!
/// </summary> /// </summary>
@ -51,7 +51,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Disable world NPC traffic, mission entities won't be affected /// Disable world NPC traffic, mission entities won't be affected
/// </summary> /// </summary>
public bool DisableTraffic { get; set; } = true; public bool DisableTraffic { get; set; } = false;
/// <summary> /// <summary>
/// Bring up pause menu but don't freeze time when FrontEndPauseAlternate(Esc) is pressed. /// Bring up pause menu but don't freeze time when FrontEndPauseAlternate(Esc) is pressed.
@ -67,6 +67,22 @@ namespace RageCoop.Client
/// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended). /// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
/// </summary> /// </summary>
public int WorldPedSoftLimit { get; set; } = 30; public int WorldPedSoftLimit { get; set; } = 30;
/// <summary>
/// The mod won't sync more vehicles if the limit is exceeded.
/// </summary>
public int GlobalVehicleSoftLimit { get; set; } = 100;
/// <summary>
/// The mod won't sync more peds if the limit is exceeded.
/// </summary>
public int GlobalPedSoftLimit { get; set; } = 100;
/// <summary>
/// The mod won't sync more projectiles if the limit is exceeded.
/// </summary>
public int GlobalProjectileSoftLimit { get; set; } = 100;
/// <summary> /// <summary>
/// The directory where log and resources downloaded from server will be placed. /// The directory where log and resources downloaded from server will be placed.
/// </summary> /// </summary>
@ -76,5 +92,15 @@ namespace RageCoop.Client
/// Show the owner name of the entity you're aiming at /// Show the owner name of the entity you're aiming at
/// </summary> /// </summary>
public bool ShowEntityOwnerName { get; set; } = false; public bool ShowEntityOwnerName { get; set; } = false;
/// <summary>
/// Show other player's nametag on your screen
/// </summary>
public bool ShowPlayerNameTag { get; set; } = true;
/// <summary>
/// Show other player's blip on map
/// </summary>
public bool ShowPlayerBlip { get; set; } = true;
} }
} }

View File

@ -116,10 +116,10 @@ namespace RageCoop.Client
case unchecked((uint)-1357824103): case unchecked((uint)-1357824103):
case unchecked((uint)-1074790547): case unchecked((uint)-1074790547):
case unchecked((uint)2132975508): case unchecked(2132975508):
case unchecked((uint)-2084633992): case unchecked((uint)-2084633992):
case unchecked((uint)-952879014): case unchecked((uint)-952879014):
case unchecked((uint)100416529): case unchecked(100416529):
case unchecked((uint)WeaponHash.Gusenberg): case unchecked((uint)WeaponHash.Gusenberg):
case unchecked((uint)WeaponHash.MG): case unchecked((uint)WeaponHash.MG):
case unchecked((uint)WeaponHash.CombatMG): case unchecked((uint)WeaponHash.CombatMG):

View File

@ -0,0 +1,81 @@
using GTA;
using GTA.Math;
using RageCoop.Core;
using System.Collections.Generic;
namespace RageCoop.Client
{
/// <summary>
/// ?
/// </summary>
public partial class SyncedPed : SyncedEntity
{
internal Blip PedBlip = null;
internal BlipColor BlipColor = (BlipColor)255;
internal BlipSprite BlipSprite = 0;
internal float BlipScale = 1;
internal int VehicleID
{
get => CurrentVehicle?.ID ?? 0;
set
{
if (CurrentVehicle == null || value != CurrentVehicle?.ID)
{
CurrentVehicle = EntityPool.GetVehicleByID(value);
}
}
}
internal SyncedVehicle CurrentVehicle { get; private set; }
internal VehicleSeat Seat;
public bool IsPlayer { get => OwnerID == ID && ID != 0; }
public Ped MainPed { get; internal set; }
internal int Health { get; set; }
internal Vector3 HeadPosition { get; set; }
internal Vector3 RightFootPosition { get; set; }
internal Vector3 LeftFootPosition { get; set; }
internal byte WeaponTint { get; set; }
private bool _lastRagdoll = false;
private ulong _lastRagdollTime = 0;
private bool _lastInCover = false;
private byte[] _lastClothes = null;
internal byte[] Clothes { get; set; }
internal float Heading { get; set; }
internal ulong LastSpeakingTime { get; set; } = 0;
internal bool IsSpeaking { get; set; } = false;
public byte Speed { get; set; }
private bool _lastIsJumping = false;
internal PedDataFlags Flags;
internal bool IsAiming => Flags.HasPedFlag(PedDataFlags.IsAiming);
internal bool _lastDriveBy;
internal bool IsReloading => Flags.HasPedFlag(PedDataFlags.IsReloading);
internal bool IsJumping => Flags.HasPedFlag(PedDataFlags.IsJumping);
internal bool IsRagdoll => Flags.HasPedFlag(PedDataFlags.IsRagdoll);
internal bool IsOnFire => Flags.HasPedFlag(PedDataFlags.IsOnFire);
internal bool IsInParachuteFreeFall => Flags.HasPedFlag(PedDataFlags.IsInParachuteFreeFall);
internal bool IsParachuteOpen => Flags.HasPedFlag(PedDataFlags.IsParachuteOpen);
internal bool IsOnLadder => Flags.HasPedFlag(PedDataFlags.IsOnLadder);
internal bool IsVaulting => Flags.HasPedFlag(PedDataFlags.IsVaulting);
internal bool IsInCover => Flags.HasPedFlag(PedDataFlags.IsInCover);
internal bool IsInLowCover => Flags.HasPedFlag(PedDataFlags.IsInLowCover);
internal bool IsInCoverFacingLeft => Flags.HasPedFlag(PedDataFlags.IsInCoverFacingLeft);
internal bool IsBlindFiring => Flags.HasPedFlag(PedDataFlags.IsBlindFiring);
internal bool IsInStealthMode => Flags.HasPedFlag(PedDataFlags.IsInStealthMode);
internal Prop ParachuteProp { get; set; } = null;
internal uint CurrentWeaponHash { get; set; }
private Dictionary<uint, bool> _lastWeaponComponents = null;
internal Dictionary<uint, bool> WeaponComponents { get; set; } = null;
private Entity _weaponObj;
internal Vector3 AimCoords { get; set; }
private readonly string[] _currentAnimation = new string[2] { "", "" };
private bool LastMoving;
}
}

View File

@ -15,7 +15,6 @@ namespace RageCoop.Client
/// </summary> /// </summary>
public partial class SyncedPed : SyncedEntity public partial class SyncedPed : SyncedEntity
{ {
#region CONSTRUCTORS
/// <summary> /// <summary>
/// Create a local entity (outgoing sync) /// Create a local entity (outgoing sync)
@ -23,13 +22,13 @@ namespace RageCoop.Client
/// <param name="p"></param> /// <param name="p"></param>
internal SyncedPed(Ped p) internal SyncedPed(Ped p)
{ {
ID=EntityPool.RequestNewID(); ID = EntityPool.RequestNewID();
p.CanWrithe=false; p.CanWrithe = false;
p.IsOnlyDamagedByPlayer=false; p.IsOnlyDamagedByPlayer = false;
MainPed=p; MainPed = p;
OwnerID=Main.LocalPlayerID; OwnerID = Main.LocalPlayerID;
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false); //Function.Call(Hash.SET_PED_IS_IGNORED_BY_AUTO_OPEN_DOORS, false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true); 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);
@ -40,82 +39,13 @@ namespace RageCoop.Client
/// </summary> /// </summary>
internal SyncedPed(int id) internal SyncedPed(int id)
{ {
ID=id; ID = id;
LastSynced=Main.Ticked; LastSynced = Main.Ticked;
} }
#endregion
internal Blip PedBlip = null;
internal BlipColor BlipColor = (BlipColor)255;
internal BlipSprite BlipSprite = 0;
internal float BlipScale = 1;
internal int VehicleID
{
get => CurrentVehicle?.ID ?? 0;
set
{
if (CurrentVehicle == null || value != CurrentVehicle?.ID)
{
CurrentVehicle=EntityPool.GetVehicleByID(value);
}
}
}
internal SyncedVehicle CurrentVehicle { get; private set; }
internal VehicleSeat Seat;
public bool IsPlayer { get => OwnerID == ID && ID != 0; }
public Ped MainPed { get; internal set; }
internal int Health { get; set; }
internal Vector3 HeadPosition { get; set; }
internal Vector3 RightFootPosition { get; set; }
internal Vector3 LeftFootPosition { get; set; }
internal byte WeaponTint { get; set; }
internal Vehicle _lastVehicle { get; set; }
internal int _lastVehicleID { get; set; }
private bool _lastRagdoll = false;
private ulong _lastRagdollTime = 0;
private bool _lastInCover = false;
private byte[] _lastClothes = null;
internal byte[] Clothes { get; set; }
internal float Heading { get; set; }
internal ulong LastSpeakingTime { get; set; } = 0;
internal bool IsSpeaking { get; set; } = false;
#region -- VARIABLES --
public byte Speed { get; set; }
private bool _lastIsJumping = false;
internal PedDataFlags Flags;
internal bool IsAiming => Flags.HasPedFlag(PedDataFlags.IsAiming);
internal bool _lastDriveBy;
internal bool IsReloading => Flags.HasPedFlag(PedDataFlags.IsReloading);
internal bool IsJumping => Flags.HasPedFlag(PedDataFlags.IsJumping);
internal bool IsRagdoll => Flags.HasPedFlag(PedDataFlags.IsRagdoll);
internal bool IsOnFire => Flags.HasPedFlag(PedDataFlags.IsOnFire);
internal bool IsInParachuteFreeFall => Flags.HasPedFlag(PedDataFlags.IsInParachuteFreeFall);
internal bool IsParachuteOpen => Flags.HasPedFlag(PedDataFlags.IsParachuteOpen);
internal bool IsOnLadder => Flags.HasPedFlag(PedDataFlags.IsOnLadder);
internal bool IsVaulting => Flags.HasPedFlag(PedDataFlags.IsVaulting);
internal bool IsInCover => Flags.HasPedFlag(PedDataFlags.IsInCover);
internal bool IsInLowCover => Flags.HasPedFlag(PedDataFlags.IsInLowCover);
internal bool IsInCoverFacingLeft => Flags.HasPedFlag(PedDataFlags.IsInCoverFacingLeft);
internal bool IsBlindFiring => Flags.HasPedFlag(PedDataFlags.IsBlindFiring);
internal bool IsInStealthMode => Flags.HasPedFlag(PedDataFlags.IsInStealthMode);
internal Prop ParachuteProp { get; set; } = null;
internal uint CurrentWeaponHash { get; set; }
private Dictionary<uint, bool> _lastWeaponComponents = null;
internal Dictionary<uint, bool> WeaponComponents { get; set; } = null;
private Entity _weaponObj;
#endregion
internal Vector3 AimCoords { get; set; }
private WeaponAsset WeaponAsset { get; set; }
internal override void Update() internal override void Update()
{ {
if (Owner==null) { OwnerID=OwnerID;return; } if (Owner == null) { OwnerID = OwnerID; return; }
if (IsPlayer) if (IsPlayer)
{ {
RenderNameTag(); RenderNameTag();
@ -136,9 +66,9 @@ namespace RageCoop.Client
} }
// Need to update state // Need to update state
if (LastFullSynced>=LastUpdated) if (LastFullSynced >= LastUpdated)
{ {
if (MainPed!=null&& (Model != MainPed.Model.Hash)) if (MainPed != null && (Model != MainPed.Model.Hash))
{ {
if (!CreateCharacter()) if (!CreateCharacter())
{ {
@ -146,29 +76,30 @@ namespace RageCoop.Client
} }
} }
if (((byte)BlipColor==255) && (PedBlip!=null)) if (!Main.Settings.ShowPlayerBlip && (byte)BlipColor != 255) BlipColor = (BlipColor)255;
if ((byte)BlipColor == 255 && PedBlip != null)
{ {
PedBlip.Delete(); PedBlip.Delete();
PedBlip=null; PedBlip = null;
} }
else if (((byte)BlipColor != 255) && PedBlip==null) else if ((byte)BlipColor != 255 && PedBlip == null)
{ {
PedBlip=MainPed.AddBlip(); PedBlip = MainPed.AddBlip();
PedBlip.Color=BlipColor; PedBlip.Color = BlipColor;
PedBlip.Sprite=BlipSprite; PedBlip.Sprite = BlipSprite;
PedBlip.Scale=BlipScale; PedBlip.Scale = BlipScale;
} }
if (PedBlip!=null) if (PedBlip != null)
{ {
if (PedBlip.Color!=BlipColor) if (PedBlip.Color != BlipColor)
{ {
PedBlip.Color=BlipColor; PedBlip.Color = BlipColor;
} }
if (PedBlip.Sprite!=BlipSprite) if (PedBlip.Sprite != BlipSprite)
{ {
PedBlip.Sprite=BlipSprite; PedBlip.Sprite = BlipSprite;
} }
if (IsPlayer) if (IsPlayer)
{ {
@ -186,7 +117,7 @@ namespace RageCoop.Client
if (MainPed.IsDead) if (MainPed.IsDead)
{ {
if (Health>0) if (Health > 0)
{ {
if (IsPlayer) if (IsPlayer)
{ {
@ -198,7 +129,7 @@ namespace RageCoop.Client
} }
} }
} }
else if (IsPlayer&&(MainPed.Health != Health)) else if (IsPlayer && (MainPed.Health != Health))
{ {
MainPed.Health = Health; MainPed.Health = Health;
@ -210,13 +141,13 @@ namespace RageCoop.Client
} }
} }
if (Speed>=4) if (Speed >= 4)
{ {
DisplayInVehicle(); DisplayInVehicle();
} }
else else
{ {
if (MainPed.IsInVehicle()) { MainPed.Task.LeaveVehicle(LeaveVehicleFlags.WarpOut);return; } if (MainPed.IsInVehicle()) { MainPed.Task.LeaveVehicle(LeaveVehicleFlags.WarpOut); return; }
DisplayOnFoot(); DisplayOnFoot();
} }
@ -235,12 +166,12 @@ namespace RageCoop.Client
} }
} }
LastUpdated=Main.Ticked; LastUpdated = Main.Ticked;
} }
private void RenderNameTag() private void RenderNameTag()
{ {
if (!Owner.DisplayNameTag || (MainPed==null) || !MainPed.IsVisible || !MainPed.IsInRange(Main.PlayerPosition, 40f)) if (!Owner.DisplayNameTag || !Main.Settings.ShowPlayerNameTag || MainPed == null || !MainPed.IsVisible || !MainPed.IsInRange(Main.PlayerPosition, 40f))
{ {
return; return;
} }
@ -249,12 +180,12 @@ namespace RageCoop.Client
Point toDraw = default; Point toDraw = default;
if (Util.WorldToScreen(targetPos, ref toDraw)) if (Util.WorldToScreen(targetPos, ref toDraw))
{ {
toDraw.Y-=100; toDraw.Y -= 100;
new ScaledText(toDraw, Owner.Username, 0.4f, GTA.UI.Font.ChaletLondon) new ScaledText(toDraw, Owner.Username, 0.4f, GTA.UI.Font.ChaletLondon)
{ {
Outline = true, Outline = true,
Alignment = GTA.UI.Alignment.Center, Alignment = GTA.UI.Alignment.Center,
Color=Owner.HasDirectConnection? Color.FromArgb(179, 229, 252) : Color.White, Color = Owner.HasDirectConnection ? Color.FromArgb(179, 229, 252) : Color.White,
}.Draw(); }.Draw();
} }
} }
@ -293,19 +224,19 @@ namespace RageCoop.Client
Model.MarkAsNoLongerNeeded(); Model.MarkAsNoLongerNeeded();
MainPed.BlockPermanentEvents = true; MainPed.BlockPermanentEvents = true;
MainPed.CanWrithe=false; MainPed.CanWrithe = false;
MainPed.CanBeDraggedOutOfVehicle = true; MainPed.CanBeDraggedOutOfVehicle = true;
MainPed.IsOnlyDamagedByPlayer = false; MainPed.IsOnlyDamagedByPlayer = false;
MainPed.RelationshipGroup=Main.SyncedPedsGroup; MainPed.RelationshipGroup = Main.SyncedPedsGroup;
MainPed.IsFireProof=false; MainPed.IsFireProof = false;
MainPed.IsExplosionProof=false; MainPed.IsExplosionProof = false;
Function.Call(Hash.SET_PED_DROPS_WEAPONS_WHEN_DEAD, 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, MainPed.Handle, true);
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, 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_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false);
Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true); 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_IS_IGNORED_BY_AUTO_OPEN_DOORS, false);
Function.Call(Hash.SET_PED_CAN_EVASIVE_DIVE, MainPed.Handle, 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_DrownsInWater, false);
@ -321,8 +252,8 @@ namespace RageCoop.Client
SetClothes(); SetClothes();
if (IsPlayer) { MainPed.IsInvincible=true; } if (IsPlayer) { MainPed.IsInvincible = true; }
if (IsInvincible) { MainPed.IsInvincible=true; } if (IsInvincible) { MainPed.IsInvincible = true; }
lock (EntityPool.PedsLock) lock (EntityPool.PedsLock)
{ {
@ -337,15 +268,12 @@ namespace RageCoop.Client
{ {
for (byte i = 0; i < 12; i++) for (byte i = 0; i < 12; i++)
{ {
Function.Call(Hash.SET_PED_COMPONENT_VARIATION, MainPed.Handle, i, (int)Clothes[i], (int)Clothes[i+12], (int)Clothes[i+24]); Function.Call(Hash.SET_PED_COMPONENT_VARIATION, MainPed.Handle, i, (int)Clothes[i], (int)Clothes[i + 12], (int)Clothes[i + 24]);
} }
_lastClothes = Clothes; _lastClothes = Clothes;
} }
#region ONFOOT
private string[] _currentAnimation = new string[2] { "", "" };
private void DisplayOnFoot() private void DisplayOnFoot()
{ {
@ -492,7 +420,7 @@ namespace RageCoop.Client
} }
_lastIsJumping = false; _lastIsJumping = false;
if (IsRagdoll || Health==0) if (IsRagdoll || Health == 0)
{ {
if (!MainPed.IsRagdoll) if (!MainPed.IsRagdoll)
{ {
@ -502,26 +430,22 @@ namespace RageCoop.Client
if (!_lastRagdoll) if (!_lastRagdoll)
{ {
_lastRagdoll = true; _lastRagdoll = true;
_lastRagdollTime=Main.Ticked; _lastRagdollTime = Main.Ticked;
} }
return; return;
} }
else if (MainPed.IsRagdoll)
{ {
if (MainPed.IsRagdoll) if (Speed == 0)
{ {
if (Speed==0) MainPed.CancelRagdoll();
{ }
MainPed.CancelRagdoll(); else
} {
else MainPed.Task.ClearAllImmediately();
{
MainPed.Task.ClearAllImmediately();
}
return;
} }
_lastRagdoll = false; _lastRagdoll = false;
return;
} }
if (IsReloading) if (IsReloading)
@ -551,11 +475,11 @@ namespace RageCoop.Client
Function.Call(Hash.TASK_STAY_IN_COVER, MainPed.Handle); Function.Call(Hash.TASK_STAY_IN_COVER, MainPed.Handle);
} }
_lastInCover=true; _lastInCover = true;
if (IsAiming) if (IsAiming)
{ {
DisplayAiming(); DisplayAiming();
_lastInCover=false; _lastInCover = false;
} }
else if (MainPed.IsInCover) else if (MainPed.IsInCover)
{ {
@ -565,7 +489,7 @@ namespace RageCoop.Client
else if (_lastInCover) else if (_lastInCover)
{ {
MainPed.Task.ClearAllImmediately(); MainPed.Task.ClearAllImmediately();
_lastInCover=false; _lastInCover = false;
} }
else if (IsAiming) else if (IsAiming)
{ {
@ -581,12 +505,12 @@ namespace RageCoop.Client
} }
} }
#region WEAPON
private void CheckCurrentWeapon() private void CheckCurrentWeapon()
{ {
if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents) || (Speed <=3 && _weaponObj?.IsVisible != true)) if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents) || (Speed <= 3 && _weaponObj?.IsVisible != true))
{ {
WeaponAsset=new WeaponAsset(CurrentWeaponHash); new WeaponAsset(CurrentWeaponHash).Request();
MainPed.Weapons.RemoveAll(); MainPed.Weapons.RemoveAll();
_weaponObj = Entity.FromHandle(Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0)); _weaponObj = Entity.FromHandle(Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0));
if (_weaponObj == null) { return; } if (_weaponObj == null) { return; }
@ -606,7 +530,7 @@ namespace RageCoop.Client
} }
_lastWeaponComponents = WeaponComponents; _lastWeaponComponents = WeaponComponents;
} }
if (Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeaponHash)!=WeaponTint) 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); Function.Call<int>(Hash.SET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeaponHash, WeaponTint);
} }
@ -614,27 +538,25 @@ namespace RageCoop.Client
private void DisplayAiming() private void DisplayAiming()
{ {
if (Velocity==default) if (Velocity == default)
{ {
MainPed.Task.AimAt(AimCoords, 1000); MainPed.Task.AimAt(AimCoords, 1000);
} }
else else
{ {
Function.Call(Hash.TASK_GO_TO_COORD_WHILE_AIMING_AT_COORD, MainPed.Handle, Function.Call(Hash.TASK_GO_TO_COORD_WHILE_AIMING_AT_COORD, MainPed.Handle,
Position.X+Velocity.X, Position.Y+Velocity.Y, Position.Z+Velocity.Z, Position.X + Velocity.X, Position.Y + Velocity.Y, Position.Z + Velocity.Z,
AimCoords.X, AimCoords.Y, AimCoords.Z, 3f, false, 0x3F000000, 0x40800000, false, 512, false, 0); AimCoords.X, AimCoords.Y, AimCoords.Z, 3f, false, 0x3F000000, 0x40800000, false, 512, false, 0);
} }
SmoothTransition(); SmoothTransition();
} }
#endregion
private bool LastMoving;
private void WalkTo() private void WalkTo()
{ {
MainPed.Task.ClearAll(); MainPed.Task.ClearAll();
Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, MainPed, IsInStealthMode, 0); Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, MainPed, IsInStealthMode, 0);
Vector3 predictPosition = Predict(Position)+Velocity; Vector3 predictPosition = Predict(Position) + Velocity;
float range = predictPosition.DistanceToSquared(MainPed.ReadPosition()); float range = predictPosition.DistanceToSquared(MainPed.ReadPosition());
switch (Speed) switch (Speed)
@ -676,6 +598,8 @@ namespace RageCoop.Client
MainPed.Task.StandStill(2000); MainPed.Task.StandStill(2000);
LastMoving = false; LastMoving = false;
} }
if (MainPed.IsTaskActive(TaskType.CTaskDiveToGround)) MainPed.Task.ClearAll();
break; break;
} }
SmoothTransition(); SmoothTransition();
@ -688,23 +612,23 @@ namespace RageCoop.Client
var dist = predicted.DistanceTo(MainPed.ReadPosition()); var dist = predicted.DistanceTo(MainPed.ReadPosition());
if (IsOff(dist)) if (IsOff(dist))
{ {
MainPed.PositionNoOffset= predicted; MainPed.PositionNoOffset = predicted;
return; return;
} }
if (!(localRagdoll || MainPed.IsDead)) if (!(localRagdoll || MainPed.IsDead))
{ {
if (!IsAiming && !MainPed.IsGettingUp) if (!IsAiming && !MainPed.IsGettingUp)
{ {
var cur=MainPed.Heading; var cur = MainPed.Heading;
var diff=Heading-cur; var diff = Heading - cur;
if (diff > 180) { diff -= 360; } if (diff > 180) { diff -= 360; }
else if (diff < -180) { diff += 360; } else if (diff < -180) { diff += 360; }
MainPed.Heading=cur+diff/2; MainPed.Heading = cur + diff / 2;
} }
MainPed.Velocity = Velocity + 5 * dist * (predicted - MainPed.ReadPosition()); MainPed.Velocity = Velocity + 5 * dist * (predicted - MainPed.ReadPosition());
} }
else if (Main.Ticked-_lastRagdollTime<10) else if (Main.Ticked - _lastRagdollTime < 10)
{ {
return; return;
} }
@ -717,10 +641,10 @@ namespace RageCoop.Client
Vector3 amount; Vector3 amount;
// 20:head, 3:left foot, 6:right foot, 17:right hand, // 20:head, 3:left foot, 6:right foot, 17:right hand,
amount= 20 * (Predict(HeadPosition) - head.Position); amount = 20 * (Predict(HeadPosition) - head.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; } if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1; helper.EqualizeAmount = 1;
helper.PartIndex=20; helper.PartIndex = 20;
helper.Impulse = amount; helper.Impulse = amount;
helper.Start(); helper.Start();
helper.Stop(); helper.Stop();
@ -728,7 +652,7 @@ namespace RageCoop.Client
amount = 20 * (Predict(RightFootPosition) - rightFoot.Position); amount = 20 * (Predict(RightFootPosition) - rightFoot.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; } if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1; helper.EqualizeAmount = 1;
helper.PartIndex=6; helper.PartIndex = 6;
helper.Impulse = amount; helper.Impulse = amount;
helper.Start(); helper.Start();
helper.Stop(); helper.Stop();
@ -736,7 +660,7 @@ namespace RageCoop.Client
amount = 20 * (Predict(LeftFootPosition) - leftFoot.Position); amount = 20 * (Predict(LeftFootPosition) - leftFoot.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; } if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1; helper.EqualizeAmount = 1;
helper.PartIndex=3; helper.PartIndex = 3;
helper.Impulse = amount; helper.Impulse = amount;
helper.Start(); helper.Start();
helper.Stop(); helper.Stop();
@ -744,23 +668,19 @@ namespace RageCoop.Client
else else
{ {
// localRagdoll // localRagdoll
var force = Velocity - MainPed.Velocity+5 * dist * (predicted - MainPed.ReadPosition()); var force = Velocity - MainPed.Velocity + 5 * dist * (predicted - MainPed.ReadPosition());
if (force.Length() > 20) { force = force.Normalized*20; } if (force.Length() > 20) { force = force.Normalized * 20; }
MainPed.ApplyForce(force); MainPed.ApplyForce(force);
} }
} }
#endregion
private void DisplayInVehicle() private void DisplayInVehicle()
{ {
if (CurrentVehicle?.MainVehicle==null) { return; } if (CurrentVehicle?.MainVehicle == null) { return; }
switch (Speed) switch (Speed)
{ {
case 4: case 4:
if (MainPed.CurrentVehicle!=CurrentVehicle.MainVehicle || MainPed.SeatIndex != Seat || (!MainPed.IsSittingInVehicle() && !MainPed.IsBeingJacked)) if (MainPed.CurrentVehicle != CurrentVehicle.MainVehicle || MainPed.SeatIndex != Seat || (!MainPed.IsSittingInVehicle() && !MainPed.IsBeingJacked))
{ {
MainPed.SetIntoVehicle(CurrentVehicle.MainVehicle, Seat); MainPed.SetIntoVehicle(CurrentVehicle.MainVehicle, Seat);
} }
@ -769,7 +689,7 @@ namespace RageCoop.Client
// Function.Call(Hash.SET_VEHICLE_TURRET_SPEED_THIS_FRAME, MainPed.CurrentVehicle, 100); // 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_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z);
} }
if (MainPed.VehicleWeapon==VehicleWeaponHash.Invalid) if (MainPed.VehicleWeapon == VehicleWeaponHash.Invalid)
{ {
// World.DrawMarker(MarkerType.DebugSphere,AimCoords,default,default,new Vector3(0.2f,0.2f,0.2f),Color.AliceBlue); // World.DrawMarker(MarkerType.DebugSphere,AimCoords,default,default,new Vector3(0.2f,0.2f,0.2f),Color.AliceBlue);
if (IsAiming) if (IsAiming)
@ -777,32 +697,32 @@ namespace RageCoop.Client
Function.Call(Hash.SET_DRIVEBY_TASK_TARGET, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z); Function.Call(Hash.SET_DRIVEBY_TASK_TARGET, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z);
if (!_lastDriveBy) if (!_lastDriveBy)
{ {
_lastDriveBy=true; _lastDriveBy = true;
Function.Call(Hash.TASK_DRIVE_BY, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z, 1, 100, 1, FiringPattern.SingleShot); Function.Call(Hash.TASK_DRIVE_BY, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z, 1, 100, 1, FiringPattern.SingleShot);
} }
} }
else if (_lastDriveBy || MainPed.IsTaskActive(TaskType.CTaskAimGunVehicleDriveBy)) else if (_lastDriveBy || MainPed.IsTaskActive(TaskType.CTaskAimGunVehicleDriveBy))
{ {
MainPed.Task.ClearAll(); MainPed.Task.ClearAll();
_lastDriveBy=false; _lastDriveBy = false;
} }
} }
else if (MainPed.VehicleWeapon!=(VehicleWeaponHash)CurrentWeaponHash) else if (MainPed.VehicleWeapon != (VehicleWeaponHash)CurrentWeaponHash)
{ {
MainPed.VehicleWeapon=(VehicleWeaponHash)CurrentWeaponHash; MainPed.VehicleWeapon = (VehicleWeaponHash)CurrentWeaponHash;
} }
break; break;
case 5: case 5:
if (MainPed.VehicleTryingToEnter!=CurrentVehicle.MainVehicle || MainPed.GetSeatTryingToEnter()!=Seat) if (MainPed.VehicleTryingToEnter != CurrentVehicle.MainVehicle || MainPed.GetSeatTryingToEnter() != Seat)
{ {
MainPed.Task.EnterVehicle(CurrentVehicle.MainVehicle,Seat,-1,5,EnterVehicleFlags.AllowJacking); MainPed.Task.EnterVehicle(CurrentVehicle.MainVehicle, Seat, -1, 5, EnterVehicleFlags.JackAnyone);
} }
break; break;
case 6: case 6:
if (!MainPed.IsTaskActive(TaskType.CTaskExitVehicle)) if (!MainPed.IsTaskActive(TaskType.CTaskExitVehicle))
{ {
MainPed.Task.LeaveVehicle(CurrentVehicle.Velocity.Length() > 5f ? LeaveVehicleFlags.BailOut:LeaveVehicleFlags.None); MainPed.Task.LeaveVehicle(CurrentVehicle.Velocity.Length() > 5f ? LeaveVehicleFlags.BailOut : LeaveVehicleFlags.None);
} }
break; break;
} }

View File

@ -33,11 +33,12 @@ namespace RageCoop.Client
get => _ownerID; get => _ownerID;
internal set internal set
{ {
if (value==_ownerID && Owner!=null) { return; } if (value == _ownerID && Owner != null) { return; }
_ownerID = value; _ownerID = value;
Owner=PlayerList.GetPlayer(value); Owner = PlayerList.GetPlayer(value);
if(this is SyncedPed && Owner!=null){ if (this is SyncedPed && Owner != null)
Owner.Character=((SyncedPed)this); {
Owner.Character = ((SyncedPed)this);
} }
} }
} }
@ -83,7 +84,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
internal protected bool _lastFrozen = false; protected internal bool _lastFrozen = false;
internal Model Model { get; set; } internal Model Model { get; set; }
internal Vector3 Position { get; set; } internal Vector3 Position { get; set; }
internal Vector3 Rotation { get; set; } internal Vector3 Rotation { get; set; }
@ -93,18 +94,18 @@ namespace RageCoop.Client
internal abstract void Update(); internal abstract void Update();
internal void PauseUpdate(ulong frames) internal void PauseUpdate(ulong frames)
{ {
LastUpdated=Main.Ticked+frames; LastUpdated = Main.Ticked + frames;
} }
protected Vector3 Predict(Vector3 input) protected Vector3 Predict(Vector3 input)
{ {
return (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Velocity + input; return (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Velocity + input;
} }
private float _accumulatedOff=0; private float _accumulatedOff = 0;
protected bool IsOff(float thisOff, float tolerance=3 , float limit = 30) protected bool IsOff(float thisOff, float tolerance = 3, float limit = 30)
{ {
_accumulatedOff += thisOff - tolerance; _accumulatedOff += thisOff - tolerance;
if (_accumulatedOff < 0) { _accumulatedOff=0;} if (_accumulatedOff < 0) { _accumulatedOff = 0; }
else if (_accumulatedOff>=limit) else if (_accumulatedOff >= limit)
{ {
_accumulatedOff = 0; _accumulatedOff = 0;
return true; return true;

View File

@ -1,13 +1,13 @@
using GTA; using GTA;
using GTA.Math; using GTA.Math;
using RageCoop.Core;
using GTA.Native; using GTA.Native;
using RageCoop.Core;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal class SyncedProjectile : SyncedEntity internal class SyncedProjectile : SyncedEntity
{ {
public ProjectileDataFlags Flags { private get; set; }=ProjectileDataFlags.None; public ProjectileDataFlags Flags { private get; set; } = ProjectileDataFlags.None;
public readonly Vector3 Origin; public readonly Vector3 Origin;
private bool _firstSend = false; private bool _firstSend = false;
@ -19,28 +19,27 @@ namespace RageCoop.Client
public SyncedEntity Shooter { get; set; } public SyncedEntity Shooter { get; set; }
public bool Exploded => Flags.HasProjDataFlag(ProjectileDataFlags.Exploded); public bool Exploded => Flags.HasProjDataFlag(ProjectileDataFlags.Exploded);
internal override Player Owner => Shooter.Owner;
/// <summary> /// <summary>
/// Invalid property for projectile. /// Invalid property for projectile.
/// </summary> /// </summary>
private new int OwnerID { set { } } private new int OwnerID { set { } }
internal override Player Owner => Shooter.Owner;
public WeaponHash WeaponHash { get; set; } public WeaponHash WeaponHash { get; set; }
private WeaponAsset Asset { get; set; } private WeaponAsset Asset { get; set; }
public void ExtractData(ref Packets.ProjectileSync p) public void ExtractData(ref Packets.ProjectileSync p)
{ {
p.Position=MainProjectile.Position; p.Position = MainProjectile.Position;
p.Velocity=MainProjectile.Velocity; p.Velocity = MainProjectile.Velocity;
p.Rotation=MainProjectile.Rotation; p.Rotation = MainProjectile.Rotation;
p.ID=ID; p.ID = ID;
p.ShooterID=Shooter.ID; p.ShooterID = Shooter.ID;
p.WeaponHash=(uint)MainProjectile.WeaponHash; p.WeaponHash = (uint)MainProjectile.WeaponHash;
p.Flags=ProjectileDataFlags.None; p.Flags = ProjectileDataFlags.None;
if (MainProjectile.IsDead) if (MainProjectile.IsDead)
{ {
p.Flags |= ProjectileDataFlags.Exploded; p.Flags |= ProjectileDataFlags.Exploded;
} }
if (MainProjectile.AttachedEntity!=null) if (MainProjectile.AttachedEntity != null)
{ {
p.Flags |= ProjectileDataFlags.IsAttached; p.Flags |= ProjectileDataFlags.IsAttached;
} }
@ -51,44 +50,44 @@ namespace RageCoop.Client
if (_firstSend) if (_firstSend)
{ {
p.Flags |= ProjectileDataFlags.IsAttached; p.Flags |= ProjectileDataFlags.IsAttached;
_firstSend=false; _firstSend = false;
} }
} }
public SyncedProjectile(Projectile p) public SyncedProjectile(Projectile p)
{ {
var owner = p.OwnerEntity; var owner = p.OwnerEntity;
if (owner==null) { IsValid=false;return; } if (owner == null) { IsValid = false; return; }
ID=EntityPool.RequestNewID(); ID = EntityPool.RequestNewID();
MainProjectile = p; MainProjectile = p;
Origin=p.Position; Origin = p.Position;
if(EntityPool.PedsByHandle.TryGetValue(owner.Handle,out var shooter)) if (EntityPool.PedsByHandle.TryGetValue(owner.Handle, out var shooter))
{ {
if (shooter.MainPed!=null if (shooter.MainPed != null
&& (p.AttachedEntity==shooter.MainPed.Weapons.CurrentWeaponObject && (p.AttachedEntity == shooter.MainPed.Weapons.CurrentWeaponObject
|| p.AttachedEntity== shooter.MainPed)) || p.AttachedEntity == shooter.MainPed))
{ {
// Reloading // Reloading
IsValid=false; IsValid = false;
return; return;
} }
Shooter=shooter; Shooter = shooter;
IsLocal=shooter.IsLocal; IsLocal = shooter.IsLocal;
} }
else if(EntityPool.VehiclesByHandle.TryGetValue(owner.Handle,out var shooterVeh)) else if (EntityPool.VehiclesByHandle.TryGetValue(owner.Handle, out var shooterVeh))
{ {
Shooter=shooterVeh; Shooter = shooterVeh;
IsLocal=shooterVeh.IsLocal; IsLocal = shooterVeh.IsLocal;
} }
else else
{ {
IsValid=false; IsValid = false;
} }
} }
public SyncedProjectile(int id) public SyncedProjectile(int id)
{ {
ID= id; ID = id;
IsLocal=false; IsLocal = false;
} }
internal override void Update() internal override void Update()
{ {
@ -101,27 +100,26 @@ namespace RageCoop.Client
CreateProjectile(); CreateProjectile();
return; return;
} }
MainProjectile.Velocity=Velocity+(Position+Shooter.Owner.PacketTravelTime*Velocity-MainProjectile.Position); MainProjectile.Velocity = Velocity + 10 * (Predict(Position) - MainProjectile.Position);
MainProjectile.Rotation=Rotation; MainProjectile.Rotation = Rotation;
LastUpdated=Main.Ticked; LastUpdated = Main.Ticked;
} }
private void CreateProjectile() private void CreateProjectile()
{ {
Asset=new WeaponAsset(WeaponHash); Asset = new WeaponAsset(WeaponHash);
if (!Asset.IsLoaded) { Asset.Request(); return; } if (!Asset.IsLoaded) { Asset.Request(); return; }
if(Shooter == null) { return; } if (Shooter == null) { return; }
Entity owner; Entity owner;
owner=(Shooter as SyncedPed)?.MainPed ?? (Entity)(Shooter as SyncedVehicle)?.MainVehicle; owner = (Shooter as SyncedPed)?.MainPed ?? (Entity)(Shooter as SyncedVehicle)?.MainVehicle;
var end = Position+Velocity; Position = (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Shooter.Velocity + Position;
Function.Call(Hash.SHOOT_SINGLE_BULLET_BETWEEN_COORDS_IGNORE_ENTITY, Position.X, Position.Y, Position.Z, end.X, end.Y, end.Z, 0, 1, WeaponHash, owner?.Handle ?? 0, 1, 0, -1,owner); var end = Position + Velocity;
Function.Call(Hash.SHOOT_SINGLE_BULLET_BETWEEN_COORDS_IGNORE_ENTITY, Position.X, Position.Y, Position.Z, end.X, end.Y, end.Z, 0, 1, WeaponHash, owner?.Handle ?? 0, 1, 0, -1);
var ps = World.GetAllProjectiles(); var ps = World.GetAllProjectiles();
MainProjectile=ps[ps.Length-1]; MainProjectile = ps[ps.Length - 1];
MainProjectile.IsCollisionEnabled=false; MainProjectile.Position = Position;
MainProjectile.Position=Position; MainProjectile.Rotation = Rotation;
MainProjectile.Rotation =Rotation; MainProjectile.Velocity = Velocity;
MainProjectile.Velocity=Velocity;
Main.Delay(()=>MainProjectile.IsCollisionEnabled=true, 100);
EntityPool.Add(this); EntityPool.Add(this);
} }
} }

View File

@ -9,7 +9,7 @@ namespace RageCoop.Client
{ {
internal SyncedProp(int id) internal SyncedProp(int id)
{ {
ID= id; ID = id;
} }
/// <summary> /// <summary>
/// The real entity /// The real entity
@ -27,15 +27,15 @@ namespace RageCoop.Client
{ {
if (!NeedUpdate) { return; } if (!NeedUpdate) { return; }
if (MainProp== null || !MainProp.Exists()) if (MainProp == null || !MainProp.Exists())
{ {
MainProp=World.CreateProp(Model, Position, Rotation, false, false); MainProp = World.CreateProp(Model, Position, Rotation, false, false);
MainProp.IsInvincible=true; MainProp.IsInvincible = true;
} }
MainProp.Position=Position; MainProp.Position = Position;
MainProp.Rotation=Rotation; MainProp.Rotation = Rotation;
MainProp.SetFrozen(true); MainProp.SetFrozen(true);
LastUpdated=Main.Ticked; LastUpdated = Main.Ticked;
} }
} }
} }

View File

@ -1,13 +1,12 @@
using System;
using RageCoop.Core;
using GTA; using GTA;
using System.Diagnostics;
using System.Collections.Generic;
using GTA.Math; using GTA.Math;
using GTA.Native; using RageCoop.Core;
using System.Collections.Generic;
namespace RageCoop.Client{ namespace RageCoop.Client
public partial class SyncedVehicle{ {
public partial class SyncedVehicle
{
public Vehicle MainVehicle { get; internal set; } public Vehicle MainVehicle { get; internal set; }
@ -68,11 +67,9 @@ namespace RageCoop.Client{
private bool _lastHornActive = false; private bool _lastHornActive = false;
private bool _lastTransformed = false; private bool _lastTransformed = false;
internal int _lastLivery = -1; internal int _lastLivery = -1;
List<Vector3> _predictedTrace = new List<Vector3>(); private readonly List<Vector3> _predictedTrace = new List<Vector3>();
List<Vector3> _orgTrace = new List<Vector3>(); private readonly List<Vector3> _orgTrace = new List<Vector3>();
private Vector3 _predictedPosition; private Vector3 _predictedPosition;
float _elapsed;
#endregion #endregion
#region OUTGOING #region OUTGOING

View File

@ -4,8 +4,6 @@ using GTA.Native;
using RageCoop.Core; using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -24,22 +22,24 @@ namespace RageCoop.Client
internal SyncedVehicle(Vehicle v) internal SyncedVehicle(Vehicle v)
{ {
ID=EntityPool.RequestNewID(); ID = EntityPool.RequestNewID();
MainVehicle=v; MainVehicle = v;
MainVehicle.CanPretendOccupants=false; MainVehicle.CanPretendOccupants = false;
OwnerID=Main.LocalPlayerID; OwnerID = Main.LocalPlayerID;
SetUpFixedData(); SetUpFixedData();
} }
private void SetUpFixedData(){ internal void SetUpFixedData()
{
if (MainVehicle == null) { return; }
IsAircraft = MainVehicle.IsAircraft; IsAircraft = MainVehicle.IsAircraft;
IsMotorcycle = MainVehicle.IsMotorcycle; IsMotorcycle = MainVehicle.IsMotorcycle;
HasRocketBoost = MainVehicle.HasRocketBoost; HasRocketBoost = MainVehicle.HasRocketBoost;
HasParachute = MainVehicle.HasParachute; HasParachute = MainVehicle.HasParachute;
HasRoof = MainVehicle.HasRoof; HasRoof = MainVehicle.HasRoof;
IsSubmarineCar=MainVehicle.IsSubmarineCar; IsSubmarineCar = MainVehicle.IsSubmarineCar;
IsDeluxo=MainVehicle.Model==1483171323; IsDeluxo = MainVehicle.Model == 1483171323;
} }
/// <summary> /// <summary>
@ -51,8 +51,8 @@ namespace RageCoop.Client
} }
internal SyncedVehicle(int id) internal SyncedVehicle(int id)
{ {
ID=id; ID = id;
LastSynced=Main.Ticked; LastSynced = Main.Ticked;
} }
#endregion #endregion
/// <summary> /// <summary>
@ -97,8 +97,8 @@ namespace RageCoop.Client
{ {
MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * SteeringAngle); MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * SteeringAngle);
} }
MainVehicle.ThrottlePower=ThrottlePower; MainVehicle.ThrottlePower = ThrottlePower;
MainVehicle.BrakePower=BrakePower; MainVehicle.BrakePower = BrakePower;
if (IsDead) if (IsDead)
{ {
@ -119,7 +119,7 @@ namespace RageCoop.Client
{ {
MainVehicle.Repair(); MainVehicle.Repair();
} }
},1000); }, 1000);
} }
} }
if (MainVehicle.IsOnFire) if (MainVehicle.IsOnFire)
@ -149,8 +149,6 @@ namespace RageCoop.Client
MainVehicle.AreHighBeamsOn = HighBeamsOn; MainVehicle.AreHighBeamsOn = HighBeamsOn;
} }
if (IsAircraft) if (IsAircraft)
{ {
if (LandingGear != (byte)MainVehicle.LandingGearState) if (LandingGear != (byte)MainVehicle.LandingGearState)
@ -179,15 +177,17 @@ namespace RageCoop.Client
MainVehicle.SoundHorn(1); MainVehicle.SoundHorn(1);
} }
if (HasRoof && MainVehicle.RoofState!=RoofState) if (HasRoof && MainVehicle.RoofState != RoofState)
{ {
MainVehicle.RoofState=RoofState; MainVehicle.RoofState = RoofState;
} }
if(HasRocketBoost && Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive) != MainVehicle.IsRocketBoostActive()){ if (HasRocketBoost && Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive) != MainVehicle.IsRocketBoostActive())
{
MainVehicle.SetRocketBoostActive(Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive)); MainVehicle.SetRocketBoostActive(Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive));
} }
if(HasParachute && Flags.HasFlag(VehicleDataFlags.IsParachuteActive) != MainVehicle.IsParachuteActive()){ if (HasParachute && Flags.HasFlag(VehicleDataFlags.IsParachuteActive) != MainVehicle.IsParachuteActive())
{
MainVehicle.SetParachuteActive(Flags.HasFlag(VehicleDataFlags.IsParachuteActive)); MainVehicle.SetParachuteActive(Flags.HasFlag(VehicleDataFlags.IsParachuteActive));
} }
if (IsSubmarineCar) if (IsSubmarineCar)
@ -197,16 +197,16 @@ namespace RageCoop.Client
if (!_lastTransformed) if (!_lastTransformed)
{ {
_lastTransformed = true; _lastTransformed = true;
Function.Call(Hash._TRANSFORM_VEHICLE_TO_SUBMARINE, MainVehicle.Handle, false); Function.Call(Hash.TRANSFORM_TO_SUBMARINE, MainVehicle.Handle, false);
} }
} }
else if (_lastTransformed) else if (_lastTransformed)
{ {
_lastTransformed = false; _lastTransformed = false;
Function.Call(Hash._TRANSFORM_SUBMARINE_TO_VEHICLE, MainVehicle.Handle, false); Function.Call(Hash.TRANSFORM_TO_CAR, MainVehicle.Handle, false);
} }
} }
else if(IsDeluxo) else if (IsDeluxo)
{ {
MainVehicle.SetDeluxoHoverState(IsDeluxoHovering); MainVehicle.SetDeluxoHoverState(IsDeluxoHovering);
if (IsDeluxoHovering) if (IsDeluxoHovering)
@ -219,9 +219,9 @@ namespace RageCoop.Client
} }
MainVehicle.LockStatus=LockStatus; MainVehicle.LockStatus = LockStatus;
if (LastFullSynced>=LastUpdated) if (LastFullSynced >= LastUpdated)
{ {
if (Flags.HasVehFlag(VehicleDataFlags.Repaired)) if (Flags.HasVehFlag(VehicleDataFlags.Repaired))
{ {
@ -233,7 +233,7 @@ namespace RageCoop.Client
_lastVehicleColors = Colors; _lastVehicleColors = Colors;
} }
MainVehicle.EngineHealth=EngineHealth; MainVehicle.EngineHealth = EngineHealth;
if (Mods != null && !Mods.Compare(_lastVehicleMods)) if (Mods != null && !Mods.Compare(_lastVehicleMods))
{ {
Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0); Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0);
@ -246,21 +246,22 @@ namespace RageCoop.Client
_lastVehicleMods = Mods; _lastVehicleMods = Mods;
} }
if (Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle)!=LicensePlate) if (Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle) != LicensePlate)
{ {
Function.Call(Hash.SET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle, LicensePlate); Function.Call(Hash.SET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle, LicensePlate);
} }
if (_lastLivery!=Livery) if (_lastLivery != Livery)
{ {
Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, Livery); Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, Livery);
_lastLivery=Livery; _lastLivery = Livery;
} }
MainVehicle.SetDamageModel(DamageModel); MainVehicle.SetDamageModel(DamageModel);
} }
LastUpdated=Main.Ticked; LastUpdated = Main.Ticked;
} }
void DisplayVehicle()
private void DisplayVehicle()
{ {
_predictedPosition = Predict(Position); _predictedPosition = Predict(Position);
var current = MainVehicle.ReadPosition(); var current = MainVehicle.ReadPosition();
@ -274,37 +275,34 @@ namespace RageCoop.Client
MainVehicle.Quaternion = Quaternion; MainVehicle.Quaternion = Quaternion;
return; return;
} }
else if (dist > 0.03) if (dist > 0.03)
{ {
MainVehicle.Velocity = Velocity + cali; MainVehicle.Velocity = Velocity + cali;
} }
Vector3 calirot; Vector3 calirot;
if (IsFlipped || (calirot = GetCalibrationRotation()).Length()>50) if (IsFlipped || (calirot = GetCalibrationRotation()).Length() > 50)
{ {
MainVehicle.Quaternion = Quaternion.Slerp(MainVehicle.ReadQuaternion(), Quaternion, 0.5f); MainVehicle.Quaternion = Quaternion.Slerp(MainVehicle.ReadQuaternion(), Quaternion, 0.5f);
MainVehicle.RotationVelocity = RotationVelocity; MainVehicle.RotationVelocity = RotationVelocity;
return; return;
} }
else MainVehicle.RotationVelocity = RotationVelocity + calirot * 0.2f;
{
MainVehicle.RotationVelocity = RotationVelocity + calirot * 0.2f;
}
} }
private Vector3 GetCalibrationRotation() private Vector3 GetCalibrationRotation()
{ {
var rot = Quaternion.LookRotation(Quaternion*Vector3.RelativeFront, Quaternion*Vector3.RelativeTop).ToEulerAngles(); var rot = Quaternion.LookRotation(Quaternion * Vector3.RelativeFront, Quaternion * Vector3.RelativeTop).ToEulerAngles();
var curRot = Quaternion.LookRotation(MainVehicle.ReadQuaternion()*Vector3.RelativeFront, MainVehicle.ReadQuaternion()*Vector3.RelativeTop).ToEulerAngles(); var curRot = Quaternion.LookRotation(MainVehicle.ReadQuaternion() * Vector3.RelativeFront, MainVehicle.ReadQuaternion() * Vector3.RelativeTop).ToEulerAngles();
var r = (rot-curRot).ToDegree(); var r = (rot - curRot).ToDegree();
if (r.X>180) { r.X=r.X-360; } if (r.X > 180) { r.X = r.X - 360; }
else if (r.X<-180) { r.X=360+r.X; } else if (r.X < -180) { r.X = 360 + r.X; }
if (r.Y>180) { r.Y=r.Y-360; } if (r.Y > 180) { r.Y = r.Y - 360; }
else if (r.Y<-180) { r.Y=360+r.Y; } else if (r.Y < -180) { r.Y = 360 + r.Y; }
if (r.Z>180) { r.Z=r.Z-360; } if (r.Z > 180) { r.Z = r.Z - 360; }
else if (r.Z<-180) { r.Z=360+r.Z; } else if (r.Z < -180) { r.Z = 360 + r.Z; }
return r; return r;
} }
private bool CreateVehicle() private bool CreateVehicle()
@ -316,7 +314,7 @@ namespace RageCoop.Client
// GTA.UI.Notification.Show($"~r~(Vehicle)Model ({CurrentVehicleModelHash}) cannot be loaded!"); // GTA.UI.Notification.Show($"~r~(Vehicle)Model ({CurrentVehicleModelHash}) cannot be loaded!");
return false; return false;
} }
else if (MainVehicle==null) if (MainVehicle == null)
{ {
Model.Request(); Model.Request();
return false; return false;
@ -328,18 +326,18 @@ namespace RageCoop.Client
MainVehicle.Quaternion = Quaternion; MainVehicle.Quaternion = Quaternion;
if (MainVehicle.HasRoof) if (MainVehicle.HasRoof)
{ {
MainVehicle.RoofState=RoofState; MainVehicle.RoofState = RoofState;
} }
foreach(var w in MainVehicle.Wheels) foreach (var w in MainVehicle.Wheels)
{ {
w.Fix(); w.Fix();
} }
if (IsInvincible) { MainVehicle.IsInvincible=true; } if (IsInvincible) { MainVehicle.IsInvincible = true; }
SetUpFixedData(); SetUpFixedData();
Model.MarkAsNoLongerNeeded(); Model.MarkAsNoLongerNeeded();
return true; return true;
} }
#region -- PEDALING -- #region -- PEDALING --
/* /*
* Thanks to @oldnapalm. * Thanks to @oldnapalm.
*/ */
@ -366,7 +364,7 @@ namespace RageCoop.Client
private void StartPedalingAnim(bool fast) private void StartPedalingAnim(bool fast)
{ {
MainVehicle.Driver?.Task.PlayAnimation(PedalingAnimDict(), PedalingAnimName(fast), 8.0f, -8.0f, -1, AnimationFlags.Loop | AnimationFlags.AllowRotation, 1.0f); MainVehicle.Driver?.Task.PlayAnimation(PedalingAnimDict(), PedalingAnimName(fast), 8.0f, -8.0f, -1, AnimationFlags.Loop | AnimationFlags.Secondary, 1.0f);
} }
@ -374,8 +372,6 @@ namespace RageCoop.Client
{ {
MainVehicle.Driver.Task.ClearAnimation(PedalingAnimDict(), PedalingAnimName(fast)); MainVehicle.Driver.Task.ClearAnimation(PedalingAnimDict(), PedalingAnimName(fast));
} }
#endregion #endregion
} }
} }

View File

@ -1,18 +1,17 @@
using GTA; using GTA;
using GTA.Native; using GTA.Native;
using Lidgren.Network;
using RageCoop.Client.Scripting; using RageCoop.Client.Scripting;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using Lidgren.Network;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal class EntityPool internal class EntityPool
{ {
public static object PedsLock = new object(); public static object PedsLock = new object();
public static int CharactersCount { get { return PedsByID.Count; } }
#if BENCHMARK #if BENCHMARK
private static Stopwatch PerfCounter=new Stopwatch(); private static Stopwatch PerfCounter=new Stopwatch();
private static Stopwatch PerfCounter2=Stopwatch.StartNew(); private static Stopwatch PerfCounter2=Stopwatch.StartNew();
@ -44,17 +43,17 @@ namespace RageCoop.Client
#endregion #endregion
public static void Cleanup(bool keepPlayer = true, bool keepMine = true) public static void Cleanup(bool keepPlayer = true, bool keepMine = true)
{ {
foreach (int id in new List<int>(PedsByID.Keys)) foreach (var ped in PedsByID.Values.ToArray())
{ {
if (keepPlayer && (id==Main.LocalPlayerID)|| keepMine && (PedsByID[id].OwnerID == Main.LocalPlayerID)) { continue; } if ((keepPlayer && (ped.ID == Main.LocalPlayerID)) || (keepMine && (ped.OwnerID == Main.LocalPlayerID))) { continue; }
RemovePed(id); RemovePed(ped.ID);
} }
PedsByID.Clear(); PedsByID.Clear();
PedsByHandle.Clear(); PedsByHandle.Clear();
foreach (int id in new List<int>(VehiclesByID.Keys)) foreach (int id in new List<int>(VehiclesByID.Keys))
{ {
if (keepMine&&(VehiclesByID[id].OwnerID==Main.LocalPlayerID)) { continue; } if (keepMine && (VehiclesByID[id].OwnerID == Main.LocalPlayerID)) { continue; }
RemoveVehicle(id); RemoveVehicle(id);
} }
VehiclesByID.Clear(); VehiclesByID.Clear();
@ -62,7 +61,7 @@ namespace RageCoop.Client
foreach (var p in ProjectilesByID.Values) foreach (var p in ProjectilesByID.Values)
{ {
if (p.Shooter.ID!=Main.LocalPlayerID && p.MainProjectile!=null && p.MainProjectile.Exists()) if (p.Shooter.ID != Main.LocalPlayerID && p.MainProjectile != null && p.MainProjectile.Exists())
{ {
p.MainProjectile.Delete(); p.MainProjectile.Delete();
} }
@ -96,7 +95,7 @@ namespace RageCoop.Client
// var clipset=p.Gender==Gender.Male? "MOVE_M@TOUGH_GUY@" : "MOVE_F@TOUGH_GUY@"; // var clipset=p.Gender==Gender.Male? "MOVE_M@TOUGH_GUY@" : "MOVE_F@TOUGH_GUY@";
// Function.Call(Hash.SET_PED_MOVEMENT_CLIPSET,p,clipset,1f); // Function.Call(Hash.SET_PED_MOVEMENT_CLIPSET,p,clipset,1f);
SyncedPed player = GetPedByID(Main.LocalPlayerID); SyncedPed player = GetPedByID(Main.LocalPlayerID);
if (player==null) if (player == null)
{ {
Main.Logger.Debug($"Creating SyncEntity for player, handle:{p.Handle}"); Main.Logger.Debug($"Creating SyncEntity for player, handle:{p.Handle}");
SyncedPed c = new SyncedPed(p); SyncedPed c = new SyncedPed(p);
@ -133,16 +132,16 @@ namespace RageCoop.Client
{ {
if (PedsByID.ContainsKey(c.ID)) if (PedsByID.ContainsKey(c.ID))
{ {
PedsByID[c.ID]=c; PedsByID[c.ID] = c;
} }
else else
{ {
PedsByID.Add(c.ID, c); PedsByID.Add(c.ID, c);
} }
if (c.MainPed==null) { return; } if (c.MainPed == null) { return; }
if (PedsByHandle.ContainsKey(c.MainPed.Handle)) if (PedsByHandle.ContainsKey(c.MainPed.Handle))
{ {
PedsByHandle[c.MainPed.Handle]=c; PedsByHandle[c.MainPed.Handle] = c;
} }
else else
{ {
@ -159,7 +158,7 @@ namespace RageCoop.Client
{ {
SyncedPed c = PedsByID[id]; SyncedPed c = PedsByID[id];
var p = c.MainPed; var p = c.MainPed;
if (p!=null) if (p != null)
{ {
if (PedsByHandle.ContainsKey(p.Handle)) if (PedsByHandle.ContainsKey(p.Handle))
{ {
@ -184,23 +183,23 @@ namespace RageCoop.Client
#endregion #endregion
#region VEHICLES #region VEHICLES
public static SyncedVehicle GetVehicleByID(int id) => VehiclesByID.TryGetValue(id,out var v) ? v : null; public static SyncedVehicle GetVehicleByID(int id) => VehiclesByID.TryGetValue(id, out var v) ? v : null;
public static SyncedVehicle GetVehicleByHandle(int handle) => VehiclesByHandle.TryGetValue(handle,out var v) ? v : null; public static SyncedVehicle GetVehicleByHandle(int handle) => VehiclesByHandle.TryGetValue(handle, out var v) ? v : null;
public static List<int> GetVehicleIDs() => new List<int>(VehiclesByID.Keys); public static List<int> GetVehicleIDs() => new List<int>(VehiclesByID.Keys);
public static void Add(SyncedVehicle v) public static void Add(SyncedVehicle v)
{ {
if (VehiclesByID.ContainsKey(v.ID)) if (VehiclesByID.ContainsKey(v.ID))
{ {
VehiclesByID[v.ID]=v; VehiclesByID[v.ID] = v;
} }
else else
{ {
VehiclesByID.Add(v.ID, v); VehiclesByID.Add(v.ID, v);
} }
if (v.MainVehicle==null) { return; } if (v.MainVehicle == null) { return; }
if (VehiclesByHandle.ContainsKey(v.MainVehicle.Handle)) if (VehiclesByHandle.ContainsKey(v.MainVehicle.Handle))
{ {
VehiclesByHandle[v.MainVehicle.Handle]=v; VehiclesByHandle[v.MainVehicle.Handle] = v;
} }
else else
{ {
@ -217,7 +216,7 @@ namespace RageCoop.Client
{ {
SyncedVehicle v = VehiclesByID[id]; SyncedVehicle v = VehiclesByID[id];
var veh = v.MainVehicle; var veh = v.MainVehicle;
if (veh!=null) if (veh != null)
{ {
if (VehiclesByHandle.ContainsKey(veh.Handle)) if (VehiclesByHandle.ContainsKey(veh.Handle))
{ {
@ -239,27 +238,27 @@ namespace RageCoop.Client
#region PROJECTILES #region PROJECTILES
public static SyncedProjectile GetProjectileByID(int id) public static SyncedProjectile GetProjectileByID(int id)
{ {
return ProjectilesByID.TryGetValue(id,out var p) ? p : null; return ProjectilesByID.TryGetValue(id, out var p) ? p : null;
} }
public static void Add(SyncedProjectile p) public static void Add(SyncedProjectile p)
{ {
if (!p.IsValid) { return; } if (!p.IsValid) { return; }
if (p.WeaponHash==(WeaponHash)VehicleWeaponHash.Tank) if (p.WeaponHash == (WeaponHash)VehicleWeaponHash.Tank)
{ {
Networking.SendBullet(p.Position, p.Position+p.Velocity, (uint)VehicleWeaponHash.Tank, ((SyncedVehicle)p.Shooter).MainVehicle.Driver.GetSyncEntity().ID); Networking.SendBullet(p.Position, p.Position + p.Velocity, (uint)VehicleWeaponHash.Tank, ((SyncedVehicle)p.Shooter).MainVehicle.Driver.GetSyncEntity().ID);
} }
if (ProjectilesByID.ContainsKey(p.ID)) if (ProjectilesByID.ContainsKey(p.ID))
{ {
ProjectilesByID[p.ID]=p; ProjectilesByID[p.ID] = p;
} }
else else
{ {
ProjectilesByID.Add(p.ID, p); ProjectilesByID.Add(p.ID, p);
} }
if (p.MainProjectile==null) { return; } if (p.MainProjectile == null) { return; }
if (ProjectilesByHandle.ContainsKey(p.MainProjectile.Handle)) if (ProjectilesByHandle.ContainsKey(p.MainProjectile.Handle))
{ {
ProjectilesByHandle[p.MainProjectile.Handle]=p; ProjectilesByHandle[p.MainProjectile.Handle] = p;
} }
else else
{ {
@ -272,14 +271,14 @@ namespace RageCoop.Client
{ {
SyncedProjectile sp = ProjectilesByID[id]; SyncedProjectile sp = ProjectilesByID[id];
var p = sp.MainProjectile; var p = sp.MainProjectile;
if (p!=null) if (p != null)
{ {
if (ProjectilesByHandle.ContainsKey(p.Handle)) if (ProjectilesByHandle.ContainsKey(p.Handle))
{ {
ProjectilesByHandle.Remove(p.Handle); ProjectilesByHandle.Remove(p.Handle);
} }
Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}"); //Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
p.Explode(); if (sp.Exploded) p.Explode();
} }
ProjectilesByID.Remove(id); ProjectilesByID.Remove(id);
} }
@ -289,11 +288,11 @@ namespace RageCoop.Client
public static bool VehicleExists(int id) => VehiclesByID.ContainsKey(id); public static bool VehicleExists(int id) => VehiclesByID.ContainsKey(id);
public static bool ProjectileExists(int id) => ProjectilesByID.ContainsKey(id); public static bool ProjectileExists(int id) => ProjectilesByID.ContainsKey(id);
#endregion #endregion
static int vehStateIndex; private static int vehStateIndex;
static int pedStateIndex; private static int pedStateIndex;
static int vehStatesPerFrame; private static int vehStatesPerFrame;
static int pedStatesPerFrame; private static int pedStatesPerFrame;
static int i; private static int i;
public static Ped[] allPeds = new Ped[0]; public static Ped[] allPeds = new Ped[0];
public static Vehicle[] allVehicles = new Vehicle[0]; public static Vehicle[] allVehicles = new Vehicle[0];
public static Projectile[] allProjectiles = new Projectile[0]; public static Projectile[] allProjectiles = new Projectile[0];
@ -306,21 +305,10 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks;
#endif #endif
allPeds = World.GetAllPeds(); allPeds = World.GetAllPeds();
allVehicles=World.GetAllVehicles(); allVehicles = World.GetAllVehicles();
allProjectiles=World.GetAllProjectiles(); allProjectiles = World.GetAllProjectiles();
vehStatesPerFrame=allVehicles.Length*2/(int)Game.FPS+1; vehStatesPerFrame = allVehicles.Length * 2 / (int)Game.FPS + 1;
pedStatesPerFrame=allPeds.Length*2/(int)Game.FPS+1; pedStatesPerFrame = allPeds.Length * 2 / (int)Game.FPS + 1;
/*
if (Main.Ticked%50==0)
{
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 #if BENCHMARK
Debug.TimeStamps[TimeStamp.GetAllEntities]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.GetAllEntities]=PerfCounter.ElapsedTicks;
@ -342,10 +330,10 @@ namespace RageCoop.Client
// Outgoing sync // Outgoing sync
if (p.IsLocal) if (p.IsLocal)
{ {
if (p.MainProjectile.AttachedEntity==null) if (p.MainProjectile.AttachedEntity == null)
{ {
// Prevent projectiles from exploding next to vehicle // Prevent projectiles from exploding next to vehicle
if (p.WeaponHash==(WeaponHash)VehicleWeaponHash.Tank || p.MainProjectile.Position.DistanceTo(p.Origin)<2) if (p.WeaponHash == (WeaponHash)VehicleWeaponHash.Tank || (p.MainProjectile.OwnerEntity?.EntityType == EntityType.Vehicle && p.MainProjectile.Position.DistanceTo(p.Origin) < 2))
{ {
continue; continue;
} }
@ -366,26 +354,27 @@ namespace RageCoop.Client
} }
} }
i=-1; i = -1;
lock (PedsLock) lock (PedsLock)
{ {
EntityPool.AddPlayer(); AddPlayer();
foreach (Ped p in allPeds) foreach (Ped p in allPeds)
{ {
SyncedPed c = EntityPool.GetPedByHandle(p.Handle); SyncedPed c = GetPedByHandle(p.Handle);
if (c==null && (p!=Game.Player.Character)) List<PedHash> mainCharacters = new List<PedHash> { PedHash.Michael, PedHash.Franklin, PedHash.Franklin02, PedHash.Trevor };
if (c == null && p != Game.Player.Character && !mainCharacters.Contains((PedHash)p.Model.Hash))
{ {
if (allPeds.Length>Main.Settings.WorldPedSoftLimit && p.PopulationType != EntityPopulationType.RandomAmbient) if (allPeds.Length > Main.Settings.WorldPedSoftLimit && p.PopulationType == EntityPopulationType.RandomAmbient && !p.IsInVehicle())
{ {
p.Delete(); p.Delete();
continue; continue;
} }
// Main.Logger.Trace($"Creating SyncEntity for ped, handle:{p.Handle}"); // Main.Logger.Trace($"Creating SyncEntity for ped, handle:{p.Handle}");
c=new SyncedPed(p); c = new SyncedPed(p);
EntityPool.Add(c); Add(c);
} }
} }
#if BENCHMARK #if BENCHMARK
@ -393,18 +382,18 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.AddPeds]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.AddPeds]=PerfCounter.ElapsedTicks;
#endif #endif
var ps = PedsByID.Values.ToArray(); var ps = PedsByID.Values.ToArray();
pedStateIndex+=pedStatesPerFrame; pedStateIndex += pedStatesPerFrame;
if (pedStateIndex>=ps.Length) if (pedStateIndex >= ps.Length)
{ {
pedStateIndex=0; pedStateIndex = 0;
} }
foreach (SyncedPed c in ps) foreach (SyncedPed c in ps)
{ {
i++; i++;
if ((c.MainPed!=null)&&(!c.MainPed.Exists())) if ((c.MainPed != null) && (!c.MainPed.Exists()))
{ {
EntityPool.RemovePed(c.ID, "non-existent"); RemovePed(c.ID, "non-existent");
continue; continue;
} }
@ -417,7 +406,7 @@ namespace RageCoop.Client
// event check // event check
SyncEvents.Check(c); SyncEvents.Check(c);
Networking.SendPed(c, (i-pedStateIndex)<pedStatesPerFrame); Networking.SendPed(c, (i - pedStateIndex) < pedStatesPerFrame);
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.SendPed]=PerfCounter2.ElapsedTicks-start; Debug.TimeStamps[TimeStamp.SendPed]=PerfCounter2.ElapsedTicks-start;
#endif #endif
@ -441,27 +430,26 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.PedTotal]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.PedTotal]=PerfCounter.ElapsedTicks;
#endif #endif
} }
var check = Main.Ticked % 100 == 0;
i=-1; i = -1;
lock (VehiclesLock) lock (VehiclesLock)
{ {
foreach (Vehicle veh in allVehicles) foreach (Vehicle veh in allVehicles)
{ {
if (!VehiclesByHandle.ContainsKey(veh.Handle)) if (!VehiclesByHandle.ContainsKey(veh.Handle))
{ {
if (allVehicles.Length>Main.Settings.WorldVehicleSoftLimit) if (allVehicles.Length > Main.Settings.WorldVehicleSoftLimit)
{ {
var type = veh.PopulationType; var type = veh.PopulationType;
if (type==EntityPopulationType.RandomAmbient || type==EntityPopulationType.RandomParked) if (type == EntityPopulationType.RandomAmbient || type == EntityPopulationType.RandomParked)
{ {
foreach (var p in veh.Occupants) foreach (var p in veh.Occupants)
{ {
p.Delete(); p.Delete();
var c = EntityPool.GetPedByHandle(p.Handle); var c = GetPedByHandle(p.Handle);
if (c!=null) if (c != null)
{ {
EntityPool.RemovePed(c.ID, "ThrottleTraffic"); RemovePed(c.ID, "ThrottleTraffic");
} }
} }
veh.Delete(); veh.Delete();
@ -477,28 +465,31 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.AddVehicles]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.AddVehicles]=PerfCounter.ElapsedTicks;
#endif #endif
var vs = VehiclesByID.Values.ToArray(); var vs = VehiclesByID.Values.ToArray();
vehStateIndex+=vehStatesPerFrame; vehStateIndex += vehStatesPerFrame;
if (vehStateIndex>=vs.Length) if (vehStateIndex >= vs.Length)
{ {
vehStateIndex=0; vehStateIndex = 0;
} }
foreach (SyncedVehicle v in vs) foreach (SyncedVehicle v in vs)
{ {
i++; i++;
if ((v.MainVehicle!=null)&&(!v.MainVehicle.Exists())) if ((v.MainVehicle != null) && (!v.MainVehicle.Exists()))
{ {
EntityPool.RemoveVehicle(v.ID, "non-existent"); RemoveVehicle(v.ID, "non-existent");
continue; continue;
} }
if (check)
{
v.SetUpFixedData();
}
// Outgoing sync // Outgoing sync
if (v.IsLocal) if (v.IsLocal)
{ {
if (!v.MainVehicle.IsVisible) { continue; } if (!v.MainVehicle.IsVisible) { continue; }
SyncEvents.Check(v); SyncEvents.Check(v);
Networking.SendVehicle(v, (i-vehStateIndex)<vehStatesPerFrame); Networking.SendVehicle(v, (i - vehStateIndex) < vehStatesPerFrame);
} }
else // Incoming sync else // Incoming sync
{ {
@ -516,12 +507,13 @@ namespace RageCoop.Client
} }
Networking.Peer.FlushSendQueue(); Networking.Peer.FlushSendQueue();
} }
static void UpdateTargets()
private static void UpdateTargets()
{ {
Networking.Targets=new List<NetConnection>(PlayerList.Players.Count) { Networking.ServerConnection }; Networking.Targets = new List<NetConnection>(PlayerList.Players.Count) { Networking.ServerConnection };
foreach (var p in PlayerList.Players.Values.ToArray()) foreach (var p in PlayerList.Players.Values.ToArray())
{ {
if (p.HasDirectConnection && p.Position.DistanceTo(Main.PlayerPosition)<500) if (p.HasDirectConnection && p.Position.DistanceTo(Main.PlayerPosition) < 500)
{ {
Networking.Targets.Add(p.Connection); Networking.Targets.Add(p.Connection);
} }
@ -532,7 +524,7 @@ namespace RageCoop.Client
{ {
foreach (SyncedPed p in PedsByID.Values.ToArray()) foreach (SyncedPed p in PedsByID.Values.ToArray())
{ {
if (p.OwnerID==playerPedId) if (p.OwnerID == playerPedId)
{ {
RemovePed(p.ID); RemovePed(p.ID);
} }
@ -540,7 +532,7 @@ namespace RageCoop.Client
foreach (SyncedVehicle v in VehiclesByID.Values.ToArray()) foreach (SyncedVehicle v in VehiclesByID.Values.ToArray())
{ {
if (v.OwnerID==playerPedId) if (v.OwnerID == playerPedId)
{ {
RemoveVehicle(v.ID); RemoveVehicle(v.ID);
} }
@ -550,7 +542,7 @@ namespace RageCoop.Client
public static int RequestNewID() public static int RequestNewID()
{ {
int ID = 0; int ID = 0;
while ((ID==0) || PedsByID.ContainsKey(ID) || VehiclesByID.ContainsKey(ID) || ProjectilesByID.ContainsKey(ID)) while ((ID == 0) || PedsByID.ContainsKey(ID) || VehiclesByID.ContainsKey(ID) || ProjectilesByID.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];
@ -581,21 +573,21 @@ namespace RageCoop.Client
{ {
public static void Add(SyncedVehicle v) public static void Add(SyncedVehicle v)
{ {
lock (EntityPool.VehiclesLock) lock (VehiclesLock)
{ {
EntityPool.Add(v); EntityPool.Add(v);
} }
} }
public static void Add(SyncedPed p) public static void Add(SyncedPed p)
{ {
lock (EntityPool.PedsLock) lock (PedsLock)
{ {
EntityPool.Add(p); EntityPool.Add(p);
} }
} }
public static void Add(SyncedProjectile sp) public static void Add(SyncedProjectile sp)
{ {
lock (EntityPool.ProjectilesLock) lock (ProjectilesLock)
{ {
EntityPool.Add(sp); EntityPool.Add(sp);
} }

View File

@ -1,10 +1,8 @@
using GTA; using GTA;
using GTA.Math; using GTA.Math;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using System; using System;
using System.Threading;
using System.Threading.Tasks;
using Lidgren.Network;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -13,7 +11,7 @@ namespace RageCoop.Client
#region TRIGGER #region TRIGGER
public static void TriggerPedKilled(SyncedPed victim) public static void TriggerPedKilled(SyncedPed victim)
{ {
Networking.SendSync(new Packets.PedKilled() { VictimID=victim.ID }, ConnectionChannel.SyncEvents); Networking.SendSync(new Packets.PedKilled() { VictimID = victim.ID }, ConnectionChannel.SyncEvents);
} }
public static void TriggerChangeOwner(int vehicleID, int newOwnerID) public static void TriggerChangeOwner(int vehicleID, int newOwnerID)
@ -21,9 +19,9 @@ namespace RageCoop.Client
Networking.SendSync(new Packets.OwnerChanged() Networking.SendSync(new Packets.OwnerChanged()
{ {
ID= vehicleID, ID = vehicleID,
NewOwnerID= newOwnerID, NewOwnerID = newOwnerID,
}, ConnectionChannel.SyncEvents,NetDeliveryMethod.ReliableOrdered); }, ConnectionChannel.SyncEvents, NetDeliveryMethod.ReliableOrdered);
} }
@ -33,11 +31,11 @@ namespace RageCoop.Client
var start = owner.MainPed.GetMuzzlePosition(); var start = owner.MainPed.GetMuzzlePosition();
if (owner.MainPed.IsOnTurretSeat()) { start=owner.MainPed.Bones[Bone.SkelHead].Position; } if (owner.MainPed.IsOnTurretSeat()) { start = owner.MainPed.Bones[Bone.SkelHead].Position; }
if (start.DistanceTo(impactPosition)>10) if (start.DistanceTo(impactPosition) > 10)
{ {
// Reduce latency // Reduce latency
start=impactPosition-(impactPosition-start).Normalized*10; start = impactPosition - (impactPosition - start).Normalized * 10;
} }
Networking.SendBullet(start, impactPosition, hash, owner.ID); Networking.SendBullet(start, impactPosition, hash, owner.ID);
} }
@ -47,14 +45,14 @@ namespace RageCoop.Client
int i; int i;
// ANNIHL // ANNIHL
if (veh.Model.Hash==837858166) if (veh.Model.Hash == 837858166)
{ {
Networking.SendVehicleBullet(hash, owner, veh.Bones[35]); Networking.SendVehicleBullet(hash, owner, veh.Bones[35]);
Networking.SendVehicleBullet(hash, owner, veh.Bones[36]); Networking.SendVehicleBullet(hash, owner, veh.Bones[36]);
Networking.SendVehicleBullet(hash, owner, veh.Bones[37]); Networking.SendVehicleBullet(hash, owner, veh.Bones[37]);
Networking.SendVehicleBullet(hash, owner, veh.Bones[38]); Networking.SendVehicleBullet(hash, owner, veh.Bones[38]);
} }
else if((i = veh.GetMuzzleIndex())!=-1) else if ((i = veh.GetMuzzleIndex()) != -1)
{ {
Networking.SendVehicleBullet(hash, owner, veh.Bones[i]); Networking.SendVehicleBullet(hash, owner, veh.Bones[i]);
} }
@ -65,7 +63,7 @@ namespace RageCoop.Client
} }
public static void TriggerNozzleTransform(int vehID, bool hover) public static void TriggerNozzleTransform(int vehID, bool hover)
{ {
Networking.SendSync(new Packets.NozzleTransform() { VehicleID=vehID, Hover=hover }, ConnectionChannel.SyncEvents); Networking.SendSync(new Packets.NozzleTransform() { VehicleID = vehID, Hover = hover }, ConnectionChannel.SyncEvents);
} }
#endregion #endregion
@ -73,9 +71,8 @@ namespace RageCoop.Client
#region HANDLE #region HANDLE
public static ParticleEffectAsset CorePFXAsset = new ParticleEffectAsset("core"); public static ParticleEffectAsset CorePFXAsset = new ParticleEffectAsset("core");
private static WeaponAsset _weaponAsset = default;
static WeaponAsset _weaponAsset = default; private static uint _lastWeaponHash;
static uint _lastWeaponHash;
private static void HandlePedKilled(Packets.PedKilled p) private static void HandlePedKilled(Packets.PedKilled p)
{ {
@ -84,11 +81,11 @@ namespace RageCoop.Client
private static void HandleOwnerChanged(Packets.OwnerChanged p) private static void HandleOwnerChanged(Packets.OwnerChanged p)
{ {
var v = EntityPool.GetVehicleByID(p.ID); var v = EntityPool.GetVehicleByID(p.ID);
if (v==null) { return; } if (v == null) { return; }
v.OwnerID=p.NewOwnerID; v.OwnerID = p.NewOwnerID;
v.LastSynced=Main.Ticked; v.LastSynced = Main.Ticked;
v.Position=v.MainVehicle.Position; v.Position = v.MainVehicle.Position;
v.Quaternion=v.MainVehicle.Quaternion; v.Quaternion = v.MainVehicle.Quaternion;
} }
private static void HandleNozzleTransform(Packets.NozzleTransform p) private static void HandleNozzleTransform(Packets.NozzleTransform p)
{ {
@ -100,47 +97,47 @@ namespace RageCoop.Client
{ {
// Minigun, not working for some reason // Minigun, not working for some reason
case (uint)WeaponHash.Minigun: case (uint)WeaponHash.Minigun:
weaponHash=1176362416; weaponHash = 1176362416;
break; break;
// Valkyire, not working for some reason // Valkyire, not working for some reason
case 2756787765: case 2756787765:
weaponHash=1176362416; weaponHash = 1176362416;
break; break;
// Tampa3, not working for some reason // Tampa3, not working for some reason
case 3670375085: case 3670375085:
weaponHash=1176362416; weaponHash = 1176362416;
break; break;
// Ruiner2, not working for some reason // Ruiner2, not working for some reason
case 50118905: case 50118905:
weaponHash=1176362416; weaponHash = 1176362416;
break; break;
// SAVAGE // SAVAGE
case 1638077257: case 1638077257:
weaponHash=(uint)VehicleWeaponHash.PlayerLazer; weaponHash = (uint)VehicleWeaponHash.PlayerLazer;
break; break;
case (uint)VehicleWeaponHash.PlayerBuzzard: case (uint)VehicleWeaponHash.PlayerBuzzard:
weaponHash=1176362416; weaponHash = 1176362416;
break; break;
} }
var p = EntityPool.GetPedByID(ownerID)?.MainPed; var p = EntityPool.GetPedByID(ownerID)?.MainPed;
if (p == null) { p=Game.Player.Character; Main.Logger.Warning("Failed to find owner for bullet"); } if (p == null) { return; /* p = Game.Player.Character; Main.Logger.Warning("Failed to find owner for bullet"); */ }
if (!CorePFXAsset.IsLoaded) { CorePFXAsset.Request(); } if (!CorePFXAsset.IsLoaded) { CorePFXAsset.Request(); }
if (_lastWeaponHash!=weaponHash) if (_lastWeaponHash != weaponHash)
{ {
_weaponAsset.MarkAsNoLongerNeeded(); _weaponAsset.MarkAsNoLongerNeeded();
_weaponAsset=new WeaponAsset(weaponHash); _weaponAsset = new WeaponAsset(weaponHash);
_lastWeaponHash=weaponHash; _lastWeaponHash = weaponHash;
} }
if (!_weaponAsset.IsLoaded) { _weaponAsset.Request(); } if (!_weaponAsset.IsLoaded) { _weaponAsset.Request(); }
World.ShootBullet(start, end, p, _weaponAsset, (int)p.GetWeaponDamage(weaponHash)); World.ShootBullet(start, end, p, _weaponAsset, (int)p.GetWeaponDamage(weaponHash));
Prop w; Prop w;
if (((w = p.Weapons.CurrentWeaponObject) != null)&&(p.VehicleWeapon==VehicleWeaponHash.Invalid)) if (((w = p.Weapons.CurrentWeaponObject) != null) && (p.VehicleWeapon == VehicleWeaponHash.Invalid))
{ {
if (p.Weapons.Current.Components.GetSuppressorComponent().Active) if (p.Weapons.Current.Components.GetSuppressorComponent().Active)
{ {
@ -154,52 +151,47 @@ namespace RageCoop.Client
} }
public static void HandleVehicleBulletShot(Packets.VehicleBulletShot p) public static void HandleVehicleBulletShot(Packets.VehicleBulletShot p)
{ {
HandleBulletShot(p.StartPosition,p.EndPosition,p.WeaponHash,p.OwnerID); HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID);
var v = EntityPool.GetPedByID(p.OwnerID)?.MainPed.CurrentVehicle; var v = EntityPool.GetPedByID(p.OwnerID)?.MainPed.CurrentVehicle;
if(v == null) { return; } if (v == null) { return; }
var b = v.Bones[p.Bone]; var b = v.Bones[p.Bone];
World.CreateParticleEffectNonLooped(CorePFXAsset, World.CreateParticleEffectNonLooped(CorePFXAsset,
WeaponUtil.GetFlashFX((WeaponHash)p.WeaponHash), WeaponUtil.GetFlashFX((WeaponHash)p.WeaponHash),
b.Position,b.ForwardVector.ToEulerRotation(v.Bones[35].UpVector),1); b.Position, b.ForwardVector.ToEulerRotation(v.Bones[35].UpVector), 1);
} }
public static void HandleEvent(PacketType type, byte[] data) public static void HandleEvent(PacketType type, NetIncomingMessage msg)
{ {
switch (type) switch (type)
{ {
case PacketType.BulletShot: case PacketType.BulletShot:
{ {
Packets.BulletShot p = new Packets.BulletShot(); Packets.BulletShot p = new Packets.BulletShot();
p.Deserialize(data); p.Deserialize(msg);
HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID); HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID);
break; break;
} }
case PacketType.VehicleBulletShot: case PacketType.VehicleBulletShot:
{ {
HandleVehicleBulletShot(data.GetPacket<Packets.VehicleBulletShot>()); HandleVehicleBulletShot(msg.GetPacket<Packets.VehicleBulletShot>());
break; break;
} }
case PacketType.OwnerChanged: case PacketType.OwnerChanged:
{ {
Packets.OwnerChanged packet = new Packets.OwnerChanged(); HandleOwnerChanged(msg.GetPacket<Packets.OwnerChanged>());
packet.Deserialize(data);
HandleOwnerChanged(packet);
} }
break; break;
case PacketType.PedKilled: case PacketType.PedKilled:
{ {
var packet = new Packets.PedKilled(); HandlePedKilled(msg.GetPacket<Packets.PedKilled>());
packet.Deserialize(data);
HandlePedKilled(packet);
} }
break; break;
case PacketType.NozzleTransform: case PacketType.NozzleTransform:
{ {
var packet = new Packets.NozzleTransform(); HandleNozzleTransform(msg.GetPacket<Packets.NozzleTransform>());
packet.Deserialize(data);
HandleNozzleTransform(packet);
break; break;
} }
} }
Networking.Peer.Recycle(msg);
} }
#endregion #endregion
@ -220,9 +212,9 @@ namespace RageCoop.Client
Func<bool> getBulletImpact = (() => Func<bool> getBulletImpact = (() =>
{ {
Vector3 endPos = subject.LastWeaponImpactPosition; Vector3 endPos = subject.LastWeaponImpactPosition;
if (endPos==default) if (endPos == default)
{ {
if (++i<=5) { return false; } if (++i <= 5) { return false; }
endPos = subject.GetAimCoord(); endPos = subject.GetAimCoord();
if (subject.IsInVehicle() && subject.VehicleWeapon != VehicleWeaponHash.Invalid) if (subject.IsInVehicle() && subject.VehicleWeapon != VehicleWeaponHash.Invalid)
@ -267,7 +259,7 @@ namespace RageCoop.Client
Main.QueueAction(getBulletImpact); Main.QueueAction(getBulletImpact);
} }
} }
else if (subject.VehicleWeapon==VehicleWeaponHash.Tank && subject.LastWeaponImpactPosition!=default) else if (subject.VehicleWeapon == VehicleWeaponHash.Tank && subject.LastWeaponImpactPosition != default)
{ {
TriggerBulletShot((uint)VehicleWeaponHash.Tank, c, subject.LastWeaponImpactPosition); TriggerBulletShot((uint)VehicleWeaponHash.Tank, c, subject.LastWeaponImpactPosition);
} }
@ -276,7 +268,7 @@ namespace RageCoop.Client
public static void Check(SyncedVehicle v) public static void Check(SyncedVehicle v)
{ {
if (v.MainVehicle==null||!v.MainVehicle.HasNozzle()) if (v.MainVehicle == null || !v.MainVehicle.HasNozzle())
{ {
return; return;
} }

View File

@ -1,6 +1,5 @@
using System.Threading; using NAudio.Wave;
using System.Threading;
using NAudio.Wave;
namespace RageCoop.Client namespace RageCoop.Client
{ {

View File

@ -0,0 +1,64 @@
using GTA;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Client
{
/// <summary>
/// Class providing support for addon mods
/// </summary>
internal class AddOnDataProvider
{
public static int GetMuzzleIndex(Model model)
{
switch (model.Hash)
{
// f14a2
case -848721350:
return 48;
// f15e
case 881261972:
return 32;
// f16c
case -2051171080:
return 25;
// F22A
case 2061630439:
return 14;
// f35c
case -343547392:
return 44;
// mig29a
case 513887552:
return 18;
// su30sm
case -733985185:
return 34;
// su33
case -722216722:
return 34;
// su35s
case -268602544:
return 28;
// su57
case 1490050781:
return 21;
default:
return -1;
}
}
}
}

View File

@ -1,18 +1,19 @@
 
using GTA;
using GTA.Math;
using RageCoop.Core;
using SHVDN;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using GTA.Math;
using GTA;
using SHVDN;
using RageCoop.Core;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal unsafe class MemPatch{ internal unsafe class MemPatch
private byte[] _data; {
private byte[] _orginal; private readonly byte[] _data;
private IntPtr _address; private readonly byte[] _orginal;
private readonly IntPtr _address;
public MemPatch(byte* address, byte[] data) public MemPatch(byte* address, byte[] data)
{ {
_data = data; _data = data;
@ -30,7 +31,7 @@ namespace RageCoop.Client
} }
} }
internal static unsafe class Memory internal static unsafe class Memory
{ {
public static MemPatch VignettingPatch; public static MemPatch VignettingPatch;
public static MemPatch VignettingCallPatch; public static MemPatch VignettingCallPatch;
@ -40,12 +41,12 @@ internal static unsafe class Memory
// Weapon/radio wheel slow-mo patch // Weapon/radio wheel slow-mo patch
// Thanks @CamxxCore, https://github.com/CamxxCore/GTAVWeaponWheelMod // Thanks @CamxxCore, https://github.com/CamxxCore/GTAVWeaponWheelMod
var result = NativeMemory.FindPattern("\x38\x51\x64\x74\x19", "xxxxx"); var result = NativeMemory.FindPattern("\x38\x51\x64\x74\x19", "xxxxx");
if(result == null) { throw new NotSupportedException("Can't find memory pattern to patch weapon/radio slow-mo"); } if (result == null) { throw new NotSupportedException("Can't find memory pattern to patch weapon/radio slow-mo"); }
var address = result+26; var address = result + 26;
address = address + *(int*)address + 4u; address = address + *(int*)address + 4u;
VignettingPatch=new MemPatch(address, new byte[] { RET, 0x90, 0x90, 0x90, 0x90 }); VignettingPatch = new MemPatch(address, new byte[] { RET, 0x90, 0x90, 0x90, 0x90 });
VignettingCallPatch=new MemPatch(result+8, new byte[]{ 0x90, 0x90, 0x90, 0x90, 0x90}); VignettingCallPatch = new MemPatch(result + 8, new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90 });
TimeScalePatch=new MemPatch(result+34, new byte[] { XOR_32_64, 0xD2 }); TimeScalePatch = new MemPatch(result + 34, new byte[] { XOR_32_64, 0xD2 });
} }
public static void ApplyPatches() public static void ApplyPatches()
@ -68,30 +69,30 @@ internal static unsafe class Memory
public const int MatrixOffset = 96; public const int MatrixOffset = 96;
#endregion #endregion
#region OPCODE #region OPCODE
const byte XOR_32_64 = 0x31; private const byte XOR_32_64 = 0x31;
const byte RET = 0xC3; private const byte RET = 0xC3;
#endregion #endregion
public static Vector3 ReadPosition(this Entity e) => ReadVector3(e.MemoryAddress + PositionOffset); public static Vector3 ReadPosition(this Entity e) => ReadVector3(e.MemoryAddress + PositionOffset);
public static Quaternion ReadQuaternion(this Entity e) => Quaternion.RotationMatrix(e.Matrix); public static Quaternion ReadQuaternion(this Entity e) => Quaternion.RotationMatrix(e.Matrix);
public static Vector3 ReadRotation(this Entity e) => e.ReadQuaternion().ToEulerDegrees(); public static Vector3 ReadRotation(this Entity e) => e.ReadQuaternion().ToEulerDegrees();
public static Vector3 ReadVelocity(this Ped e) => ReadVector3(e.MemoryAddress+VelocityOffset); public static Vector3 ReadVelocity(this Ped e) => ReadVector3(e.MemoryAddress + VelocityOffset);
public static Vector3 ReadVector3(IntPtr address) public static Vector3 ReadVector3(IntPtr address)
{ {
float* ptr = (float*)address.ToPointer(); float* ptr = (float*)address.ToPointer();
return new Vector3() return new Vector3()
{ {
X=*ptr, X = *ptr,
Y=ptr[1], Y = ptr[1],
Z=ptr[2] Z = ptr[2]
}; };
} }
public static List<int> FindOffset(float toSearch,IntPtr start, int range=1000, float tolerance = 0.01f) public static List<int> FindOffset(float toSearch, IntPtr start, int range = 1000, float tolerance = 0.01f)
{ {
var foundOffsets = new List<int>(100); var foundOffsets = new List<int>(100);
for (int i = 0; i <= range; i++) for (int i = 0; i <= range; i++)
{ {
var val = NativeMemory.ReadFloat(start+i); var val = NativeMemory.ReadFloat(start + i);
if (Math.Abs(val-toSearch)<tolerance) if (Math.Abs(val - toSearch) < tolerance)
{ {
foundOffsets.Add(i); foundOffsets.Add(i);
} }

View File

@ -1,152 +1,145 @@
using System; using GTA.Math;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Native; using GTA.Native;
using GTA.Math; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LemonUI.Elements;
using System.Drawing;
namespace RageCoop.Client namespace RageCoop.Client
{ {
[StructLayout(LayoutKind.Explicit, Size = 80)] [StructLayout(LayoutKind.Explicit, Size = 80)]
public struct HeadBlendData public struct HeadBlendData
{ {
[FieldOffset(0)] [FieldOffset(0)]
public int ShapeFirst; public int ShapeFirst;
[FieldOffset(8)] [FieldOffset(8)]
public int ShapeSecond; public int ShapeSecond;
[FieldOffset(16)] [FieldOffset(16)]
public int ShapeThird; public int ShapeThird;
[FieldOffset(24)] [FieldOffset(24)]
public int SkinFirst; public int SkinFirst;
[FieldOffset(32)] [FieldOffset(32)]
public int SkinSecond; public int SkinSecond;
[FieldOffset(40)] [FieldOffset(40)]
public int SkinThird; public int SkinThird;
[FieldOffset(48)] [FieldOffset(48)]
public float ShapeMix; public float ShapeMix;
[FieldOffset(56)] [FieldOffset(56)]
public float SkinMix; public float SkinMix;
[FieldOffset(64)] [FieldOffset(64)]
public float ThirdMix; public float ThirdMix;
} }
[StructLayout(LayoutKind.Explicit, Size = 24)] [StructLayout(LayoutKind.Explicit, Size = 24)]
public struct NativeVector3 public struct NativeVector3
{ {
[FieldOffset(0)] [FieldOffset(0)]
public float X; public float X;
[FieldOffset(8)] [FieldOffset(8)]
public float Y; public float Y;
[FieldOffset(16)] [FieldOffset(16)]
public float Z; public float Z;
public static implicit operator Vector3(NativeVector3 vec) public static implicit operator Vector3(NativeVector3 vec)
{ {
return new Vector3() { X=vec.X, Y=vec.Y, Z=vec.Z }; return new Vector3() { X = vec.X, Y = vec.Y, Z = vec.Z };
} }
public static implicit operator NativeVector3(Vector3 vec) public static implicit operator NativeVector3(Vector3 vec)
{ {
return new NativeVector3() { X=vec.X, Y=vec.Y, Z=vec.Z }; return new NativeVector3() { X = vec.X, Y = vec.Y, Z = vec.Z };
} }
} }
public static class NativeCaller public static class NativeCaller
{ {
// These are borrowed from ScriptHookVDotNet's // These are borrowed from ScriptHookVDotNet's
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeInit@@YAX_K@Z")] [DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeInit@@YAX_K@Z")]
static extern void NativeInit(ulong hash); private static extern void NativeInit(ulong hash);
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativePush64@@YAX_K@Z")] [DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativePush64@@YAX_K@Z")]
static extern void NativePush64(ulong val); private static extern void NativePush64(ulong val);
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeCall@@YAPEA_KXZ")] [DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeCall@@YAPEA_KXZ")]
static extern unsafe ulong* NativeCall(); private static extern unsafe ulong* NativeCall();
// These are from ScriptHookV's nativeCaller.h // These are from ScriptHookV's nativeCaller.h
static unsafe void NativePush<T>(T val) where T : unmanaged private static unsafe void NativePush<T>(T val) where T : unmanaged
{ {
ulong val64 = 0; ulong val64 = 0;
*(T*)(&val64) = val; *(T*)(&val64) = val;
NativePush64(val64); NativePush64(val64);
} }
public static unsafe R Invoke<R>(ulong hash) where R : unmanaged public static unsafe R Invoke<R>(ulong hash) where R : unmanaged
{ {
NativeInit(hash); NativeInit(hash);
return *(R*)(NativeCall()); return *(R*)(NativeCall());
} }
public static unsafe R Invoke<R>(Hash hash, params object[] args) public static unsafe R Invoke<R>(Hash hash, params object[] args)
where R : unmanaged where R : unmanaged
{ {
NativeInit((ulong)hash); NativeInit((ulong)hash);
var arguments=ConvertPrimitiveArguments(args); var arguments = ConvertPrimitiveArguments(args);
foreach (var arg in arguments) foreach (var arg in arguments)
NativePush(arg); NativePush(arg);
return *(R*)(NativeCall()); return *(R*)(NativeCall());
} }
/// <summary> /// <summary>
/// Helper function that converts an array of primitive values to a native stack. /// Helper function that converts an array of primitive values to a native stack.
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <returns></returns> /// <returns></returns>
static unsafe ulong[] ConvertPrimitiveArguments(object[] args) private static unsafe ulong[] ConvertPrimitiveArguments(object[] args)
{ {
var result = new ulong[args.Length]; var result = new ulong[args.Length];
for (int i = 0; i < args.Length; ++i) for (int i = 0; i < args.Length; ++i)
{ {
if (args[i] is bool valueBool) if (args[i] is bool valueBool)
{ {
result[i] = valueBool ? 1ul : 0ul; result[i] = valueBool ? 1ul : 0ul;
continue; continue;
} }
if (args[i] is byte valueByte) if (args[i] is byte valueByte)
{ {
result[i] = (ulong)valueByte; result[i] = valueByte;
continue; continue;
} }
if (args[i] is int valueInt32) if (args[i] is int valueInt32)
{ {
result[i] = (ulong)valueInt32; result[i] = (ulong)valueInt32;
continue; continue;
} }
if (args[i] is ulong valueUInt64) if (args[i] is ulong valueUInt64)
{ {
result[i] = valueUInt64; result[i] = valueUInt64;
continue; continue;
} }
if (args[i] is float valueFloat) if (args[i] is float valueFloat)
{ {
result[i] = *(ulong*)&valueFloat; result[i] = *(ulong*)&valueFloat;
continue; continue;
} }
if (args[i] is IntPtr valueIntPtr) if (args[i] is IntPtr valueIntPtr)
{ {
result[i] = (ulong)valueIntPtr.ToInt64(); result[i] = (ulong)valueIntPtr.ToInt64();
continue; continue;
} }
throw new ArgumentException("Unknown primitive type in native argument list", nameof(args)); throw new ArgumentException("Unknown primitive type in native argument list", nameof(args));
} }
return result; return result;
} }
} }
} }

View File

@ -2,8 +2,8 @@
using GTA.Math; using GTA.Math;
using GTA.Native; using GTA.Native;
using RageCoop.Core; using RageCoop.Core;
using System.Collections.Generic;
using System; using System;
using System.Collections.Generic;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -80,9 +80,9 @@ namespace RageCoop.Client
var result = new byte[36]; var result = new byte[36];
for (byte i = 0; i < 12; i++) for (byte i = 0; i < 12; i++)
{ {
result[i]=(byte)Function.Call<short>(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i); result[i] = (byte)Function.Call<short>(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i);
result[i+12]=(byte)Function.Call<short>(Hash.GET_PED_TEXTURE_VARIATION, ped.Handle, i); result[i + 12] = (byte)Function.Call<short>(Hash.GET_PED_TEXTURE_VARIATION, ped.Handle, i);
result[i+24]=(byte)Function.Call<short>(Hash.GET_PED_PALETTE_VARIATION, ped.Handle, i); result[i + 24] = (byte)Function.Call<short>(Hash.GET_PED_PALETTE_VARIATION, ped.Handle, i);
} }
return result; return result;
} }
@ -108,7 +108,7 @@ namespace RageCoop.Client
} }
// Fake death // Fake death
if (ped.IsRagdoll || (ped.Health==1 && ped.IsPlayer)) if (ped.IsRagdoll || (ped.Health == 1 && ped.IsPlayer))
{ {
flags |= PedDataFlags.IsRagdoll; flags |= PedDataFlags.IsRagdoll;
} }
@ -144,14 +144,15 @@ namespace RageCoop.Client
flags |= PedDataFlags.IsInCover; flags |= PedDataFlags.IsInCover;
if (ped.IsInCoverFacingLeft) if (ped.IsInCoverFacingLeft)
{ {
flags |=PedDataFlags.IsInCover; flags |= PedDataFlags.IsInCover;
} }
if (!Function.Call<bool>(Hash.IS_PED_IN_HIGH_COVER, ped)){ if (!Function.Call<bool>(Hash.IS_PED_IN_HIGH_COVER, ped))
flags|=PedDataFlags.IsInLowCover; {
flags |= PedDataFlags.IsInLowCover;
} }
if (ped.IsTaskActive(TaskType.CTaskAimGunBlindFire)) if (ped.IsTaskActive(TaskType.CTaskAimGunBlindFire))
{ {
flags|=PedDataFlags.IsBlindFiring; flags |= PedDataFlags.IsBlindFiring;
} }
} }
@ -276,7 +277,7 @@ namespace RageCoop.Client
veh, veh,
veh.Bones[text].Index veh.Bones[text].Index
})); }));
bool flag2 = (num2 < distanceToignoreDoors) && (num2 < num)&& IsSeatUsableByPed(ped, veh, dictionary[text]); bool flag2 = (num2 < distanceToignoreDoors) && (num2 < num) && IsSeatUsableByPed(ped, veh, dictionary[text]);
if (flag2) if (flag2)
{ {
num = num2; num = num2;
@ -295,7 +296,7 @@ namespace RageCoop.Client
{ {
result = true; result = true;
} }
else else if (veh.GetPedOnSeat(seat) != null)
{ {
bool isDead = veh.GetPedOnSeat(seat).IsDead; bool isDead = veh.GetPedOnSeat(seat).IsDead;
@ -330,29 +331,29 @@ namespace RageCoop.Client
var v = p.CurrentVehicle; var v = p.CurrentVehicle;
// Rhino // Rhino
if (v!=null && v.Model.Hash==782665360) if (v != null && v.Model.Hash == 782665360)
{ {
return v.Bones[35].Position+v.Bones[35].ForwardVector*100; return v.Bones[35].Position + v.Bones[35].ForwardVector * 100;
} }
if (p.IsOnTurretSeat()) { return p.GetLookingCoord(); } if (p.IsOnTurretSeat()) { return p.GetLookingCoord(); }
if (weapon!=null) if (weapon != null)
{ {
// Not very accurate, but doesn't matter // Not very accurate, but doesn't matter
Vector3 dir = weapon.RightVector; Vector3 dir = weapon.RightVector;
return weapon.Position+dir*20; return weapon.Position + dir * 20;
} }
return GetLookingCoord(p); return GetLookingCoord(p);
} }
public static Vector3 GetLookingCoord(this Ped p) public static Vector3 GetLookingCoord(this Ped p)
{ {
if (p==Main.P && Function.Call<int>(Hash.GET_FOLLOW_PED_CAM_VIEW_MODE)==4) if (p == Main.P && Function.Call<int>(Hash.GET_FOLLOW_PED_CAM_VIEW_MODE) == 4)
{ {
return RaycastEverything(default); return RaycastEverything(default);
} }
EntityBone b = p.Bones[Bone.FacialForehead]; EntityBone b = p.Bones[Bone.FacialForehead];
Vector3 v = b.UpVector.Normalized; Vector3 v = b.UpVector.Normalized;
return b.Position+200*v; return b.Position + 200 * v;
} }
public static VehicleSeat GetSeatTryingToEnter(this Ped p) public static VehicleSeat GetSeatTryingToEnter(this Ped p)
{ {
@ -475,7 +476,7 @@ namespace RageCoop.Client
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus3; || (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus3;
case 0: case 0:
return (VehicleHash)veh.Model.Hash == VehicleHash.Apc return (VehicleHash)veh.Model.Hash == VehicleHash.Apc
|| (VehicleHash)veh.Model.Hash==VehicleHash.Dune3; || (VehicleHash)veh.Model.Hash == VehicleHash.Dune3;
case 1: case 1:
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2 || (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2

View File

@ -6,13 +6,10 @@ using System;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using System.Xml.Serialization; using System.Xml.Serialization;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")] [assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
namespace RageCoop.Client namespace RageCoop.Client
@ -42,7 +39,7 @@ namespace RageCoop.Client
var res = ResolutionMaintainRatio; var res = ResolutionMaintainRatio;
if (Function.Call<bool>(Hash.GET_SCREEN_COORD_FROM_WORLD_COORD, pos.X, pos.Y, pos.Z, &x, &y)) if (Function.Call<bool>(Hash.GET_SCREEN_COORD_FROM_WORLD_COORD, pos.X, pos.Y, pos.Z, &x, &y))
{ {
screenPos =new Point((int)(res.Width*x), (int)(y*1080)); screenPos = new Point((int)(res.Width * x), (int)(y * 1080));
return true; return true;
} }
} }
@ -114,7 +111,7 @@ namespace RageCoop.Client
#endregion #endregion
public static string SettingsPath = "Scripts\\RageCoop\\Data\\RageCoop.Client.Settings.xml"; public static string SettingsPath = "Scripts\\RageCoop\\Data\\RageCoop.Client.Settings.xml";
public static Settings ReadSettings(string path=null) public static Settings ReadSettings(string path = null)
{ {
path = path ?? SettingsPath; path = path ?? SettingsPath;
XmlSerializer ser = new XmlSerializer(typeof(Settings)); XmlSerializer ser = new XmlSerializer(typeof(Settings));
@ -139,7 +136,7 @@ namespace RageCoop.Client
return settings; return settings;
} }
public static bool SaveSettings(string path = null,Settings settings=null) public static bool SaveSettings(string path = null, Settings settings = null)
{ {
try try
{ {
@ -192,7 +189,7 @@ namespace RageCoop.Client
{ {
if (p == null) { return null; } if (p == null) { return null; }
var c = EntityPool.GetPedByHandle(p.Handle); var c = EntityPool.GetPedByHandle(p.Handle);
if (c==null) { EntityPool.Add(c=new SyncedPed(p)); } if (c == null) { EntityPool.Add(c = new SyncedPed(p)); }
return c; return c;
} }
@ -200,7 +197,7 @@ namespace RageCoop.Client
{ {
if (veh == null) { return null; } if (veh == null) { return null; }
var v = EntityPool.GetVehicleByHandle(veh.Handle); var v = EntityPool.GetVehicleByHandle(veh.Handle);
if (v==null) { EntityPool.Add(v=new SyncedVehicle(veh)); } if (v == null) { EntityPool.Add(v = new SyncedVehicle(veh)); }
return v; return v;
} }
@ -219,7 +216,7 @@ namespace RageCoop.Client
#region WIN32 #region WIN32
const UInt32 WM_KEYDOWN = 0x0100; private const UInt32 WM_KEYDOWN = 0x0100;
public static void Reload() public static void Reload()
{ {
string reloadKey = "None"; string reloadKey = "None";
@ -227,18 +224,19 @@ namespace RageCoop.Client
foreach (var l in lines) foreach (var l in lines)
{ {
var ss = l.Split('='); var ss = l.Split('=');
if (ss.Length > 0 && ss[0]=="ReloadKey") if (ss.Length > 0 && ss[0] == "ReloadKey")
{ {
reloadKey = ss[1]; reloadKey = ss[1];
} }
} }
var lineList = lines.ToList(); var lineList = lines.ToList();
if (reloadKey=="None") if (reloadKey == "None")
{ {
foreach (var l in lines) foreach (var l in lines)
{ {
var ss = l.Split('='); var ss = l.Split('=');
if (ss.Length > 0 && ss[0]=="ReloadKey") ss.ForEach(s => s.Replace(" ", ""));
if (ss.Length > 0 && ss[0] == "ReloadKey")
{ {
reloadKey = ss[1]; reloadKey = ss[1];
lineList.Remove(l); lineList.Remove(l);
@ -253,7 +251,7 @@ namespace RageCoop.Client
// Move log file so it doesn't get deleted // Move log file so it doesn't get deleted
Main.Logger.Dispose(); Main.Logger.Dispose();
var path = Main.Logger.LogPath+".last.log"; var path = Main.Logger.LogPath + ".last.log";
try try
{ {
if (File.Exists(path)) { File.Delete(path); } if (File.Exists(path)) { File.Delete(path); }
@ -268,7 +266,7 @@ namespace RageCoop.Client
} }
[DllImport("user32.dll")] [DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam); private static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]

View File

@ -12,7 +12,7 @@ namespace RageCoop.Client
public static VehicleDataFlags GetVehicleFlags(this SyncedVehicle v) public static VehicleDataFlags GetVehicleFlags(this SyncedVehicle v)
{ {
var veh=v.MainVehicle; var veh = v.MainVehicle;
VehicleDataFlags flags = 0; VehicleDataFlags flags = 0;
if (veh.IsEngineRunning) if (veh.IsEngineRunning)
@ -50,51 +50,58 @@ namespace RageCoop.Client
flags |= VehicleDataFlags.IsHornActive; flags |= VehicleDataFlags.IsHornActive;
} }
if (v.IsSubmarineCar && Function.Call<bool>(Hash._GET_IS_SUBMARINE_VEHICLE_TRANSFORMED, veh.Handle)) if (v.IsSubmarineCar && Function.Call<bool>(Hash.IS_VEHICLE_IN_SUBMARINE_MODE, veh.Handle))
{ {
flags |= VehicleDataFlags.IsTransformed; flags |= VehicleDataFlags.IsTransformed;
} }
if (v.IsAircraft) if (v.IsAircraft)
{ {
flags |= VehicleDataFlags.IsAircraft; flags |= VehicleDataFlags.IsAircraft;
} }
if (v.IsDeluxo && veh.IsDeluxoHovering()) if (v.IsDeluxo && veh.IsDeluxoHovering())
{ {
flags|= VehicleDataFlags.IsDeluxoHovering; flags |= VehicleDataFlags.IsDeluxoHovering;
}
if (v.HasRoof)
{
flags|=VehicleDataFlags.HasRoof;
}
if (v.HasRocketBoost && veh.IsRocketBoostActive())
{
flags|=VehicleDataFlags.IsRocketBoostActive;
}
if(v.HasParachute && veh.IsParachuteActive()){
flags|=VehicleDataFlags.IsParachuteActive;
}
if (veh.IsOnFire)
{
flags|=VehicleDataFlags.IsOnFire;
} }
if (v.HasRoof)
{
flags |= VehicleDataFlags.HasRoof;
}
if (v.HasRocketBoost && veh.IsRocketBoostActive())
{
flags |= VehicleDataFlags.IsRocketBoostActive;
}
if (v.HasParachute && veh.IsParachuteActive())
{
flags |= VehicleDataFlags.IsParachuteActive;
}
if (veh.IsOnFire)
{
flags |= VehicleDataFlags.IsOnFire;
}
return flags; return flags;
} }
public static bool IsRocketBoostActive(this Vehicle veh) public static bool IsRocketBoostActive(this Vehicle veh)
{ {
return Function.Call<bool>(Hash._IS_VEHICLE_ROCKET_BOOST_ACTIVE,veh); return Function.Call<bool>(Hash.IS_ROCKET_BOOST_ACTIVE, veh);
} }
public static bool IsParachuteActive(this Vehicle veh){ public static bool IsParachuteActive(this Vehicle veh)
return Function.Call<bool>((Hash)0x3DE51E9C80B116CF,veh);
}
public static void SetRocketBoostActive(this Vehicle veh,bool toggle)
{ {
Function.Call(Hash._SET_VEHICLE_ROCKET_BOOST_ACTIVE,veh,toggle); return Function.Call<bool>((Hash)0x3DE51E9C80B116CF, veh);
} }
public static void SetParachuteActive(this Vehicle veh,bool toggle) public static void SetRocketBoostActive(this Vehicle veh, bool toggle)
{ {
Function.Call((Hash)0x0BFFB028B3DD0A97,veh,toggle); Function.Call(Hash.SET_ROCKET_BOOST_ACTIVE, veh, toggle);
}
public static void SetParachuteActive(this Vehicle veh, bool toggle)
{
Function.Call((Hash)0x0BFFB028B3DD0A97, veh, toggle);
} }
public static Dictionary<int, int> GetVehicleMods(this VehicleModCollection mods) public static Dictionary<int, int> GetVehicleMods(this VehicleModCollection mods)
{ {
@ -133,7 +140,6 @@ namespace RageCoop.Client
} }
} }
// Bursted tires // Bursted tires
short burstedTires = 0; short burstedTires = 0;
foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels()) foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels())
@ -176,7 +182,7 @@ namespace RageCoop.Client
} }
if ((model.OpenedDoors & (byte)(1 << i)) != 0) if ((model.OpenedDoors & (byte)(1 << i)) != 0)
{ {
if ((!door.IsOpen)&&(!door.IsBroken)) if ((!door.IsOpen) && (!door.IsBroken))
{ {
door.Open(); door.Open();
} }
@ -220,7 +226,7 @@ namespace RageCoop.Client
{ {
Dictionary<int, int> ps = new Dictionary<int, int>(); Dictionary<int, int> ps = new Dictionary<int, int>();
var d = veh.Driver; var d = veh.Driver;
if (d!=null&&d.IsSittingInVehicle()) if (d != null && d.IsSittingInVehicle())
{ {
ps.Add(-1, d.GetSyncEntity().ID); ps.Add(-1, d.GetSyncEntity().ID);
} }
@ -228,32 +234,31 @@ namespace RageCoop.Client
{ {
if (p.IsSittingInVehicle()) if (p.IsSittingInVehicle())
{ {
ps.Add((int)p.SeatIndex, (int)p.GetSyncEntity().ID); ps.Add((int)p.SeatIndex, p.GetSyncEntity().ID);
} }
} }
return ps; return ps;
} }
public static void SetDeluxoHoverState(this Vehicle deluxo, bool hover) public static void SetDeluxoHoverState(this Vehicle deluxo, bool hover)
{ {
Function.Call(Hash._SET_VEHICLE_HOVER_TRANSFORM_PERCENTAGE, deluxo, hover ? 1f : 0f); Function.Call(Hash.SET_SPECIAL_FLIGHT_MODE_TARGET_RATIO, deluxo, hover ? 1f : 0f);
} }
public static bool IsDeluxoHovering(this Vehicle deluxo) public static bool IsDeluxoHovering(this Vehicle deluxo)
{ {
return Math.Abs(deluxo.Bones[27].ForwardVector.GetCosTheta(deluxo.ForwardVector)-1)>0.05; return Math.Abs(deluxo.Bones[27].ForwardVector.GetCosTheta(deluxo.ForwardVector) - 1) > 0.05;
} }
public static void SetDeluxoWingRatio(this Vehicle v, float ratio) public static void SetDeluxoWingRatio(this Vehicle v, float ratio)
{ {
Function.Call(Hash._SET_SPECIALFLIGHT_WING_RATIO, v, ratio); Function.Call(Hash.SET_HOVER_MODE_WING_RATIO, v, ratio);
} }
public static float GetDeluxoWingRatio(this Vehicle v) public static float GetDeluxoWingRatio(this Vehicle v)
{ {
return v.Bones[99].Position.DistanceTo(v.Bones[92].Position)-1.43f; return v.Bones[99].Position.DistanceTo(v.Bones[92].Position) - 1.43f;
} }
public static float GetNozzleAngel(this Vehicle plane) public static float GetNozzleAngel(this Vehicle plane)
{ {
return Function.Call<float>(Hash._GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane); return Function.Call<float>(Hash.GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane);
} }
public static bool HasNozzle(this Vehicle v) public static bool HasNozzle(this Vehicle v)
{ {
@ -283,8 +288,6 @@ namespace RageCoop.Client
{ {
Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio); Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio);
} }
#endregion #endregion
} }
} }

View File

@ -2,7 +2,6 @@
using GTA.Math; using GTA.Math;
using GTA.Native; using GTA.Native;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -11,7 +10,7 @@ namespace RageCoop.Client
public MuzzleInfo(Vector3 pos, Vector3 forward) public MuzzleInfo(Vector3 pos, Vector3 forward)
{ {
Position = pos; Position = pos;
ForawardVector=forward; ForawardVector = forward;
} }
public Vector3 Position; public Vector3 Position;
public Vector3 ForawardVector; public Vector3 ForawardVector;
@ -38,7 +37,7 @@ namespace RageCoop.Client
public static Vector3 GetMuzzlePosition(this Ped p) public static Vector3 GetMuzzlePosition(this Ped p)
{ {
var w = p.Weapons.CurrentWeaponObject; var w = p.Weapons.CurrentWeaponObject;
if (w!=null) if (w != null)
{ {
var hash = p.Weapons.Current.Hash; var hash = p.Weapons.Current.Hash;
if (MuzzleBoneIndexes.ContainsKey(hash)) { return w.Bones[MuzzleBoneIndexes[hash]].Position; } if (MuzzleBoneIndexes.ContainsKey(hash)) { return w.Bones[MuzzleBoneIndexes[hash]].Position; }
@ -46,7 +45,8 @@ namespace RageCoop.Client
} }
return p.Bones[Bone.SkelRightHand].Position; return p.Bones[Bone.SkelRightHand].Position;
} }
static long BulletsShot = 0;
private static long BulletsShot = 0;
public static float GetWeaponDamage(this Ped P, uint hash) public static float GetWeaponDamage(this Ped P, uint hash)
{ {
@ -103,49 +103,49 @@ namespace RageCoop.Client
// ISSI6 // ISSI6
case 1239571361: case 1239571361:
return BulletsShot%2==0 ? 12 : 14; return BulletsShot % 2 == 0 ? 12 : 14;
// ISSI5 // ISSI5
case 1537277726: case 1537277726:
return BulletsShot%2==0 ? 30 : 32; return BulletsShot % 2 == 0 ? 30 : 32;
// ISSI4 // ISSI4
case 628003514: case 628003514:
return BulletsShot%2==0 ? 14 : 12; return BulletsShot % 2 == 0 ? 14 : 12;
// DOMINATOR6 // DOMINATOR6
case -1293924613: case -1293924613:
return BulletsShot%2==0 ? 51 : 55; return BulletsShot % 2 == 0 ? 51 : 55;
// IMPALER4 // IMPALER4
case -1744505657: case -1744505657:
return BulletsShot%2==0 ? 64 : 63; return BulletsShot % 2 == 0 ? 64 : 63;
// IMPERATOR3 // IMPERATOR3
case -755532233: case -755532233:
return BulletsShot%2==0 ? 86 : 88; return BulletsShot % 2 == 0 ? 86 : 88;
// SLAMVAN6 // SLAMVAN6
case 1742022738: case 1742022738:
return BulletsShot%2==0 ? 78 : 76; return BulletsShot % 2 == 0 ? 78 : 76;
// CHAMPION // CHAMPION
case -915234475: case -915234475:
return BulletsShot%2==0 ? 60 : 61; return BulletsShot % 2 == 0 ? 60 : 61;
// MONSTER4 // MONSTER4
case 840387324: case 840387324:
return BulletsShot%2==0 ? 63 : 65; return BulletsShot % 2 == 0 ? 63 : 65;
// BRUTUS2 // BRUTUS2
@ -154,7 +154,7 @@ namespace RageCoop.Client
// BRUISER2 // BRUISER2
case -1694081890: case -1694081890:
return BulletsShot%2==0 ? 45 : 51; return BulletsShot % 2 == 0 ? 45 : 51;
// TECHNICAL3 // TECHNICAL3
@ -171,11 +171,11 @@ namespace RageCoop.Client
// PATRIOT3 // PATRIOT3
case -670086588: case -670086588:
return BulletsShot%2==0 ? 87 : 89; return BulletsShot % 2 == 0 ? 87 : 89;
// NIGHTSHARK // NIGHTSHARK
case 433954513: case 433954513:
return BulletsShot%2==0 ? 1 : 2; return BulletsShot % 2 == 0 ? 1 : 2;
/* /*
// NIGHTSHARK (second) // NIGHTSHARK (second)
@ -185,7 +185,7 @@ namespace RageCoop.Client
// MENACER // MENACER
case 2044532910: case 2044532910:
return BulletsShot%2==0 ? 91 : 90; return BulletsShot % 2 == 0 ? 91 : 90;
/* /*
// MENACER // MENACER
case 2044532910: case 2044532910:
@ -222,11 +222,11 @@ namespace RageCoop.Client
// BLAZER5 // BLAZER5
case -1590337689: case -1590337689:
return BulletsShot%2==0 ? 17 : 18; return BulletsShot % 2 == 0 ? 17 : 18;
// BRUISER // BRUISER
case 668439077: case 668439077:
return BulletsShot%2==0 ? 66 : 68; return BulletsShot % 2 == 0 ? 66 : 68;
// BRUTUS // BRUTUS
@ -236,12 +236,12 @@ namespace RageCoop.Client
// MONSTER3 // MONSTER3
case 1721676810: case 1721676810:
return BulletsShot%2==0 ? 53 : 55; return BulletsShot % 2 == 0 ? 53 : 55;
// BRUISER3 // BRUISER3
case -2042350822: case -2042350822:
return BulletsShot%2==0 ? 52 : 50; return BulletsShot % 2 == 0 ? 52 : 50;
// BRUTUS3 // BRUTUS3
case 2038858402: case 2038858402:
@ -249,34 +249,34 @@ namespace RageCoop.Client
// MONSTER5 // MONSTER5
case -715746948: case -715746948:
return BulletsShot%2==0 ? 63 : 65; return BulletsShot % 2 == 0 ? 63 : 65;
// JB7002 // JB7002
case 394110044: case 394110044:
return BulletsShot%2==0 ? 54 : 53; return BulletsShot % 2 == 0 ? 54 : 53;
// DOMINATOR5 // DOMINATOR5
case -1375060657: case -1375060657:
return BulletsShot%2==0 ? 35 : 36; return BulletsShot % 2 == 0 ? 35 : 36;
// IMPALER3 // IMPALER3
case -1924800695: case -1924800695:
return BulletsShot%2==0 ? 75 : 76; return BulletsShot % 2 == 0 ? 75 : 76;
// IMPERATOR2 // IMPERATOR2
case 1637620610: case 1637620610:
return BulletsShot%2==0 ? 97 : 99; return BulletsShot % 2 == 0 ? 97 : 99;
// SLAMVAN5 // SLAMVAN5
case 373261600: case 373261600:
return BulletsShot%2==0 ? 51 : 53; return BulletsShot % 2 == 0 ? 51 : 53;
// RUINER2 // RUINER2
case 941494461: case 941494461:
return BulletsShot%2==0 ? 65 : 66; return BulletsShot % 2 == 0 ? 65 : 66;
// TAMPA3 // TAMPA3
@ -285,52 +285,52 @@ namespace RageCoop.Client
// SCRAMJET // SCRAMJET
case -638562243: case -638562243:
return BulletsShot%2==0 ? 44 : 45; return BulletsShot % 2 == 0 ? 44 : 45;
// VIGILANTE // VIGILANTE
case -1242608589: case -1242608589:
return BulletsShot%2==0 ? 42 : 43; return BulletsShot % 2 == 0 ? 42 : 43;
// ZR380 // ZR380
case 540101442: case 540101442:
return BulletsShot%2==0 ? 57 : 63; return BulletsShot % 2 == 0 ? 57 : 63;
// ZR3802 // ZR3802
case -1106120762: case -1106120762:
return BulletsShot%2==0 ? 57 : 63; return BulletsShot % 2 == 0 ? 57 : 63;
// ZR3803 // ZR3803
case -1478704292: case -1478704292:
return BulletsShot%2==0 ? 53 : 59; return BulletsShot % 2 == 0 ? 53 : 59;
// STROMBERG // STROMBERG
case 886810209: case 886810209:
return BulletsShot%2==0 ? 85 : 84; return BulletsShot % 2 == 0 ? 85 : 84;
// SLAMVAN4 // SLAMVAN4
case -2061049099: case -2061049099:
return BulletsShot%2==0 ? 76 : 78; return BulletsShot % 2 == 0 ? 76 : 78;
// IMPERATOR // IMPERATOR
case 444994115: case 444994115:
return BulletsShot%2==0 ? 88 : 86; return BulletsShot % 2 == 0 ? 88 : 86;
// IMPALER2 // IMPALER2
case 1009171724: case 1009171724:
return BulletsShot%2==0 ? 63 : 64; return BulletsShot % 2 == 0 ? 63 : 64;
// DOMINATOR4 // DOMINATOR4
case -688189648: case -688189648:
return BulletsShot%2==0 ? 59 : 60; return BulletsShot % 2 == 0 ? 59 : 60;
// SAVAGE // SAVAGE
@ -339,22 +339,22 @@ namespace RageCoop.Client
// BUZZARD // BUZZARD
case 788747387: case 788747387:
return BulletsShot%2==0 ? 28 : 23; return BulletsShot % 2 == 0 ? 28 : 23;
// ANNIHL // ANNIHL
case 837858166: case 837858166:
return (int)BulletsShot%4+35; return (int)BulletsShot % 4 + 35;
// HYDRA // HYDRA
case 970385471: case 970385471:
return BulletsShot%2==0 ? 29 : 28; return BulletsShot % 2 == 0 ? 29 : 28;
// STARLING // STARLING
case -1700874274: case -1700874274:
return BulletsShot%2==0 ? 24 : 12; return BulletsShot % 2 == 0 ? 24 : 12;
// RHINO // RHINO
@ -362,26 +362,20 @@ namespace RageCoop.Client
return 30; return 30;
default: default:
return -1; return AddOnDataProvider.GetMuzzleIndex(v.Model.Hash);
} }
} }
public static bool IsUsingProjectileWeapon(this Ped p) public static bool IsUsingProjectileWeapon(this Ped p)
{ {
var vp = p.VehicleWeapon; var vp = p.VehicleWeapon;
var type = Function.Call<int>(Hash.GET_WEAPON_DAMAGE_TYPE, vp); var type = Function.Call<int>(Hash.GET_WEAPON_DAMAGE_TYPE, vp);
if (vp!=VehicleWeaponHash.Invalid) if (vp != VehicleWeaponHash.Invalid)
{ {
if (type==3) return type == 3 ? false : VehicleProjectileWeapons.Contains(vp) || (type == 5 && !ExplosiveBullets.Contains((uint)vp));
{
return false;
}
return VehicleProjectileWeapons.Contains(vp) || (type==5 && !ExplosiveBullets.Contains((uint)vp));
}
else
{
var w = p.Weapons.Current;
return w.Group==WeaponGroup.Thrown || ProjectileWeapons.Contains(w.Hash);
} }
var w = p.Weapons.Current;
return w.Group == WeaponGroup.Thrown || ProjectileWeapons.Contains(w.Hash);
} }
public static readonly HashSet<uint> ExplosiveBullets = new HashSet<uint> public static readonly HashSet<uint> ExplosiveBullets = new HashSet<uint>
@ -460,7 +454,6 @@ namespace RageCoop.Client
}; };
public static readonly HashSet<WeaponHash> ProjectileWeapons = new HashSet<WeaponHash> { public static readonly HashSet<WeaponHash> ProjectileWeapons = new HashSet<WeaponHash> {
WeaponHash.HomingLauncher, WeaponHash.HomingLauncher,
WeaponHash.RPG, WeaponHash.RPG,

View File

@ -1,8 +1,6 @@
using GTA; using GTA;
using GTA.Native; using GTA.Native;
using System; using System;
using System.Threading.Tasks;
using System.Threading;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -22,28 +20,11 @@ namespace RageCoop.Client
{ {
ChangeTraffic(true); ChangeTraffic(true);
}; };
Task.Run(() =>
{
while (true)
{
Thread.Sleep(5000);
Main.QueueAction(() => { ChangeTraffic(_trafficEnabled); });
}
});
} }
static bool _trafficEnabled;
private static bool _trafficEnabled;
private void OnTick(object sender, EventArgs e) private void OnTick(object sender, EventArgs e)
{ {
if (!_trafficEnabled)
{
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
Function.Call(Hash.SET_RANDOM_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
Function.Call(Hash.SET_PARKED_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
Function.Call(Hash.SUPPRESS_SHOCKING_EVENTS_NEXT_FRAME);
Function.Call(Hash.SUPPRESS_AGITATION_EVENTS_NEXT_FRAME);
}
if (Game.IsLoading || !Networking.IsOnServer) if (Game.IsLoading || !Networking.IsOnServer)
{ {
return; return;
@ -84,6 +65,16 @@ namespace RageCoop.Client
} }
} }
} }
if (!_trafficEnabled)
{
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
Function.Call(Hash.SET_RANDOM_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
Function.Call(Hash.SET_PARKED_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
Function.Call(Hash.SUPPRESS_SHOCKING_EVENTS_NEXT_FRAME);
Function.Call(Hash.SUPPRESS_AGITATION_EVENTS_NEXT_FRAME);
}
} }
public static void Traffic(bool enable) public static void Traffic(bool enable)
{ {
@ -94,7 +85,7 @@ namespace RageCoop.Client
{ {
if (enable) if (enable)
{ {
// Function.Call(Hash.REMOVE_SCENARIO_BLOCKING_AREAS); Function.Call(Hash.REMOVE_SCENARIO_BLOCKING_AREAS);
Function.Call(Hash.SET_CREATE_RANDOM_COPS, true); Function.Call(Hash.SET_CREATE_RANDOM_COPS, true);
Function.Call(Hash.SET_RANDOM_TRAINS, true); Function.Call(Hash.SET_RANDOM_TRAINS, true);
Function.Call(Hash.SET_RANDOM_BOATS, true); Function.Call(Hash.SET_RANDOM_BOATS, true);
@ -107,9 +98,9 @@ namespace RageCoop.Client
Function.Call(Hash.SET_DISTANT_CARS_ENABLED, true); Function.Call(Hash.SET_DISTANT_CARS_ENABLED, true);
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, false); Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, false);
} }
else else if (Networking.IsOnServer)
{ {
// Function.Call(Hash.ADD_SCENARIO_BLOCKING_AREA, -10000.0f, -10000.0f, -1000.0f, 10000.0f, 10000.0f, 1000.0f, 0, 1, 1, 1); Function.Call(Hash.ADD_SCENARIO_BLOCKING_AREA, -10000.0f, -10000.0f, -1000.0f, 10000.0f, 10000.0f, 1000.0f, 0, 1, 1, 1);
Function.Call(Hash.SET_CREATE_RANDOM_COPS, false); Function.Call(Hash.SET_CREATE_RANDOM_COPS, false);
Function.Call(Hash.SET_RANDOM_TRAINS, false); Function.Call(Hash.SET_RANDOM_TRAINS, false);
Function.Call(Hash.SET_RANDOM_BOATS, false); Function.Call(Hash.SET_RANDOM_BOATS, false);
@ -122,64 +113,33 @@ namespace RageCoop.Client
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, 0); Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, 0);
Function.Call(Hash.SET_DISTANT_CARS_ENABLED, false); Function.Call(Hash.SET_DISTANT_CARS_ENABLED, false);
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, true); Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, true);
foreach (Ped ped in World.GetAllPeds())
if (Networking.IsOnServer)
{ {
if (ped == Game.Player.Character) { continue; }
foreach (Ped ped in World.GetAllPeds()) SyncedPed c = EntityPool.GetPedByHandle(ped.Handle);
if ((c == null) || (c.IsLocal && (ped.Handle != Game.Player.Character.Handle) && ped.PopulationType != EntityPopulationType.Mission))
{ {
SyncedPed c = EntityPool.GetPedByHandle(ped.Handle);
if ((c == null) || (c.IsLocal && (ped.Handle != Game.Player.Character.Handle) && ped.PopulationType != EntityPopulationType.Mission))
{
if (ped.Handle == Game.Player.Character.Handle) { continue; }
// Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic"); Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
ped.CurrentVehicle?.Delete(); ped.CurrentVehicle?.Delete();
ped.Kill(); ped.Kill();
ped.Delete(); ped.Delete();
}
}
foreach (Vehicle veh in World.GetAllVehicles())
{
SyncedVehicle v = veh.GetSyncEntity();
if (v.MainVehicle == Game.Player.LastVehicle)
{
// Don't delete player's vehicle
continue;
}
if ((v == null) || (v.IsLocal && veh.PopulationType != EntityPopulationType.Mission))
{
// Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
veh.Delete();
}
} }
} }
else
foreach (Vehicle veh in World.GetAllVehicles())
{ {
foreach (Ped ped in World.GetAllPeds()) SyncedVehicle v = veh.GetSyncEntity();
if (v.MainVehicle == Game.Player.LastVehicle || v.MainVehicle == Game.Player.Character.CurrentVehicle)
{ {
if ((ped != Game.Player.Character) && (ped.PopulationType != EntityPopulationType.Mission)) // Don't delete player's vehicle
{ continue;
// Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
ped.CurrentVehicle?.Delete();
ped.Kill();
ped.Delete();
}
} }
var last = Game.Player.Character.LastVehicle; if ((v == null) || (v.IsLocal && veh.PopulationType != EntityPopulationType.Mission))
var current = Game.Player.Character.CurrentVehicle;
foreach (Vehicle veh in World.GetAllVehicles())
{ {
if (veh.PopulationType != EntityPopulationType.Mission && veh != last && veh!=current) // Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
{
// Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
veh.Delete(); veh.Delete();
}
} }
} }
} }

View File

@ -1,15 +1,13 @@
using System; using GTA.Math;
using System.Text;
using System.Linq;
using GTA.Math;
using System.IO; using System.IO;
using System.Text;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal class BitReader:BinaryReader internal class BitReader : BinaryReader
{ {
public BitReader(byte[] array):base(new MemoryStream(array)) public BitReader(byte[] array) : base(new MemoryStream(array))
{ {
} }

View File

@ -1,17 +1,18 @@
using System; using GTA.Math;
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.Net.Http;
using System.Net.Sockets;
using System.IO;
using System.Runtime.CompilerServices;
using Lidgren.Network; using Lidgren.Network;
using Newtonsoft.Json; using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
[assembly: InternalsVisibleTo("RageCoop.Server")] [assembly: InternalsVisibleTo("RageCoop.Server")]
[assembly: InternalsVisibleTo("RageCoop.Client")] [assembly: InternalsVisibleTo("RageCoop.Client")]
@ -33,43 +34,44 @@ namespace RageCoop.Core
{ {
return ToIgnore.Contains(name); return ToIgnore.Contains(name);
} }
public static (byte, byte[]) GetBytesFromObject(object obj) public static void GetBytesFromObject(object obj, NetOutgoingMessage m)
{ {
switch (obj) switch (obj)
{ {
case byte _: case byte value:
return (0x01, new byte[] { (byte)obj }); m.Write((byte)0x01); m.Write(value); break;
case short _: case short value:
return (0x02, BitConverter.GetBytes((short)obj)); m.Write((byte)0x02); m.Write(value); break;
case ushort _: case ushort value:
return (0x03, BitConverter.GetBytes((ushort)obj)); m.Write((byte)0x03); m.Write(value); break;
case int _: case int value:
return (0x04, BitConverter.GetBytes((int)obj)); m.Write((byte)0x04); m.Write(value); break;
case uint _: case uint value:
return (0x05, BitConverter.GetBytes((uint)obj)); m.Write((byte)0x05); m.Write(value); break;
case long _: case long value:
return (0x06, BitConverter.GetBytes((long)obj)); m.Write((byte)0x06); m.Write(value); break;
case ulong _: case ulong value:
return (0x07, BitConverter.GetBytes((ulong)obj)); m.Write((byte)0x07); m.Write(value); break;
case float _: case float value:
return (0x08, BitConverter.GetBytes((float)obj)); m.Write((byte)0x08); m.Write(value); break;
case bool _: case bool value:
return (0x09, BitConverter.GetBytes((bool)obj)); m.Write((byte)0x09); m.Write(value); break;
case string _: case string value:
return (0x10, ((string)obj).GetBytesWithLength()); m.Write((byte)0x10); m.Write(value); break;
case Vector3 _: case Vector3 value:
return (0x11,((Vector3)obj).GetBytes()); m.Write((byte)0x11); m.Write(value); break;
case Quaternion _: case Quaternion value:
return (0x12, ((Quaternion)obj).GetBytes()); m.Write((byte)0x12); m.Write(value); break;
case GTA.Model _: case GTA.Model value:
return (0x13, BitConverter.GetBytes((GTA.Model)obj)); m.Write((byte)0x13); m.Write(value); break;
case Vector2 _: case Vector2 value:
return (0x14, ((Vector2)obj).GetBytes()); m.Write((byte)0x14); m.Write(value); break;
case Tuple<byte, byte[]> _: case byte[] value:
var tup = (Tuple<byte, byte[]>)obj; m.Write((byte)0x15); m.WriteByteArray(value); break;
return (tup.Item1, tup.Item2); case Tuple<byte, byte[]> value:
m.Write(value.Item1); m.Write(value.Item2); break;
default: default:
return (0x0, null); throw new Exception("Unsupported object type: " + obj.GetType());
} }
} }
public static IPEndPoint StringToEndPoint(string endpointstring) public static IPEndPoint StringToEndPoint(string endpointstring)
@ -136,9 +138,8 @@ namespace RageCoop.Core
private static int getPort(string p) private static int getPort(string p)
{ {
int port;
if (!int.TryParse(p, out port) if (!int.TryParse(p, out int port)
|| port < IPEndPoint.MinPort || port < IPEndPoint.MinPort
|| port > IPEndPoint.MaxPort) || port > IPEndPoint.MaxPort)
{ {
@ -147,7 +148,7 @@ namespace RageCoop.Core
return port; return port;
} }
public static IPAddress GetLocalAddress(string target= "8.8.8.8") public static IPAddress GetLocalAddress(string target = "8.8.8.8")
{ {
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0)) using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{ {
@ -190,7 +191,40 @@ namespace RageCoop.Core
foreach (FileInfo file in source.GetFiles()) foreach (FileInfo file in source.GetFiles())
file.CopyTo(Path.Combine(target.FullName, file.Name), true); file.CopyTo(Path.Combine(target.FullName, file.Name), true);
} }
public static string GetInvariantRID()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "win-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return "linux-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return "osx-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
}
return "unknown";
}
/// <summary>
/// Get local ip addresses on all network interfaces
/// </summary>
/// <returns></returns>
public static List<IPAddress> GetLocalAddress()
{
var addresses = new List<IPAddress>();
foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
IPInterfaceProperties ipProps = netInterface.GetIPProperties();
foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses)
{
addresses.Add(addr.Address);
}
}
return addresses;
}
} }
internal class IpInfo internal class IpInfo
{ {
@ -202,74 +236,10 @@ namespace RageCoop.Core
} }
internal static class Extensions 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 AddBool(this List<byte> bytes, bool b)
{
bytes.Add(b? (byte)1 :(byte)0);
}
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 byte[] GetBytes(this string s) public static byte[] GetBytes(this string s)
{ {
return Encoding.UTF8.GetBytes(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) public static string GetString(this byte[] data)
{ {
return Encoding.UTF8.GetString(data); return Encoding.UTF8.GetString(data);
@ -296,35 +266,28 @@ namespace RageCoop.Core
// 16 bytes // 16 bytes
return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4); return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4);
} }
public static T GetPacket<T>(this NetIncomingMessage msg) where T : Packet, new()
public static T GetPacket<T>(this NetIncomingMessage msg, T existingPacket = null) where T : Packet, new()
{ {
msg.ReadByte(); var p = new T();
return GetPacket<T>(msg.ReadBytes(msg.ReadInt32()),existingPacket); p.Deserialize(msg);
}
public static T GetPacket<T>(this byte[] data, T existingPacket=null) where T : Packet, new()
{
var p = existingPacket??new T();
p.Deserialize(data);
return p; return p;
} }
public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag) public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag)
{ {
return (flagToCheck & flag)!=0; return (flagToCheck & flag) != 0;
} }
public static bool HasProjDataFlag(this ProjectileDataFlags flagToCheck, ProjectileDataFlags flag) public static bool HasProjDataFlag(this ProjectileDataFlags flagToCheck, ProjectileDataFlags flag)
{ {
return (flagToCheck & flag)!=0; return (flagToCheck & flag) != 0;
} }
public static bool HasVehFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag) public static bool HasVehFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag)
{ {
return (flagToCheck & flag)!=0; return (flagToCheck & flag) != 0;
} }
public static bool HasConfigFlag(this PlayerConfigFlags flagToCheck, PlayerConfigFlags flag) public static bool HasConfigFlag(this PlayerConfigFlags flagToCheck, PlayerConfigFlags flag)
{ {
return (flagToCheck & flag)!=0; return (flagToCheck & flag) != 0;
} }
public static Type GetActualType(this TypeCode code) public static Type GetActualType(this TypeCode code)
{ {
@ -391,19 +354,19 @@ namespace RageCoop.Core
public static string DumpWithType(this IEnumerable<object> objects) public static string DumpWithType(this IEnumerable<object> objects)
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
foreach(var obj in objects) foreach (var obj in objects)
{ {
sb.Append(obj.GetType()+":"+obj.ToString()+"\n"); sb.Append(obj.GetType() + ":" + obj.ToString() + "\n");
} }
return sb.ToString(); return sb.ToString();
} }
public static string Dump<T>(this IEnumerable<T> objects) public static string Dump<T>(this IEnumerable<T> objects)
{ {
return "{"+string.Join(",",objects)+"}"; return $"{{{string.Join(",", objects)}}}";
} }
public static void ForEach<T>(this IEnumerable<T> objects,Action<T> action) public static void ForEach<T>(this IEnumerable<T> objects, Action<T> action)
{ {
foreach(var obj in objects) foreach (var obj in objects)
{ {
action(obj); action(obj);
} }
@ -425,10 +388,10 @@ namespace RageCoop.Core
stream.CopyTo(memoryStream); stream.CopyTo(memoryStream);
return memoryStream; return memoryStream;
} }
public static byte[] Join(this List<byte[]> arrays,int lengthPerArray=-1) public static byte[] Join(this List<byte[]> arrays, int lengthPerArray = -1)
{ {
if (arrays.Count==1) { return arrays[0]; } if (arrays.Count == 1) { return arrays[0]; }
var output = lengthPerArray== -1 ? new byte[arrays.Sum(arr => arr.Length)] : new byte[arrays.Count*lengthPerArray]; var output = lengthPerArray == -1 ? new byte[arrays.Sum(arr => arr.Length)] : new byte[arrays.Count * lengthPerArray];
int writeIdx = 0; int writeIdx = 0;
foreach (var byteArr in arrays) foreach (var byteArr in arrays)
{ {
@ -482,5 +445,6 @@ namespace RageCoop.Core
{ {
return IPAddress.Parse(ip); return IPAddress.Parse(ip);
} }
} }
} }

View File

@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Text;
using System.Threading; using System.Threading;
namespace RageCoop.Core namespace RageCoop.Core
@ -32,34 +31,34 @@ namespace RageCoop.Core
private StreamWriter logWriter; private StreamWriter logWriter;
private string Buffer = ""; private string Buffer = "";
private Thread LoggerThread; private readonly Thread LoggerThread;
private bool Stopping = false; private bool Stopping = false;
private bool FlushImmediately; private readonly bool FlushImmediately;
internal Logger(bool flushImmediately = false, bool overwrite = true) internal Logger(bool flushImmediately = false, bool overwrite = true)
{ {
FlushImmediately = flushImmediately; FlushImmediately = flushImmediately;
if (File.Exists(LogPath)&&overwrite) { File.Delete(LogPath); } if (File.Exists(LogPath) && overwrite) { File.Delete(LogPath); }
Name=Process.GetCurrentProcess().Id.ToString(); Name = Process.GetCurrentProcess().Id.ToString();
if (!flushImmediately) if (!flushImmediately)
{ {
LoggerThread=new Thread(() => LoggerThread = new Thread(() =>
{ {
if (!UseConsole) if (!UseConsole)
{ {
while (LogPath==default) while (LogPath == default)
{ {
Thread.Sleep(100); Thread.Sleep(100);
} }
if (File.Exists(LogPath)&&overwrite) { File.Delete(LogPath); } if (File.Exists(LogPath) && overwrite) { File.Delete(LogPath); }
} }
while (!Stopping) while (!Stopping)
{ {
Flush(); Flush();
Thread.Sleep(1000); Thread.Sleep(1000);
} }
Flush(); Flush();
}); });
LoggerThread.Start(); LoggerThread.Start();
} }
} }
@ -69,12 +68,12 @@ namespace RageCoop.Core
/// <param name="message"></param> /// <param name="message"></param>
public void Info(string message) public void Info(string message)
{ {
if (LogLevel>2) { return; } if (LogLevel > 2) { return; }
lock (Buffer) lock (Buffer)
{ {
string msg = string.Format("[{0}][{2}] [INF] {1}", Date(), message, Name); string msg = string.Format("[{0}][{2}] [INF] {1}", Date(), message, Name);
Buffer+=msg+"\r\n"; Buffer += msg + "\r\n";
} }
if (FlushImmediately) if (FlushImmediately)
{ {
@ -87,12 +86,12 @@ namespace RageCoop.Core
/// <param name="message"></param> /// <param name="message"></param>
public void Warning(string message) public void Warning(string message)
{ {
if (LogLevel>3) { return; } if (LogLevel > 3) { return; }
lock (Buffer) lock (Buffer)
{ {
string msg = string.Format("[{0}][{2}] [WRN] {1}", Date(), message, Name); string msg = string.Format("[{0}][{2}] [WRN] {1}", Date(), message, Name);
Buffer+=msg+"\r\n"; Buffer += msg + "\r\n";
} }
if (FlushImmediately) if (FlushImmediately)
@ -106,12 +105,12 @@ namespace RageCoop.Core
/// <param name="message"></param> /// <param name="message"></param>
public void Error(string message) public void Error(string message)
{ {
if (LogLevel>4) { return; } if (LogLevel > 4) { return; }
lock (Buffer) lock (Buffer)
{ {
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), message, Name); string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), message, Name);
Buffer+=msg+"\r\n"; Buffer += msg + "\r\n";
} }
if (FlushImmediately) if (FlushImmediately)
{ {
@ -125,11 +124,11 @@ namespace RageCoop.Core
/// <param name="error"></param> /// <param name="error"></param>
public void Error(string message, Exception error) public void Error(string message, Exception error)
{ {
if (LogLevel>4) { return; } if (LogLevel > 4) { return; }
lock (Buffer) lock (Buffer)
{ {
string msg = string.Format("[{0}][{2}] [ERR] {1}:{3}", Date(), message, Name,error.Message); string msg = string.Format("[{0}][{2}] [ERR] {1}:{3}", Date(), message, Name, error.Message);
Buffer+=msg+"\r\n"; Buffer += msg + "\r\n";
Trace(error.ToString()); Trace(error.ToString());
} }
@ -144,11 +143,11 @@ namespace RageCoop.Core
/// <param name="ex"></param> /// <param name="ex"></param>
public void Error(Exception ex) public void Error(Exception ex)
{ {
if (LogLevel>4) { return; } if (LogLevel > 4) { return; }
lock (Buffer) lock (Buffer)
{ {
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n"+ex.Message, Name); string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n" + ex.Message, Name);
Buffer+=msg+"\r\n"; Buffer += msg + "\r\n";
Trace(ex.ToString()); Trace(ex.ToString());
} }
if (FlushImmediately) if (FlushImmediately)
@ -163,12 +162,12 @@ namespace RageCoop.Core
public void Debug(string message) public void Debug(string message)
{ {
if (LogLevel>1) { return; } if (LogLevel > 1) { return; }
lock (Buffer) lock (Buffer)
{ {
string msg = string.Format("[{0}][{2}] [DBG] {1}", Date(), message, Name); string msg = string.Format("[{0}][{2}] [DBG] {1}", Date(), message, Name);
Buffer+=msg+"\r\n"; Buffer += msg + "\r\n";
} }
if (FlushImmediately) if (FlushImmediately)
{ {
@ -181,12 +180,12 @@ namespace RageCoop.Core
/// <param name="message"></param> /// <param name="message"></param>
public void Trace(string message) public void Trace(string message)
{ {
if (LogLevel>0) { return; } if (LogLevel > 0) { return; }
lock (Buffer) lock (Buffer)
{ {
string msg = string.Format("[{0}][{2}] [TRC] {1}", Date(), message, Name); string msg = string.Format("[{0}][{2}] [TRC] {1}", Date(), message, Name);
Buffer+=msg+"\r\n"; Buffer += msg + "\r\n";
} }
if (FlushImmediately) if (FlushImmediately)
{ {
@ -205,21 +204,21 @@ namespace RageCoop.Core
{ {
lock (Buffer) lock (Buffer)
{ {
if (Buffer!="") if (Buffer != "")
{ {
if (UseConsole) if (UseConsole)
{ {
Console.Write(Buffer); Console.Write(Buffer);
Buffer=""; Buffer = "";
} }
else else
{ {
try try
{ {
logWriter=new StreamWriter(LogPath, true, Encoding.UTF8); logWriter = new StreamWriter(LogPath, true, Encoding.UTF8);
logWriter.Write(Buffer); logWriter.Write(Buffer);
logWriter.Close(); logWriter.Close();
Buffer=""; Buffer = "";
} }
catch { } catch { }
} }
@ -232,7 +231,7 @@ namespace RageCoop.Core
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
Stopping=true; Stopping = true;
LoggerThread?.Join(); LoggerThread?.Join();
} }
} }

View File

@ -1,12 +1,11 @@
using System; using GTA.Math;
using GTA.Math; using System;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal static class MathExtensions internal static class MathExtensions
{ {
public const float Deg2Rad=(float)(Math.PI* 2) / 360; public const float Deg2Rad = (float)(Math.PI * 2) / 360;
public const float Rad2Deg = 360 / (float)(Math.PI * 2); public const float Rad2Deg = 360 / (float)(Math.PI * 2);
public static Vector3 ToDirection(this Vector3 rotation) public static Vector3 ToDirection(this Vector3 rotation)
@ -82,14 +81,14 @@ namespace RageCoop.Core
vect = vect.ToRadians(); vect = vect.ToRadians();
float rollOver2 = vect.Z * 0.5f; float rollOver2 = vect.Z * 0.5f;
float sinRollOver2 = (float)Math.Sin((double)rollOver2); float sinRollOver2 = (float)Math.Sin(rollOver2);
float cosRollOver2 = (float)Math.Cos((double)rollOver2); float cosRollOver2 = (float)Math.Cos(rollOver2);
float pitchOver2 = vect.Y * 0.5f; float pitchOver2 = vect.Y * 0.5f;
float sinPitchOver2 = (float)Math.Sin((double)pitchOver2); float sinPitchOver2 = (float)Math.Sin(pitchOver2);
float cosPitchOver2 = (float)Math.Cos((double)pitchOver2); float cosPitchOver2 = (float)Math.Cos(pitchOver2);
float yawOver2 = vect.X * 0.5f; // pitch float yawOver2 = vect.X * 0.5f; // pitch
float sinYawOver2 = (float)Math.Sin((double)yawOver2); float sinYawOver2 = (float)Math.Sin(yawOver2);
float cosYawOver2 = (float)Math.Cos((double)yawOver2); float cosYawOver2 = (float)Math.Cos(yawOver2);
Quaternion result = new Quaternion() Quaternion result = new Quaternion()
{ {
X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2, X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2,
@ -111,7 +110,7 @@ namespace RageCoop.Core
} }
public static Vector3 ToDegree(this Vector3 radian) public static Vector3 ToDegree(this Vector3 radian)
{ {
return radian*(float)(180/Math.PI); return radian * (float)(180 / Math.PI);
} }
public static Vector3 ToEulerDegrees(this Quaternion q) public static Vector3 ToEulerDegrees(this Quaternion q)
{ {
@ -146,16 +145,12 @@ namespace RageCoop.Core
} }
private static float CopySign(double x, double y) private static float CopySign(double x, double y)
{ {
bool isPositive = y>=0; if (y >= 0)
if (isPositive)
{ {
if (x>=0) { return (float)x; } else { return (float)-x; } return x >= 0 ? (float)x : (float)-x;
} }
else
{
if (x>=0) { return (float)-x; } else { return (float)x; }
} return x >= 0 ? (float)-x : (float)x;
} }
public static double AngelTo(this Vector3 v1, Vector3 v2) public static double AngelTo(this Vector3 v1, Vector3 v2)
{ {
@ -163,8 +158,7 @@ namespace RageCoop.Core
} }
public static float GetCosTheta(this Vector3 v1, Vector3 v2) public static float GetCosTheta(this Vector3 v1, Vector3 v2)
{ {
return Vector3.Dot(v1, v2) / (v1.Length() * v2.Length());
return Vector3.Dot(v1, v2)/(v1.Length()*v2.Length());
} }
} }

View File

@ -1,7 +1,6 @@
using System; using Lidgren.Network;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
using System.Threading; using System.Threading;
namespace RageCoop.Core namespace RageCoop.Core
@ -10,22 +9,22 @@ namespace RageCoop.Core
{ {
public EventHandler<NetIncomingMessage> OnMessageReceived; public EventHandler<NetIncomingMessage> OnMessageReceived;
private readonly Thread ListenerThread; private readonly Thread ListenerThread;
private bool _stopping=false; private bool _stopping = false;
public CoopPeer(NetPeerConfiguration config):base(config) public CoopPeer(NetPeerConfiguration config) : base(config)
{ {
Start(); Start();
NetIncomingMessage msg; NetIncomingMessage msg;
ListenerThread=new Thread(() => ListenerThread = new Thread(() =>
{ {
while (!_stopping) while (!_stopping)
{ {
msg=WaitMessage(200); msg = WaitMessage(200);
if (msg!=null) if (msg != null)
{ {
OnMessageReceived?.Invoke(this,msg); OnMessageReceived?.Invoke(this, msg);
} }
} }
}); });
ListenerThread.Start(); ListenerThread.Start();
} }
@ -34,7 +33,7 @@ namespace RageCoop.Core
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
_stopping=true; _stopping = true;
Shutdown("Bye!"); Shutdown("Bye!");
ListenerThread.Join(); ListenerThread.Join();
} }
@ -51,12 +50,11 @@ namespace RageCoop.Core
p.Pack(outgoingMessage); p.Pack(outgoingMessage);
SendMessage(outgoingMessage, connections, method, (int)channel); SendMessage(outgoingMessage, connections, method, (int)channel);
} }
public void Send(Packet p,IList<NetConnection> cons, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced) public void Send(Packet p, IList<NetConnection> cons, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{ {
NetOutgoingMessage outgoingMessage = CreateMessage(); NetOutgoingMessage outgoingMessage = CreateMessage();
p.Pack(outgoingMessage); p.Pack(outgoingMessage);
SendMessage(outgoingMessage, cons, method, (int)channel); SendMessage(outgoingMessage, cons, method, (int)channel);
} }
} }
} }

View File

@ -1,18 +1,16 @@
using System; using System;
using System.Collections.Generic; using System.IO;
using System.Text;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.IO;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal static class HttpHelper internal static class HttpHelper
{ {
public static void DownloadFile(string url,string destination,Action<int> progressCallback) public static void DownloadFile(string url, string destination, Action<int> progressCallback)
{ {
if (File.Exists(destination)) { File.Delete(destination); } if (File.Exists(destination)) { File.Delete(destination); }
AutoResetEvent ae=new AutoResetEvent(false); AutoResetEvent ae = new AutoResetEvent(false);
WebClient client = new WebClient(); WebClient client = new WebClient();
// TLS only // TLS only
@ -32,7 +30,9 @@ namespace RageCoop.Core
{ {
// TLS only // TLS only
ServicePointManager.Expect100Continue = true; ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
WebClient client = new WebClient(); WebClient client = new WebClient();

View File

@ -1,19 +1,19 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
using System.Threading;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal class PublicKey{ internal class PublicKey
public PublicKey(){ {
public PublicKey()
{
} }
public static PublicKey FromServerInfo(ServerInfo info){ public static PublicKey FromServerInfo(ServerInfo info)
return new PublicKey{ {
Modulus=Convert.FromBase64String(info.publicKeyModulus), return new PublicKey
Exponent=Convert.FromBase64String(info.publicKeyExponent) {
Modulus = Convert.FromBase64String(info.publicKeyModulus),
Exponent = Convert.FromBase64String(info.publicKeyExponent)
}; };
} }
public byte[] Modulus; public byte[] Modulus;

View File

@ -1,13 +1,11 @@
using System; namespace RageCoop.Core
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
namespace RageCoop.Core
{ {
/// <summary>
internal class ServerInfo /// A json object representing a server's information as annouced to master server.
/// </summary>
public class ServerInfo
{ {
#pragma warning disable 1591
public string address { get; set; } public string address { get; set; }
public string port { get; set; } public string port { get; set; }
public string name { get; set; } public string name { get; set; }
@ -27,8 +25,8 @@ namespace RageCoop.Core
public string ztID { get; set; } public string ztID { get; set; }
public string ztAddress { get; set; } public string ztAddress { get; set; }
public string publicKeyModulus{get;set;} public string publicKeyModulus { get; set; }
public string publicKeyExponent{get;set;} public string publicKeyExponent { get; set; }
} }
} }

View File

@ -1,11 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;
using System.Net;
using Newtonsoft.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace RageCoop.Core namespace RageCoop.Core
@ -15,13 +13,13 @@ namespace RageCoop.Core
public ZeroTierNetwork(string line) public ZeroTierNetwork(string line)
{ {
// <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips> // <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>
var v = Regex.Split(line," ").Skip(2).ToArray(); var v = Regex.Split(line, " ").Skip(2).ToArray();
ID=v[0]; ID = v[0];
Name=v[1]; Name = v[1];
Mac=v[2]; Mac = v[2];
Status=v[3]; Status = v[3];
Type=v[4]; Type = v[4];
Device=v[5]; Device = v[5];
foreach (var i in v[6].Split(',')) foreach (var i in v[6].Split(','))
{ {
Addresses.Add(i.Split('/')[0]); Addresses.Add(i.Split('/')[0]);
@ -33,43 +31,43 @@ namespace RageCoop.Core
public string Status; public string Status;
public string Type; public string Type;
public string Device; public string Device;
public List<string> Addresses=new List<string>(); public List<string> Addresses = new List<string>();
} }
internal static class ZeroTierHelper internal static class ZeroTierHelper
{ {
private static readonly string _path="zerotier-cli"; private static readonly string _path = "zerotier-cli";
private static readonly string _arg = ""; private static readonly string _arg = "";
static ZeroTierHelper() static ZeroTierHelper()
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
var batpath= Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "ZeroTier", "One", "zerotier-cli.bat"); var batpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "ZeroTier", "One", "zerotier-cli.bat");
_arg=$"/c \"{batpath}\" "; _arg = $"/c \"{batpath}\" ";
_path="cmd.exe"; _path = "cmd.exe";
} }
var status = RunCommand("status"); var status = RunCommand("status");
if (!status.StartsWith("200")) if (!status.StartsWith("200"))
{ {
throw new Exception("ZeroTier not ready: "+status); throw new Exception("ZeroTier not ready: " + status);
} }
} }
public static ZeroTierNetwork Join(string networkId, int timeout=10000) public static ZeroTierNetwork Join(string networkId, int timeout = 10000)
{ {
var p = Run("join "+networkId); var p = Run("join " + networkId);
var o = p.StandardOutput.ReadToEnd(); var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 join OK")) if (!o.StartsWith("200 join OK"))
{ {
throw new Exception(o+p.StandardError.ReadToEnd()); throw new Exception(o + p.StandardError.ReadToEnd());
} }
if (timeout==0) { return Networks[networkId]; } if (timeout == 0) { return Networks[networkId]; }
int i = 0; int i = 0;
while (i<=timeout) while (i <= timeout)
{ {
i+=100; i += 100;
if(Networks.TryGetValue(networkId,out var n)) if (Networks.TryGetValue(networkId, out var n))
{ {
if (n.Addresses.Count!=0 && (!n.Addresses.Where(x=>x=="-").Any())) if (n.Addresses.Count != 0 && (!n.Addresses.Where(x => x == "-").Any()))
{ {
return n; return n;
} }
@ -84,16 +82,17 @@ namespace RageCoop.Core
} }
public static void Leave(string networkId) public static void Leave(string networkId)
{ {
var p = Run("leave "+networkId); var p = Run("leave " + networkId);
var o = p.StandardOutput.ReadToEnd(); var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 leave OK")) if (!o.StartsWith("200 leave OK"))
{ {
throw new Exception(o+p.StandardError.ReadToEnd()); throw new Exception(o + p.StandardError.ReadToEnd());
} }
} }
public static Dictionary<string, ZeroTierNetwork> Networks public static Dictionary<string, ZeroTierNetwork> Networks
{ {
get { get
{
Dictionary<string, ZeroTierNetwork> networks = new Dictionary<string, ZeroTierNetwork>(); Dictionary<string, ZeroTierNetwork> networks = new Dictionary<string, ZeroTierNetwork>();
var p = Run("listnetworks"); var p = Run("listnetworks");
var lines = Regex.Split(p.StandardOutput.ReadToEnd(), "\n").Skip(1); var lines = Regex.Split(p.StandardOutput.ReadToEnd(), "\n").Skip(1);
@ -113,14 +112,14 @@ namespace RageCoop.Core
private static Process Run(string args) private static Process Run(string args)
{ {
var p = new Process(); var p = new Process();
p.StartInfo=new ProcessStartInfo() p.StartInfo = new ProcessStartInfo()
{ {
FileName = _path, FileName = _path,
Arguments =_arg+args, Arguments = _arg + args,
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true, RedirectStandardOutput = true,
RedirectStandardError = true, RedirectStandardError = true,
CreateNoWindow=true, CreateNoWindow = true,
}; };
p.Start(); p.Start();
p.WaitForExit(); p.WaitForExit();
@ -129,7 +128,7 @@ namespace RageCoop.Core
private static string RunCommand(string command) private static string RunCommand(string command)
{ {
var p = Run(command); var p = Run(command);
return p.StandardOutput.ReadToEnd()+p.StandardError.ReadToEnd(); return p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd();
} }
public static void Check() public static void Check()
{ {

View File

@ -0,0 +1,71 @@
using GTA.Math;
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Text;
namespace RageCoop.Core
{
internal static class PacketExtensions
{
#region MESSAGE-READ
public static Vector3 ReadVector3(this NetIncomingMessage m)
{
return new Vector3
{
X = m.ReadFloat(),
Y = m.ReadFloat(),
Z = m.ReadFloat(),
};
}
public static Vector2 ReadVector2(this NetIncomingMessage m)
{
return new Vector2
{
X = m.ReadFloat(),
Y = m.ReadFloat(),
};
}
public static Quaternion ReadQuaternion(this NetIncomingMessage m)
{
return new Quaternion
{
X = m.ReadFloat(),
Y = m.ReadFloat(),
Z = m.ReadFloat(),
W = m.ReadFloat(),
};
}
public static byte[] ReadByteArray(this NetIncomingMessage m)
{
return m.ReadBytes(m.ReadInt32());
}
#endregion
#region MESSAGE-WRITE
public static void Write(this NetOutgoingMessage m, Vector3 v)
{
m.Write(v.X);
m.Write(v.Y);
m.Write(v.Z);
}
public static void Write(this NetOutgoingMessage m, Quaternion q)
{
m.Write(q.X);
m.Write(q.Y);
m.Write(q.Z);
m.Write(q.W);
}
public static void WriteByteArray(this NetOutgoingMessage m, byte[] b)
{
m.Write(b.Length);
m.Write(b);
}
#endregion
internal static bool IsSyncEvent(this PacketType p)
{
return (30 <= (byte)p) && ((byte)p <= 40);
}
}
}

View File

@ -1,6 +1,5 @@
using System; using Lidgren.Network;
using System.Collections.Generic; using System;
using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -10,9 +9,9 @@ namespace RageCoop.Core
internal class ChatMessage : Packet internal class ChatMessage : Packet
{ {
public override PacketType Type => PacketType.ChatMessage; public override PacketType Type => PacketType.ChatMessage;
private Func<string, byte[]> crypt; private readonly Func<string, byte[]> crypt;
private Func<byte[], byte[]> decrypt; private readonly Func<byte[], byte[]> decrypt;
public ChatMessage(Func<string, byte[]> crypter) public ChatMessage(Func<string, byte[]> crypter)
{ {
crypt = crypter; crypt = crypter;
@ -25,33 +24,31 @@ namespace RageCoop.Core
public string Message { get; set; } public string Message { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// Write Username // Write Username
byteArray.AddString(Username); m.Write(Username);
// Write Message // Write Message
byteArray.AddArray(crypt(Message)); m.WriteByteArray(crypt(Message));
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read username // Read username
Username = reader.ReadString(); Username = m.ReadString();
Message = decrypt(reader.ReadByteArray()).GetString(); Message = decrypt(m.ReadByteArray()).GetString();
#endregion #endregion
} }
} }

View File

@ -1,7 +1,5 @@
using System; using Lidgren.Network;
using System.Collections.Generic; using System;
using System.Text;
using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal partial class Packets internal partial class Packets
@ -9,86 +7,79 @@ namespace RageCoop.Core
internal class CustomEvent : Packet internal class CustomEvent : Packet
{ {
public override PacketType Type => (_queued ? PacketType.CustomEventQueued : PacketType.CustomEvent); public override PacketType Type => (_queued ? PacketType.CustomEventQueued : PacketType.CustomEvent);
public CustomEvent(Func<byte,BitReader,object> onResolve = null,bool queued=false) public CustomEvent(Func<byte, NetIncomingMessage, object> onResolve = null, bool queued = false)
{ {
_resolve= onResolve; _resolve = onResolve;
_queued= queued; _queued = queued;
} }
private bool _queued; private readonly bool _queued;
private Func<byte, BitReader, object> _resolve { get; set; } private Func<byte, NetIncomingMessage, object> _resolve { get; set; }
public int Hash { get; set; } public int Hash { get; set; }
public object[] Args { get; set; } public object[] Args { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
Args= Args ?? new object[] { }; Args = Args ?? new object[] { };
List<byte> result = new List<byte>(); m.Write(Hash);
result.AddInt(Hash); m.Write(Args.Length);
result.AddInt(Args.Length);
(byte, byte[]) tup;
foreach (var arg in Args) foreach (var arg in Args)
{ {
tup=CoreUtils.GetBytesFromObject(arg); CoreUtils.GetBytesFromObject(arg, m);
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);
} }
return result.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
Hash = reader.ReadInt32();
var len=reader.ReadInt32(); Hash = m.ReadInt32();
Args=new object[len]; var len = m.ReadInt32();
Args = new object[len];
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
{ {
byte type = reader.ReadByte(); byte type = m.ReadByte();
switch (type) switch (type)
{ {
case 0x01: case 0x01:
Args[i]=reader.ReadByte(); break; Args[i] = m.ReadByte(); break;
case 0x02: case 0x02:
Args[i]=reader.ReadInt32(); break; Args[i] = m.ReadInt32(); break;
case 0x03: case 0x03:
Args[i]=reader.ReadUInt16(); break; Args[i] = m.ReadUInt16(); break;
case 0x04: case 0x04:
Args[i]=reader.ReadInt32(); break; Args[i] = m.ReadInt32(); break;
case 0x05: case 0x05:
Args[i]=reader.ReadUInt32(); break; Args[i] = m.ReadUInt32(); break;
case 0x06: case 0x06:
Args[i]=reader.ReadInt64(); break; Args[i] = m.ReadInt64(); break;
case 0x07: case 0x07:
Args[i]=reader.ReadUInt64(); break; Args[i] = m.ReadUInt64(); break;
case 0x08: case 0x08:
Args[i]=reader.ReadSingle(); break; Args[i] = m.ReadFloat(); break;
case 0x09: case 0x09:
Args[i]=reader.ReadBoolean(); break; Args[i] = m.ReadBoolean(); break;
case 0x10: case 0x10:
Args[i]=reader.ReadString(); break; Args[i] = m.ReadString(); break;
case 0x11: case 0x11:
Args[i]=reader.ReadVector3(); break; Args[i] = m.ReadVector3(); break;
case 0x12: case 0x12:
Args[i]=reader.ReadQuaternion(); break; Args[i] = m.ReadQuaternion(); break;
case 0x13: case 0x13:
Args[i]=(GTA.Model)reader.ReadInt32(); break; Args[i] = (GTA.Model)m.ReadInt32(); break;
case 0x14: case 0x14:
Args[i]=reader.ReadVector2(); break; Args[i] = m.ReadVector2(); break;
case 0x15:
Args[i] = m.ReadByteArray(); break;
default: default:
if (_resolve==null) if (_resolve == null)
{ {
throw new InvalidOperationException($"Unexpected type:{type}\r\n{array.Dump()}"); throw new InvalidOperationException($"Unexpected type: {type}");
} }
else else
{ {
Args[i]=_resolve(type, reader); break; Args[i] = _resolve(type, m); break;
} }
} }
} }

View File

@ -1,18 +1,15 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal enum FileResponse:byte internal enum FileResponse : byte
{ {
NeedToDownload=0, NeedToDownload = 0,
AlreadyExists=1, AlreadyExists = 1,
Completed=2, Completed = 2,
Loaded=3, Loaded = 3,
LoadFailed=4, LoadFailed = 4,
} }
internal partial class Packets internal partial class Packets
{ {
@ -25,34 +22,30 @@ namespace RageCoop.Core
public long FileLength { get; set; } public long FileLength { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// The ID from the download // The ID from the download
byteArray.AddInt(ID); m.Write(ID);
// The name of the file // The name of the file
byte[] nameBytes = Encoding.UTF8.GetBytes(Name); m.Write(Name);
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
byteArray.AddRange(nameBytes);
// The length of the file // The length of the file
byteArray.AddRange(BitConverter.GetBytes(FileLength)); m.Write(FileLength);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
ID = reader.ReadInt32();
Name = reader.ReadString(); ID = m.ReadInt32();
FileLength = reader.ReadInt64(); Name = m.ReadString();
FileLength = m.ReadInt64();
} }
} }
@ -61,86 +54,74 @@ namespace RageCoop.Core
public override PacketType Type => PacketType.FileTransferResponse; public override PacketType Type => PacketType.FileTransferResponse;
public int ID { get; set; } public int ID { get; set; }
public FileResponse Response { get; set; } public FileResponse Response { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// The ID from the download // The ID from the download
byteArray.AddInt(ID); m.Write(ID);
byteArray.Add((byte)Response); m.Write((byte)Response);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
ID = reader.ReadInt32(); ID = m.ReadInt32();
Response = (FileResponse)reader.ReadByte(); Response = (FileResponse)m.ReadByte();
} }
} }
internal class FileTransferChunk : Packet internal class FileTransferChunk : Packet
{ {
public override PacketType Type => PacketType.FileTransferChunk; public override PacketType Type => PacketType.FileTransferChunk;
public int ID { get; set; } public int ID { get; set; }
public byte[] FileChunk { get; set; } public byte[] FileChunk { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// The ID from the download // The ID from the download
byteArray.AddInt(ID); m.Write(ID);
m.WriteByteArray(FileChunk);
// The chunk of the file
byteArray.AddInt(FileChunk.Length);
byteArray.AddRange(FileChunk);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
ID = reader.ReadInt32(); ID = m.ReadInt32();
FileChunk = reader.ReadByteArray(); FileChunk = m.ReadByteArray();
} }
} }
internal class FileTransferComplete : Packet internal class FileTransferComplete : Packet
{ {
public override PacketType Type => PacketType.FileTransferComplete; public override PacketType Type => PacketType.FileTransferComplete;
public int ID { get; set; } public int ID { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// The ID for the download // The ID for the download
byteArray.AddInt(ID); m.Write(ID);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
ID = reader.ReadInt32();
ID = m.ReadInt32();
} }
} }
internal class AllResourcesSent : Packet internal class AllResourcesSent : Packet
{ {
public override PacketType Type => PacketType.AllResourcesSent; public override PacketType Type => PacketType.AllResourcesSent;
} }
} }
} }

View File

@ -1,6 +1,4 @@
using System; using Lidgren.Network;
using System.Collections.Generic;
using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -14,26 +12,26 @@ namespace RageCoop.Core
public string TargetInternal { get; set; } public string TargetInternal { get; set; }
public string TargetExternal { get; set; } public string TargetExternal { get; set; }
public bool Connect { get; set; } public bool Connect { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
byteArray.AddInt(TargetID); m.Write(TargetID);
byteArray.AddString(TargetInternal); m.Write(TargetInternal);
byteArray.AddString(TargetExternal); m.Write(TargetExternal);
byteArray.AddBool(Connect); m.Write(Connect);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
TargetID = reader.ReadInt32(); TargetID = m.ReadInt32();
TargetInternal = reader.ReadString(); TargetInternal = m.ReadString();
TargetExternal = reader.ReadString(); TargetExternal = m.ReadString();
Connect=reader.ReadBoolean(); Connect = m.ReadBoolean();
#endregion #endregion
} }
} }
@ -45,23 +43,23 @@ namespace RageCoop.Core
/// <summary> /// <summary>
/// 1:initial, 2:acknowledged, 3:confirmed /// 1:initial, 2:acknowledged, 3:confirmed
/// </summary> /// </summary>
public byte Status { get;set;} public byte Status { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
byteArray.AddInt(Puncher); m.Write(Puncher);
byteArray.Add(Status); m.Write(Status);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
Puncher = reader.ReadInt32(); Puncher = m.ReadInt32();
Status = reader.ReadByte(); Status = m.ReadByte();
#endregion #endregion
} }
} }

View File

@ -1,4 +1,4 @@
using System; using Lidgren.Network;
using System.Collections.Generic; using System.Collections.Generic;
namespace RageCoop.Core namespace RageCoop.Core
@ -12,16 +12,15 @@ namespace RageCoop.Core
{ {
public int TargetID { get; set; } public int TargetID { get; set; }
public override PacketType Type => PacketType.ConnectionRequest; public override PacketType Type => PacketType.ConnectionRequest;
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
var data=new List<byte>(10); var data = new List<byte>(10);
data.AddInt(TargetID); m.Write(TargetID);
return data.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
var reader=new BitReader(array);
TargetID = reader.ReadInt32(); TargetID = m.ReadInt32();
} }
} }
@ -33,16 +32,16 @@ namespace RageCoop.Core
{ {
public int ID { get; set; } public int ID { get; set; }
public override PacketType Type => PacketType.P2PConnect; public override PacketType Type => PacketType.P2PConnect;
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
var data = new List<byte>(10); var data = new List<byte>(10);
data.AddInt(ID); m.Write(ID);
return data.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
var reader = new BitReader(array);
ID = reader.ReadInt32(); ID = m.ReadInt32();
} }
} }
} }

View File

@ -1,68 +1,56 @@
using System; using Lidgren.Network;
using System.Collections.Generic; using System;
using System.Text;
using Lidgren.Network;
using Newtonsoft.Json;
using GTA.Math;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal enum PacketType:byte internal enum PacketType : byte
{ {
Handshake=0, Handshake = 0,
PlayerConnect=1, PlayerConnect = 1,
PlayerDisconnect=2, PlayerDisconnect = 2,
PlayerInfoUpdate=3, PlayerInfoUpdate = 3,
PublicKeyRequest=4, PublicKeyRequest = 4,
PublicKeyResponse=5, PublicKeyResponse = 5,
Request=6, Request = 6,
Response=7, Response = 7,
PingPong = 8, PingPong = 8,
HandshakeSuccess = 9, HandshakeSuccess = 9,
ChatMessage =10, ChatMessage = 10,
FileTransferChunk=11, FileTransferChunk = 11,
FileTransferRequest=12, FileTransferRequest = 12,
FileTransferResponse = 13, FileTransferResponse = 13,
FileTransferComplete =14, FileTransferComplete = 14,
AllResourcesSent=15, AllResourcesSent = 15,
CustomEvent = 16, CustomEvent = 16,
CustomEventQueued = 17, CustomEventQueued = 17,
ConnectionRequest=18, ConnectionRequest = 18,
P2PConnect = 19, P2PConnect = 19,
HolePunchInit=20, HolePunchInit = 20,
HolePunch=21, HolePunch = 21,
Voice = 22, Voice = 22,
#region Sync #region Sync
PedSync = 23, PedSync = 23,
VehicleSync = 24, VehicleSync = 24,
ProjectileSync =25, ProjectileSync = 25,
#endregion #endregion
#region EVENT #region EVENT
PedKilled=30, PedKilled = 30,
BulletShot=31, BulletShot = 31,
VehicleBulletShot = 32, VehicleBulletShot = 32,
OwnerChanged =35, OwnerChanged = 35,
NozzleTransform=37, NozzleTransform = 37,
#endregion #endregion
Unknown=255 Unknown = 255
} }
internal static class PacketExtensions
{
internal static bool IsSyncEvent(this PacketType p)
{
return (30<=(byte)p)&&((byte)p<=40);
}
}
internal enum ConnectionChannel internal enum ConnectionChannel
{ {
Default = 0, Default = 0,
@ -72,18 +60,18 @@ namespace RageCoop.Core
Mod = 4, Mod = 4,
File = 5, File = 5,
Event = 6, Event = 6,
RequestResponse=7, RequestResponse = 7,
PingPong = 8, PingPong = 8,
VehicleSync = 9, VehicleSync = 9,
PedSync= 10, PedSync = 10,
ProjectileSync = 11, ProjectileSync = 11,
SyncEvents = 12, SyncEvents = 12,
} }
[Flags] [Flags]
internal enum PedDataFlags:ushort internal enum PedDataFlags : ushort
{ {
None=0, None = 0,
IsAiming = 1 << 0, IsAiming = 1 << 0,
IsInStealthMode = 1 << 1, IsInStealthMode = 1 << 1,
IsReloading = 1 << 2, IsReloading = 1 << 2,
@ -99,10 +87,10 @@ namespace RageCoop.Core
IsInCoverFacingLeft = 1 << 12, IsInCoverFacingLeft = 1 << 12,
IsBlindFiring = 1 << 13, IsBlindFiring = 1 << 13,
IsInvincible = 1 << 14, IsInvincible = 1 << 14,
IsFullSync = 1<<15 , IsFullSync = 1 << 15,
} }
internal enum ProjectileDataFlags:byte internal enum ProjectileDataFlags : byte
{ {
None = 0, None = 0,
Exploded = 1 << 0, Exploded = 1 << 0,
@ -111,9 +99,9 @@ namespace RageCoop.Core
IsShotByVehicle = 1 << 3, IsShotByVehicle = 1 << 3,
} }
#region ===== VEHICLE DATA ===== #region ===== VEHICLE DATA =====
internal enum VehicleDataFlags:ushort internal enum VehicleDataFlags : ushort
{ {
None=0, None = 0,
IsEngineRunning = 1 << 0, IsEngineRunning = 1 << 0,
AreLightsOn = 1 << 1, AreLightsOn = 1 << 1,
AreBrakeLightsOn = 1 << 2, AreBrakeLightsOn = 1 << 2,
@ -125,18 +113,18 @@ namespace RageCoop.Core
IsParachuteActive = 1 << 8, IsParachuteActive = 1 << 8,
IsRocketBoostActive = 1 << 9, IsRocketBoostActive = 1 << 9,
IsAircraft = 1 << 10, IsAircraft = 1 << 10,
IsDeluxoHovering=1 << 11, IsDeluxoHovering = 1 << 11,
HasRoof=1 << 12, HasRoof = 1 << 12,
IsFullSync = 1<<13, IsFullSync = 1 << 13,
IsOnFire = 1<<14, IsOnFire = 1 << 14,
Repaired = 1<<15, Repaired = 1 << 15,
} }
internal enum PlayerConfigFlags : byte internal enum PlayerConfigFlags : byte
{ {
None = 0, None = 0,
ShowBlip= 1 << 0, ShowBlip = 1 << 0,
ShowNameTag= 1 << 1 ShowNameTag = 1 << 1
} }
internal struct VehicleDamageModel internal struct VehicleDamageModel
@ -153,25 +141,19 @@ namespace RageCoop.Core
internal interface IPacket internal interface IPacket
{ {
PacketType Type { get; } PacketType Type { get; }
byte[] Serialize();
void Deserialize(byte[] data); void Deserialize(NetIncomingMessage m);
} }
internal abstract class Packet : IPacket internal abstract class Packet : IPacket
{ {
public abstract PacketType Type { get; } public abstract PacketType Type { get; }
public virtual byte[] Serialize() public void Pack(NetOutgoingMessage m)
{ {
return new byte[0]; m.Write((byte)Type);
} Serialize(m);
public virtual void Deserialize(byte[] array) { }
public void Pack(NetOutgoingMessage message)
{
var d=Serialize();
message.Write((byte)Type);
message.Write(d.Length);
message.Write(d);
} }
protected virtual void Serialize(NetOutgoingMessage m) { }
public virtual void Deserialize(NetIncomingMessage m) { }
} }
} }

View File

@ -1,9 +1,7 @@
using System; using GTA;
using System.Collections.Generic;
using System.Text;
using GTA.Math; using GTA.Math;
using GTA;
using Lidgren.Network; using Lidgren.Network;
using System.Collections.Generic;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -13,7 +11,7 @@ namespace RageCoop.Core
internal class PedSync : Packet internal class PedSync : Packet
{ {
public override PacketType Type => PacketType.PedSync; public override PacketType Type => PacketType.PedSync;
public int ID { get; set; } public int ID { get; set; }
public int OwnerID { get; set; } public int OwnerID { get; set; }
@ -59,149 +57,149 @@ namespace RageCoop.Core
public BlipSprite BlipSprite { get; set; } = 0; public BlipSprite BlipSprite { get; set; } = 0;
public float BlipScale { get; set; } = 1; public float BlipScale { get; set; } = 1;
#endregion #endregion
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
byteArray.AddInt(ID); m.Write(ID);
byteArray.AddInt(OwnerID); m.Write(OwnerID);
byteArray.AddRange(BitConverter.GetBytes((ushort)Flags)); m.Write((ushort)Flags);
byteArray.AddRange(BitConverter.GetBytes(Health)); m.Write(Health);
byteArray.Add(Speed); m.Write(Speed);
if (Flags.HasPedFlag(PedDataFlags.IsRagdoll)) if (Flags.HasPedFlag(PedDataFlags.IsRagdoll))
{ {
byteArray.AddVector3(HeadPosition); m.Write(HeadPosition);
byteArray.AddVector3(RightFootPosition); m.Write(RightFootPosition);
byteArray.AddVector3(LeftFootPosition); m.Write(LeftFootPosition);
} }
else else
{ {
if (Speed>=4) if (Speed >= 4)
{ {
byteArray.AddInt(VehicleID); m.Write(VehicleID);
byteArray.Add((byte)(Seat+3)); m.Write((byte)(Seat + 3));
} }
byteArray.AddVector3(Position); m.Write(Position);
} }
byteArray.AddVector3(Rotation); m.Write(Rotation);
byteArray.AddVector3(Velocity); m.Write(Velocity);
if (Flags.HasPedFlag(PedDataFlags.IsAiming)) if (Flags.HasPedFlag(PedDataFlags.IsAiming))
{ {
byteArray.AddVector3(AimCoords); m.Write(AimCoords);
} }
byteArray.AddFloat(Heading); m.Write(Heading);
if (Flags.HasPedFlag(PedDataFlags.IsFullSync)) if (Flags.HasPedFlag(PedDataFlags.IsFullSync))
{ {
byteArray.AddInt(ModelHash); m.Write(ModelHash);
byteArray.AddUint(CurrentWeaponHash); m.Write(CurrentWeaponHash);
byteArray.AddRange(Clothes); m.Write(Clothes);
if (WeaponComponents != null) if (WeaponComponents != null)
{ {
byteArray.Add(0x01); m.Write(true);
byteArray.AddRange(BitConverter.GetBytes((ushort)WeaponComponents.Count)); m.Write((ushort)WeaponComponents.Count);
foreach (KeyValuePair<uint, bool> component in WeaponComponents) foreach (KeyValuePair<uint, bool> component in WeaponComponents)
{ {
byteArray.AddRange(BitConverter.GetBytes(component.Key)); m.Write(component.Key);
byteArray.AddRange(BitConverter.GetBytes(component.Value)); m.Write(component.Value);
} }
} }
else else
{ {
// Player weapon doesn't have any components // Player weapon doesn't have any components
byteArray.Add(0x00); m.Write(false);
} }
byteArray.Add(WeaponTint); m.Write(WeaponTint);
byteArray.Add((byte)BlipColor); m.Write((byte)BlipColor);
if ((byte)BlipColor!=255) if ((byte)BlipColor != 255)
{ {
byteArray.AddUshort((ushort)BlipSprite); m.Write((ushort)BlipSprite);
byteArray.AddFloat(BlipScale); m.Write(BlipScale);
} }
} }
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadInt32();
OwnerID=reader.ReadInt32(); ID = m.ReadInt32();
Flags = (PedDataFlags)reader.ReadUInt16(); OwnerID = m.ReadInt32();
Health = reader.ReadInt32(); Flags = (PedDataFlags)m.ReadUInt16();
Speed = reader.ReadByte(); Health = m.ReadInt32();
Speed = m.ReadByte();
if (Flags.HasPedFlag(PedDataFlags.IsRagdoll)) if (Flags.HasPedFlag(PedDataFlags.IsRagdoll))
{ {
HeadPosition=reader.ReadVector3(); HeadPosition = m.ReadVector3();
RightFootPosition=reader.ReadVector3(); RightFootPosition = m.ReadVector3();
LeftFootPosition=reader.ReadVector3(); LeftFootPosition = m.ReadVector3();
Position=HeadPosition; Position = HeadPosition;
} }
else else
{ {
// Vehicle related // Vehicle related
if (Speed>=4) if (Speed >= 4)
{ {
VehicleID=reader.ReadInt32(); VehicleID = m.ReadInt32();
Seat=(VehicleSeat)(reader.ReadByte()-3); Seat = (VehicleSeat)(m.ReadByte() - 3);
} }
// Read player position // Read player position
Position = reader.ReadVector3(); Position = m.ReadVector3();
} }
Rotation = reader.ReadVector3(); Rotation = m.ReadVector3();
Velocity = reader.ReadVector3(); Velocity = m.ReadVector3();
if (Flags.HasPedFlag(PedDataFlags.IsAiming)) if (Flags.HasPedFlag(PedDataFlags.IsAiming))
{ {
// Read player aim coords // Read player aim coords
AimCoords = reader.ReadVector3(); AimCoords = m.ReadVector3();
} }
Heading=reader.ReadSingle(); Heading = m.ReadFloat();
if (Flags.HasPedFlag(PedDataFlags.IsFullSync)) if (Flags.HasPedFlag(PedDataFlags.IsFullSync))
{ {
// Read player model hash // Read player model hash
ModelHash = reader.ReadInt32(); ModelHash = m.ReadInt32();
// Read player weapon hash // Read player weapon hash
CurrentWeaponHash = reader.ReadUInt32(); CurrentWeaponHash = m.ReadUInt32();
// Read player clothes // Read player clothes
Clothes =reader.ReadBytes(36); Clothes = m.ReadBytes(36);
// Read player weapon components // Read player weapon components
if (reader.ReadBoolean()) if (m.ReadBoolean())
{ {
WeaponComponents = new Dictionary<uint, bool>(); WeaponComponents = new Dictionary<uint, bool>();
ushort comCount = reader.ReadUInt16(); ushort comCount = m.ReadUInt16();
for (ushort i = 0; i < comCount; i++) for (ushort i = 0; i < comCount; i++)
{ {
WeaponComponents.Add(reader.ReadUInt32(), reader.ReadBoolean()); WeaponComponents.Add(m.ReadUInt32(), m.ReadBoolean());
} }
} }
WeaponTint=reader.ReadByte(); WeaponTint = m.ReadByte();
BlipColor=(BlipColor)reader.ReadByte(); BlipColor = (BlipColor)m.ReadByte();
if ((byte)BlipColor!=255) if ((byte)BlipColor != 255)
{ {
BlipSprite=(BlipSprite)reader.ReadUInt16(); BlipSprite = (BlipSprite)m.ReadUInt16();
BlipScale=reader.ReadSingle(); BlipScale = m.ReadFloat();
} }
} }
#endregion #endregion

View File

@ -1,7 +1,5 @@
using System; using GTA.Math;
using System.Collections.Generic; using Lidgren.Network;
using System.Text;
using GTA.Math;
using System.Net; using System.Net;
namespace RageCoop.Core namespace RageCoop.Core
@ -15,7 +13,7 @@ namespace RageCoop.Core
} }
public class Handshake : Packet public class Handshake : Packet
{ {
public override PacketType Type => PacketType.Handshake; public override PacketType Type => PacketType.Handshake;
public int PedID { get; set; } public int PedID { get; set; }
public string Username { get; set; } public string Username { get; set; }
@ -38,62 +36,55 @@ namespace RageCoop.Core
public byte[] PasswordEncrypted { get; set; } public byte[] PasswordEncrypted { get; set; }
public IPEndPoint InternalEndPoint { get; set; } public IPEndPoint InternalEndPoint { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// Write Player Ped ID // Write Player Ped ID
byteArray.AddRange(BitConverter.GetBytes(PedID)); m.Write(PedID);
// Write Username // Write Username
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username); m.Write(Username);
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
byteArray.AddRange(usernameBytes);
// Write ModVersion // Write ModVersion
byte[] modVersionBytes = Encoding.UTF8.GetBytes(ModVersion); m.Write(ModVersion);
byteArray.AddRange(BitConverter.GetBytes(modVersionBytes.Length));
byteArray.AddRange(modVersionBytes);
byteArray.AddString(InternalEndPoint.ToString()); m.Write(InternalEndPoint.ToString());
// Write AesKeyCrypted // Write AesKeyCrypted
byteArray.AddArray(AesKeyCrypted); m.WriteByteArray(AesKeyCrypted);
// Write AesIVCrypted // Write AesIVCrypted
byteArray.AddArray(AesIVCrypted); m.WriteByteArray(AesIVCrypted);
// Write PassHash // Write PassHash
byteArray.AddArray(PasswordEncrypted); m.WriteByteArray(PasswordEncrypted);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read player netHandle // Read player netHandle
PedID = reader.ReadInt32(); PedID = m.ReadInt32();
// Read Username // Read Username
Username = reader.ReadString(); Username = m.ReadString();
// Read ModVersion // Read ModVersion
ModVersion = reader.ReadString(); ModVersion = m.ReadString();
InternalEndPoint=CoreUtils.StringToEndPoint(reader.ReadString()); InternalEndPoint = CoreUtils.StringToEndPoint(m.ReadString());
AesKeyCrypted=reader.ReadByteArray(); AesKeyCrypted = m.ReadByteArray();
AesIVCrypted=reader.ReadByteArray(); AesIVCrypted = m.ReadByteArray();
PasswordEncrypted=reader.ReadByteArray(); PasswordEncrypted = m.ReadByteArray();
#endregion #endregion
} }
} }
@ -101,99 +92,81 @@ namespace RageCoop.Core
{ {
public PlayerData[] Players { get; set; } public PlayerData[] Players { get; set; }
public override PacketType Type => PacketType.HandshakeSuccess; public override PacketType Type => PacketType.HandshakeSuccess;
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
var data = new List<byte>(); m.Write(Players.Length);
data.AddInt(Players.Length); foreach (var p in Players)
foreach(var p in Players)
{ {
data.AddInt(p.ID); m.Write(p.ID);
data.AddString(p.Username); m.Write(p.Username);
} }
return data.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
var reader = new BitReader(array);
Players=new PlayerData[reader.ReadInt32()]; Players = new PlayerData[m.ReadInt32()];
for(int i = 0; i<Players.Length; i++) for (int i = 0; i < Players.Length; i++)
{ {
Players[i]=new PlayerData() Players[i] = new PlayerData()
{ {
ID=reader.ReadInt32(), ID = m.ReadInt32(),
Username=reader.ReadString(), Username = m.ReadString(),
}; };
} }
} }
} }
public class PlayerConnect : Packet public class PlayerConnect : Packet
{ {
public override PacketType Type => PacketType.PlayerConnect; public override PacketType Type => PacketType.PlayerConnect;
public int PedID { get; set; } public int PedID { get; set; }
public string Username { get; set; } public string Username { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// Write NetHandle // Write NetHandle
byteArray.AddRange(BitConverter.GetBytes(PedID)); m.Write(PedID);
// Get Username bytes m.Write(Username);
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username);
// Write UsernameLength
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
// Write Username
byteArray.AddRange(usernameBytes);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read player netHandle // Read player netHandle
PedID = reader.ReadInt32(); PedID = m.ReadInt32();
// Read Username // Read Username
Username = reader.ReadString(); Username = m.ReadString();
#endregion #endregion
} }
} }
public class PlayerDisconnect : Packet public class PlayerDisconnect : Packet
{ {
public override PacketType Type => PacketType.PlayerDisconnect; public override PacketType Type => PacketType.PlayerDisconnect;
public int PedID { get; set; } public int PedID { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>(); m.Write(PedID);
byteArray.AddRange(BitConverter.GetBytes(PedID));
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
PedID = reader.ReadInt32(); PedID = m.ReadInt32();
#endregion #endregion
} }
} }
public class PlayerInfoUpdate : Packet public class PlayerInfoUpdate : Packet
{ {
public override PacketType Type => PacketType.PlayerInfoUpdate; public override PacketType Type => PacketType.PlayerInfoUpdate;
/// <summary> /// <summary>
/// Ped ID for this Player /// Ped ID for this Player
@ -203,70 +176,66 @@ namespace RageCoop.Core
public float Latency { get; set; } public float Latency { get; set; }
public Vector3 Position { get; set; } public Vector3 Position { get; set; }
public bool IsHost; public bool IsHost;
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// Write ID // Write ID
byteArray.AddRange(BitConverter.GetBytes(PedID)); m.Write(PedID);
// Write Username // Write Username
byteArray.AddString(Username); m.Write(Username);
// Write Latency // Write Latency
byteArray.AddFloat(Latency); m.Write(Latency);
byteArray.AddVector3(Position); m.Write(Position);
byteArray.AddBool(IsHost); m.Write(IsHost);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
// Read player ID // Read player ID
PedID = reader.ReadInt32(); PedID = m.ReadInt32();
// Read Username // Read Username
Username = reader.ReadString(); Username = m.ReadString();
Latency=reader.ReadSingle(); Latency = m.ReadFloat();
Position=reader.ReadVector3(); Position = m.ReadVector3();
IsHost=reader.ReadBoolean(); IsHost = m.ReadBoolean();
} }
} }
public class PublicKeyResponse : Packet public class PublicKeyResponse : Packet
{ {
public override PacketType Type => PacketType.PublicKeyResponse; public override PacketType Type => PacketType.PublicKeyResponse;
public byte[] Modulus; public byte[] Modulus;
public byte[] Exponent; public byte[] Exponent;
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
byteArray.AddArray(Modulus);
byteArray.AddArray(Exponent);
return byteArray.ToArray(); m.WriteByteArray(Modulus);
m.WriteByteArray(Exponent);
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
var reader=new BitReader(array);
Modulus=reader.ReadByteArray(); Modulus = m.ReadByteArray();
Exponent=reader.ReadByteArray(); Exponent = m.ReadByteArray();
#endregion #endregion
} }
@ -274,7 +243,7 @@ namespace RageCoop.Core
public class PublicKeyRequest : Packet public class PublicKeyRequest : Packet
{ {
public override PacketType Type => PacketType.PublicKeyRequest; public override PacketType Type => PacketType.PublicKeyRequest;
} }
} }
} }

View File

@ -1,7 +1,4 @@
using System; using GTA.Math;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -10,7 +7,7 @@ namespace RageCoop.Core
{ {
internal class ProjectileSync : Packet internal class ProjectileSync : Packet
{ {
public override PacketType Type => PacketType.ProjectileSync; public override PacketType Type => PacketType.ProjectileSync;
public int ID { get; set; } public int ID { get; set; }
public int ShooterID { get; set; } public int ShooterID { get; set; }
@ -26,57 +23,57 @@ namespace RageCoop.Core
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// Write id // Write id
byteArray.AddInt(ID); m.Write(ID);
// Write ShooterID // Write ShooterID
byteArray.AddInt(ShooterID); m.Write(ShooterID);
byteArray.AddUint(WeaponHash); m.Write(WeaponHash);
// Write position // Write position
byteArray.AddVector3(Position); m.Write(Position);
// Write rotation // Write rotation
byteArray.AddVector3(Rotation); m.Write(Rotation);
// Write velocity // Write velocity
byteArray.AddVector3(Velocity); m.Write(Velocity);
byteArray.Add((byte)Flags); m.Write((byte)Flags);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read id // Read id
ID = reader.ReadInt32(); ID = m.ReadInt32();
// Read ShooterID // Read ShooterID
ShooterID= reader.ReadInt32(); ShooterID = m.ReadInt32();
WeaponHash= reader.ReadUInt32(); WeaponHash = m.ReadUInt32();
// Read position // Read position
Position = reader.ReadVector3(); Position = m.ReadVector3();
// Read rotation // Read rotation
Rotation = reader.ReadVector3(); Rotation = m.ReadVector3();
// Read velocity // Read velocity
Velocity =reader.ReadVector3(); Velocity = m.ReadVector3();
Flags=(ProjectileDataFlags)reader.ReadByte(); Flags = (ProjectileDataFlags)m.ReadByte();
#endregion #endregion
} }

View File

@ -1,7 +1,4 @@
using System; using GTA.Math;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -11,7 +8,7 @@ namespace RageCoop.Core
internal class BulletShot : Packet internal class BulletShot : Packet
{ {
public override PacketType Type => PacketType.BulletShot; public override PacketType Type => PacketType.BulletShot;
public int OwnerID { get; set; } public int OwnerID { get; set; }
public uint WeaponHash { get; set; } public uint WeaponHash { get; set; }
@ -19,44 +16,44 @@ namespace RageCoop.Core
public Vector3 StartPosition { get; set; } public Vector3 StartPosition { get; set; }
public Vector3 EndPosition { get; set; } public Vector3 EndPosition { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// Write OwnerID // Write OwnerID
byteArray.AddRange(BitConverter.GetBytes(OwnerID)); m.Write(OwnerID);
// Write weapon hash // Write weapon hash
byteArray.AddRange(BitConverter.GetBytes(WeaponHash)); m.Write(WeaponHash);
// Write StartPosition // Write StartPosition
byteArray.AddVector3(StartPosition); m.Write(StartPosition);
// Write EndPosition // Write EndPosition
byteArray.AddVector3(EndPosition); m.Write(EndPosition);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read OwnerID // Read OwnerID
OwnerID=reader.ReadInt32(); OwnerID = m.ReadInt32();
// Read WeponHash // Read WeponHash
WeaponHash=reader.ReadUInt32(); WeaponHash = m.ReadUInt32();
// Read StartPosition // Read StartPosition
StartPosition=reader.ReadVector3(); StartPosition = m.ReadVector3();
// Read EndPosition // Read EndPosition
EndPosition=reader.ReadVector3(); EndPosition = m.ReadVector3();
#endregion #endregion
} }
} }

View File

@ -1,7 +1,4 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -10,29 +7,29 @@ namespace RageCoop.Core
{ {
internal class NozzleTransform : Packet internal class NozzleTransform : Packet
{ {
public override PacketType Type => PacketType.NozzleTransform; public override PacketType Type => PacketType.NozzleTransform;
public int VehicleID { get; set; } public int VehicleID { get; set; }
public bool Hover { get; set; } public bool Hover { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
byteArray.AddInt(VehicleID);
byteArray.AddBool(Hover);
return byteArray.ToArray(); m.Write(VehicleID);
m.Write(Hover);
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
VehicleID=reader.ReadInt32(); VehicleID = m.ReadInt32();
Hover=reader.ReadBoolean(); Hover = m.ReadBoolean();
#endregion #endregion
} }

View File

@ -1,7 +1,4 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -11,29 +8,24 @@ namespace RageCoop.Core
internal class OwnerChanged : Packet internal class OwnerChanged : Packet
{ {
public override PacketType Type => PacketType.OwnerChanged; public override PacketType Type => PacketType.OwnerChanged;
public int ID { get; set; } public int ID { get; set; }
public int NewOwnerID { get; set; } public int NewOwnerID { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
m.Write(ID);
List<byte> byteArray = new List<byte>(); m.Write(NewOwnerID);
byteArray.AddInt(ID);
byteArray.AddInt(NewOwnerID);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID=reader.ReadInt32();
NewOwnerID=reader.ReadInt32(); ID = m.ReadInt32();
NewOwnerID = m.ReadInt32();
#endregion #endregion
} }

View File

@ -1,7 +1,4 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -11,25 +8,25 @@ namespace RageCoop.Core
internal class PedKilled : Packet internal class PedKilled : Packet
{ {
public override PacketType Type => PacketType.PedKilled; public override PacketType Type => PacketType.PedKilled;
public int VictimID { get; set; } public int VictimID { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
byteArray.AddInt(VictimID);
return byteArray.ToArray(); m.Write(VictimID);
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
VictimID=reader.ReadInt32();
VictimID = m.ReadInt32();
#endregion #endregion
} }

View File

@ -1,7 +1,5 @@
using System; using GTA.Math;
using System.Collections.Generic; using Lidgren.Network;
using System.Text;
using GTA.Math;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -18,31 +16,31 @@ namespace RageCoop.Core
public Vector3 StartPosition { get; set; } public Vector3 StartPosition { get; set; }
public Vector3 EndPosition { get; set; } public Vector3 EndPosition { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
byteArray.AddInt(OwnerID);
byteArray.AddUshort(Bone);
byteArray.AddUint(WeaponHash);
byteArray.AddVector3(StartPosition);
byteArray.AddVector3(EndPosition);
return byteArray.ToArray(); m.Write(OwnerID);
m.Write(Bone);
m.Write(WeaponHash);
m.Write(StartPosition);
m.Write(EndPosition);
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
OwnerID=reader.ReadInt32();
Bone=reader.ReadUInt16(); OwnerID = m.ReadInt32();
WeaponHash=reader.ReadUInt32(); Bone = m.ReadUInt16();
StartPosition=reader.ReadVector3(); WeaponHash = m.ReadUInt32();
EndPosition=reader.ReadVector3(); StartPosition = m.ReadVector3();
EndPosition = m.ReadVector3();
#endregion #endregion
} }
} }

View File

@ -1,10 +1,7 @@
using System; using GTA;
using System.Collections.Generic;
using System.Text;
using GTA;
using GTA.Math; using GTA.Math;
using Lidgren.Network; using Lidgren.Network;
using System.Linq; using System.Collections.Generic;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -13,7 +10,7 @@ namespace RageCoop.Core
public class VehicleSync : Packet public class VehicleSync : Packet
{ {
public override PacketType Type => PacketType.VehicleSync; public override PacketType Type => PacketType.VehicleSync;
public int ID { get; set; } public int ID { get; set; }
public int OwnerID { get; set; } public int OwnerID { get; set; }
@ -58,200 +55,170 @@ namespace RageCoop.Core
public string LicensePlate { get; set; } public string LicensePlate { get; set; }
#endregion #endregion
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>(100);
byteArray.AddInt(ID); m.Write(ID);
byteArray.AddInt(OwnerID); m.Write(OwnerID);
byteArray.AddUshort((ushort)Flags); m.Write((ushort)Flags);
byteArray.AddVector3(Position); m.Write(Position);
byteArray.AddQuaternion(Quaternion); m.Write(Quaternion);
byteArray.AddVector3(Velocity); m.Write(Velocity);
byteArray.AddVector3(RotationVelocity); m.Write(RotationVelocity);
byteArray.AddFloat(ThrottlePower); m.Write(ThrottlePower);
byteArray.AddFloat(BrakePower); m.Write(BrakePower);
byteArray.AddFloat(SteeringAngle); m.Write(SteeringAngle);
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering)) if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering))
{ {
byteArray.AddFloat(DeluxoWingRatio); m.Write(DeluxoWingRatio);
} }
if (Flags.HasVehFlag(VehicleDataFlags.IsFullSync)) if (Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{ {
byteArray.AddInt(ModelHash); m.Write(ModelHash);
byteArray.AddFloat(EngineHealth); m.Write(EngineHealth);
// Check // Check
if (Flags.HasVehFlag(VehicleDataFlags.IsAircraft)) if (Flags.HasVehFlag(VehicleDataFlags.IsAircraft))
{ {
// Write the vehicle landing gear // Write the vehicle landing gear
byteArray.Add(LandingGear); m.Write(LandingGear);
} }
if (Flags.HasVehFlag(VehicleDataFlags.HasRoof)) if (Flags.HasVehFlag(VehicleDataFlags.HasRoof))
{ {
byteArray.Add(RoofState); m.Write(RoofState);
} }
// Write vehicle colors // Write vehicle colors
byteArray.Add(Colors[0]); m.Write(Colors[0]);
byteArray.Add(Colors[1]); m.Write(Colors[1]);
// Write vehicle mods // Write vehicle mods
// Write the count of mods // Write the count of mods
byteArray.AddRange(BitConverter.GetBytes((short)Mods.Count)); m.Write((short)Mods.Count);
// Loop the dictionary and add the values // Loop the dictionary and add the values
foreach (KeyValuePair<int, int> mod in Mods) foreach (KeyValuePair<int, int> mod in Mods)
{ {
// Write the mod value // Write the mod value
byteArray.AddRange(BitConverter.GetBytes(mod.Key)); m.Write(mod.Key);
byteArray.AddRange(BitConverter.GetBytes(mod.Value)); m.Write(mod.Value);
} }
if (!DamageModel.Equals(default(VehicleDamageModel))) if (!DamageModel.Equals(default(VehicleDamageModel)))
{ {
// Write boolean = true // Write boolean = true
byteArray.Add(0x01); m.Write(true);
// Write vehicle damage model // Write vehicle damage model
byteArray.Add(DamageModel.BrokenDoors); m.Write(DamageModel.BrokenDoors);
byteArray.Add(DamageModel.OpenedDoors); m.Write(DamageModel.OpenedDoors);
byteArray.Add(DamageModel.BrokenWindows); m.Write(DamageModel.BrokenWindows);
byteArray.AddRange(BitConverter.GetBytes(DamageModel.BurstedTires)); m.Write(DamageModel.BurstedTires);
byteArray.Add(DamageModel.LeftHeadLightBroken); m.Write(DamageModel.LeftHeadLightBroken);
byteArray.Add(DamageModel.RightHeadLightBroken); m.Write(DamageModel.RightHeadLightBroken);
} }
else else
{ {
// Write boolean = false // Write boolean = false
byteArray.Add(0x00); m.Write(false);
} }
// Write LockStatus // Write LockStatus
byteArray.Add((byte)LockStatus); m.Write((byte)LockStatus);
// Write RadioStation // Write RadioStation
byteArray.Add(RadioStation); m.Write(RadioStation);
// Write LicensePlate // Write LicensePlate
while (LicensePlate.Length<8) m.Write(LicensePlate);
{
LicensePlate+=" ";
}
if (LicensePlate.Length>8)
{
LicensePlate=new string(LicensePlate.Take(8).ToArray());
}
byteArray.AddRange(Encoding.ASCII.GetBytes(LicensePlate));
byteArray.Add((byte)(Livery+1)); m.Write((byte)(Livery + 1));
} }
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read vehicle id ID = m.ReadInt32();
ID = reader.ReadInt32(); OwnerID = m.ReadInt32();
Flags = (VehicleDataFlags)m.ReadUInt16();
OwnerID = reader.ReadInt32(); Position = m.ReadVector3();
Quaternion = m.ReadQuaternion();
Flags=(VehicleDataFlags)reader.ReadUInt16(); Velocity = m.ReadVector3();
RotationVelocity = m.ReadVector3();
// Read position ThrottlePower = m.ReadFloat();
Position = reader.ReadVector3(); BrakePower = m.ReadFloat();
SteeringAngle = m.ReadFloat();
// Read quaternion
Quaternion=reader.ReadQuaternion();
// Read velocity
Velocity =reader.ReadVector3();
// Read rotation velocity
RotationVelocity=reader.ReadVector3();
// Read throttle power
ThrottlePower=reader.ReadSingle();
// Read brake power
BrakePower=reader.ReadSingle();
// Read steering angle
SteeringAngle = reader.ReadSingle();
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering)) if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering))
{ {
DeluxoWingRatio = reader.ReadSingle(); DeluxoWingRatio = m.ReadFloat();
} }
if (Flags.HasVehFlag(VehicleDataFlags.IsFullSync)) if (Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{ {
// Read vehicle model hash // Read vehicle model hash
ModelHash = reader.ReadInt32(); ModelHash = m.ReadInt32();
// Read vehicle engine health // Read vehicle engine health
EngineHealth = reader.ReadSingle(); EngineHealth = m.ReadFloat();
// Check // Check
if (Flags.HasVehFlag(VehicleDataFlags.IsAircraft)) if (Flags.HasVehFlag(VehicleDataFlags.IsAircraft))
{ {
// Read vehicle landing gear // Read vehicle landing gear
LandingGear = reader.ReadByte(); LandingGear = m.ReadByte();
} }
if (Flags.HasVehFlag(VehicleDataFlags.HasRoof)) if (Flags.HasVehFlag(VehicleDataFlags.HasRoof))
{ {
RoofState=reader.ReadByte(); RoofState = m.ReadByte();
} }
// Read vehicle colors // Read vehicle colors
byte vehColor1 = reader.ReadByte(); byte vehColor1 = m.ReadByte();
byte vehColor2 = reader.ReadByte(); byte vehColor2 = m.ReadByte();
Colors = new byte[] { vehColor1, vehColor2 }; Colors = new byte[] { vehColor1, vehColor2 };
// Read vehicle mods // Read vehicle mods
// Create new Dictionary // Create new Dictionary
Mods = new Dictionary<int, int>(); Mods = new Dictionary<int, int>();
// Read count of mods // Read count of mods
short vehModCount = reader.ReadInt16(); short vehModCount = m.ReadInt16();
// Loop // Loop
for (int i = 0; i < vehModCount; i++) for (int i = 0; i < vehModCount; i++)
{ {
// Read the mod value // Read the mod value
Mods.Add(reader.ReadInt32(), reader.ReadInt32()); Mods.Add(m.ReadInt32(), m.ReadInt32());
} }
if (reader.ReadBoolean()) if (m.ReadBoolean())
{ {
// Read vehicle damage model // Read vehicle damage model
DamageModel = new VehicleDamageModel() DamageModel = new VehicleDamageModel()
{ {
BrokenDoors = reader.ReadByte(), BrokenDoors = m.ReadByte(),
OpenedDoors=reader.ReadByte(), OpenedDoors = m.ReadByte(),
BrokenWindows = reader.ReadByte(), BrokenWindows = m.ReadByte(),
BurstedTires = reader.ReadInt16(), BurstedTires = m.ReadInt16(),
LeftHeadLightBroken = reader.ReadByte(), LeftHeadLightBroken = m.ReadByte(),
RightHeadLightBroken = reader.ReadByte() RightHeadLightBroken = m.ReadByte()
}; };
} }
// Read LockStatus // Read LockStatus
LockStatus=(VehicleLockStatus)reader.ReadByte(); LockStatus = (VehicleLockStatus)m.ReadByte();
// Read RadioStation // Read RadioStation
RadioStation=reader.ReadByte(); RadioStation = m.ReadByte();
LicensePlate=Encoding.ASCII.GetString(reader.ReadBytes(8)); LicensePlate = m.ReadString();
Livery=(int)(reader.ReadByte()-1); Livery = m.ReadByte() - 1;
} }
#endregion #endregion
} }

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -10,20 +10,19 @@ namespace RageCoop.Core
public byte[] Buffer { get; set; } public byte[] Buffer { get; set; }
public int Recorded { get; set; } public int Recorded { get; set; }
public override PacketType Type => PacketType.Voice; public override PacketType Type => PacketType.Voice;
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
var data = new List<byte>(); m.Write(ID);
data.AddInt(ID); m.Write(Buffer);
data.AddArray(Buffer); m.Write(Recorded);
data.AddInt(Recorded);
return data.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
var reader = new BitReader(array);
ID = reader.ReadInt32(); ID = m.ReadInt32();
Buffer = reader.ReadByteArray(); Buffer = m.ReadByteArray();
Recorded = reader.ReadInt32(); Recorded = m.ReadInt32();
} }
} }
} }

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
namespace RageCoop.Core.Scripting namespace RageCoop.Core.Scripting
{ {
@ -10,8 +10,8 @@ namespace RageCoop.Core.Scripting
/// </summary> /// </summary>
public static class CustomEvents public static class CustomEvents
{ {
static MD5 Hasher = MD5.Create(); private static readonly MD5 Hasher = MD5.Create();
static Dictionary<int,string> Hashed=new Dictionary<int,string>(); private static readonly Dictionary<int, string> Hashed = new Dictionary<int, string>();
internal static readonly int OnPlayerDied = Hash("RageCoop.OnPlayerDied"); internal static readonly int OnPlayerDied = Hash("RageCoop.OnPlayerDied");
internal static readonly int SetWeather = Hash("RageCoop.SetWeather"); internal static readonly int SetWeather = Hash("RageCoop.SetWeather");
internal static readonly int OnPedDeleted = Hash("RageCoop.OnPedDeleted"); internal static readonly int OnPedDeleted = Hash("RageCoop.OnPedDeleted");
@ -40,25 +40,20 @@ namespace RageCoop.Core.Scripting
public static int Hash(string s) public static int Hash(string s)
{ {
var hash = BitConverter.ToInt32(Hasher.ComputeHash(Encoding.UTF8.GetBytes(s)), 0); var hash = BitConverter.ToInt32(Hasher.ComputeHash(Encoding.UTF8.GetBytes(s)), 0);
string name;
lock (Hashed) lock (Hashed)
{ {
if (Hashed.TryGetValue(hash, out name)) if (Hashed.TryGetValue(hash, out string name))
{ {
if (name!=s) if (name != s)
{ {
throw new ArgumentException($"Hashed value has collision with another name:{name}, hashed value:{hash}"); throw new ArgumentException($"Hashed value has collision with another name:{name}, hashed value:{hash}");
} }
else
{
return hash;
}
}
else
{
Hashed.Add(hash, s);
return hash; return hash;
} }
Hashed.Add(hash, s);
return hash;
} }
} }
} }

View File

@ -1,25 +1,24 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
namespace RageCoop.Core.Scripting namespace RageCoop.Core.Scripting
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public class ResourceFile public class ResourceFile
{ {
/// <summary> /// <summary>
/// Full name with relative path of this file /// Full name with relative path of this file
/// </summary> /// </summary>
public string Name { get; internal set; } public string Name { get; internal set; }
/// <summary> /// <summary>
/// Whether this is a directory /// Whether this is a directory
/// </summary> /// </summary>
public bool IsDirectory { get; internal set; } public bool IsDirectory { get; internal set; }
/// <summary> /// <summary>
/// Get a stream that can be used to read file content. /// Get a stream that can be used to read file content.
/// </summary> /// </summary>
public Func<Stream> GetStream { get; internal set; } public Func<Stream> GetStream { get; internal set; }
} }
} }

View File

@ -1,17 +1,17 @@
using System; using System;
using System.Threading;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading;
namespace RageCoop.Core namespace RageCoop.Core
{ {
/// <summary> /// <summary>
/// A worker that constantly execute jobs in a background thread. /// A worker that constantly execute jobs in a background thread.
/// </summary> /// </summary>
public class Worker:IDisposable public class Worker : IDisposable
{ {
private SemaphoreSlim _semaphoreSlim; private readonly SemaphoreSlim _semaphoreSlim;
private Thread _workerThread; private readonly Thread _workerThread;
private bool _stopping=false; private bool _stopping = false;
/// <summary> /// <summary>
/// Name of the worker /// Name of the worker
/// </summary> /// </summary>
@ -19,37 +19,37 @@ namespace RageCoop.Core
/// <summary> /// <summary>
/// Whether this worker is busy executing job(s). /// Whether this worker is busy executing job(s).
/// </summary> /// </summary>
public bool IsBusy { get;private set; } public bool IsBusy { get; private set; }
internal Worker(string name,Logger logger,int maxJobs = Int32.MaxValue) internal Worker(string name, Logger logger, int maxJobs = Int32.MaxValue)
{ {
Name = name; Name = name;
_semaphoreSlim = new SemaphoreSlim(0,maxJobs); _semaphoreSlim = new SemaphoreSlim(0, maxJobs);
_workerThread=new Thread(() => _workerThread = new Thread(() =>
{ {
while (!_stopping) while (!_stopping)
{ {
IsBusy=false; IsBusy = false;
_semaphoreSlim.Wait(); _semaphoreSlim.Wait();
if(Jobs.TryDequeue(out var job)) if (Jobs.TryDequeue(out var job))
{ {
IsBusy=true; IsBusy = true;
try try
{ {
job.Invoke(); job.Invoke();
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error("Error occurred when executing queued job:"); logger.Error("Error occurred when executing queued job:");
logger.Error(ex); logger.Error(ex);
} }
} }
else else
{ {
throw new InvalidOperationException("Hmm... that's unexpected."); throw new InvalidOperationException("Hmm... that's unexpected.");
} }
} }
IsBusy=false; IsBusy = false;
}); });
_workerThread.Start(); _workerThread.Start();
} }
/// <summary> /// <summary>
@ -66,7 +66,7 @@ namespace RageCoop.Core
/// </summary> /// </summary>
public void Stop() public void Stop()
{ {
_stopping=true; _stopping = true;
QueueJob(() => { }); QueueJob(() => { });
if (_workerThread.IsAlive) if (_workerThread.IsAlive)
{ {
@ -81,6 +81,6 @@ namespace RageCoop.Core
Stop(); Stop();
_semaphoreSlim.Dispose(); _semaphoreSlim.Dispose();
} }
private ConcurrentQueue<Action> Jobs=new ConcurrentQueue<Action>(); private readonly ConcurrentQueue<Action> Jobs = new ConcurrentQueue<Action>();
} }
} }

View File

@ -1,12 +1,12 @@
using System; using Lidgren.Network;
using System.Collections.Generic;
using RageCoop.Core; using RageCoop.Core;
using Lidgren.Network;
using System.Diagnostics;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using System.Security.Cryptography;
using RageCoop.Server.Scripting; using RageCoop.Server.Scripting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net; using System.Net;
using System.Security.Cryptography;
namespace RageCoop.Server namespace RageCoop.Server
{ {
@ -18,7 +18,7 @@ namespace RageCoop.Server
private readonly Server Server; private readonly Server Server;
internal Client(Server server) internal Client(Server server)
{ {
Server=server; Server = server;
} }
/// <summary> /// <summary>
@ -28,7 +28,7 @@ namespace RageCoop.Server
/// <summary> /// <summary>
/// Th client's IP address and port. /// Th client's IP address and port.
/// </summary> /// </summary>
public IPEndPoint EndPoint { get { return Connection?.RemoteEndPoint; } } public IPEndPoint EndPoint => Connection?.RemoteEndPoint;
/// <summary> /// <summary>
/// Internal(LAN) address of this client, used for NAT hole-punching /// Internal(LAN) address of this client, used for NAT hole-punching
@ -36,7 +36,7 @@ namespace RageCoop.Server
public IPEndPoint InternalEndPoint { get; internal set; } public IPEndPoint InternalEndPoint { get; internal set; }
internal long NetHandle = 0; internal long NetHandle = 0;
internal NetConnection Connection { get;set; } internal NetConnection Connection { get; set; }
/// <summary> /// <summary>
/// The <see cref="ServerPed"/> instance representing the client's main character. /// The <see cref="ServerPed"/> instance representing the client's main character.
/// </summary> /// </summary>
@ -44,45 +44,47 @@ namespace RageCoop.Server
/// <summary> /// <summary>
/// The client's latency in seconds. /// The client's latency in seconds.
/// </summary> /// </summary>
public float Latency => Connection.AverageRoundtripTime/2; public float Latency => Connection.AverageRoundtripTime / 2;
internal readonly Dictionary<int, Action<object>> Callbacks = new(); internal readonly Dictionary<int, Action<object>> Callbacks = new();
internal byte[] PublicKey { get; set; } internal byte[] PublicKey { get; set; }
/// <summary> /// <summary>
/// Indicates whether the client has succefully loaded all resources. /// Indicates whether the client has succefully loaded all resources.
/// </summary> /// </summary>
public bool IsReady { get; internal set; }=false; public bool IsReady { get; internal set; } = false;
/// <summary> /// <summary>
/// The client's username. /// The client's username.
/// </summary> /// </summary>
public string Username { get;internal set; } = "N/A"; public string Username { get; internal set; } = "N/A";
private bool _autoRespawn=true; private bool _autoRespawn = true;
/// <summary> /// <summary>
/// Gets or sets whether to enable automatic respawn for this client's main ped. /// Gets or sets whether to enable automatic respawn for this client's main ped.
/// </summary> /// </summary>
public bool EnableAutoRespawn { public bool EnableAutoRespawn
get { return _autoRespawn; } {
set { get => _autoRespawn;
BaseScript.SetAutoRespawn(this,value); set
_autoRespawn=value; {
BaseScript.SetAutoRespawn(this, value);
_autoRespawn = value;
} }
} }
private bool _displayNameTag=true; private bool _displayNameTag = true;
private Stopwatch _latencyWatch = new Stopwatch(); private readonly Stopwatch _latencyWatch = new Stopwatch();
/// <summary> /// <summary>
/// Gets or sets whether to enable automatic respawn for this client's main ped. /// Gets or sets whether to enable automatic respawn for this client's main ped.
/// </summary> /// </summary>
public bool DisplayNameTag public bool DisplayNameTag
{ {
get { return _displayNameTag; } get => _displayNameTag;
set set
{ {
Server.BaseScript.SetNameTag(this,value); Server.BaseScript.SetNameTag(this, value);
_displayNameTag=value; _displayNameTag = value;
} }
} }
#region FUNCTIONS #region FUNCTIONS
@ -90,7 +92,7 @@ namespace RageCoop.Server
/// Kick this client /// Kick this client
/// </summary> /// </summary>
/// <param name="reason"></param> /// <param name="reason"></param>
public void Kick(string reason="You have been kicked!") public void Kick(string reason = "You have been kicked!")
{ {
Connection?.Disconnect(reason); Connection?.Disconnect(reason);
} }
@ -129,7 +131,7 @@ namespace RageCoop.Server
/// <param name="args"></param> /// <param name="args"></param>
public void SendNativeCall<T>(Action<object> callBack, GTA.Native.Hash hash, params object[] args) public void SendNativeCall<T>(Action<object> callBack, GTA.Native.Hash hash, params object[] args)
{ {
var argsList= new List<object>(args); var argsList = new List<object>(args);
argsList.InsertRange(0, new object[] { (byte)Type.GetTypeCode(typeof(T)), RequestNativeCallID<T>(callBack), (ulong)hash }); argsList.InsertRange(0, new object[] { (byte)Type.GetTypeCode(typeof(T)), RequestNativeCallID<T>(callBack), (ulong)hash });
SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray()); SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray());
@ -142,7 +144,7 @@ namespace RageCoop.Server
public void SendNativeCall(GTA.Native.Hash hash, params object[] args) public void SendNativeCall(GTA.Native.Hash hash, params object[] args)
{ {
var argsList = new List<object>(args); var argsList = new List<object>(args);
argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty,(ulong)hash }); argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty, (ulong)hash });
// Server.Logger?.Debug(argsList.DumpWithType()); // Server.Logger?.Debug(argsList.DumpWithType());
SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray()); SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray());
} }
@ -151,7 +153,7 @@ namespace RageCoop.Server
int ID = 0; int ID = 0;
lock (Callbacks) lock (Callbacks)
{ {
while ((ID==0) while ((ID == 0)
|| Callbacks.ContainsKey(ID)) || Callbacks.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];
@ -170,7 +172,7 @@ namespace RageCoop.Server
/// </summary> /// </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="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> /// <param name="args">Arguments</param>
public void SendCustomEvent(int hash,params object[] args) public void SendCustomEvent(int hash, params object[] args)
{ {
if (!IsReady) if (!IsReady)
{ {
@ -183,8 +185,8 @@ namespace RageCoop.Server
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.CustomEvent() new Packets.CustomEvent()
{ {
Hash=hash, Hash = hash,
Args=args Args = args
}.Pack(outgoingMessage); }.Pack(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event); Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event);
@ -211,10 +213,10 @@ namespace RageCoop.Server
{ {
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.CustomEvent(null,true) new Packets.CustomEvent(null, true)
{ {
Hash=hash, Hash = hash,
Args=args Args = args
}.Pack(outgoingMessage); }.Pack(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event); Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event);

View File

@ -1,16 +1,10 @@
using System; namespace RageCoop.Server
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Server
{ {
internal class FileTransfer internal class FileTransfer
{ {
public int ID { get; set; } public int ID { get; set; }
public float Progress { get; set; } public float Progress { get; set; }
public string Name { get; set; } public string Name { get; set; }
public bool Cancel { get; set; }=false; public bool Cancel { get; set; } = false;
} }
} }

View File

@ -1,12 +1,4 @@
using System; namespace RageCoop.Server
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 internal class HolePunch
{ {

View File

@ -1,25 +1,23 @@
using System; using ICSharpCode.SharpZipLib.Zip;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
using Newtonsoft.Json;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Server.Scripting;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using Newtonsoft.Json;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
using System.Threading;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.IO; using System.Text;
using ICSharpCode.SharpZipLib.Zip; using System.Threading;
using System.Diagnostics;
namespace RageCoop.Server namespace RageCoop.Server
{ {
public partial class Server public partial class Server
{ {
const string _versionURL = "https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/main/RageCoop.Server/Properties/AssemblyInfo.cs"; private const string _versionURL = "https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/main/RageCoop.Server/Properties/AssemblyInfo.cs";
private void SendPlayerUpdate() private void SendPlayerUpdate()
{ {
foreach (var c in ClientsByNetHandle.Values.ToArray()) foreach (var c in ClientsByNetHandle.Values.ToArray())
@ -44,6 +42,7 @@ namespace RageCoop.Server
} }
} }
private IpInfo IpInfo = null; private IpInfo IpInfo = null;
private bool CanAnnounce = false;
private void Announce() private void Announce()
{ {
HttpResponseMessage response = null; HttpResponseMessage response = null;
@ -54,7 +53,10 @@ namespace RageCoop.Server
{ {
// TLS only // TLS only
ServicePointManager.Expect100Continue = true; ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 |
SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
try try
@ -77,6 +79,19 @@ namespace RageCoop.Server
Logger?.Error($"MasterServer: {ex.Message}"); Logger?.Error($"MasterServer: {ex.Message}");
} }
} }
if (!CanAnnounce)
{
var existing = JsonConvert.DeserializeObject<List<ServerInfo>>(HttpHelper.DownloadString(Util.GetFinalRedirect(Settings.MasterServer))).Where(x => x.address == IpInfo.Address && x.port == Settings.Port.ToString()).FirstOrDefault();
if(existing != null)
{
Logger.Warning("Server info already present in master server, waiting for 10 seconds...");
return;
}
else
{
CanAnnounce = true;
}
}
try try
{ {
Security.GetPublicKey(out var pModulus, out var pExpoenet); Security.GetPublicKey(out var pModulus, out var pExpoenet);
@ -144,7 +159,7 @@ namespace RageCoop.Server
Thread.Sleep(10 * 60 * 1000); Thread.Sleep(10 * 60 * 1000);
API.SendChatMessage("downloading update..."); API.SendChatMessage("downloading update...");
var downloadURL = $"https://github.com/RAGECOOP/RAGECOOP-V/releases/download/nightly/RageCoop.Server-{GetRID()}.zip"; var downloadURL = $"https://github.com/RAGECOOP/RAGECOOP-V/releases/download/nightly/RageCoop.Server-{CoreUtils.GetInvariantRID()}.zip";
if (Directory.Exists("Update")) { Directory.Delete("Update", true); } if (Directory.Exists("Update")) { Directory.Delete("Update", true); }
HttpHelper.DownloadFile(downloadURL, "Update.zip", null); HttpHelper.DownloadFile(downloadURL, "Update.zip", null);
Logger?.Info("Installing update"); Logger?.Info("Installing update");
@ -153,31 +168,20 @@ namespace RageCoop.Server
MainNetServer.Shutdown("Server updating"); MainNetServer.Shutdown("Server updating");
Logger.Info("Server shutting down!"); Logger.Info("Server shutting down!");
Logger.Flush(); Logger.Flush();
Process.Start(Path.Combine("Update", RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "RageCoop.Server.exe": "RageCoop.Server"), "update \"" + AppDomain.CurrentDomain.BaseDirectory[0..^1] + "\""); Process.Start(Path.Combine("Update", RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "RageCoop.Server.exe" : "RageCoop.Server"), "update \"" + AppDomain.CurrentDomain.BaseDirectory[0..^1] + "\"");
Environment.Exit(0); Environment.Exit(0);
} }
catch(Exception ex) catch (Exception ex)
{ {
Logger?.Error("Update",ex); Logger?.Error("Update", ex);
} }
} }
static string GetRID()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "win-"+RuntimeInformation.OSArchitecture.ToString().ToLower();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return "linux-"+RuntimeInformation.OSArchitecture.ToString().ToLower();
}
return "unknown";
}
private void KickAssholes() private void KickAssholes()
{ {
foreach(var c in ClientsByNetHandle.Values.ToArray()) foreach (var c in ClientsByNetHandle.Values.ToArray())
{ {
if (c.EntitiesCount > 100 && Settings.KickSpamming) if (c.EntitiesCount > Settings.SpamLimit && Settings.KickSpamming)
{ {
c.Kick("Bye bye asshole: spamming"); c.Kick("Bye bye asshole: spamming");
API.SendChatMessage($"Asshole {c.Username} was kicked: Spamming"); API.SendChatMessage($"Asshole {c.Username} was kicked: Spamming");

View File

@ -1,12 +1,10 @@
using System; using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting; using RageCoop.Server.Scripting;
using System;
using System.Linq;
using System.Text;
namespace RageCoop.Server namespace RageCoop.Server
{ {
@ -43,16 +41,17 @@ namespace RageCoop.Server
connection.Deny("Username is already taken!"); connection.Deny("Username is already taken!");
return; return;
} }
try try
{ {
Security.AddConnection(connection.RemoteEndPoint, packet.AesKeyCrypted, packet.AesIVCrypted); Security.AddConnection(connection.RemoteEndPoint, packet.AesKeyCrypted, packet.AesIVCrypted);
var args = new HandshakeEventArgs() var args = new HandshakeEventArgs()
{ {
EndPoint=connection.RemoteEndPoint, EndPoint = connection.RemoteEndPoint,
ID=packet.PedID, ID = packet.PedID,
Username=packet.Username, Username = packet.Username,
PasswordHash=Security.Decrypt(packet.PasswordEncrypted, connection.RemoteEndPoint).GetString().GetSHA256Hash().ToHexString(), PasswordHash = Security.Decrypt(packet.PasswordEncrypted, connection.RemoteEndPoint).GetString().GetSHA256Hash().ToHexString(),
}; };
API.Events.InvokePlayerHandshake(args); API.Events.InvokePlayerHandshake(args);
if (args.Cancel) if (args.Cancel)
@ -68,20 +67,22 @@ namespace RageCoop.Server
connection.Deny("Malformed handshak packet!"); connection.Deny("Malformed handshak packet!");
return; return;
} }
var handshakeSuccess = MainNetServer.CreateMessage(); var handshakeSuccess = MainNetServer.CreateMessage();
var currentClients = ClientsByID.Values.ToArray(); var currentClients = ClientsByID.Values.ToArray();
var players = new Packets.PlayerData[currentClients.Length]; var players = new Packets.PlayerData[currentClients.Length];
for (int i = 0; i<players.Length; i++) for (int i = 0; i < players.Length; i++)
{ {
players[i]=new Packets.PlayerData() players[i] = new Packets.PlayerData()
{ {
ID=currentClients[i].Player.ID, ID = currentClients[i].Player.ID,
Username=currentClients[i].Username, Username = currentClients[i].Username,
}; };
} }
new Packets.HandshakeSuccess() new Packets.HandshakeSuccess()
{ {
Players=players Players = players
}.Pack(handshakeSuccess); }.Pack(handshakeSuccess);
connection.Approve(handshakeSuccess); connection.Approve(handshakeSuccess);
Client tmpClient; Client tmpClient;
@ -91,25 +92,25 @@ namespace RageCoop.Server
{ {
var player = new ServerPed(this) var player = new ServerPed(this)
{ {
ID= packet.PedID, ID = packet.PedID,
}; };
Entities.Add(player); Entities.Add(player);
ClientsByNetHandle.Add(connection.RemoteUniqueIdentifier, ClientsByNetHandle.Add(connection.RemoteUniqueIdentifier,
tmpClient = new Client(this) tmpClient = new Client(this)
{ {
NetHandle = connection.RemoteUniqueIdentifier, NetHandle = connection.RemoteUniqueIdentifier,
Connection=connection, Connection = connection,
Username=packet.Username, Username = packet.Username,
Player = player, Player = player,
InternalEndPoint=packet.InternalEndPoint, InternalEndPoint = packet.InternalEndPoint,
} }
); );
player.Owner=tmpClient; player.Owner = tmpClient;
ClientsByName.Add(packet.Username.ToLower(), tmpClient); ClientsByName.Add(packet.Username.ToLower(), tmpClient);
ClientsByID.Add(player.ID, tmpClient); ClientsByID.Add(player.ID, tmpClient);
if (ClientsByNetHandle.Count==1) if (ClientsByNetHandle.Count == 1)
{ {
_hostClient=tmpClient; _hostClient = tmpClient;
} }
} }
@ -120,19 +121,19 @@ namespace RageCoop.Server
// The connection has been approved, now we need to send all other players to the new player and the new player to all players // The connection has been approved, now we need to send all other players to the new player and the new player to all players
private void PlayerConnected(Client newClient) private void PlayerConnected(Client newClient)
{ {
if (newClient==_hostClient) if (newClient == _hostClient)
{ {
API.SendCustomEvent(new() { newClient }, CustomEvents.IsHost, true); API.SendCustomEvent(new() { newClient }, CustomEvents.IsHost, true);
} }
// Send new client to all players // Send new client to all players
var cons = MainNetServer.Connections.Exclude(newClient.Connection); var cons = MainNetServer.Connections.Exclude(newClient.Connection);
if (cons.Count!=0) if (cons.Count != 0)
{ {
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect() new Packets.PlayerConnect()
{ {
PedID=newClient.Player.ID, PedID = newClient.Player.ID,
Username = newClient.Username Username = newClient.Username
}.Pack(outgoingMessage); }.Pack(outgoingMessage);
@ -146,15 +147,13 @@ namespace RageCoop.Server
// Send all blips to this player // Send all blips to this player
BaseScript.SendServerBlipsTo(new(Entities.Blips.Values), new() { newClient }); BaseScript.SendServerBlipsTo(new(Entities.Blips.Values), new() { newClient });
// Create P2P connection // Create P2P connection
if (Settings.UseP2P) if (Settings.UseP2P)
{ {
ClientsByNetHandle.Values.ForEach(target => ClientsByNetHandle.Values.ForEach(target =>
{ {
if (target==newClient) { return; } if (target == newClient) { return; }
HolePunch(target,newClient); HolePunch(target, newClient);
}); });
} }
@ -170,23 +169,23 @@ namespace RageCoop.Server
private void PlayerDisconnected(Client localClient) private void PlayerDisconnected(Client localClient)
{ {
var cons = MainNetServer.Connections.Exclude(localClient.Connection); var cons = MainNetServer.Connections.Exclude(localClient.Connection);
if (cons.Count!=0) if (cons.Count != 0)
{ {
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerDisconnect() new Packets.PlayerDisconnect()
{ {
PedID=localClient.Player.ID, PedID = localClient.Player.ID,
}.Pack(outgoingMessage); }.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, cons, NetDeliveryMethod.ReliableOrdered, 0); MainNetServer.SendMessage(outgoingMessage, cons, NetDeliveryMethod.ReliableOrdered, 0);
} }
Entities.CleanUp(localClient); Entities.CleanUp(localClient);
_worker.QueueJob(() => API.Events.InvokePlayerDisconnected(localClient)); QueueJob(() => API.Events.InvokePlayerDisconnected(localClient));
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.Player.ID}"); Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.Player.ID}");
if (ClientsByNetHandle.ContainsKey(localClient.NetHandle)) { ClientsByNetHandle.Remove(localClient.NetHandle); } if (ClientsByNetHandle.ContainsKey(localClient.NetHandle)) { ClientsByNetHandle.Remove(localClient.NetHandle); }
if (ClientsByName.ContainsKey(localClient.Username.ToLower())) { ClientsByName.Remove(localClient.Username.ToLower()); } if (ClientsByName.ContainsKey(localClient.Username.ToLower())) { ClientsByName.Remove(localClient.Username.ToLower()); }
if (ClientsByID.ContainsKey(localClient.Player.ID)) { ClientsByID.Remove(localClient.Player.ID); } if (ClientsByID.ContainsKey(localClient.Player.ID)) { ClientsByID.Remove(localClient.Player.ID); }
if (localClient==_hostClient) if (localClient == _hostClient)
{ {
_hostClient = ClientsByNetHandle.Values.FirstOrDefault(); _hostClient = ClientsByNetHandle.Values.FirstOrDefault();

View File

@ -1,11 +1,5 @@
using System; using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting; using RageCoop.Server.Scripting;
namespace RageCoop.Server namespace RageCoop.Server
@ -14,12 +8,12 @@ namespace RageCoop.Server
{ {
private void PedSync(Packets.PedSync packet, Client client) private void PedSync(Packets.PedSync packet, Client client)
{ {
_worker.QueueJob(() => Entities.Update(packet, client)); QueueJob(() => Entities.Update(packet, client));
bool isPlayer = packet.ID==client.Player.ID; bool isPlayer = packet.ID == client.Player.ID;
if (isPlayer) if (isPlayer)
{ {
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client)); QueueJob(() => API.Events.InvokePlayerUpdate(client));
} }
if (Settings.UseP2P) { return; } if (Settings.UseP2P) { return; }
@ -27,17 +21,17 @@ namespace RageCoop.Server
{ {
// Don't send data back // Don't send data back
if (c.NetHandle==client.NetHandle) { continue; } if (c.NetHandle == client.NetHandle) { continue; }
// Check streaming distance // Check streaming distance
if (isPlayer) if (isPlayer)
{ {
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance)) if ((Settings.PlayerStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.PlayerStreamingDistance))
{ {
continue; continue;
} }
} }
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance)) else if ((Settings.NpcStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.NpcStreamingDistance))
{ {
continue; continue;
} }
@ -49,35 +43,33 @@ namespace RageCoop.Server
} }
private void VehicleSync(Packets.VehicleSync packet, Client client) private void VehicleSync(Packets.VehicleSync packet, Client client)
{ {
_worker.QueueJob(() => Entities.Update(packet, client)); QueueJob(() => Entities.Update(packet, client));
bool isPlayer = packet.ID==client.Player?.LastVehicle?.ID; bool isPlayer = packet.ID == client.Player?.LastVehicle?.ID;
if (Settings.UseP2P) { return; } if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values) foreach (var c in ClientsByNetHandle.Values)
{ {
if (c.NetHandle==client.NetHandle) { continue; } if (c.NetHandle == client.NetHandle) { continue; }
if (isPlayer) if (isPlayer)
{ {
// Player's vehicle // Player's vehicle
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance)) if ((Settings.PlayerStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.PlayerStreamingDistance))
{ {
continue; continue;
} }
} }
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance)) else if ((Settings.NpcStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.NpcStreamingDistance))
{ {
continue; continue;
} }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage); packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync); MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.VehicleSync);
} }
} }
private void ProjectileSync(Packets.ProjectileSync packet, Client client) private void ProjectileSync(Packets.ProjectileSync packet, Client client)
{ {
if (Settings.UseP2P) { return; } if (Settings.UseP2P) { return; }
Forward(packet, client, ConnectionChannel.ProjectileSync); Forward(packet, client, ConnectionChannel.ProjectileSync);
} }

View File

@ -1,12 +1,5 @@
using System; using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
namespace RageCoop.Server namespace RageCoop.Server
{ {
@ -17,19 +10,19 @@ namespace RageCoop.Server
// Send to host // Send to host
Send(new Packets.HolePunchInit Send(new Packets.HolePunchInit
{ {
Connect=false, Connect = false,
TargetID=client.Player.ID, TargetID = client.Player.ID,
TargetInternal=client.InternalEndPoint.ToString(), TargetInternal = client.InternalEndPoint.ToString(),
TargetExternal=client.EndPoint.ToString() TargetExternal = client.EndPoint.ToString()
}, host, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered); }, host, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
// Send to client // Send to client
Send(new Packets.HolePunchInit Send(new Packets.HolePunchInit
{ {
Connect=true, Connect = true,
TargetID=host.Player.ID, TargetID = host.Player.ID,
TargetInternal=host.InternalEndPoint.ToString(), TargetInternal = host.InternalEndPoint.ToString(),
TargetExternal=host.EndPoint.ToString() TargetExternal = host.EndPoint.ToString()
}, client, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered); }, client, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
} }
} }

View File

@ -1,12 +1,7 @@
using System; using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting; using RageCoop.Server.Scripting;
using System;
namespace RageCoop.Server namespace RageCoop.Server
{ {
@ -56,9 +51,7 @@ namespace RageCoop.Server
{ {
try try
{ {
int len = message.ReadInt32(); GetHandshake(message.SenderConnection, message.GetPacket<Packets.Handshake>());
byte[] data = message.ReadBytes(len);
GetHandshake(message.SenderConnection, data.GetPacket<Packets.Handshake>());
} }
catch (Exception e) catch (Exception e)
{ {
@ -86,7 +79,7 @@ namespace RageCoop.Server
else if (status == NetConnectionStatus.Connected) else if (status == NetConnectionStatus.Connected)
{ {
PlayerConnected(sender); PlayerConnected(sender);
_worker.QueueJob(() => API.Events.InvokePlayerConnected(sender)); QueueJob(() => API.Events.InvokePlayerConnected(sender));
Resources.SendTo(sender); Resources.SendTo(sender);
} }
break; break;
@ -106,7 +99,7 @@ namespace RageCoop.Server
int id = message.ReadInt32(); int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback)) if (PendingResponses.TryGetValue(id, out var callback))
{ {
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32())); callback((PacketType)message.ReadByte(), message);
PendingResponses.Remove(id); PendingResponses.Remove(id);
} }
break; break;
@ -114,19 +107,23 @@ namespace RageCoop.Server
case PacketType.Request: case PacketType.Request:
{ {
int id = message.ReadInt32(); int id = message.ReadInt32();
if (RequestHandlers.TryGetValue((PacketType)message.ReadByte(), out var handler)) var reqType = (PacketType)message.ReadByte();
if (RequestHandlers.TryGetValue(reqType, out var handler))
{ {
var response = MainNetServer.CreateMessage(); var response = MainNetServer.CreateMessage();
response.Write((byte)PacketType.Response); response.Write((byte)PacketType.Response);
response.Write(id); response.Write(id);
handler(message.ReadBytes(message.ReadInt32()), sender).Pack(response); handler(message, sender).Pack(response);
MainNetServer.SendMessage(response, message.SenderConnection, NetDeliveryMethod.ReliableOrdered); MainNetServer.SendMessage(response, message.SenderConnection, NetDeliveryMethod.ReliableOrdered);
} }
else
{
Logger.Warning("Did not find a request handler of type: " + reqType);
}
break; break;
} }
default: default:
{ {
byte[] data = message.ReadBytes(message.ReadInt32());
if (type.IsSyncEvent()) if (type.IsSyncEvent())
{ {
// Sync Events // Sync Events
@ -139,8 +136,7 @@ namespace RageCoop.Server
{ {
var outgoingMessage = MainNetServer.CreateMessage(); var outgoingMessage = MainNetServer.CreateMessage();
outgoingMessage.Write((byte)type); outgoingMessage.Write((byte)type);
outgoingMessage.Write(data.Length); outgoingMessage.Write(message.ReadBytes(message.LengthBytes - 1));
outgoingMessage.Write(data);
MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents); MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents);
} }
} }
@ -151,7 +147,7 @@ namespace RageCoop.Server
} }
else else
{ {
HandlePacket(type, data, sender); HandlePacket(type, message, sender);
} }
break; break;
} }
@ -191,22 +187,22 @@ namespace RageCoop.Server
MainNetServer.Recycle(message); MainNetServer.Recycle(message);
} }
private void HandlePacket(PacketType type, byte[] data, Client sender) private void HandlePacket(PacketType type, NetIncomingMessage msg, Client sender)
{ {
try try
{ {
switch (type) switch (type)
{ {
case PacketType.PedSync: case PacketType.PedSync:
PedSync(data.GetPacket<Packets.PedSync>(), sender); PedSync(msg.GetPacket<Packets.PedSync>(), sender);
break; break;
case PacketType.VehicleSync: case PacketType.VehicleSync:
VehicleSync(data.GetPacket<Packets.VehicleSync>(), sender); VehicleSync(msg.GetPacket<Packets.VehicleSync>(), sender);
break; break;
case PacketType.ProjectileSync: case PacketType.ProjectileSync:
ProjectileSync(data.GetPacket<Packets.ProjectileSync>(), sender); ProjectileSync(msg.GetPacket<Packets.ProjectileSync>(), sender);
break; break;
case PacketType.ChatMessage: case PacketType.ChatMessage:
@ -215,7 +211,7 @@ namespace RageCoop.Server
{ {
return Security.Decrypt(b, sender.EndPoint); return Security.Decrypt(b, sender.EndPoint);
}); });
packet.Deserialize(data); packet.Deserialize(msg);
ChatMessageReceived(packet.Username, packet.Message, sender); ChatMessageReceived(packet.Username, packet.Message, sender);
} }
break; break;
@ -224,7 +220,7 @@ namespace RageCoop.Server
{ {
if (Settings.UseVoice && !Settings.UseP2P) if (Settings.UseVoice && !Settings.UseP2P)
{ {
Forward(data.GetPacket<Packets.Voice>(), sender, ConnectionChannel.Voice); Forward(msg.GetPacket<Packets.Voice>(), sender, ConnectionChannel.Voice);
} }
} }
break; break;
@ -232,8 +228,8 @@ namespace RageCoop.Server
case PacketType.CustomEvent: case PacketType.CustomEvent:
{ {
Packets.CustomEvent packet = new Packets.CustomEvent(); Packets.CustomEvent packet = new Packets.CustomEvent();
packet.Deserialize(data); packet.Deserialize(msg);
_worker.QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender)); QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender));
} }
break; break;
default: default:

View File

@ -1,23 +1,16 @@
using System; using Lidgren.Network;
using System.Diagnostics;
using System.Text;
using System.Net;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Reflection;
using System.IO;
using System.Net.Http;
using RageCoop.Core; using RageCoop.Core;
using Newtonsoft.Json;
using Lidgren.Network;
using System.Timers;
using System.Security.Cryptography;
using RageCoop.Server.Scripting; using RageCoop.Server.Scripting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using Timer = System.Timers.Timer; using Timer = System.Timers.Timer;
using System.Net.Sockets;
using System.Threading.Tasks;
using RageCoop.Core.Scripting;
namespace RageCoop.Server namespace RageCoop.Server
{ {
@ -37,12 +30,12 @@ namespace RageCoop.Server
internal ServerEntities Entities; internal ServerEntities Entities;
internal readonly Dictionary<Command, Action<CommandContext>> Commands = new(); internal readonly Dictionary<Command, Action<CommandContext>> Commands = new();
internal readonly Dictionary<long,Client> ClientsByNetHandle = new(); internal readonly Dictionary<long, Client> ClientsByNetHandle = new();
internal readonly Dictionary<string, Client> ClientsByName = new(); internal readonly Dictionary<string, Client> ClientsByName = new();
internal readonly Dictionary<int, Client> ClientsByID = new(); internal readonly Dictionary<int, Client> ClientsByID = new();
internal Client _hostClient; internal Client _hostClient;
private Dictionary<int,FileTransfer> InProgressFileTransfers=new(); private readonly Dictionary<int, FileTransfer> InProgressFileTransfers = new();
internal Resources Resources; internal Resources Resources;
internal Logger Logger; internal Logger Logger;
internal Security Security; internal Security Security;
@ -54,8 +47,8 @@ namespace RageCoop.Server
private readonly Timer _updateTimer = new(); private readonly Timer _updateTimer = new();
private readonly Worker _worker; private readonly Worker _worker;
private readonly HashSet<char> _allowedCharacterSet; private readonly HashSet<char> _allowedCharacterSet;
private Dictionary<int,Action<PacketType,byte[]>> PendingResponses=new(); private readonly Dictionary<int, Action<PacketType, NetIncomingMessage>> PendingResponses = new();
internal Dictionary<PacketType, Func<byte[],Client,Packet>> RequestHandlers=new(); internal Dictionary<PacketType, Func<NetIncomingMessage, Client, Packet>> RequestHandlers = new();
/// <summary> /// <summary>
/// Get the current server version /// Get the current server version
/// </summary> /// </summary>
@ -66,23 +59,23 @@ namespace RageCoop.Server
/// <param name="settings"></param> /// <param name="settings"></param>
/// <param name="logger"></param> /// <param name="logger"></param>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
public Server(Settings settings,Logger logger=null) public Server(Settings settings, Logger logger = null)
{ {
Settings = settings; Settings = settings;
if (settings==null) { throw new ArgumentNullException("Server settings cannot be null!"); } if (settings == null) { throw new ArgumentNullException("Server settings cannot be null!"); }
Logger=logger; Logger = logger;
if (Logger!=null) { Logger.LogLevel=Settings.LogLevel;} if (Logger != null) { Logger.LogLevel = Settings.LogLevel; }
API=new API(this); API = new API(this);
Resources=new Resources(this); Resources = new Resources(this);
Security=new Security(Logger); Security = new Security(Logger);
Entities=new ServerEntities(this); Entities = new ServerEntities(this);
BaseScript=new BaseScript(this); BaseScript = new BaseScript(this);
_allowedCharacterSet=new HashSet<char>(Settings.AllowedUsernameChars.ToCharArray()); _allowedCharacterSet = new HashSet<char>(Settings.AllowedUsernameChars.ToCharArray());
_worker=new Worker("ServerWorker", Logger); _worker = new Worker("ServerWorker", Logger);
_listenerThread=new Thread(() => Listen()); _listenerThread = new Thread(() => Listen());
_announceTimer.Interval = 1; _announceTimer.Interval = 1;
_announceTimer.Elapsed += (s, e) => _announceTimer.Elapsed += (s, e) =>
@ -104,7 +97,7 @@ namespace RageCoop.Server
_updateTimer.Interval = 1; _updateTimer.Interval = 1;
_updateTimer.Elapsed += (s, e) => _updateTimer.Elapsed += (s, e) =>
{ {
_updateTimer.Interval= 1000 * 60 * 10; // 10 minutes _updateTimer.Interval = 1000 * 60 * 10; // 10 minutes
_updateTimer.Stop(); _updateTimer.Stop();
CheckUpdate(); CheckUpdate();
_updateTimer.Start(); _updateTimer.Start();
@ -118,15 +111,26 @@ namespace RageCoop.Server
public void Start() public void Start()
{ {
Logger?.Info("================"); Logger?.Info("================");
Logger?.Info($"Server bound to: 0.0.0.0:{Settings.Port}"); Logger?.Info($"Listening port: {Settings.Port}");
Logger?.Info($"Server version: {Version}"); Logger?.Info($"Server version: {Version}");
Logger?.Info($"Compatible RAGECOOP versions: {Version.ToString(3)}"); Logger?.Info($"Compatible client version: {Version.ToString(3)}");
Logger?.Info($"Runtime: {CoreUtils.GetInvariantRID()} => {System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier}");
Logger?.Info("================"); Logger?.Info("================");
Logger?.Info($"Listening addresses:");
foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
Logger?.Info($"[{netInterface.Description}]:");
IPInterfaceProperties ipProps = netInterface.GetIPProperties();
foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses)
{
Logger.Info(string.Join(", ", addr.Address));
}
Logger.Info("");
}
if (Settings.UseZeroTier) if (Settings.UseZeroTier)
{ {
Logger?.Info($"Joining ZeroTier network: "+Settings.ZeroTierNetworkID); Logger?.Info($"Joining ZeroTier network: " + Settings.ZeroTierNetworkID);
if (ZeroTierHelper.Join(Settings.ZeroTierNetworkID)==null) if (ZeroTierHelper.Join(Settings.ZeroTierNetworkID) == null)
{ {
throw new Exception("Failed to obtain ZeroTier network IP"); throw new Exception("Failed to obtain ZeroTier network IP");
} }
@ -143,7 +147,7 @@ namespace RageCoop.Server
MaximumConnections = Settings.MaxPlayers, MaximumConnections = Settings.MaxPlayers,
EnableUPnP = false, EnableUPnP = false,
AutoFlushSendQueue = true, AutoFlushSendQueue = true,
PingInterval=5 PingInterval = 5
}; };
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval); config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
@ -151,16 +155,16 @@ namespace RageCoop.Server
MainNetServer = new NetServer(config); MainNetServer = new NetServer(config);
MainNetServer.Start(); MainNetServer.Start();
Logger?.Info(string.Format("Server listening on {0}:{1}", config.LocalAddress.ToString(), config.Port)); BaseScript.API = API;
BaseScript.API=API;
BaseScript.OnStart(); BaseScript.OnStart();
Resources.LoadAll(); Resources.LoadAll();
_listenerThread.Start(); _listenerThread.Start();
_playerUpdateTimer.Enabled=true; Logger?.Info("Listening for clients");
_playerUpdateTimer.Enabled = true;
if (Settings.AnnounceSelf) if (Settings.AnnounceSelf)
{ {
_announceTimer.Enabled=true; _announceTimer.Enabled = true;
} }
if (Settings.AutoUpdate) if (Settings.AutoUpdate)
{ {
@ -169,7 +173,6 @@ namespace RageCoop.Server
_antiAssholesTimer.Enabled = true; _antiAssholesTimer.Enabled = true;
Logger?.Info("Listening for clients");
} }
/// <summary> /// <summary>
/// Terminate threads and stop the server /// Terminate threads and stop the server
@ -190,20 +193,20 @@ namespace RageCoop.Server
} }
// Send a message to targets or all players // Send a message to targets or all players
internal void ChatMessageReceived(string name, string message,Client sender=null) internal void ChatMessageReceived(string name, string message, Client sender = null)
{ {
if (message.StartsWith('/')) if (message.StartsWith('/'))
{ {
string[] cmdArgs = message.Split(" "); string[] cmdArgs = message.Split(" ");
string cmdName = cmdArgs[0].Remove(0, 1); string cmdName = cmdArgs[0].Remove(0, 1);
_worker.QueueJob(()=>API.Events.InvokeOnCommandReceived(cmdName, cmdArgs, sender)); QueueJob(() => API.Events.InvokeOnCommandReceived(cmdName, cmdArgs, sender));
return; return;
} }
message = message.Replace("~", ""); message = message.Replace("~", "");
_worker.QueueJob(() => API.Events.InvokeOnChatMessage(message, sender)); QueueJob(() => API.Events.InvokeOnChatMessage(message, sender));
foreach(var c in ClientsByNetHandle.Values) foreach (var c in ClientsByNetHandle.Values)
{ {
var msg = MainNetServer.CreateMessage(); var msg = MainNetServer.CreateMessage();
var crypt = new Func<string, byte[]>((s) => var crypt = new Func<string, byte[]>((s) =>
@ -212,23 +215,23 @@ namespace RageCoop.Server
}); });
new Packets.ChatMessage(crypt) new Packets.ChatMessage(crypt)
{ {
Username=name, Username = name,
Message=message Message = message
}.Pack(msg); }.Pack(msg);
MainNetServer.SendMessage(msg,c.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat); MainNetServer.SendMessage(msg, c.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat);
} }
} }
internal void SendChatMessage(string name, string message, Client target) internal void SendChatMessage(string name, string message, Client target)
{ {
if(target == null) { return; } if (target == null) { return; }
var msg = MainNetServer.CreateMessage(); var msg = MainNetServer.CreateMessage();
new Packets.ChatMessage(new Func<string, byte[]>((s) => new Packets.ChatMessage(new Func<string, byte[]>((s) =>
{ {
return Security.Encrypt(s.GetBytes(), target.EndPoint); return Security.Encrypt(s.GetBytes(), target.EndPoint);
})) }))
{ {
Username= name, Username = name,
Message=message, Message = message,
}.Pack(msg); }.Pack(msg);
MainNetServer.SendMessage(msg, target.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat); MainNetServer.SendMessage(msg, target.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat);
} }
@ -267,67 +270,60 @@ namespace RageCoop.Server
RegisterCommand(attribute.Name, attribute.Usage, attribute.ArgsLength, (Action<CommandContext>)Delegate.CreateDelegate(typeof(Action<CommandContext>), method)); RegisterCommand(attribute.Name, attribute.Usage, attribute.ArgsLength, (Action<CommandContext>)Delegate.CreateDelegate(typeof(Action<CommandContext>), method));
} }
} }
internal T GetResponse<T>(Client client,Packet request, ConnectionChannel channel = ConnectionChannel.RequestResponse,int timeout=5000) where T:Packet, new() internal T GetResponse<T>(Client client, Packet request, ConnectionChannel channel = ConnectionChannel.RequestResponse, int timeout = 5000) where T : Packet, new()
{ {
if (Thread.CurrentThread==_listenerThread) if (Thread.CurrentThread == _listenerThread)
{ {
throw new InvalidOperationException("Cannot wait for response from the listener thread!"); throw new InvalidOperationException("Cannot wait for response from the listener thread!");
} }
var received=new AutoResetEvent(false);
byte[] response=null; var received = new AutoResetEvent(false);
T response = new T();
var id = NewRequestID(); var id = NewRequestID();
PendingResponses.Add(id, (type,p) => PendingResponses.Add(id, (type, m) =>
{ {
response=p; response.Deserialize(m);
received.Set(); received.Set();
}); });
var msg = MainNetServer.CreateMessage(); var msg = MainNetServer.CreateMessage();
msg.Write((byte)PacketType.Request); msg.Write((byte)PacketType.Request);
msg.Write(id); msg.Write(id);
request.Pack(msg); request.Pack(msg);
MainNetServer.SendMessage(msg,client.Connection,NetDeliveryMethod.ReliableOrdered,(int)channel); MainNetServer.SendMessage(msg, client.Connection, NetDeliveryMethod.ReliableOrdered, (int)channel);
if (received.WaitOne(timeout)) if (received.WaitOne(timeout))
{ {
var p = new T(); return response;
p.Deserialize(response);
return p;
}
else
{
return null;
} }
return null;
} }
internal void SendFile(string path,string name,Client client,Action<float> updateCallback=null) internal void SendFile(string path, string name, Client client, Action<float> updateCallback = null)
{ {
var fs = File.OpenRead(path); var fs = File.OpenRead(path);
SendFile(fs, name,client,NewFileID(),updateCallback); SendFile(fs, name, client, NewFileID(), updateCallback);
fs.Close(); fs.Close();
fs.Dispose(); fs.Dispose();
} }
internal void SendFile(Stream stream, string name, Client client,int id=default, Action<float> updateCallback = null) internal void SendFile(Stream stream, string name, Client client, int id = default, Action<float> updateCallback = null)
{ {
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
// Logger.Debug("1"); id = id == default ? NewFileID() : id;
id = id ==default? NewFileID(): id ;
// Logger.Debug("2");
var total = stream.Length; var total = stream.Length;
// Logger.Debug("3"); Logger?.Debug($"Requesting file transfer:{name}, {total}");
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferRequest() if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferRequest()
{ {
FileLength= total, FileLength = total,
Name=name, Name = name,
ID=id, ID = id,
}, ConnectionChannel.File)?.Response!=FileResponse.NeedToDownload) }, ConnectionChannel.File)?.Response != FileResponse.NeedToDownload)
{ {
Logger?.Info($"Skipping file transfer \"{name}\" to {client.Username}"); Logger?.Info($"Skipping file transfer \"{name}\" to {client.Username}");
// stream.Close();
// stream.Dispose();
return; return;
} }
Logger?.Debug($"Initiating file transfer:{name}, {total}"); Logger?.Debug($"Initiating file transfer:{name}, {total}");
FileTransfer transfer = new() FileTransfer transfer = new()
{ {
ID=id, ID = id,
Name = name, Name = name,
}; };
InProgressFileTransfers.Add(id, transfer); InProgressFileTransfers.Add(id, transfer);
@ -337,40 +333,38 @@ namespace RageCoop.Server
{ {
// 4 KB chunk // 4 KB chunk
byte[] chunk = new byte[4096]; byte[] chunk = new byte[4096];
read += thisRead=stream.Read(chunk, 0, 4096); read += thisRead = stream.Read(chunk, 0, 4096);
if (thisRead!=chunk.Length) if (thisRead != chunk.Length)
{ {
if (thisRead==0) { break; } if (thisRead == 0) { break; }
Logger?.Trace($"Purging chunk:{thisRead}"); Logger?.Trace($"Purging chunk:{thisRead}");
Array.Resize(ref chunk, thisRead); Array.Resize(ref chunk, thisRead);
} }
Send( Send(
new Packets.FileTransferChunk() new Packets.FileTransferChunk()
{ {
ID=id, ID = id,
FileChunk=chunk, FileChunk = chunk,
}, },
client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered); client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered);
transfer.Progress=read/stream.Length; transfer.Progress = read / stream.Length;
if (updateCallback!=null) { updateCallback(transfer.Progress); } if (updateCallback != null) { updateCallback(transfer.Progress); }
} while (thisRead>0); } while (thisRead > 0);
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferComplete() if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferComplete()
{ {
ID= id, ID = id,
}, ConnectionChannel.File)?.Response!=FileResponse.Completed) }, ConnectionChannel.File)?.Response != FileResponse.Completed)
{ {
Logger.Warning($"File trasfer to {client.Username} failed: "+name); Logger.Warning($"File trasfer to {client.Username} failed: " + name);
} }
// stream.Close();
// stream.Dispose();
Logger?.Debug($"All file chunks sent:{name}"); Logger?.Debug($"All file chunks sent:{name}");
InProgressFileTransfers.Remove(id); InProgressFileTransfers.Remove(id);
} }
internal int NewFileID() internal int NewFileID()
{ {
int ID = 0; int ID = 0;
while ((ID==0) while ((ID == 0)
|| InProgressFileTransfers.ContainsKey(ID)) || InProgressFileTransfers.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];
@ -385,7 +379,7 @@ namespace RageCoop.Server
private int NewRequestID() private int NewRequestID()
{ {
int ID = 0; int ID = 0;
while ((ID==0) while ((ID == 0)
|| PendingResponses.ContainsKey(ID)) || PendingResponses.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];
@ -397,11 +391,11 @@ namespace RageCoop.Server
} }
return ID; return ID;
} }
internal void Send(Packet p,Client client, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced) internal void Send(Packet p, Client client, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{ {
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
p.Pack(outgoingMessage); p.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, client.Connection,method,(int)channel); MainNetServer.SendMessage(outgoingMessage, client.Connection, method, (int)channel);
} }
internal void Forward(Packet p, Client except, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced) internal void Forward(Packet p, Client except, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{ {

View File

@ -1,36 +1,33 @@
using System; using RageCoop.Core;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using RageCoop.Core;
using Newtonsoft.Json;
using System.Linq;
namespace RageCoop.Server namespace RageCoop.Server
{ {
class Program internal class Program
{ {
private static bool Stopping = false; private static bool Stopping = false;
static Logger mainLogger; private static Logger mainLogger;
static void Main(string[] args)
private static void Main(string[] args)
{ {
if (args.Length>=2 && args[0]=="update") if (args.Length >= 2 && args[0] == "update")
{ {
var target = args[1]; var target = args[1];
int i =0; int i = 0;
while (i < 10) while (i++ < 10)
{ {
i++;
try try
{ {
Console.WriteLine("Applying update to "+target); Console.WriteLine("Applying update to " + target);
CoreUtils.CopyFilesRecursively(new(AppDomain.CurrentDomain.BaseDirectory), new(target)); CoreUtils.CopyFilesRecursively(new(AppDomain.CurrentDomain.BaseDirectory), new(target));
Process.Start(Path.Combine(target, "RageCoop.Server")); Process.Start(Path.Combine(target, "RageCoop.Server"));
Environment.Exit(0); Environment.Exit(0);
} }
catch(Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.ToString()); Console.WriteLine(ex.ToString());
Thread.Sleep(3000); Thread.Sleep(3000);
@ -38,22 +35,22 @@ namespace RageCoop.Server
} }
Environment.Exit(i); Environment.Exit(i);
} }
AppDomain.CurrentDomain.UnhandledException+=UnhandledException; AppDomain.CurrentDomain.UnhandledException += UnhandledException;
mainLogger = new Logger() mainLogger = new Logger()
{ {
LogPath="RageCoop.Server.log", LogPath = "RageCoop.Server.log",
UseConsole=true, UseConsole = true,
Name="Server" Name = "Server"
}; };
try try
{ {
Console.Title = "RAGECOOP"; Console.Title = "RAGECOOP";
var setting = Util.Read<Settings>("Settings.xml"); var setting = Util.Read<Settings>("Settings.xml");
#if DEBUG #if DEBUG
setting.LogLevel=0; setting.LogLevel = 0;
#endif #endif
var server = new Server(setting, mainLogger); var server = new Server(setting, mainLogger);
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
{ {
mainLogger.Info("Initiating shutdown sequence..."); mainLogger.Info("Initiating shutdown sequence...");
mainLogger.Info("Press Ctrl+C again to commence an emergency shutdown."); mainLogger.Info("Press Ctrl+C again to commence an emergency shutdown.");
@ -83,8 +80,8 @@ namespace RageCoop.Server
while (true) while (true)
{ {
var s=Console.ReadLine(); var s = Console.ReadLine();
if (!Stopping && s!=null) if (!Stopping && s != null)
{ {
server.ChatMessageReceived("Server", s, null); server.ChatMessageReceived("Server", s, null);
} }
@ -99,11 +96,11 @@ namespace RageCoop.Server
private static void UnhandledException(object sender, UnhandledExceptionEventArgs e) private static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
{ {
mainLogger.Error($"Unhandled exception thrown from user thread:",e.ExceptionObject as Exception); mainLogger.Error($"Unhandled exception thrown from user thread", e.ExceptionObject as Exception);
mainLogger.Flush(); mainLogger.Flush();
} }
static void Fatal(Exception e) private static void Fatal(Exception e)
{ {
mainLogger.Error(e); mainLogger.Error(e);
mainLogger.Error($"Fatal error occurred, server shutting down."); mainLogger.Error($"Fatal error occurred, server shutting down.");

View File

@ -15,7 +15,7 @@ using System.Resources;
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
// Version information // Version information
[assembly: AssemblyVersion("1.5.3.107")] [assembly: AssemblyVersion("1.5.4.5")]
[assembly: AssemblyFileVersion("1.5.3.107")] [assembly: AssemblyFileVersion("1.5.4.5")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )] [assembly: NeutralResourcesLanguageAttribute( "en-US" )]

View File

@ -49,12 +49,12 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Fody" Version="6.6.3"> <PackageReference Include="Fody" Version="6.8.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" /> <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.9" />
<PackageReference Include="SharpZipLib" Version="1.3.3" /> <PackageReference Include="SharpZipLib" Version="1.4.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,13 +1,12 @@
using System; using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using System.Reflection; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace RageCoop.Server.Scripting namespace RageCoop.Server.Scripting
{ {
@ -54,14 +53,14 @@ namespace RageCoop.Server.Scripting
public event EventHandler<Client> OnPlayerUpdate; public event EventHandler<Client> OnPlayerUpdate;
internal void ClearHandlers() internal void ClearHandlers()
{ {
OnChatMessage=null; OnChatMessage = null;
OnPlayerHandshake=null; OnPlayerHandshake = null;
OnPlayerConnected=null; OnPlayerConnected = null;
OnPlayerReady=null; OnPlayerReady = null;
OnPlayerDisconnected=null; OnPlayerDisconnected = null;
// OnCustomEventReceived=null; // OnCustomEventReceived=null;
OnCommandReceived=null; OnCommandReceived = null;
OnPlayerUpdate=null; OnPlayerUpdate = null;
} }
#region INVOKE #region INVOKE
internal void InvokePlayerHandshake(HandshakeEventArgs args) internal void InvokePlayerHandshake(HandshakeEventArgs args)
@ -70,9 +69,9 @@ namespace RageCoop.Server.Scripting
{ {
var args = new OnCommandEventArgs() var args = new OnCommandEventArgs()
{ {
Name=cmdName, Name = cmdName,
Args=cmdArgs, Args = cmdArgs,
Client=sender Client = sender
}; };
OnCommandReceived?.Invoke(this, args); OnCommandReceived?.Invoke(this, args);
if (args.Cancel) if (args.Cancel)
@ -99,27 +98,26 @@ namespace RageCoop.Server.Scripting
} }
} }
internal void InvokeOnChatMessage(string msg, Client sender, string clamiedSender=null) internal void InvokeOnChatMessage(string msg, Client sender, string clamiedSender = null)
{ {
OnChatMessage?.Invoke(this, new ChatEventArgs() OnChatMessage?.Invoke(this, new ChatEventArgs()
{ {
Client=sender, Client = sender,
Message=msg, Message = msg,
ClaimedSender=clamiedSender ClaimedSender = clamiedSender
}); });
} }
internal void InvokePlayerConnected(Client client) internal void InvokePlayerConnected(Client client)
{ OnPlayerConnected?.Invoke(this,client); } { OnPlayerConnected?.Invoke(this, client); }
internal void InvokePlayerReady(Client client) internal void InvokePlayerReady(Client client)
{ OnPlayerReady?.Invoke(this, client); } { OnPlayerReady?.Invoke(this, client); }
internal void InvokePlayerDisconnected(Client client) internal void InvokePlayerDisconnected(Client client)
{ OnPlayerDisconnected?.Invoke(this,client); } { OnPlayerDisconnected?.Invoke(this, client); }
internal void InvokeCustomEventReceived(Packets.CustomEvent p, Client sender) internal void InvokeCustomEventReceived(Packets.CustomEvent p, Client sender)
{ {
var args = new CustomEventReceivedArgs() { Hash=p.Hash, Args=p.Args, Client=sender }; var args = new CustomEventReceivedArgs() { Hash = p.Hash, Args = p.Args, Client = sender };
List<Action<CustomEventReceivedArgs>> handlers; if (CustomEventHandlers.TryGetValue(p.Hash, out List<Action<CustomEventReceivedArgs>> handlers))
if (CustomEventHandlers.TryGetValue(p.Hash, out handlers))
{ {
handlers.ForEach((x) => { x.Invoke(args); }); handlers.ForEach((x) => { x.Invoke(args); });
} }
@ -136,32 +134,32 @@ namespace RageCoop.Server.Scripting
public class API public class API
{ {
internal readonly Server Server; internal readonly Server Server;
internal readonly Dictionary<string, Func<Stream>> RegisteredFiles=new Dictionary<string, Func<System.IO.Stream>>(); internal readonly Dictionary<string, Func<Stream>> RegisteredFiles = new Dictionary<string, Func<System.IO.Stream>>();
internal API(Server server) internal API(Server server)
{ {
Server=server; Server = server;
Events=new(server); Events = new(server);
Server.RequestHandlers.Add(PacketType.FileTransferRequest, (data,client) => Server.RequestHandlers.Add(PacketType.FileTransferRequest, (data, client) =>
{ {
var p = new Packets.FileTransferRequest(); var p = new Packets.FileTransferRequest();
p.Deserialize(data); p.Deserialize(data);
var id=Server.NewFileID(); var id = Server.NewFileID();
if(RegisteredFiles.TryGetValue(p.Name,out var s)) if (RegisteredFiles.TryGetValue(p.Name, out var s))
{ {
Task.Run(() => Task.Run(() =>
{ {
Server.SendFile(s(), p.Name, client,id); Server.SendFile(s(), p.Name, client, id);
}); });
return new Packets.FileTransferResponse() return new Packets.FileTransferResponse()
{ {
ID=id, ID = id,
Response=FileResponse.Loaded Response = FileResponse.Loaded
}; };
} }
return new Packets.FileTransferResponse() return new Packets.FileTransferResponse()
{ {
ID=id, ID = id,
Response=FileResponse.LoadFailed Response = FileResponse.LoadFailed
}; };
}); });
} }
@ -173,7 +171,7 @@ namespace RageCoop.Server.Scripting
/// <summary> /// <summary>
/// All synchronized entities on this server. /// All synchronized entities on this server.
/// </summary> /// </summary>
public ServerEntities Entities { get { return Server.Entities; } } public ServerEntities Entities => Server.Entities;
#region FUNCTIONS #region FUNCTIONS
/// <summary> /// <summary>
@ -204,9 +202,9 @@ namespace RageCoop.Server.Scripting
/// <param name="username">The username which send this message (default = "Server")</param> /// <param name="username">The username which send this message (default = "Server")</param>
/// <param name="raiseEvent">Weather to raise the <see cref="ServerEvents.OnChatMessage"/> event defined in <see cref="API.Events"/></param> /// <param name="raiseEvent">Weather to raise the <see cref="ServerEvents.OnChatMessage"/> event defined in <see cref="API.Events"/></param>
/// <remarks>When <paramref name="raiseEvent"/> is unspecified and <paramref name="targets"/> is null or unspecified, <paramref name="raiseEvent"/> will be set to true</remarks> /// <remarks>When <paramref name="raiseEvent"/> is unspecified and <paramref name="targets"/> is null or unspecified, <paramref name="raiseEvent"/> will be set to true</remarks>
public void SendChatMessage(string message, List<Client> targets = null, string username = "Server",bool? raiseEvent=null) public void SendChatMessage(string message, List<Client> targets = null, string username = "Server", bool? raiseEvent = null)
{ {
raiseEvent ??= targets==null; raiseEvent ??= targets == null;
try try
{ {
if (Server.MainNetServer.ConnectionsCount != 0) if (Server.MainNetServer.ConnectionsCount != 0)
@ -233,7 +231,7 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
/// <param name="name">name of this file</param> /// <param name="name">name of this file</param>
/// <param name="path">path to this file</param> /// <param name="path">path to this file</param>
public void RegisterSharedFile(string name,string path) public void RegisterSharedFile(string name, string path)
{ {
RegisteredFiles.Add(name, () => { return File.OpenRead(path); }); RegisteredFiles.Add(name, () => { return File.OpenRead(path); });
} }
@ -300,7 +298,7 @@ namespace RageCoop.Server.Scripting
/// <param name="hash"></param> /// <param name="hash"></param>
/// <param name="args"></param> /// <param name="args"></param>
/// /// <param name="clients">Clients to send, null for all clients</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) public void SendNativeCall(List<Client> clients, GTA.Native.Hash hash, params object[] args)
{ {
var argsList = new List<object>(args); var argsList = new List<object>(args);
argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty, (ulong)hash }); argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty, (ulong)hash });
@ -314,14 +312,14 @@ namespace RageCoop.Server.Scripting
/// <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="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="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> /// <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) public void SendCustomEvent(List<Client> targets, int eventHash, params object[] args)
{ {
targets ??= new(Server.ClientsByNetHandle.Values); targets ??= new(Server.ClientsByNetHandle.Values);
var p = new Packets.CustomEvent() var p = new Packets.CustomEvent()
{ {
Args=args, Args = args,
Hash=eventHash Hash = eventHash
}; };
foreach (var c in targets) foreach (var c in targets)
{ {
@ -339,10 +337,10 @@ namespace RageCoop.Server.Scripting
{ {
targets ??= new(Server.ClientsByNetHandle.Values); targets ??= new(Server.ClientsByNetHandle.Values);
var p = new Packets.CustomEvent(null,true) var p = new Packets.CustomEvent(null, true)
{ {
Args=args, Args = args,
Hash=eventHash Hash = eventHash
}; };
foreach (var c in targets) foreach (var c in targets)
{ {
@ -354,12 +352,11 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </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="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> /// <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) public void RegisterCustomEventHandler(int hash, Action<CustomEventReceivedArgs> handler)
{ {
List<Action<CustomEventReceivedArgs>> handlers;
lock (Events.CustomEventHandlers) lock (Events.CustomEventHandlers)
{ {
if (!Events.CustomEventHandlers.TryGetValue(hash,out handlers)) if (!Events.CustomEventHandlers.TryGetValue(hash, out List<Action<CustomEventReceivedArgs>> handlers))
{ {
Events.CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>()); Events.CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>());
} }
@ -385,11 +382,11 @@ namespace RageCoop.Server.Scripting
/// <returns>A <see langword="dynamic"/> object reprensenting the script, or <see langword="null"/> if not found.</returns> /// <returns>A <see langword="dynamic"/> object reprensenting the script, or <see langword="null"/> if not found.</returns>
/// <remarks>Explicitly casting the return value to orginal type will case a exception to be thrown due to the dependency isolation mechanism in resource system. /// <remarks>Explicitly casting the return value to orginal type will case a exception to be thrown due to the dependency isolation mechanism in resource system.
/// You shouldn't reference the target resource assemblies either, since it causes the referenced assembly to be loaded and started in your resource.</remarks> /// You shouldn't reference the target resource assemblies either, since it causes the referenced assembly to be loaded and started in your resource.</remarks>
public dynamic FindScript(string scriptFullName,string resourceName=null) public dynamic FindScript(string scriptFullName, string resourceName = null)
{ {
if (resourceName==null) if (resourceName == null)
{ {
foreach(var res in LoadedResources.Values) foreach (var res in LoadedResources.Values)
{ {
if (res.Scripts.TryGetValue(scriptFullName, out var script)) if (res.Scripts.TryGetValue(scriptFullName, out var script))
{ {
@ -399,7 +396,7 @@ namespace RageCoop.Server.Scripting
} }
else if (LoadedResources.TryGetValue(resourceName, out var res)) else if (LoadedResources.TryGetValue(resourceName, out var res))
{ {
if(res.Scripts.TryGetValue(scriptFullName, out var script)) if (res.Scripts.TryGetValue(scriptFullName, out var script))
{ {
return script; return script;
} }
@ -415,7 +412,7 @@ namespace RageCoop.Server.Scripting
/// <summary> /// <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. /// 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> /// </summary>
public Logger Logger { get { return Server.Logger; } } public Logger Logger => Server.Logger;
/// <summary> /// <summary>
@ -423,8 +420,9 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public Client Host public Client Host
{ {
get { return Server._hostClient; } get => Server._hostClient;
set { set
{
if (Server._hostClient != value) if (Server._hostClient != value)
{ {
Server._hostClient?.SendCustomEvent(CustomEvents.IsHost, false); Server._hostClient?.SendCustomEvent(CustomEvents.IsHost, false);
@ -440,7 +438,7 @@ namespace RageCoop.Server.Scripting
/// <remarks>Accessing this property from script constructor is stronly discouraged since other scripts and resources might have yet been loaded. /// <remarks>Accessing this property from script constructor is stronly discouraged since other scripts and resources might have yet been loaded.
/// Accessing from <see cref="ServerScript.OnStart"/> is not recommended either. Although all script assemblies will have been loaded to memory and instantiated, <see cref="ServerScript.OnStart"/> invocation of other scripts are not guaranteed. /// Accessing from <see cref="ServerScript.OnStart"/> is not recommended either. Although all script assemblies will have been loaded to memory and instantiated, <see cref="ServerScript.OnStart"/> invocation of other scripts are not guaranteed.
/// </remarks> /// </remarks>
public Dictionary<string,ServerResource> LoadedResources public Dictionary<string, ServerResource> LoadedResources
{ {
get get
{ {

View File

@ -1,17 +1,14 @@
using System; using RageCoop.Core.Scripting;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RageCoop.Core.Scripting;
using RageCoop.Core;
namespace RageCoop.Server.Scripting namespace RageCoop.Server.Scripting
{ {
internal class BaseScript:ServerScript internal class BaseScript : ServerScript
{ {
private readonly Server Server; private readonly Server Server;
public BaseScript(Server server) { Server=server; } public BaseScript(Server server) { Server = server; }
public override void OnStart() public override void OnStart()
{ {
API.RegisterCustomEventHandler(CustomEvents.NativeResponse, NativeResponse); API.RegisterCustomEventHandler(CustomEvents.NativeResponse, NativeResponse);
@ -31,7 +28,7 @@ namespace RageCoop.Server.Scripting
foreach (var c in API.GetAllClients().Values) foreach (var c in API.GetAllClients().Values)
{ {
if (c==e.Client) if (c == e.Client)
{ {
continue; continue;
} }
@ -41,49 +38,49 @@ namespace RageCoop.Server.Scripting
}); });
API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied, (e) => API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied, (e) =>
{ {
API.SendCustomEventQueued(API.GetAllClients().Values.Where(x=>x!=e.Client).ToList(),CustomEvents.OnPlayerDied,e.Client.Username); API.SendCustomEventQueued(API.GetAllClients().Values.Where(x => x != e.Client).ToList(), CustomEvents.OnPlayerDied, e.Args);
}); });
API.Events.OnChatMessage+=(s,e) => API.Events.OnChatMessage += (s, e) =>
Server.Logger?.Info((e.Client?.Username ?? e.ClaimedSender ?? "Unknown") + ": " + e.Message); Server.Logger?.Info((e.Client?.Username ?? e.ClaimedSender ?? "Unknown") + ": " + e.Message);
} }
public override void OnStop() public override void OnStop()
{ {
} }
public static void SetAutoRespawn(Client c,bool toggle) public static void SetAutoRespawn(Client c, bool toggle)
{ {
c.SendCustomEvent(CustomEvents.SetAutoRespawn, toggle ); c.SendCustomEvent(CustomEvents.SetAutoRespawn, toggle);
} }
public void SetNameTag(Client c, bool toggle) public void SetNameTag(Client c, bool toggle)
{ {
foreach(var other in API.GetAllClients().Values) foreach (var other in API.GetAllClients().Values)
{ {
if (c==other) { continue; } if (c == other) { continue; }
other.SendCustomEvent(CustomEvents.SetDisplayNameTag,c.Player.ID, toggle); other.SendCustomEvent(CustomEvents.SetDisplayNameTag, c.Player.ID, toggle);
} }
} }
public void SendServerPropsTo(List<ServerProp> objects,List<Client> clients=null) public void SendServerPropsTo(List<ServerProp> objects, List<Client> clients = null)
{ {
foreach(var obj in objects) foreach (var obj in objects)
{ {
API.SendCustomEventQueued(clients, CustomEvents.ServerPropSync,obj.ID, obj.Model ,obj.Position,obj.Rotation ); API.SendCustomEventQueued(clients, CustomEvents.ServerPropSync, obj.ID, obj.Model, obj.Position, obj.Rotation);
} }
} }
public void SendServerBlipsTo(List<ServerBlip> objects, List<Client> clients = null) public void SendServerBlipsTo(List<ServerBlip> objects, List<Client> clients = null)
{ {
foreach (var obj in objects) 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 ); 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)
private void NativeResponse(CustomEventReceivedArgs e)
{ {
try try
{ {
int id = (int)e.Args[0]; int id = (int)e.Args[0];
Action<object> callback;
lock (e.Client.Callbacks) lock (e.Client.Callbacks)
{ {
if (e.Client.Callbacks.TryGetValue(id, out callback)) if (e.Client.Callbacks.TryGetValue(id, out Action<object> callback))
{ {
callback(e.Args[1]); callback(e.Args[1]);
e.Client.Callbacks.Remove(id); e.Client.Callbacks.Remove(id);

View File

@ -1,8 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net; using System.Net;
namespace RageCoop.Server.Scripting namespace RageCoop.Server.Scripting
@ -97,10 +94,10 @@ namespace RageCoop.Server.Scripting
/// <param name="reason"></param> /// <param name="reason"></param>
public void Deny(string reason) public void Deny(string reason)
{ {
DenyReason=reason; DenyReason = reason;
Cancel=true; Cancel = true;
} }
internal string DenyReason { get; set; } internal string DenyReason { get; set; }
internal bool Cancel { get; set; }=false; internal bool Cancel { get; set; } = false;
} }
} }

View File

@ -1,83 +1,75 @@
using System; using ICSharpCode.SharpZipLib.Zip;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RageCoop.Core.Scripting;
using RageCoop.Core; using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using ICSharpCode.SharpZipLib.Zip; using System.Threading.Tasks;
using System.Reflection;
using McMaster.NETCore.Plugins;
namespace RageCoop.Server.Scripting namespace RageCoop.Server.Scripting
{ {
internal class Resources internal class Resources
{ {
public Dictionary<string,ServerResource> LoadedResources=new(); public Dictionary<string, ServerResource> LoadedResources = new();
private readonly Server Server; private readonly Server Server;
private readonly Logger Logger; private readonly Logger Logger;
public bool IsLoaded { get; private set; } = false; public bool IsLoaded { get; private set; } = false;
public Resources(Server server) public Resources(Server server)
{ {
Server = server; Server = server;
Logger=server.Logger; Logger = server.Logger;
} }
private Dictionary<string,Func<Stream>> ClientResources=new(); private readonly Dictionary<string, Stream> ClientResources = new();
private Dictionary<string,Func<Stream>> ResourceStreams=new(); private readonly Dictionary<string, Stream> ResourceStreams = new();
private List<MemoryStream> MemStreams = new(); public void LoadAll()
public void LoadAll() {
{ // Packages
// Packages
{
var path = Path.Combine("Resources", "Packages");
Directory.CreateDirectory(path);
foreach (var pkg in Directory.GetFiles(path,"*.respkg",SearchOption.AllDirectories))
{
Logger?.Debug($"Adding resourece from package \"{Path.GetFileNameWithoutExtension(pkg)}\"");
var pkgZip = new ZipFile(pkg);
foreach (ZipEntry e in pkgZip)
{
if (!e.IsFile) { continue; }
if (e.Name.StartsWith("Client") && e.Name.EndsWith(".res"))
{
var stream = pkgZip.GetInputStream(e).ToMemStream();
MemStreams.Add(stream);
ClientResources.Add(Path.GetFileNameWithoutExtension(e.Name), () => stream);
Logger?.Debug("Resource added: "+ Path.GetFileNameWithoutExtension(e.Name));
}
else if (e.Name.StartsWith("Server") && e.Name.EndsWith(".res"))
{
var stream = pkgZip.GetInputStream(e).ToMemStream();
MemStreams.Add(stream);
ResourceStreams.Add(Path.GetFileNameWithoutExtension(e.Name), () => stream);
Logger?.Debug("Resource added: " + Path.GetFileNameWithoutExtension(e.Name));
}
}
pkgZip.Close();
}
}
// Client
{ {
var path = Path.Combine("Resources", "Client"); var path = Path.Combine("Resources", "Packages");
var tmpDir = Path.Combine("Resources", "Temp","Client"); Directory.CreateDirectory(path);
Directory.CreateDirectory(path); foreach (var pkg in Directory.GetFiles(path, "*.respkg", SearchOption.AllDirectories))
if (Directory.Exists(tmpDir)) {
{ Logger?.Debug($"Adding resources from package \"{Path.GetFileNameWithoutExtension(pkg)}\"");
Directory.Delete(tmpDir, true); var pkgZip = new ZipFile(pkg);
} foreach (ZipEntry e in pkgZip)
Directory.CreateDirectory(tmpDir); {
var resourceFolders = Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly); if (!e.IsFile) { continue; }
if (resourceFolders.Length!=0) if (e.Name.StartsWith("Client") && e.Name.EndsWith(".res"))
{ {
foreach (var resourceFolder in resourceFolders) var stream = pkgZip.GetInputStream(e).ToMemStream();
{ ClientResources.Add(Path.GetFileNameWithoutExtension(e.Name), stream);
// Pack client side resource as a zip file Logger?.Debug("Resource added: " + Path.GetFileNameWithoutExtension(e.Name));
Logger?.Info("Packing client-side resource: "+resourceFolder); }
var zipPath = Path.Combine(tmpDir, Path.GetFileName(resourceFolder))+".res"; else if (e.Name.StartsWith("Server") && e.Name.EndsWith(".res"))
try {
{ var stream = pkgZip.GetInputStream(e).ToMemStream();
ResourceStreams.Add(Path.GetFileNameWithoutExtension(e.Name), stream);
Logger?.Debug("Resource added: " + Path.GetFileNameWithoutExtension(e.Name));
}
}
pkgZip.Close();
}
}
// Client
{
var path = Path.Combine("Resources", "Client");
var tmpDir = Path.Combine("Resources", "Temp", "Client");
Directory.CreateDirectory(path);
if (Directory.Exists(tmpDir))
{
Directory.Delete(tmpDir, true);
}
Directory.CreateDirectory(tmpDir);
var resourceFolders = Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly);
if (resourceFolders.Length != 0)
{
foreach (var resourceFolder in resourceFolders)
{
// Pack client side resource as a zip file
Logger?.Info("Packing client-side resource: " + resourceFolder);
var zipPath = Path.Combine(tmpDir, Path.GetFileName(resourceFolder)) + ".res";
try
{
using ZipFile zip = ZipFile.Create(zipPath); using ZipFile zip = ZipFile.Create(zipPath);
zip.BeginUpdate(); zip.BeginUpdate();
foreach (var dir in Directory.GetDirectories(resourceFolder, "*", SearchOption.AllDirectories)) foreach (var dir in Directory.GetDirectories(resourceFolder, "*", SearchOption.AllDirectories))
@ -91,169 +83,189 @@ namespace RageCoop.Server.Scripting
} }
zip.CommitUpdate(); zip.CommitUpdate();
zip.Close(); zip.Close();
ClientResources.Add(Path.GetFileNameWithoutExtension(zipPath), () => File.OpenRead(zipPath)); ClientResources.Add(Path.GetFileNameWithoutExtension(zipPath), File.OpenRead(zipPath));
}
catch (Exception ex)
{
Logger?.Error($"Failed to pack client resource:{resourceFolder}");
Logger?.Error(ex);
} }
catch (Exception ex)
{
Logger?.Error($"Failed to pack client resource:{resourceFolder}");
Logger?.Error(ex);
}
}
}
var packed = Directory.GetFiles(path, "*.res", SearchOption.TopDirectoryOnly);
if (packed.Length>0)
{
foreach(var file in packed)
{
ClientResources.Add(Path.GetFileNameWithoutExtension(file),()=>File.OpenRead(file));
} }
} }
} var packed = Directory.GetFiles(path, "*.res", SearchOption.TopDirectoryOnly);
if (packed.Length > 0)
{
foreach (var file in packed)
{
ClientResources.Add(Path.GetFileNameWithoutExtension(file), File.OpenRead(file));
}
}
}
// Server // Server
{ {
var path = Path.Combine("Resources", "Server"); var path = Path.Combine("Resources", "Server");
var dataFolder = Path.Combine(path, "data"); var dataFolder = Path.Combine(path, "data");
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
foreach (var resource in Directory.GetDirectories(path)) foreach (var resource in Directory.GetDirectories(path))
{ {
try try
{ {
var name = Path.GetFileName(resource); var name = Path.GetFileName(resource);
if (LoadedResources.ContainsKey(name)) if (LoadedResources.ContainsKey(name))
{ {
Logger?.Warning($"Resource \"{name}\" has already been loaded, ignoring..."); Logger?.Warning($"Resource \"{name}\" has already been loaded, ignoring...");
continue; continue;
} }
if (name.ToLower()=="data") { continue; } if (name.ToLower() == "data") { continue; }
Logger?.Info($"Loading resource: {name}"); Logger?.Info($"Loading resource: {name}");
var r = ServerResource.LoadFrom(resource, dataFolder, Logger); var r = ServerResource.LoadFrom(resource, dataFolder, Logger);
LoadedResources.Add(r.Name, r); LoadedResources.Add(r.Name, r);
} }
catch(Exception ex) catch (Exception ex)
{ {
Logger?.Error($"Failed to load resource: {Path.GetFileName(resource)}"); Logger?.Error($"Failed to load resource: {Path.GetFileName(resource)}");
Logger?.Error(ex); Logger?.Error(ex);
} }
} }
foreach (var res in Directory.GetFiles(path, "*.res", SearchOption.TopDirectoryOnly)) foreach (var res in Directory.GetFiles(path, "*.res", SearchOption.TopDirectoryOnly))
{
if (!ResourceStreams.TryAdd(Path.GetFileNameWithoutExtension(res),()=>File.OpenRead(res)))
{
Logger?.Warning($"Resource \"{res}\" cannot be loaded, ignoring...");
continue;
}
}
foreach(var res in ResourceStreams)
{ {
try if (!ResourceStreams.TryAdd(Path.GetFileNameWithoutExtension(res), File.OpenRead(res)))
{ {
var name = res.Key; Logger?.Warning($"Resource \"{res}\" cannot be loaded, ignoring...");
if (LoadedResources.ContainsKey(name)) continue;
{ }
Logger?.Warning($"Resource \"{name}\" has already been loaded, ignoring..."); }
continue; foreach (var res in ResourceStreams)
} {
Logger?.Info($"Loading resource: "+name); try
var r = ServerResource.LoadFrom(res.Value(),name, Path.Combine("Resources", "Temp", "Server"), dataFolder, Logger);
LoadedResources.Add(r.Name, r);
}
catch(Exception ex)
{ {
Logger?.Error($"Failed to load resource: {res.Key}"); var name = res.Key;
Logger?.Error(ex); if (LoadedResources.ContainsKey(name))
{
Logger?.Warning($"Resource \"{name}\" has already been loaded, ignoring...");
continue;
}
Logger?.Info($"Loading resource: " + name);
var r = ServerResource.LoadFrom(res.Value, name, Path.Combine("Resources", "Temp", "Server"), dataFolder, Logger);
LoadedResources.Add(r.Name, r);
}
catch (Exception ex)
{
Logger?.Error($"Failed to load resource: {res.Key}");
Logger?.Error(ex);
} }
} }
// Start scripts // Start scripts
lock (LoadedResources) lock (LoadedResources)
{ {
foreach (var r in LoadedResources.Values) foreach (var r in LoadedResources.Values)
{ {
foreach (ServerScript s in r.Scripts.Values) foreach (ServerScript s in r.Scripts.Values)
{ {
s.API=Server.API; s.API = Server.API;
try try
{ {
Logger?.Debug("Starting script:"+s.CurrentFile.Name); Logger?.Debug("Starting script:" + s.CurrentFile.Name);
s.OnStart(); s.OnStart();
} }
catch (Exception ex) { Logger?.Error($"Failed to start resource: {r.Name}"); Logger?.Error(ex); } catch (Exception ex) { Logger?.Error($"Failed to start resource: {r.Name}"); Logger?.Error(ex); }
} }
} }
} }
IsLoaded=true; IsLoaded = true;
} }
} }
public void UnloadAll() public void UnloadAll()
{ {
lock (LoadedResources) lock (LoadedResources)
{ {
foreach (var d in LoadedResources.Values) foreach (var d in LoadedResources.Values)
{ {
foreach (var s in d.Scripts.Values) foreach (var s in d.Scripts.Values)
{ {
try try
{ {
s.OnStop(); s.OnStop();
} }
catch(Exception ex) catch (Exception ex)
{ {
Logger?.Error(ex); Logger?.Error(ex);
} }
} }
try try
{ {
d.Dispose(); d.Dispose();
}
catch(Exception ex)
{
Logger.Error($"Resource \"{d.Name}\" cannot be unloaded.");
Logger.Error(ex);
} }
} catch (Exception ex)
LoadedResources.Clear(); {
} Logger.Error($"Resource \"{d.Name}\" cannot be unloaded.");
foreach(var s in MemStreams) Logger.Error(ex);
}
}
LoadedResources.Clear();
}
foreach (var s in ResourceStreams.Values)
{ {
try try
{ {
s.Close(); s.Close();
s.Dispose(); s.Dispose();
} }
catch(Exception ex) catch (Exception ex)
{ {
Logger?.Error("[Resources.CloseMemStream]",ex); Logger?.Error("[Resources.CloseStream]", ex);
} }
} }
} foreach (var s in ClientResources.Values)
public void SendTo(Client client) {
{ try
Task.Run(() =>
{
if (ClientResources.Count!=0)
{
Logger?.Info($"Sending resources to client:{client.Username}");
foreach (var rs in ClientResources)
{
Logger?.Debug(rs.Key);
Server.SendFile(rs.Value(),rs.Key+".res", client);
}
Logger?.Info($"Resources sent to:{client.Username}");
}
if (Server.GetResponse<Packets.FileTransferResponse>(client, new Packets.AllResourcesSent())?.Response==FileResponse.Loaded)
{
client.IsReady=true;
Server.API.Events.InvokePlayerReady(client);
}
else
{ {
Logger?.Warning($"Client {client.Username} failed to load resource."); s.Close();
s.Dispose();
} }
}); catch (Exception ex)
} {
} Logger?.Error("[Resources.CloseStream]", ex);
}
}
}
public void SendTo(Client client)
{
Task.Run(() =>
{
try
{
if (ClientResources.Count != 0)
{
Logger?.Info($"Sending resources to client:{client.Username}");
foreach (var rs in ClientResources)
{
Logger?.Debug(rs.Key);
Server.SendFile(rs.Value, rs.Key + ".res", client);
}
Logger?.Info($"Resources sent to:{client.Username}");
}
if (Server.GetResponse<Packets.FileTransferResponse>(client, new Packets.AllResourcesSent())?.Response == FileResponse.Loaded)
{
client.IsReady = true;
Server.API.Events.InvokePlayerReady(client);
}
else
{
Logger?.Warning($"Client {client.Username} failed to load resource.");
}
}
catch (Exception ex)
{
Logger.Error("Failed to send resource to client: " + client.Username, ex);
client.Kick("Resource error!");
}
});
}
}
} }

View File

@ -1,14 +1,11 @@
using System; using GTA;
using System.Collections.Generic; using GTA.Math;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using GTA.Math;
using GTA;
namespace RageCoop.Server.Scripting namespace RageCoop.Server.Scripting
{ {
@ -25,76 +22,36 @@ namespace RageCoop.Server.Scripting
} }
internal ConcurrentDictionary<int, ServerPed> Peds { get; set; } = new(); internal ConcurrentDictionary<int, ServerPed> Peds { get; set; } = new();
internal ConcurrentDictionary<int, ServerVehicle> Vehicles { get; set; } = new(); internal ConcurrentDictionary<int, ServerVehicle> Vehicles { get; set; } = new();
internal ConcurrentDictionary<int,ServerProp> ServerProps { get; set; }=new(); internal ConcurrentDictionary<int, ServerProp> ServerProps { get; set; } = new();
internal ConcurrentDictionary<int,ServerBlip> Blips { get; set; } = new(); internal ConcurrentDictionary<int, ServerBlip> Blips { get; set; } = new();
/// <summary> /// <summary>
/// Get a <see cref="ServerPed"/> by it's id /// Get a <see cref="ServerPed"/> by it's id
/// </summary> /// </summary>
/// <param name="id"></param> /// <param name="id"></param>
/// <returns></returns> /// <returns></returns>
public ServerPed GetPedByID(int id) public ServerPed GetPedByID(int id) => Peds.TryGetValue(id, out var ped) ? ped : null;
{
if(Peds.TryGetValue(id,out var ped))
{
return ped;
}
else
{
return null;
}
}
/// <summary> /// <summary>
/// Get a <see cref="ServerVehicle"/> by it's id /// Get a <see cref="ServerVehicle"/> by it's id
/// </summary> /// </summary>
/// <param name="id"></param> /// <param name="id"></param>
/// <returns></returns> /// <returns></returns>
public ServerVehicle GetVehicleByID(int id) public ServerVehicle GetVehicleByID(int id) => Vehicles.TryGetValue(id, out var veh) ? veh : null;
{
if (Vehicles.TryGetValue(id, out var veh))
{
return veh;
}
else
{
return null;
}
}
/// <summary> /// <summary>
/// Get a <see cref="ServerProp"/> owned by server from it's ID. /// Get a <see cref="ServerProp"/> owned by server from it's ID.
/// </summary> /// </summary>
/// <param name="id"></param> /// <param name="id"></param>
/// <returns></returns> /// <returns></returns>
public ServerProp GetPropByID(int id) public ServerProp GetPropByID(int id) => ServerProps.TryGetValue(id, out var obj) ? obj : null;
{
if (ServerProps.TryGetValue(id, out var obj))
{
return obj;
}
else
{
return null;
}
}
/// <summary> /// <summary>
/// Get a <see cref="ServerBlip"/> by it's id. /// Get a <see cref="ServerBlip"/> by it's id.
/// </summary> /// </summary>
/// <param name="id"></param> /// <param name="id"></param>
/// <returns></returns> /// <returns></returns>
public ServerBlip GetBlipByID(int id) public ServerBlip GetBlipByID(int id) => Blips.TryGetValue(id, out var obj) ? obj : null;
{
if (Blips.TryGetValue(id, out var obj))
{
return obj;
}
else
{
return null;
}
}
/// <summary> /// <summary>
/// Create a static prop owned by server. /// Create a static prop owned by server.
@ -103,16 +60,16 @@ namespace RageCoop.Server.Scripting
/// <param name="pos"></param> /// <param name="pos"></param>
/// <param name="rot"></param> /// <param name="rot"></param>
/// <returns></returns> /// <returns></returns>
public ServerProp CreateProp(Model model,Vector3 pos,Vector3 rot) public ServerProp CreateProp(Model model, Vector3 pos, Vector3 rot)
{ {
int id = RequestNetworkID(); int id = RequestNetworkID();
ServerProp prop; ServerProp prop;
ServerProps.TryAdd(id,prop=new ServerProp(Server) ServerProps.TryAdd(id, prop = new ServerProp(Server)
{ {
ID=id, ID = id,
Model=model, Model = model,
_pos=pos, _pos = pos,
_rot=rot _rot = rot
}); });
prop.Update(); prop.Update();
return prop; return prop;
@ -126,17 +83,17 @@ namespace RageCoop.Server.Scripting
/// <param name="pos">position</param> /// <param name="pos">position</param>
/// <param name="heading">heading of this vehicle</param> /// <param name="heading">heading of this vehicle</param>
/// <returns></returns> /// <returns></returns>
public ServerVehicle CreateVehicle(Client owner,Model model,Vector3 pos,float heading) public ServerVehicle CreateVehicle(Client owner, Model model, Vector3 pos, float heading)
{ {
if(owner == null) { throw new ArgumentNullException("Owner cannot be null"); } if (owner == null) { throw new ArgumentNullException("Owner cannot be null"); }
ServerVehicle veh = new(Server) ServerVehicle veh = new(Server)
{ {
Owner=owner, Owner = owner,
ID=RequestNetworkID(), ID = RequestNetworkID(),
Model=model, Model = model,
_pos= pos, _pos = pos,
}; };
owner.SendCustomEventQueued(CustomEvents.CreateVehicle,veh.ID, model, pos, heading); owner.SendCustomEventQueued(CustomEvents.CreateVehicle, veh.ID, model, pos, heading);
Vehicles.TryAdd(veh.ID, veh); Vehicles.TryAdd(veh.ID, veh);
return veh; return veh;
} }
@ -147,15 +104,15 @@ namespace RageCoop.Server.Scripting
/// <param name="pos"></param> /// <param name="pos"></param>
/// <param name="rotation"></param> /// <param name="rotation"></param>
/// <returns></returns> /// <returns></returns>
public ServerBlip CreateBlip(Vector3 pos,int rotation) public ServerBlip CreateBlip(Vector3 pos, int rotation)
{ {
var b = new ServerBlip(Server) var b = new ServerBlip(Server)
{ {
ID=RequestNetworkID(), ID = RequestNetworkID(),
Position=pos, Position = pos,
Rotation=rotation Rotation = rotation
}; };
Blips.TryAdd(b.ID,b); Blips.TryAdd(b.ID, b);
b.Update(); b.Update();
return b; return b;
} }
@ -164,57 +121,44 @@ namespace RageCoop.Server.Scripting
/// Get all peds on this server /// Get all peds on this server
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public ServerPed[] GetAllPeds() public ServerPed[] GetAllPeds() => Peds.Values.ToArray();
{
return Peds.Values.ToArray();
}
/// <summary> /// <summary>
/// Get all vehicles on this server /// Get all vehicles on this server
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public ServerVehicle[] GetAllVehicles() public ServerVehicle[] GetAllVehicles() => Vehicles.Values.ToArray();
{
return Vehicles.Values.ToArray();
}
/// <summary> /// <summary>
/// Get all static prop objects owned by server /// Get all static prop objects owned by server
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public ServerProp[] GetAllProps() public ServerProp[] GetAllProps() => ServerProps.Values.ToArray();
{
return ServerProps.Values.ToArray();
}
/// <summary> /// <summary>
/// Get all blips owned by server /// Get all blips owned by server
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public ServerBlip[] GetAllBlips() public ServerBlip[] GetAllBlips() => Blips.Values.ToArray();
{
return Blips.Values.ToArray();
}
/// <summary> /// <summary>
/// Not thread safe /// Not thread safe
/// </summary> /// </summary>
internal void Update(Packets.PedSync p,Client sender) internal void Update(Packets.PedSync p, Client sender)
{ {
ServerPed ped; if (!Peds.TryGetValue(p.ID, out ServerPed ped))
if(!Peds.TryGetValue(p.ID,out ped))
{ {
Peds.TryAdd(p.ID,ped=new ServerPed(Server)); Peds.TryAdd(p.ID, ped = new ServerPed(Server));
ped.ID=p.ID; ped.ID = p.ID;
} }
ped._pos = p.Position; ped._pos = p.Position;
ped.Owner=sender; ped.Owner = sender;
ped.Health=p.Health; ped.Health = p.Health;
ped._rot=p.Rotation; ped._rot = p.Rotation;
ped._isInvincible = p.Flags.HasPedFlag(PedDataFlags.IsInvincible); ped._isInvincible = p.Flags.HasPedFlag(PedDataFlags.IsInvincible);
if (p.Speed>=4 && Vehicles.TryGetValue(p.VehicleID,out var v)) if (p.Speed >= 4 && Vehicles.TryGetValue(p.VehicleID, out var v))
{ {
ped.LastVehicle=v; ped.LastVehicle = v;
} }
if (ped.Owner != sender) if (ped.Owner != sender)
@ -229,15 +173,14 @@ namespace RageCoop.Server.Scripting
} }
internal void Update(Packets.VehicleSync p, Client sender) internal void Update(Packets.VehicleSync p, Client sender)
{ {
ServerVehicle veh; if (!Vehicles.TryGetValue(p.ID, out ServerVehicle veh))
if (!Vehicles.TryGetValue(p.ID, out veh))
{ {
Vehicles.TryAdd(p.ID, veh=new ServerVehicle(Server)); Vehicles.TryAdd(p.ID, veh = new ServerVehicle(Server));
veh.ID=p.ID; veh.ID = p.ID;
} }
veh._pos = p.Position+p.Velocity*sender.Latency; veh._pos = p.Position + p.Velocity * sender.Latency;
veh._quat=p.Quaternion; veh._quat = p.Quaternion;
if(veh.Owner != sender) if (veh.Owner != sender)
{ {
if (veh.Owner != null) if (veh.Owner != null)
{ {
@ -253,20 +196,20 @@ namespace RageCoop.Server.Scripting
foreach (var pair in Peds) foreach (var pair in Peds)
{ {
if (pair.Value.Owner==left) if (pair.Value.Owner == left)
{ {
Server.QueueJob(()=>Peds.TryRemove(pair.Key,out _)); Server.QueueJob(() => Peds.TryRemove(pair.Key, out _));
} }
} }
foreach (var pair in Vehicles) foreach (var pair in Vehicles)
{ {
if (pair.Value.Owner==left) if (pair.Value.Owner == left)
{ {
Server.QueueJob(() => Vehicles.TryRemove(pair.Key, out _)); Server.QueueJob(() => Vehicles.TryRemove(pair.Key, out _));
} }
} }
// Server.QueueJob(() => // Server.QueueJob(() =>
// Server.Logger?.Trace("Remaining entities: "+(Peds.Count+Vehicles.Count+ServerProps.Count))); // Server.Logger?.Trace("Remaining entities: "+(Peds.Count+Vehicles.Count+ServerProps.Count)));
} }
internal void RemoveVehicle(int id) internal void RemoveVehicle(int id)
{ {
@ -277,14 +220,8 @@ namespace RageCoop.Server.Scripting
} }
} }
internal void RemoveProp(int id) internal void RemoveProp(int id) => ServerProps.TryRemove(id, out _);
{ internal void RemoveServerBlip(int id) => Blips.TryRemove(id, out _);
ServerProps.TryRemove(id, out _);
}
internal void RemoveServerBlip(int id)
{
Blips.TryRemove(id, out _);
}
internal void RemovePed(int id) internal void RemovePed(int id)
{ {
Peds.TryRemove(id, out var ped); Peds.TryRemove(id, out var ped);
@ -298,17 +235,16 @@ namespace RageCoop.Server.Scripting
{ {
if (Peds.ContainsKey(ped.ID)) if (Peds.ContainsKey(ped.ID))
{ {
Peds[ped.ID]=ped; Peds[ped.ID] = ped;
} return;
else
{
Peds.TryAdd(ped.ID, ped);
} }
Peds.TryAdd(ped.ID, ped);
} }
internal int RequestNetworkID() internal int RequestNetworkID()
{ {
int ID = 0; int ID = 0;
while ((ID==0) while ((ID == 0)
|| ServerProps.ContainsKey(ID) || ServerProps.ContainsKey(ID)
|| Peds.ContainsKey(ID) || Peds.ContainsKey(ID)
|| Vehicles.ContainsKey(ID) || Vehicles.ContainsKey(ID)

View File

@ -1,14 +1,11 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GTA;
using GTA.Native;
using GTA.Math; using GTA.Math;
using GTA.Native;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace RageCoop.Server.Scripting namespace RageCoop.Server.Scripting
{ {
@ -22,7 +19,7 @@ namespace RageCoop.Server.Scripting
/// Server that this object belongs to /// Server that this object belongs to
/// </summary> /// </summary>
internal readonly Server Server; internal readonly Server Server;
internal ServerObject(Server server) { Server=server; } internal ServerObject(Server server) { Server = server; }
/// <summary> /// <summary>
/// Pass this as an argument in CustomEvent or NativeCall to convert this object to handle at client side. /// Pass this as an argument in CustomEvent or NativeCall to convert this object to handle at client side.
@ -70,8 +67,8 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public virtual Vector3 Position public virtual Vector3 Position
{ {
get { return _pos; } get => _pos;
set { _pos=value; Owner.SendNativeCall(Hash.SET_ENTITY_COORDS_NO_OFFSET, Handle, value.X, value.Y, value.Z,1, 1,1 ); } set { _pos = value; Owner.SendNativeCall(Hash.SET_ENTITY_COORDS_NO_OFFSET, Handle, value.X, value.Y, value.Z, 1, 1, 1); }
} }
internal Vector3 _pos; internal Vector3 _pos;
@ -80,8 +77,8 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public virtual Vector3 Rotation public virtual Vector3 Rotation
{ {
get { return _rot; } get => _rot;
set { _rot=value; Owner.SendNativeCall(Hash.SET_ENTITY_ROTATION, Handle, value.X, value.Y, value.Z ,2,1); } set { _rot = value; Owner.SendNativeCall(Hash.SET_ENTITY_ROTATION, Handle, value.X, value.Y, value.Z, 2, 1); }
} }
internal Vector3 _rot; internal Vector3 _rot;
@ -105,7 +102,7 @@ namespace RageCoop.Server.Scripting
/// <exception cref="InvalidOperationException"></exception> /// <exception cref="InvalidOperationException"></exception>
public virtual void Freeze(bool toggle) public virtual void Freeze(bool toggle)
{ {
if (GetTypeByte()==50) if (GetTypeByte() == 50)
{ {
throw new InvalidOperationException("Can't freeze or unfreeze static server object"); throw new InvalidOperationException("Can't freeze or unfreeze static server object");
} }
@ -128,7 +125,7 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public override void Delete() public override void Delete()
{ {
Server.API.SendCustomEventQueued(null,CustomEvents.DeleteServerProp,ID); Server.API.SendCustomEventQueued(null, CustomEvents.DeleteServerProp, ID);
Server.API.Entities.RemoveProp(ID); Server.API.Entities.RemoveProp(ID);
} }
@ -137,8 +134,8 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public override Vector3 Position public override Vector3 Position
{ {
get { return _pos; } get => _pos;
set { _pos=value; Server.API.SendNativeCall(null, Hash.SET_ENTITY_COORDS_NO_OFFSET, Handle, value.X, value.Y, value.Z, 1, 1, 1); } set { _pos = value; Server.API.SendNativeCall(null, Hash.SET_ENTITY_COORDS_NO_OFFSET, Handle, value.X, value.Y, value.Z, 1, 1, 1); }
} }
/// <summary> /// <summary>
@ -146,8 +143,8 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public override Vector3 Rotation public override Vector3 Rotation
{ {
get { return _rot; } get => _rot;
set { _rot=value; Server.API.SendNativeCall(null, Hash.SET_ENTITY_ROTATION, Handle, value.X, value.Y, value.Z, 2, 1); } set { _rot = value; Server.API.SendNativeCall(null, Hash.SET_ENTITY_ROTATION, Handle, value.X, value.Y, value.Z, 2, 1); }
} }
@ -156,7 +153,7 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
internal void Update() internal void Update()
{ {
Server.API.Server.BaseScript.SendServerPropsTo(new() { this }); Server.API.Server.BaseScript.SendServerPropsTo(new() { this });
} }
} }
@ -198,9 +195,10 @@ namespace RageCoop.Server.Scripting
/// <summary> /// <summary>
/// Get or set whether this ped is invincible /// Get or set whether this ped is invincible
/// </summary> /// </summary>
public bool IsInvincible { public bool IsInvincible
{
get => _isInvincible; get => _isInvincible;
set => Owner.SendNativeCall(Hash.SET_ENTITY_INVINCIBLE,Handle,value); set => Owner.SendNativeCall(Hash.SET_ENTITY_INVINCIBLE, Handle, value);
} }
} }
/// <summary> /// <summary>
@ -215,8 +213,8 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public override Vector3 Rotation public override Vector3 Rotation
{ {
get { return _quat.ToEulerAngles().ToDegree(); } get => _quat.ToEulerAngles().ToDegree();
set { Owner.SendNativeCall(Hash.SET_ENTITY_ROTATION, Handle ,value.X, value.Y ,value.Z); } set { Owner.SendNativeCall(Hash.SET_ENTITY_ROTATION, Handle, value.X, value.Y, value.Z); }
} }
internal Quaternion _quat; internal Quaternion _quat;
@ -225,8 +223,8 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public Quaternion Quaternion public Quaternion Quaternion
{ {
get { return _quat; } get => _quat;
set { _quat = value ;Owner.SendNativeCall(Hash.SET_ENTITY_QUATERNION, Handle, value.X, value.Y, value.Z, value.W); } set { _quat = value; Owner.SendNativeCall(Hash.SET_ENTITY_QUATERNION, Handle, value.X, value.Y, value.Z, value.W); }
} }
} }
@ -263,28 +261,30 @@ namespace RageCoop.Server.Scripting
/// <summary> /// <summary>
/// Color of this blip /// Color of this blip
/// </summary> /// </summary>
public BlipColor Color { public BlipColor Color
get { return _color; } {
set { _color=value; Update(); } get => _color;
set { _color = value; Update(); }
} }
internal BlipSprite _sprite=BlipSprite.Standard; internal BlipSprite _sprite = BlipSprite.Standard;
/// <summary> /// <summary>
/// Sprite of this blip /// Sprite of this blip
/// </summary> /// </summary>
public BlipSprite Sprite { public BlipSprite Sprite
get { return _sprite; } {
set { _sprite=value; Update();} get => _sprite;
set { _sprite = value; Update(); }
} }
internal float _scale =1; internal float _scale = 1;
/// <summary> /// <summary>
/// Scale of this blip /// Scale of this blip
/// </summary> /// </summary>
public float Scale public float Scale
{ {
get { return _scale; } get => _scale;
set { _scale=value;Update(); } set { _scale = value; Update(); }
} }
internal Vector3 _pos = new(); internal Vector3 _pos = new();
@ -293,8 +293,8 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public Vector3 Position public Vector3 Position
{ {
get { return _pos; } get => _pos;
set { _pos=value; Update(); } set { _pos = value; Update(); }
} }
internal int _rot; internal int _rot;
@ -303,18 +303,18 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public int Rotation public int Rotation
{ {
get { return _rot; } get => _rot;
set { _rot=value; Update(); } set { _rot = value; Update(); }
} }
internal string _name="Beeeeeee"; internal string _name = "Beeeeeee";
/// <summary> /// <summary>
/// Name of this blip /// Name of this blip
/// </summary> /// </summary>
public string Name public string Name
{ {
get { return _name;} get => _name;
set { _name=value; Update(); } set { _name = value; Update(); }
} }
/// <summary> /// <summary>
@ -322,23 +322,23 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public void Delete() public void Delete()
{ {
Server.API.SendCustomEventQueued(null, CustomEvents.DeleteServerBlip,ID); Server.API.SendCustomEventQueued(null, CustomEvents.DeleteServerBlip, ID);
Server.Entities.RemoveServerBlip(ID); Server.Entities.RemoveServerBlip(ID);
} }
private bool _bouncing=false; private bool _bouncing = false;
internal void Update() internal void Update()
{ {
// 5ms debounce // 5ms debounce
if (!_bouncing) if (!_bouncing)
{ {
_bouncing=true; _bouncing = true;
Task.Run(() => Task.Run(() =>
{ {
Thread.Sleep(5); Thread.Sleep(5);
DoUpdate(); DoUpdate();
_bouncing=false; _bouncing = false;
}); });
} }
} }
@ -359,7 +359,7 @@ namespace RageCoop.Server.Scripting
/// <summary> /// <summary>
/// Get the <see cref="ServerPed"/> that this blip attached to. /// Get the <see cref="ServerPed"/> that this blip attached to.
/// </summary> /// </summary>
public ServerPed Ped { get;internal set; } public ServerPed Ped { get; internal set; }
internal PedBlip(ServerPed ped) internal PedBlip(ServerPed ped)
{ {
Ped = ped; Ped = ped;
@ -372,18 +372,18 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public BlipColor Color public BlipColor Color
{ {
get { return _color; } get => _color;
set { _color=value; Update(); } set { _color = value; Update(); }
} }
internal BlipSprite _sprite=BlipSprite.Standard; internal BlipSprite _sprite = BlipSprite.Standard;
/// <summary> /// <summary>
/// Sprite of this blip /// Sprite of this blip
/// </summary> /// </summary>
public BlipSprite Sprite public BlipSprite Sprite
{ {
get { return _sprite; } get => _sprite;
set { _sprite=value; Update(); } set { _sprite = value; Update(); }
} }
internal float _scale = 1; internal float _scale = 1;
@ -392,8 +392,8 @@ namespace RageCoop.Server.Scripting
/// </summary> /// </summary>
public float Scale public float Scale
{ {
get { return _scale; } get => _scale;
set { _scale=value; Update(); } set { _scale = value; Update(); }
} }
private bool _bouncing = false; private bool _bouncing = false;
@ -402,18 +402,18 @@ namespace RageCoop.Server.Scripting
// 5ms debounce // 5ms debounce
if (!_bouncing) if (!_bouncing)
{ {
_bouncing=true; _bouncing = true;
Task.Run(() => Task.Run(() =>
{ {
Thread.Sleep(5); Thread.Sleep(5);
DoUpdate(); DoUpdate();
_bouncing=false; _bouncing = false;
}); });
} }
} }
private void DoUpdate() private void DoUpdate()
{ {
Ped.Owner.SendCustomEventQueued(CustomEvents.UpdatePedBlip,Ped.Handle,(byte)Color,(ushort)Sprite,Scale); Ped.Owner.SendCustomEventQueued(CustomEvents.UpdatePedBlip, Ped.Handle, (byte)Color, (ushort)Sprite, Scale);
} }
} }

View File

@ -1,214 +1,235 @@
using System; using ICSharpCode.SharpZipLib.Zip;
using System.Collections.Generic;
using System.Linq;
using RageCoop.Core;
using System.Reflection;
using McMaster.NETCore.Plugins; using McMaster.NETCore.Plugins;
using System.IO; using RageCoop.Core;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using ICSharpCode.SharpZipLib.Zip; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
namespace RageCoop.Server.Scripting namespace RageCoop.Server.Scripting
{ {
/// <summary> /// <summary>
/// A class representing a server side resource, each resource is isolated from another and will be started alongside the server. /// A class representing a server side resource, each resource is isolated from another and will be started alongside the server.
/// </summary> /// </summary>
public class ServerResource : PluginLoader public class ServerResource : PluginLoader
{ {
internal ServerResource(PluginConfig config) : base(config) { } internal ServerResource(PluginConfig config) : base(config) { }
internal static ServerResource LoadFrom(string resDir, string dataFolder, Logger logger = null, bool isTemp = false) internal static ServerResource LoadFrom(string resDir, string dataFolder, Logger logger = null)
{ {
var conf = new PluginConfig(Path.GetFullPath(Path.Combine(resDir, Path.GetFileName(resDir)+".dll"))) var runtimeLibs = Path.Combine(resDir, "RuntimeLibs", CoreUtils.GetInvariantRID());
{ if (Directory.Exists(runtimeLibs))
PreferSharedTypes = true,
EnableHotReload=false,
IsUnloadable=false,
LoadInMemory=true,
};
ServerResource r = new(conf);
r.Logger= logger;
r.Name=Path.GetFileName(resDir);
if (!File.Exists(conf.MainAssemblyPath))
{
r.Dispose();
throw new FileNotFoundException($"Main assembly for resource \"{r.Name}\" cannot be found.");
}
r.Scripts = new();
r.DataFolder=Path.Combine(dataFolder, r.Name);
r.Reloaded+=(s, e) => { r.Logger?.Info($"Resource: {r.Name} has been reloaded"); };
Directory.CreateDirectory(r.DataFolder);
foreach (var dir in Directory.GetDirectories(resDir, "*", SearchOption.AllDirectories))
{
r.Files.Add(dir, new ResourceFile()
{
IsDirectory=true,
Name=dir.Substring(resDir.Length+1).Replace('\\','/')
});;
}
var assemblies=new Dictionary<ResourceFile,Assembly>();
foreach (var file in Directory.GetFiles(resDir, "*", SearchOption.AllDirectories))
{
if (Path.GetFileName(file).CanBeIgnored()) { try { File.Delete(file); } catch { } continue; }
var relativeName = file.Substring(resDir.Length+1).Replace('\\', '/');
var rfile = new ResourceFile()
{
GetStream=() => { return new FileStream(file, FileMode.Open, FileAccess.Read); },
IsDirectory=false,
Name=relativeName
};
if (file.EndsWith(".dll") && !relativeName.Contains('/') && IsManagedAssembly(file))
{
assemblies.Add(rfile, r.LoadAssemblyFromPath(Path.GetFullPath(file)));
}
r.Files.Add(relativeName, rfile);
}
foreach(var a in assemblies)
{ {
try logger?.Debug("Applying runtime libraries from " + CoreUtils.GetInvariantRID());
{ CoreUtils.CopyFilesRecursively(new(runtimeLibs), new(resDir));
r.LoadScriptsFromAssembly(a.Key,a.Value); }
}
catch (FileLoadException ex)
{
if (!ex.Message.EndsWith("Assembly with same name is already loaded"))
{
logger?.Warning("Failed to load assembly: "+a.Key.Name);
logger?.Trace(ex.Message);
}
}
}
return r;
}
internal static ServerResource LoadFrom(Stream input,string name, string tmpDir, string dataFolder, Logger logger = null)
{
tmpDir=Path.Combine(tmpDir, name);
new FastZip().ExtractZip(input, tmpDir, FastZip.Overwrite.Always,null,null,null,true,true);
return LoadFrom(tmpDir, dataFolder, logger, true);
}
/// <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="ServerScript"/> instance in this resource
/// </summary>
public Dictionary<string,ServerScript> Scripts { get; internal set; } = new();
/// <summary>
/// Get all <see cref="ResourceFile"/> that can be used to acces files in this resource
/// </summary>
public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>();
/// <summary>
/// Get a <see cref="Logger"/> instance that can be used to show information in console.
/// </summary>
public Logger Logger;
private bool LoadScriptsFromAssembly(ResourceFile rfile, Assembly assembly)
{
int count = 0;
try runtimeLibs = Path.Combine(resDir, "RuntimeLibs", RuntimeInformation.RuntimeIdentifier);
{ if (Directory.Exists(runtimeLibs))
// Find all script types in the assembly
foreach (var type in assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(ServerScript))))
{
ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);
if (constructor != null && constructor.IsPublic)
{
try
{
// Invoke script constructor
var script = constructor.Invoke(null) as ServerScript;
script.CurrentResource = this;
script.CurrentFile=rfile;
Scripts.Add(script.GetType().FullName,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;
}
if(count != 0)
{ {
Logger?.Info($"Loaded {count} script(s) in {rfile.Name}"); logger?.Debug("Applying runtime libraries from " + CoreUtils.GetInvariantRID());
} CoreUtils.CopyFilesRecursively(new(runtimeLibs), new(resDir));
return count != 0; }
}
internal new void Dispose() var conf = new PluginConfig(Path.GetFullPath(Path.Combine(resDir, Path.GetFileName(resDir) + ".dll")))
{ {
base.Dispose(); PreferSharedTypes = true,
} EnableHotReload = false,
private static bool IsManagedAssembly(string filename) IsUnloadable = false,
{ LoadInMemory = true,
try };
{ ServerResource r = new(conf);
using (Stream file = new FileStream(filename, FileMode.Open, FileAccess.Read)) r.Logger = logger;
{ r.Name = Path.GetFileName(resDir);
if (file.Length < 64) if (!File.Exists(conf.MainAssemblyPath))
return false; {
r.Dispose();
throw new FileNotFoundException($"Main assembly for resource \"{r.Name}\" cannot be found.");
}
r.Scripts = new();
r.DataFolder = Path.Combine(dataFolder, r.Name);
r.Reloaded += (s, e) => { r.Logger?.Info($"Resource: {r.Name} has been reloaded"); };
using (BinaryReader bin = new BinaryReader(file)) Directory.CreateDirectory(r.DataFolder);
{ foreach (var dir in Directory.GetDirectories(resDir, "*", SearchOption.AllDirectories))
// PE header starts at offset 0x3C (60). Its a 4 byte header. {
file.Position = 0x3C; r.Files.Add(dir, new ResourceFile()
uint offset = bin.ReadUInt32(); {
if (offset == 0) IsDirectory = true,
offset = 0x80; Name = dir.Substring(resDir.Length + 1).Replace('\\', '/')
}); ;
}
var assemblies = new Dictionary<ResourceFile, Assembly>();
foreach (var file in Directory.GetFiles(resDir, "*", SearchOption.AllDirectories))
{
if (Path.GetFileName(file).CanBeIgnored()) { try { File.Delete(file); } catch { } continue; }
var relativeName = file.Substring(resDir.Length + 1).Replace('\\', '/');
var rfile = new ResourceFile()
{
GetStream = () => { return new FileStream(file, FileMode.Open, FileAccess.Read); },
IsDirectory = false,
Name = relativeName
};
if (file.EndsWith(".dll") && !relativeName.Contains('/') && IsManagedAssembly(file))
{
assemblies.Add(rfile, r.LoadAssemblyFromPath(Path.GetFullPath(file)));
}
r.Files.Add(relativeName, rfile);
}
foreach (var a in assemblies)
{
if (a.Key.Name.ToLower() == r.Name.ToLower() + ".dll")
{
// Ensure there is at least enough room for the following structures: try
// 24 byte PE Signature & Header {
// 28 byte Standard Fields (24 bytes for PE32+) r.LoadScriptsFromAssembly(a.Key, a.Value);
// 68 byte NT Fields (88 bytes for PE32+) }
// >= 128 byte Data Dictionary Table catch (FileLoadException ex)
if (offset > file.Length - 256) {
return false; if (!ex.Message.EndsWith("Assembly with same name is already loaded"))
{
logger?.Warning("Failed to load assembly: " + a.Key.Name);
logger?.Trace(ex.Message);
}
}
}
}
return r;
}
internal static ServerResource LoadFrom(Stream input, string name, string tmpDir, string dataFolder, Logger logger = null)
{
tmpDir = Path.Combine(tmpDir, name);
if (Directory.Exists(tmpDir)) { Directory.Delete(tmpDir, true); }
Directory.CreateDirectory(tmpDir);
new FastZip().ExtractZip(input, tmpDir, FastZip.Overwrite.Always, null, null, null, true, true);
return LoadFrom(tmpDir, dataFolder, logger);
}
/// <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="ServerScript"/> instance in this resource
/// </summary>
public Dictionary<string, ServerScript> Scripts { get; internal set; } = new();
/// <summary>
/// Get all <see cref="ResourceFile"/> that can be used to acces files in this resource
/// </summary>
public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>();
/// <summary>
/// Get a <see cref="Logger"/> instance that can be used to show information in console.
/// </summary>
public Logger Logger;
private bool LoadScriptsFromAssembly(ResourceFile rfile, Assembly assembly)
{
int count = 0;
// Check the PE signature. Should equal 'PE\0\0'. try
file.Position = offset; {
if (bin.ReadUInt32() != 0x00004550) // Find all script types in the assembly
return false; foreach (var type in assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(ServerScript))))
{
ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);
if (constructor != null && constructor.IsPublic)
{
try
{
// Invoke script constructor
var script = constructor.Invoke(null) as ServerScript;
script.CurrentResource = this;
script.CurrentFile = rfile;
Scripts.Add(script.GetType().FullName, 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;
}
if (count != 0)
{
Logger?.Info($"Loaded {count} script(s) in {rfile.Name}");
}
return count != 0;
}
// Read PE magic number from Standard Fields to determine format. internal new void Dispose()
file.Position += 20; {
var peFormat = bin.ReadUInt16(); base.Dispose();
if (peFormat != 0x10b /* PE32 */ && peFormat != 0x20b /* PE32Plus */) }
return false; private static bool IsManagedAssembly(string filename)
{
try
{
using (Stream file = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
if (file.Length < 64)
return false;
// Read the 15th Data Dictionary RVA field which contains the CLI header RVA. using (BinaryReader bin = new BinaryReader(file))
// When this is non-zero then the file contains CLI data otherwise not. {
file.Position = offset + (peFormat == 0x10b ? 232 : 248); // PE header starts at offset 0x3C (60). Its a 4 byte header.
return bin.ReadUInt32() != 0; file.Position = 0x3C;
} uint offset = bin.ReadUInt32();
} if (offset == 0)
} offset = 0x80;
catch
{ // Ensure there is at least enough room for the following structures:
// This is likely not a valid assembly if any IO exceptions occur during reading // 24 byte PE Signature & Header
return false; // 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;
}
}
}
} }

View File

@ -1,5 +1,5 @@
using System; using RageCoop.Core.Scripting;
using RageCoop.Core.Scripting; using System;
namespace RageCoop.Server.Scripting namespace RageCoop.Server.Scripting
{ {
@ -35,7 +35,7 @@ namespace RageCoop.Server.Scripting
/// <summary> /// <summary>
/// Eqivalent of <see cref="ServerResource.Logger"/> in <see cref="CurrentResource"/> /// Eqivalent of <see cref="ServerResource.Logger"/> in <see cref="CurrentResource"/>
/// </summary> /// </summary>
public Core.Logger Logger { get { return CurrentResource.Logger; } } public Core.Logger Logger => CurrentResource.Logger;
} }
/// <summary> /// <summary>
/// Decorate your method with this attribute and use <see cref="API.RegisterCommands{T}"/> or <see cref="API.RegisterCommands(object)"/> to register commands. /// Decorate your method with this attribute and use <see cref="API.RegisterCommands{T}"/> or <see cref="API.RegisterCommands(object)"/> to register commands.

Some files were not shown because too many files have changed in this diff Show More