66 Commits

Author SHA1 Message Date
sardelka9515 89ebd0305c Fix ped running not displayed 2022-08-23 18:10:52 +08:00
sardelka9515 b209202292 Add option to show entity owner 2022-08-23 17:43:24 +08:00
sardelka9515 f199241ed8 Time for a release 2022-08-23 16:29:43 +08:00
sardelka9515 50229116a7 blah 2022-08-23 14:04:18 +08:00
sardelka9515 f192caeae1 Add sdk 2022-08-23 14:03:33 +08:00
sardelka9515 6005b2ba11 Add slipstreaming 2022-08-23 13:51:38 +08:00
sardelka9515 6e213611cc Fix deluxo wing 2022-08-23 13:45:45 +08:00
sardelka9515 347d65af62 Expose chat meesage API to client script 2022-08-23 13:34:12 +08:00
sardelka9515 636ee3a33f Fix memory stream close on send 2022-08-23 13:22:14 +08:00
sardelka9515 8ff08e0804 Add resource package support 2022-08-23 12:21:17 +08:00
sardelka9515 6d7fe58719 Ped sync tweaks 2022-08-23 10:55:54 +08:00
sardelka9515 c7e14ef191 Fix disable traffic not always work 2022-08-22 21:59:01 +08:00
sardelka9515 39eb5ef80a Hmm... 2022-08-22 19:38:04 +08:00
sardelka9515 d25c2539e0 Tweaks 2022-08-22 19:27:25 +08:00
sardelka9515 eb31a33104 Fix vibration on stopped vehicle 2022-08-22 19:25:03 +08:00
sardelka9515 8b3f8ffd9b Check ped weapon on full sync only 2022-08-22 19:13:52 +08:00
sardelka9515 cca09c4178 Fixed ped weapon display after exiting vehicle 2022-08-22 19:08:09 +08:00
sardelka9515 871bb7e64e ver 2022-08-22 17:57:43 +08:00
sardelka9515 02da3ce8af Fix voice on P2P mode 2022-08-22 17:55:58 +08:00
sardelka9515 711ba62fc2 Update installer 2022-08-22 17:30:15 +08:00
sardelka9515 8f7d3135db Revert "Fix output path overlap"
This reverts commit 2af9482da9.
2022-08-22 17:27:35 +08:00
sardelka9515 cb563a1bf8 Revert "Update nightly-build.yaml"
This reverts commit 6720636d48.
2022-08-22 17:27:29 +08:00
sardelka9515 a84ab42a42 blah 2022-08-22 17:01:08 +08:00
sardelka9515 b3e285f92f Wait ten minutes before updating 2022-08-22 16:59:00 +08:00
sardelka9515 01433f0de6 Restore damage model for aircraft 2022-08-22 11:36:52 +08:00
sardelka9515 c034cd8aa9 Fix traffic 2022-08-22 10:01:09 +08:00
sardelka9515 363c2ccb00 Don't disable vehicle enter/exit key 2022-08-22 09:40:02 +08:00
sardelka9515 7380c162be Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-22 09:34:52 +08:00
sardelka9515 f1c7192029 Change kicking options 2022-08-22 09:33:54 +08:00
Sardelka9515 6720636d48 Update nightly-build.yaml 2022-08-22 00:46:46 +08:00
Sardelka9515 2af9482da9 Fix output path overlap 2022-08-22 00:36:43 +08:00
sardelka9515 bf22c17bba Add AntiAsshole and host display 2022-08-22 00:23:46 +08:00
sardelka9515 01492a0c55 Auto server update should work now 2022-08-21 19:56:59 +08:00
Sardelka9515 c17b234057 Update AssemblyInfo.cs 2022-08-21 19:41:25 +08:00
sardelka9515 739577bbf1 Hmm... 2022-08-21 19:30:27 +08:00
sardelka9515 2d2da624e4 Add automatic server update (need testing) 2022-08-21 19:13:12 +08:00
sardelka9515 9e66762061 Add ZeroTier check to installer 2022-08-21 15:13:36 +08:00
sardelka9515 84d578366a Fix installer 2022-08-21 14:28:37 +08:00
sardelka9515 03bc3a7742 blah 2022-08-21 14:22:41 +08:00
sardelka9515 5bfc7f836e Fix vehicle seat sync 2022-08-21 14:15:01 +08:00
sardelka9515 54977c79f1 Fix unintended carjacking 2022-08-21 14:12:44 +08:00
sardelka9515 0352bfa328 Add menyoo and directory check for installer 2022-08-21 13:42:28 +08:00
sardelka9515 6fbc386b45 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-21 12:37:47 +08:00
sardelka9515 203e3b2528 Add Client.EntitiesCount 2022-08-21 12:37:37 +08:00
Sardelka9515 c9aa995224 Update README.md 2022-08-21 12:17:20 +08:00
sardelka9515 c201b94897 Better vehicle rotation display 2022-08-20 22:36:48 +08:00
sardelka9515 87f873d1a5 blah 2022-08-20 22:22:06 +08:00
sardelka9515 6c355b109d Fix player blip (again) 2022-08-20 22:15:01 +08:00
sardelka9515 2ef3012672 Fix installer not overwriting files 2022-08-20 17:27:05 +08:00
sardelka9515 7229574ff4 Hmm 2022-08-20 17:17:49 +08:00
sardelka9515 eff9fd50b1 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-20 15:31:03 +08:00
sardelka9515 e2cbf26360 Fix installer output 2022-08-20 15:30:54 +08:00
sardelka9515 b0f448fd5f Fix insyaller output 2022-08-20 15:29:44 +08:00
sardelka9515 721c4d3dee Add client installer 2022-08-20 15:20:25 +08:00
Sardelka9515 87dfc18d91 Smoother ped heading calibration 2022-08-20 12:33:55 +08:00
Sardelka9515 fa4fe2c10f Fix player blip 2022-08-20 11:32:35 +08:00
Sardelka9515 3a088ddbfc Fix blip duplication and name issue 2022-08-20 11:08:51 +08:00
Sardelka9515 52cc9b647c Vehicle duplication prevention 2022-08-19 22:31:13 +08:00
Sardelka9515 1aa23901d5 Update lidgren again 2022-08-19 22:10:01 +08:00
Sardelka9515 52584d5774 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-19 21:43:34 +08:00
Sardelka9515 62bf15dede default LogLevel to 0 2022-08-19 21:43:23 +08:00
Sardelka9515 722ca16f57 Update README.md 2022-08-19 21:39:40 +08:00
Sardelka9515 3e61498366 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-19 21:35:08 +08:00
Sardelka9515 1ff2fa5691 Update lidgren 2022-08-19 21:35:05 +08:00
Sardelka9515 5088973474 Update README.md 2022-08-19 21:29:24 +08:00
Sardelka9515 b63378f4d0 Update README.md 2022-08-19 21:28:44 +08:00
55 changed files with 1580 additions and 661 deletions
+8 -9
View File
@@ -24,15 +24,14 @@ jobs:
run: dotnet restore
- name: Restore nuget packages
run: nuget restore
- name: Build client
run: dotnet build RageCoop.Client/RageCoop.Client.csproj --configuration Release -o bin/Release/Client/RageCoop
- name: Build client and installer
run: dotnet build RageCoop.Client.Installer/RageCoop.Client.Installer.csproj --configuration Release -o bin/Release/Client/RageCoop
- name: Build server win-x64
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r win-x64 -o RageCoop.Server/bin/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
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r linux-x64 -o RageCoop.Server/bin/linux-x64 -c Release
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 RageCoop.Server/bin/linux-arm -c Release
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
with:
files: bin/Release/Client
@@ -40,17 +39,17 @@ jobs:
- uses: vimtor/action-zip@v1
with:
files: RageCoop.Server/bin/win-x64
files: bin/Release/Server/win-x64
dest: RageCoop.Server-win-x64.zip
- uses: vimtor/action-zip@v1
with:
files: RageCoop.Server/bin/linux-x64
files: bin/Release/Server/linux-x64
dest: RageCoop.Server-linux-x64.zip
- uses: vimtor/action-zip@v1
with:
files: RageCoop.Server/bin/linux-arm
files: bin/Release/Server/linux-arm
dest: RageCoop.Server-linux-arm.zip
- uses: WebFreak001/deploy-nightly@v1.1.0
+30 -13
View File
@@ -1,6 +1,7 @@
# 🌐 RAGECOOP
[![Downloads][downloads-shield]][downloads-url]
[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
@@ -9,24 +10,37 @@
# 🧠 That's it
RAGECOOP brings multiplayer experience to the story mode, you can complete missions together with your friends, use mods without any restriction/getting banned, or just mess around with your fella!
# 📋 Requirements
- Visual Studio 2022
- .NET 6.0
- .NET Framework 4.8
# 👁 Requirements
- ScriptHookV
- ScriptHookVDotNet 3.5.1 or later
- .NET Framework 4.8 Runtime or SDK
# 📚 Libraries
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet/releases/tag/v3.4.0)
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI/releases/tag/v1.6)
- Lidgren Network Custom (***PRIVATE***)
# 📋 Building the project
You'll need:
- .NET 6.0 SDK
- .NET Framework 4.8 SDK
Recommended IDE:
- Visual Studio Code
- Visul Studio 2022
Then run `dotnet build` in the solution directory, built binaries are in the `bin` folder
# 📚 Third-party libraries
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet)
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI)
- Lidgren Network Custom
- - No new features (only improvements)
- [Newtonsoft.Json](https://www.nuget.org/packages/Newtonsoft.Json/13.0.1)
- [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
- [ClearScript](https://github.com/microsoft/ClearScript)
- [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
- [DotNetCorePlugins](https://github.com/natemcmaster/DotNetCorePlugins)
# Features
# 👋 Features
1. Synchronized bullets
2. Synchronized vehicle/player/NPC
@@ -37,15 +51,15 @@ RAGECOOP brings multiplayer experience to the story mode, you can complete missi
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).
# Known issues
# ⚠ Known issues
See [Bugs](https://github.com/RAGECOOP/RAGECOOP-V/issues/33)
## Installation
# 🔫 Installation
Refer to the [wiki](https://github.com/RAGECOOP/RAGECOOP-V/wiki)
# Downloads
# 🧨 Downloads
Download latest release [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/latest)
@@ -56,6 +70,7 @@ Please note that this is incompatible with all previous versions of ragecoop, re
# 🦆 Special thanks to
- [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm)
- - For testing, ideas, contributions and the first modification with the API
- [crosire](https://github.com/crosire)
@@ -64,6 +79,7 @@ Please note that this is incompatible with all previous versions of ragecoop, re
- - For the extensive work in LemonUI
# 📝 License
This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOOP-V/blob/main/LICENSE)
[downloads-shield]: https://img.shields.io/github/downloads/RAGECOOP/RAGECOOP-V/total?style=for-the-badge
@@ -77,3 +93,4 @@ This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOO
[issues-shield]: https://img.shields.io/github/issues/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
[issues-url]: https://github.com/RAGECOOP/RAGECOOP-V/issues
+10
View File
@@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "RageCoop.C
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "RageCoop.Client\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client.Installer", "RageCoop.Client.Installer\RageCoop.Client.Installer.csproj", "{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -41,6 +43,14 @@ Global
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|Any CPU.Build.0 = Release|Any CPU
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.ActiveCfg = Release|Any CPU
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.Build.0 = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|x64.ActiveCfg = Debug|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|x64.Build.0 = Debug|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|Any CPU.Build.0 = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.ActiveCfg = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+9
View File
@@ -0,0 +1,9 @@
<Application x:Class="RageCoop.Client.Installer.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RageCoop.Client.Installer"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
+17
View File
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace RageCoop.Client.Installer
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}
+10
View File
@@ -0,0 +1,10 @@
using System.Windows;
[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
+15
View File
@@ -0,0 +1,15 @@
<Window x:Class="RageCoop.Client.Installer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RageCoop.Client.Installer"
mc:Ignorable="d"
Title="MainWindow" Height="376" Width="617" Background="#FFBABABA">
<Grid>
<Grid.Background>
<ImageBrush ImageSource="/bg.png" Opacity="0.2"/>
</Grid.Background>
<Label x:Name="Status" FontSize="20" Foreground="#FF232323" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
@@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 Path = System.IO.Path;
using MessageBox = System.Windows.MessageBox;
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
namespace RageCoop.Client.Installer
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Choose();
}
private void Choose()
{
var od = new OpenFileDialog()
{
Filter = "GTA 5 executable |GTA5.exe;PlayGTAV.exe",
Title="Select you GTAV executable"
};
if (od.ShowDialog() ?? false == true)
{
Task.Run(() => {
try
{
Install(Directory.GetParent(od.FileName).FullName);
}
catch (Exception ex)
{
MessageBox.Show("Installation failed: " + ex.ToString());
Environment.Exit(1);
}
});
}
else
{
Environment.Exit(0);
}
}
void Install(string root)
{
UpdateStatus("Checking requirements");
var shvPath = Path.Combine(root, "ScriptHookV.dll");
var shvdnPath = Path.Combine(root, "ScriptHookVDotNet3.dll");
var scriptsPath = Path.Combine(root, "Scripts");
var lemonPath = Path.Combine(scriptsPath, "LemonUI.SHVDN3.dll");
var installPath = Path.Combine(scriptsPath, "RageCoop");
if(Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName == installPath)
{
throw new InvalidOperationException("The installer is not meant to be run in the game folder, please extract the zip to somewhere else and run again.");
}
Directory.CreateDirectory(installPath);
if (!File.Exists(shvPath))
{
MessageBox.Show("Please install ScriptHookV first!");
Environment.Exit(1);
}
if (!File.Exists(shvdnPath))
{
MessageBox.Show("Please install ScriptHookVDotNet first!");
Environment.Exit(1);
}
var shvdnVer = GetVer(shvdnPath);
if (shvdnVer<new Version(3,5,1))
{
MessageBox.Show("Please update ScriptHookVDotNet to latest version!" +
$"\nCurrent version is {shvdnVer}, 3.5.1 or higher is required");
Environment.Exit(1);
}
if (File.Exists(lemonPath))
{
var lemonVer=GetVer(lemonPath);
if(lemonVer<new Version(1, 7))
{
UpdateStatus("Updating LemonUI");
File.WriteAllBytes(lemonPath,getLemon());
}
}
UpdateStatus("Removing old versions");
foreach (var f in Directory.GetFiles(scriptsPath, "RageCoop.*", SearchOption.AllDirectories))
{
if (f.EndsWith("RageCoop.Client.Settings.xml")) { continue; }
File.Delete(f);
}
foreach (var f in Directory.GetFiles(installPath, "*.dll", SearchOption.AllDirectories))
{
File.Delete(f);
}
if (File.Exists("RageCoop.Core.dll") && File.Exists("RageCoop.Client.dll"))
{
UpdateStatus("Installing...");
CoreUtils.CopyFilesRecursively(new DirectoryInfo(Directory.GetCurrentDirectory()), new DirectoryInfo(installPath));
Finish();
}
else
{
throw new Exception("Required files are missing, please re-download the zip from official website");
}
void Finish()
{
checkKeys:
UpdateStatus("Checking conflicts");
var menyooConfig = Path.Combine(root, @"menyooStuff\menyooConfig.ini");
var settingsPath = Path.Combine(installPath, @"Data\RageCoop.Client.Settings.xml");
Settings settings = null;
try
{
settings = Util.ReadSettings(settingsPath);
}
catch
{
settings = new Settings();
}
if (File.Exists(menyooConfig))
{
var lines = File.ReadAllLines(menyooConfig).Where(x => !x.StartsWith(";") && x.EndsWith(" = " +(int)settings.MenuKey));
if (lines.Any())
{
if(MessageBox.Show("Following menyoo config value will conflict with RAGECOOP menu key\n" +
string.Join("\n", lines)
+ "\nDo you wish to change the Menu Key?", "Warning!", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
var ae=new AutoResetEvent(false);
UpdateStatus("Press the key you wish to change to");
Dispatcher.BeginInvoke(new Action(() =>
KeyDown += (s,e) =>
{
settings.MenuKey = (Keys)KeyInterop.VirtualKeyFromKey(e.Key);
ae.Set();
}));
ae.WaitOne();
if (!Util.SaveSettings(settingsPath,settings))
{
MessageBox.Show("Error occurred when saving settings");
Environment.Exit(1);
}
MessageBox.Show("Menu key changed to "+settings.MenuKey);
goto checkKeys;
}
}
}
UpdateStatus("Checking ZeroTier");
try
{
ZeroTierHelper.Check();
}
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)
{
var url = "https://download.zerotier.com/dist/ZeroTier%20One.msi";
UpdateStatus("Downloading ZeroTier from "+url);
try
{
HttpHelper.DownloadFile(url, "ZeroTier.msi", (p) => UpdateStatus("Downloading ZeroTier " + p + "%"));
UpdateStatus("Installing ZeroTier");
Process.Start("ZeroTier.msi").WaitForExit();
}
catch
{
MessageBox.Show("Failed to download ZeroTier, please download it from officail website");
Process.Start(url);
}
}
}
UpdateStatus("Completed!");
MessageBox.Show("Installation sucessful!");
Environment.Exit(0);
}
}
void UpdateStatus(string status)
{
Dispatcher.BeginInvoke(new Action(() => Status.Content = status));
}
Version GetVer(string location)
{
return Version.Parse(FileVersionInfo.GetVersionInfo(location).FileVersion);
}
byte[] getLemon()
{
return (byte[])Resource.ResourceManager.GetObject("LemonUI_SHVDN3");
}
}
}
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<None Remove="bg.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RageCoop.Client\RageCoop.Client.csproj" />
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Resource Include="bg.png" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resource.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>
+73
View File
@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace RageCoop.Client.Installer {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RageCoop.Client.Installer.Resource", typeof(Resource).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] LemonUI_SHVDN3 {
get {
object obj = ResourceManager.GetObject("LemonUI_SHVDN3", resourceCulture);
return ((byte[])(obj));
}
}
}
}
+124
View File
@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="LemonUI_SHVDN3" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\LemonUI.SHVDN3.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

+17 -22
View File
@@ -129,7 +129,6 @@ namespace RageCoop.Client
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)
{
return;
@@ -138,6 +137,7 @@ namespace RageCoop.Client
{
#if !NON_INTERACTIVE
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
}
@@ -273,25 +273,6 @@ namespace RageCoop.Client
PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000 ? (currentTimestamp - 6000) : currentTimestamp;
}
}
else if (Game.IsControlJustPressed(GTA.Control.VehicleExit))
{
if (P.IsInVehicle())
{
P.Task.LeaveVehicle();
}
else if (P.IsTaskActive(TaskType.CTaskMoveGoToVehicleDoor))
{
P.Task.ClearAll();
}
else
{
var v = World.GetClosestVehicle(P.Position, 10);
if (v!=null)
{
P.Task.EnterVehicle(v, VehicleSeat.Driver, -1, 5, EnterVehicleFlags.AllowJacking);
}
}
}
else if (e.KeyCode==Settings.PassengerKey)
{
var P = Game.Player.Character;
@@ -309,7 +290,20 @@ namespace RageCoop.Client
if (V!=null)
{
var seat = P.GetNearestSeat(V);
P.Task.EnterVehicle(V, seat);
var p = V.GetPedOnSeat(seat);
if (p != null && !p.IsDead)
{
for(int i = -1; i < V.PassengerCapacity; i++)
{
seat = (VehicleSeat)i;
p = V.GetPedOnSeat(seat);
if (p == null || p.IsDead)
{
break;
}
}
}
P.Task.EnterVehicle(V, seat,-1,5,EnterVehicleFlags.None);
}
}
}
@@ -322,7 +316,8 @@ namespace RageCoop.Client
EntityPool.Cleanup();
PlayerList.Cleanup();
LocalPlayerID=default;
WorldThread.Traffic(true);
WorldThread.Traffic(!Settings.DisableTraffic);
Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, false);
}
private static void DoQueuedActions()
{
+5 -2
View File
@@ -18,6 +18,7 @@ namespace RageCoop.Client
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
public static NativeItem SimulatedLatencyItem = new NativeItem("Simulated network latency", "Simulated network latency in ms (one way)", "0");
public static NativeCheckboxItem ShowOwnerItem = new NativeCheckboxItem("Show entity owner", "Show the owner name of the entity you're aiming at", false);
private static readonly NativeCheckboxItem ShowNetworkInfoItem = new NativeCheckboxItem("Show Network Info", Networking.ShowNetworkInfo);
static DebugMenu()
@@ -43,9 +44,11 @@ namespace RageCoop.Client
}
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(); };
Menu.Add(SimulatedLatencyItem);
Menu.Add(ShowNetworkInfoItem);
Menu.Add(ShowNetworkInfoItem);
Menu.Add(ShowOwnerItem);
Menu.AddSubMenu(DiagnosticMenu);
}
+1 -1
View File
@@ -85,7 +85,7 @@ namespace RageCoop.Client.Menus
if (server.useZT)
{
address=$"{server.ztAddress}:{server.port}";
Main.QueueAction(() => { Notification.Show($"~y~Joining ZeroTier network... {server.ztID}"); });
Notification.Show($"~y~Joining ZeroTier network... {server.ztID}");
if (ZeroTierHelper.Join(server.ztID)==null)
{
throw new Exception("Failed to obtain ZeroTier network IP");
+2 -1
View File
@@ -16,7 +16,7 @@ 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 _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.DisableTraffic);
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 _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());
@@ -108,6 +108,7 @@ namespace RageCoop.Client.Menus
public static void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
{
WorldThread.Traffic(!_disableTrafficItem.Checked);
Main.Settings.DisableTraffic = _disableTrafficItem.Checked;
Util.SaveSettings();
}
+2
View File
@@ -71,6 +71,8 @@ namespace RageCoop.Client.Menus
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();
+4
View File
@@ -5,6 +5,7 @@ using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Threading;
using GTA.Native;
namespace RageCoop.Client
{
@@ -75,6 +76,8 @@ namespace RageCoop.Client
}
Main.QueueAction(() =>
{
WorldThread.Traffic(!Main.Settings.DisableTraffic);
Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, true);
CoopMenu.ConnectedMenuSetting();
Main.MainChat.Init();
if (Main.Settings.Voice && !Voice.WasInitialized())
@@ -335,6 +338,7 @@ namespace RageCoop.Client
c.Flags=packet.Flags;
c.Heading=packet.Heading;
c.Position = packet.Position;
c.LastSyncedStopWatch.Restart();
if (c.IsRagdoll)
{
c.HeadPosition=packet.HeadPosition;
+10 -9
View File
@@ -54,7 +54,7 @@ namespace RageCoop.Client
foreach (var player in Players.Values)
{
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Ping * 1000:N0}ms", player.Username, 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");
@@ -83,24 +83,24 @@ namespace RageCoop.Client
{
p._latencyToServer = packet.Latency;
p.Position = packet.Position;
p.IsHost= packet.IsHost;
Main.QueueAction(() =>
{
if (p.FakeBlip?.Exists()!=true)
{
p.FakeBlip=World.CreateBlip(p.Position);
}
if (p.Position.DistanceTo(Main.PlayerPosition)>500)
if (EntityPool.PedExists(p.PedID))
{
p.FakeBlip.Color=Scripting.API.Config.BlipColor;
p.FakeBlip.Scale=Scripting.API.Config.BlipScale;
p.FakeBlip.Sprite=Scripting.API.Config.BlipSprite;
p.FakeBlip.DisplayType=BlipDisplayType.Default;
p.FakeBlip.Position=p.Position;
p.FakeBlip.DisplayType = BlipDisplayType.NoDisplay;
}
else
{
p.FakeBlip.DisplayType=BlipDisplayType.NoDisplay;
p.FakeBlip.Color = Scripting.API.Config.BlipColor;
p.FakeBlip.Scale = Scripting.API.Config.BlipScale;
p.FakeBlip.Sprite = Scripting.API.Config.BlipSprite;
p.FakeBlip.DisplayType = BlipDisplayType.Default;
p.FakeBlip.Position = p.Position;
}
});
@@ -142,6 +142,7 @@ namespace RageCoop.Client
internal class Player
{
public byte HolePunchStatus { get; set; } = 1;
public bool IsHost;
public string Username { get; internal set; }
/// <summary>
/// Universal character ID.
+2 -2
View File
@@ -16,7 +16,7 @@ using System.Resources;
// Version informationr(
[assembly: AssemblyVersion("1.5.1.13")]
[assembly: AssemblyFileVersion("1.5.1.13")]
[assembly: AssemblyVersion("1.5.3.124")]
[assembly: AssemblyFileVersion("1.5.3.124")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]
+2 -2
View File
@@ -60,8 +60,8 @@
<Compile Include="Sync\Entities\Ped\SyncedPed.cs" />
<Compile Include="Sync\Entities\SyncedProjectile.cs" />
<Compile Include="Sync\Entities\SyncedProp.cs" />
<Compile Include="Sync\Entities\SyncedVehicle.cs" />
<Compile Include="Sync\Entities\SyncedVehicle.Members.cs" />
<Compile Include="Sync\Entities\Vehicle\SyncedVehicle.cs" />
<Compile Include="Sync\Entities\Vehicle\SyncedVehicle.Members.cs" />
<Compile Include="Sync\EntityPool.cs" />
<Compile Include="Sync\SyncEvents.cs" />
<Compile Include="Sync\Voice.cs" />
+10
View File
@@ -245,6 +245,7 @@ namespace RageCoop.Client.Scripting
Networking.ToggleConnection(null);
}
}
/// <summary>
/// Send a local chat message to this player
/// </summary>
@@ -255,6 +256,15 @@ namespace RageCoop.Client.Scripting
Main.MainChat.AddMessage(from, message);
}
/// <summary>
/// Send a chat message or command to server/other players
/// </summary>
/// <param name="message"></param>
public static void SendChatMessage(string message)
{
Networking.SendChatMessage(message);
}
/// <summary>
/// Queue an action to be executed on next tick.
/// </summary>
-7
View File
@@ -106,13 +106,6 @@ namespace RageCoop.Client.Scripting
}
LoadedResources.Clear();
}
private List<string> ToIgnore = new List<string>
{
"RageCoop.Client.dll",
"RageCoop.Core.dll",
"RageCoop.Server.dll",
"ScriptHookVDotNet3.dll"
};
private List<ClientResource> LoadedResources = new List<ClientResource>();
private string BaseScriptType;
public Logger Logger { get; set; }
+6 -1
View File
@@ -36,7 +36,7 @@ namespace RageCoop.Client
/// LogLevel for RageCoop.
/// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
/// </summary>
public int LogLevel = 2;
public int LogLevel = 00;
/// <summary>
/// The key to open menu
@@ -71,5 +71,10 @@ namespace RageCoop.Client
/// The directory where log and resources downloaded from server will be placed.
/// </summary>
public string DataDirectory { get; set; } = "Scripts\\RageCoop\\Data";
/// <summary>
/// Show the owner name of the entity you're aiming at
/// </summary>
public bool ShowEntityOwnerName { get; set; } = false;
}
}
+45 -31
View File
@@ -107,7 +107,7 @@ namespace RageCoop.Client
internal uint CurrentWeaponHash { get; set; }
private Dictionary<uint, bool> _lastWeaponComponents = null;
internal Dictionary<uint, bool> WeaponComponents { get; set; } = null;
private int _lastWeaponObj = 0;
private Entity _weaponObj;
#endregion
internal Vector3 AimCoords { get; set; }
@@ -115,7 +115,7 @@ namespace RageCoop.Client
internal override void Update()
{
if (Owner==null) { return; }
if (Owner==null) { OwnerID=OwnerID;return; }
if (IsPlayer)
{
RenderNameTag();
@@ -155,11 +155,7 @@ namespace RageCoop.Client
{
PedBlip=MainPed.AddBlip();
if (IsPlayer)
{
// Main.Logger.Debug("blip:"+Player.Username);
Main.QueueAction(() => { PedBlip.Name=Owner.Username; });
}
PedBlip.Color=BlipColor;
PedBlip.Sprite=BlipSprite;
PedBlip.Scale=BlipScale;
@@ -174,12 +170,18 @@ namespace RageCoop.Client
{
PedBlip.Sprite=BlipSprite;
}
if (IsPlayer)
{
PedBlip.Name = Owner.Username;
}
}
if (!Clothes.SequenceEqual(_lastClothes))
{
SetClothes();
}
CheckCurrentWeapon();
}
if (MainPed.IsDead)
@@ -207,13 +209,14 @@ namespace RageCoop.Client
return;
}
}
if (Speed>=4)
{
DisplayInVehicle();
}
else
{
if (MainPed.IsInVehicle()) { MainPed.Task.LeaveVehicle(LeaveVehicleFlags.WarpOut); }
if (MainPed.IsInVehicle()) { MainPed.Task.LeaveVehicle(LeaveVehicleFlags.WarpOut);return; }
DisplayOnFoot();
}
@@ -346,7 +349,6 @@ namespace RageCoop.Client
private void DisplayOnFoot()
{
CheckCurrentWeapon();
if (IsInParachuteFreeFall)
{
MainPed.PositionNoOffset = Vector3.Lerp(MainPed.ReadPosition(), Position + Velocity, 0.5f);
@@ -582,14 +584,12 @@ namespace RageCoop.Client
#region WEAPON
private void CheckCurrentWeapon()
{
if (!WeaponAsset.IsLoaded) { WeaponAsset.Request(); }
if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents))
if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents) || (Speed <=3 && _weaponObj?.IsVisible != true))
{
if (WeaponAsset!=null) { WeaponAsset.MarkAsNoLongerNeeded(); }
WeaponAsset=new WeaponAsset(CurrentWeaponHash);
MainPed.Weapons.RemoveAll();
_lastWeaponObj = Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0);
MainPed.Weapons.RemoveAll();
_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 (CurrentWeaponHash != (uint)WeaponHash.Unarmed)
{
if (WeaponComponents != null && WeaponComponents.Count != 0)
@@ -598,11 +598,11 @@ namespace RageCoop.Client
{
if (comp.Value)
{
Function.Call(Hash.GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, _lastWeaponObj, comp.Key);
Function.Call(Hash.GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, _weaponObj, comp.Key);
}
}
}
Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, _lastWeaponObj, MainPed.Handle);
Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, _weaponObj, MainPed.Handle);
}
_lastWeaponComponents = WeaponComponents;
}
@@ -634,7 +634,7 @@ namespace RageCoop.Client
{
MainPed.Task.ClearAll();
Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, MainPed, IsInStealthMode, 0);
Vector3 predictPosition = Position + (Position - MainPed.ReadPosition()) + Velocity * 0.5f;
Vector3 predictPosition = Predict(Position)+Velocity;
float range = predictPosition.DistanceToSquared(MainPed.ReadPosition());
switch (Speed)
@@ -684,19 +684,25 @@ namespace RageCoop.Client
private void SmoothTransition()
{
var localRagdoll = MainPed.IsRagdoll;
var dist = Position.DistanceTo(MainPed.ReadPosition());
if (dist>3)
var predicted = Predict(Position);
var dist = predicted.DistanceTo(MainPed.ReadPosition());
if (IsOff(dist))
{
MainPed.PositionNoOffset=Position;
MainPed.PositionNoOffset= predicted;
return;
}
if (!(localRagdoll || MainPed.IsDead))
{
if (!IsAiming && !MainPed.IsGettingUp)
{
MainPed.Heading=Heading;
var cur=MainPed.Heading;
var diff=Heading-cur;
if (diff > 180) { diff -= 360; }
else if (diff < -180) { diff += 360; }
MainPed.Heading=cur+diff/2;
}
MainPed.Velocity=Velocity+5*dist*(Position-MainPed.ReadPosition());
MainPed.Velocity = Velocity + 5 * dist * (predicted - MainPed.ReadPosition());
}
else if (Main.Ticked-_lastRagdollTime<10)
{
@@ -708,30 +714,39 @@ namespace RageCoop.Client
var head = MainPed.Bones[Bone.SkelHead];
var rightFoot = MainPed.Bones[Bone.SkelRightFoot];
var leftFoot = MainPed.Bones[Bone.SkelLeftFoot];
Vector3 amount;
// 20:head, 3:left foot, 6:right foot, 17:right hand,
amount= 20 * (Predict(HeadPosition) - head.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1;
helper.PartIndex=20;
helper.Impulse=20*(HeadPosition-head.Position);
helper.Impulse = amount;
helper.Start();
helper.Stop();
amount = 20 * (Predict(RightFootPosition) - rightFoot.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1;
helper.PartIndex=6;
helper.Impulse=20*(RightFootPosition-rightFoot.Position);
helper.Impulse = amount;
helper.Start();
helper.Stop();
amount = 20 * (Predict(LeftFootPosition) - leftFoot.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1;
helper.PartIndex=3;
helper.Impulse=20*(LeftFootPosition-leftFoot.Position);
helper.Impulse = amount;
helper.Start();
helper.Stop();
}
else
{
MainPed.Velocity=Velocity+5*dist*(Position-MainPed.ReadPosition());
// localRagdoll
var force = Velocity - MainPed.Velocity+5 * dist * (predicted - MainPed.ReadPosition());
if (force.Length() > 20) { force = force.Normalized*20; }
MainPed.ApplyForce(force);
}
}
@@ -741,11 +756,11 @@ namespace RageCoop.Client
#endregion
private void DisplayInVehicle()
{
if (CurrentVehicle==null || CurrentVehicle.MainVehicle==null) { Main.Logger.Error("Veh not found"); return; }
if (CurrentVehicle?.MainVehicle==null) { return; }
switch (Speed)
{
case 4:
if (MainPed.CurrentVehicle!=CurrentVehicle.MainVehicle)
if (MainPed.CurrentVehicle!=CurrentVehicle.MainVehicle || MainPed.SeatIndex != Seat || (!MainPed.IsSittingInVehicle() && !MainPed.IsBeingJacked))
{
MainPed.SetIntoVehicle(CurrentVehicle.MainVehicle, Seat);
}
@@ -757,7 +772,6 @@ namespace RageCoop.Client
if (MainPed.VehicleWeapon==VehicleWeaponHash.Invalid)
{
// World.DrawMarker(MarkerType.DebugSphere,AimCoords,default,default,new Vector3(0.2f,0.2f,0.2f),Color.AliceBlue);
CheckCurrentWeapon();
if (IsAiming)
{
Function.Call(Hash.SET_DRIVEBY_TASK_TARGET, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z);
@@ -36,6 +36,9 @@ namespace RageCoop.Client
if (value==_ownerID && Owner!=null) { return; }
_ownerID = value;
Owner=PlayerList.GetPlayer(value);
if(this is SyncedPed && Owner!=null){
Owner.Character=((SyncedPed)this);
}
}
}
@@ -86,10 +89,27 @@ namespace RageCoop.Client
internal Vector3 Rotation { get; set; }
internal Quaternion Quaternion { get; set; }
internal Vector3 Velocity { get; set; }
public Stopwatch LastSyncedStopWatch = new Stopwatch();
internal abstract void Update();
internal void PauseUpdate(ulong frames)
{
LastUpdated=Main.Ticked+frames;
}
protected Vector3 Predict(Vector3 input)
{
return (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Velocity + input;
}
private float _accumulatedOff=0;
protected bool IsOff(float thisOff, float tolerance=3 , float limit = 30)
{
_accumulatedOff += thisOff - tolerance;
if (_accumulatedOff < 0) { _accumulatedOff=0;}
else if (_accumulatedOff>=limit)
{
_accumulatedOff = 0;
return true;
}
return false;
}
}
}
@@ -9,7 +9,6 @@ using GTA.Native;
namespace RageCoop.Client{
public partial class SyncedVehicle{
public Vehicle MainVehicle { get; internal set; }
public Stopwatch LastSyncedStopWatch = new Stopwatch();
#region -- SYNC DATA --
@@ -84,7 +84,9 @@ namespace RageCoop.Client
return;
}
}
DisplayVehicle();
// Skip update if no new sync message has arrived.
if (!NeedUpdate)
{
@@ -97,8 +99,6 @@ namespace RageCoop.Client
}
MainVehicle.ThrottlePower=ThrottlePower;
MainVehicle.BrakePower=BrakePower;
var v = Main.P.CurrentVehicle;
DisplayVehicle(v != null && MainVehicle.IsTouching(v));
if (IsDead)
{
@@ -206,22 +206,23 @@ namespace RageCoop.Client
Function.Call(Hash._TRANSFORM_SUBMARINE_TO_VEHICLE, MainVehicle.Handle, false);
}
}
else if(IsDeluxo && IsDeluxoHovering!=MainVehicle.IsDeluxoHovering()){
else if(IsDeluxo)
{
MainVehicle.SetDeluxoHoverState(IsDeluxoHovering);
if(IsDeluxoHovering){
if (IsDeluxoHovering)
{
MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio);
}
}
Function.Call(Hash.SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn);
MainVehicle.SetDamageModel(DamageModel);
}
MainVehicle.LockStatus=LockStatus;
if (LastFullSynced>=LastUpdated)
{
#region -- SYNC STATE --
if (Flags.HasVehFlag(VehicleDataFlags.Repaired))
{
MainVehicle.Repair();
@@ -255,14 +256,13 @@ namespace RageCoop.Client
Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, Livery);
_lastLivery=Livery;
}
#endregion
MainVehicle.SetDamageModel(DamageModel);
}
LastUpdated=Main.Ticked;
}
void DisplayVehicle(bool touching)
void DisplayVehicle()
{
_elapsed = Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds;
_predictedPosition = Position + _elapsed * Velocity;
_predictedPosition = Predict(Position);
var current = MainVehicle.ReadPosition();
var dist = current.DistanceTo(_predictedPosition);
var cali = dist * (_predictedPosition - current);
@@ -274,40 +274,22 @@ namespace RageCoop.Client
MainVehicle.Quaternion = Quaternion;
return;
}
else if (dist > 0.03)
{
MainVehicle.Velocity = Velocity + cali;
}
MainVehicle.Velocity = Velocity+cali;
if (IsFlipped)
Vector3 calirot;
if (IsFlipped || (calirot = GetCalibrationRotation()).Length()>50)
{
MainVehicle.Quaternion = Quaternion.Slerp(MainVehicle.ReadQuaternion(), Quaternion, 0.5f);
MainVehicle.RotationVelocity = RotationVelocity;
return;
}
Vector3 calirot = GetCalibrationRotation();
if (calirot.Length() < 50)
else
{
MainVehicle.RotationVelocity = RotationVelocity + calirot * 0.2f;
}
else
{
MainVehicle.Quaternion = Quaternion;
MainVehicle.RotationVelocity = RotationVelocity;
}
#if DEBUG_VEH
if (_orgTrace.Count >= 30)
{
_orgTrace.RemoveAt(0);
}
if (_predictedTrace.Count >= 30)
{
_predictedTrace.RemoveAt(0);
}
_orgTrace.Add(Position);
_predictedTrace.Add(_predictedPos);
#endif
}
private Vector3 GetCalibrationRotation()
{
+2
View File
@@ -93,6 +93,8 @@ namespace RageCoop.Client
public static bool AddPlayer()
{
Ped p = Game.Player.Character;
// var clipset=p.Gender==Gender.Male? "MOVE_M@TOUGH_GUY@" : "MOVE_F@TOUGH_GUY@";
// Function.Call(Hash.SET_PED_MOVEMENT_CLIPSET,p,clipset,1f);
SyncedPed player = GetPedByID(Main.LocalPlayerID);
if (player==null)
{
+5
View File
@@ -155,6 +155,11 @@ namespace RageCoop.Client
}
}
if (ped.IsInvincible)
{
flags |= PedDataFlags.IsInvincible;
}
if (Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, ped))
{
flags |= PedDataFlags.IsInStealthMode;
+12 -12
View File
@@ -12,7 +12,9 @@ using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
namespace RageCoop.Client
{
internal static class Util
@@ -112,11 +114,11 @@ namespace RageCoop.Client
#endregion
public static string SettingsPath = "Scripts\\RageCoop\\Data\\RageCoop.Client.Settings.xml";
public static Settings ReadSettings()
public static Settings ReadSettings(string path=null)
{
path = path ?? SettingsPath;
XmlSerializer ser = new XmlSerializer(typeof(Settings));
string path = SettingsPath;
Directory.CreateDirectory(Directory.GetParent(path).FullName);
Settings settings = null;
@@ -124,12 +126,7 @@ namespace RageCoop.Client
{
using (FileStream stream = File.OpenRead(path))
{
settings = (RageCoop.Client.Settings)ser.Deserialize(stream);
}
using (FileStream stream = new FileStream(path, FileMode.Truncate, FileAccess.ReadWrite))
{
ser.Serialize(stream, settings);
settings = (Settings)ser.Deserialize(stream);
}
}
else
@@ -142,22 +139,25 @@ namespace RageCoop.Client
return settings;
}
public static void SaveSettings()
public static bool SaveSettings(string path = null,Settings settings=null)
{
try
{
string path = SettingsPath;
path = path ?? SettingsPath;
settings = settings ?? Main.Settings;
Directory.CreateDirectory(Directory.GetParent(path).FullName);
using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite))
{
XmlSerializer ser = new XmlSerializer(typeof(Settings));
ser.Serialize(stream, Main.Settings);
ser.Serialize(stream, settings);
}
return true;
}
catch (Exception ex)
{
GTA.UI.Notification.Show("Error saving player settings: " + ex.Message);
return false;
// GTA.UI.Notification.Show("Error saving player settings: " + ex.Message);
}
}
+110 -64
View File
@@ -1,6 +1,8 @@
using GTA;
using GTA.Native;
using System;
using System.Threading.Tasks;
using System.Threading;
namespace RageCoop.Client
{
@@ -9,7 +11,6 @@ namespace RageCoop.Client
/// </summary>
public class WorldThread : Script
{
private static bool _lastDisableTraffic = false;
/// <summary>
/// Don't use it!
@@ -19,43 +20,22 @@ namespace RageCoop.Client
Tick += OnTick;
Aborted += (sender, e) =>
{
if (_lastDisableTraffic)
{
Traffic(true);
}
ChangeTraffic(true);
};
Task.Run(() =>
{
while (true)
{
Thread.Sleep(5000);
Main.QueueAction(() => { ChangeTraffic(_trafficEnabled); });
}
});
}
static bool _trafficEnabled;
private void OnTick(object sender, EventArgs e)
{
if (Game.IsLoading || !Networking.IsOnServer)
if (!_trafficEnabled)
{
return;
}
Game.DisableControlThisFrame(Control.FrontendPause);
Game.DisableControlThisFrame(Control.VehicleExit);
Game.DisableControlThisFrame(Control.Enter);
if (Main.Settings.DisableAlternatePause)
{
Game.DisableControlThisFrame(Control.FrontendPauseAlternate);
}
var P = Game.Player.Character;
// Sets a value that determines how aggressive the ocean waves will be.
// Values of 2.0 or more make for very aggressive waves like you see during a thunderstorm.
Function.Call(Hash.SET_DEEP_OCEAN_SCALER, 0.0f); // Works only ~200 meters around the player
// Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, Game.Player.Character.Handle, true, false);
if (Main.Settings==null) { return; }
if (Main.Settings.DisableTraffic)
{
if (!_lastDisableTraffic)
{
Traffic(false);
}
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);
@@ -64,25 +44,63 @@ namespace RageCoop.Client
Function.Call(Hash.SUPPRESS_SHOCKING_EVENTS_NEXT_FRAME);
Function.Call(Hash.SUPPRESS_AGITATION_EVENTS_NEXT_FRAME);
}
else if (_lastDisableTraffic)
if (Game.IsLoading || !Networking.IsOnServer)
{
Traffic(true);
return;
}
_lastDisableTraffic = Main.Settings.DisableTraffic;
}
Game.DisableControlThisFrame(Control.FrontendPause);
if (Main.Settings.DisableAlternatePause)
{
Game.DisableControlThisFrame(Control.FrontendPauseAlternate);
}
// Sets a value that determines how aggressive the ocean waves will be.
// Values of 2.0 or more make for very aggressive waves like you see during a thunderstorm.
Function.Call(Hash.SET_DEEP_OCEAN_SCALER, 0.0f); // Works only ~200 meters around the player
if (Main.Settings.ShowEntityOwnerName)
{
unsafe
{
int handle;
if (Function.Call<bool>(Hash.GET_ENTITY_PLAYER_IS_FREE_AIMING_AT, 0, &handle))
{
var entity = Entity.FromHandle(handle);
if (entity != null)
{
var owner = "invalid";
if (entity.EntityType == EntityType.Vehicle)
{
owner = (entity as Vehicle).GetSyncEntity()?.Owner?.Username ?? "unknown";
}
if (entity.EntityType == EntityType.Ped)
{
owner = (entity as Ped).GetSyncEntity()?.Owner?.Username ?? "unknown";
}
GTA.UI.Screen.ShowHelpTextThisFrame("Entity owner: " + owner);
}
}
}
}
}
public static void Traffic(bool enable)
{
ChangeTraffic(enable);
_trafficEnabled = enable;
}
private static void ChangeTraffic(bool 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_RANDOM_TRAINS, true);
Function.Call(Hash.SET_RANDOM_BOATS, true);
Function.Call(Hash.SET_GARBAGE_TRUCKS, true);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 1); // 0 - 3
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 1); // 0 - 3
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 3); // 0 - 3
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 3); // 0 - 3
Function.Call(Hash.SET_ALL_VEHICLE_GENERATORS_ACTIVE);
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, true);
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, -1);
@@ -91,49 +109,77 @@ namespace RageCoop.Client
}
else
{
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_RANDOM_TRAINS, false);
Function.Call(Hash.SET_RANDOM_BOATS, false);
Function.Call(Hash.SET_GARBAGE_TRUCKS, false);
Function.Call(Hash.DELETE_ALL_TRAINS);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 3);
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 3);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, false);
Function.Call(Hash.SET_FAR_DRAW_VEHICLES, false);
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, 0);
Function.Call(Hash.SET_DISTANT_CARS_ENABLED, false);
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, true);
foreach (Ped ped in World.GetAllPeds())
if (Networking.IsOnServer)
{
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");
ped.CurrentVehicle?.Delete();
ped.Kill();
ped.Delete();
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))
{
if (ped.Handle == Game.Player.Character.Handle) { continue; }
// Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
ped.CurrentVehicle?.Delete();
ped.Kill();
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();
}
}
}
foreach (Vehicle veh in World.GetAllVehicles())
else
{
SyncedVehicle v = veh.GetSyncEntity();
if (v.MainVehicle==Game.Player.LastVehicle)
foreach (Ped ped in World.GetAllPeds())
{
// 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");
if ((ped != Game.Player.Character) && (ped.PopulationType != EntityPopulationType.Mission))
{
// Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
ped.CurrentVehicle?.Delete();
ped.Kill();
ped.Delete();
}
veh.Delete();
}
var last = Game.Player.Character.LastVehicle;
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");
veh.Delete();
}
}
}
}
+16 -2
View File
@@ -15,6 +15,8 @@ using Newtonsoft.Json;
[assembly: InternalsVisibleTo("RageCoop.Server")]
[assembly: InternalsVisibleTo("RageCoop.Client")]
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
[assembly: InternalsVisibleTo("RageCoop.ResourceBuilder")]
namespace RageCoop.Core
{
internal static class CoreUtils
@@ -181,10 +183,16 @@ namespace RageCoop.Core
return JsonConvert.DeserializeObject<IpInfo>(content);
}
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
foreach (DirectoryInfo dir in source.GetDirectories())
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
foreach (FileInfo file in source.GetFiles())
file.CopyTo(Path.Combine(target.FullName, file.Name), true);
}
}
internal struct IpInfo
internal class IpInfo
{
[JsonProperty("ip")]
public string Address { get; set; }
@@ -411,6 +419,12 @@ namespace RageCoop.Core
return memoryStream.ToArray();
}
}
public static MemoryStream ToMemStream(this Stream stream)
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return memoryStream;
}
public static byte[] Join(this List<byte[]> arrays,int lengthPerArray=-1)
{
if (arrays.Count==1) { return arrays[0]; }
+42
View File
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Threading;
using System.IO;
namespace RageCoop.Core
{
internal static class HttpHelper
{
public static void DownloadFile(string url,string destination,Action<int> progressCallback)
{
if (File.Exists(destination)) { File.Delete(destination); }
AutoResetEvent ae=new AutoResetEvent(false);
WebClient client = new WebClient();
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
client.DownloadProgressChanged += (s, e1) => progressCallback?.Invoke(e1.ProgressPercentage);
client.DownloadFileCompleted += (s, e2) =>
{
ae.Set();
};
client.DownloadFileAsync(new Uri(url), destination);
ae.WaitOne();
}
public static string DownloadString(string url)
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
WebClient client = new WebClient();
return client.DownloadString(url);
}
}
}
@@ -131,5 +131,9 @@ namespace RageCoop.Core
var p = Run(command);
return p.StandardOutput.ReadToEnd()+p.StandardError.ReadToEnd();
}
public static void Check()
{
}
}
}
+1
View File
@@ -98,6 +98,7 @@ namespace RageCoop.Core
IsInLowCover = 1 << 11,
IsInCoverFacingLeft = 1 << 12,
IsBlindFiring = 1 << 13,
IsInvincible = 1 << 14,
IsFullSync = 1<<15 ,
}
+5
View File
@@ -202,6 +202,7 @@ namespace RageCoop.Core
public string Username { get; set; }
public float Latency { get; set; }
public Vector3 Position { get; set; }
public bool IsHost;
public override byte[] Serialize()
{
@@ -218,6 +219,8 @@ namespace RageCoop.Core
byteArray.AddVector3(Position);
byteArray.AddBool(IsHost);
return byteArray.ToArray();
}
@@ -234,6 +237,8 @@ namespace RageCoop.Core
Latency=reader.ReadSingle();
Position=reader.ReadVector3();
IsHost=reader.ReadBoolean();
}
}
+5
View File
@@ -20,6 +20,11 @@ namespace RageCoop.Server
{
Server=server;
}
/// <summary>
/// Gets the total number of entities owned by this client
/// </summary>
public int EntitiesCount { get; internal set; }
/// <summary>
/// Th client's IP address and port.
/// </summary>
@@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidgren.Network;
using RageCoop.Core;
using System.Net;
using System.Net.Http;
using Newtonsoft.Json;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using System.Diagnostics;
namespace RageCoop.Server
{
public partial class Server
{
const string _versionURL = "https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/main/RageCoop.Server/Properties/AssemblyInfo.cs";
private void SendPlayerUpdate()
{
foreach (var c in ClientsByNetHandle.Values.ToArray())
{
try
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID = c.Player.ID,
Username = c.Username,
Latency = c.Latency,
Position = c.Player.Position,
IsHost = c == _hostClient
}.Pack(outgoingMessage);
MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
}
catch (Exception ex)
{
Logger?.Error(ex);
}
}
}
private IpInfo IpInfo = null;
private void Announce()
{
HttpResponseMessage response = null;
HttpClient httpClient = new();
if (IpInfo == null)
{
try
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
try
{
IpInfo = CoreUtils.GetIPInfo();
Logger?.Info($"Your public IP is {IpInfo.Address}, announcing to master server...");
}
catch (Exception ex)
{
Logger?.Error(ex.InnerException?.Message ?? ex.Message);
return;
}
}
catch (HttpRequestException ex)
{
Logger?.Error($"MasterServer: {ex.InnerException.Message}");
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
}
}
try
{
Security.GetPublicKey(out var pModulus, out var pExpoenet);
var serverInfo = new ServerInfo
{
address = IpInfo.Address,
port = Settings.Port.ToString(),
country = IpInfo.Country,
name = Settings.Name,
version = Version.ToString(),
players = MainNetServer.ConnectionsCount.ToString(),
maxPlayers = Settings.MaxPlayers.ToString(),
description = Settings.Description,
website = Settings.Website,
gameMode = Settings.GameMode,
language = Settings.Language,
useP2P = Settings.UseP2P,
useZT = Settings.UseZeroTier,
ztID = Settings.UseZeroTier ? Settings.ZeroTierNetworkID : "",
ztAddress = Settings.UseZeroTier ? ZeroTierHelper.Networks[Settings.ZeroTierNetworkID].Addresses.Where(x => !x.Contains(":")).First() : "0.0.0.0",
publicKeyModulus = Convert.ToBase64String(pModulus),
publicKeyExponent = Convert.ToBase64String(pExpoenet)
};
string msg = JsonConvert.SerializeObject(serverInfo);
var realUrl = Util.GetFinalRedirect(Settings.MasterServer);
response = httpClient.PostAsync(realUrl, new StringContent(msg, Encoding.UTF8, "application/json")).GetAwaiter().GetResult();
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
return;
}
if (response == null)
{
Logger?.Error("MasterServer: Something went wrong!");
}
else if (response.StatusCode != HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
{
string requestContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Logger?.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}");
}
else
{
Logger?.Error($"MasterServer: [{(int)response.StatusCode}]");
Logger?.Error($"MasterServer: [{response.Content.ReadAsStringAsync().GetAwaiter().GetResult()}]");
}
}
}
private void CheckUpdate()
{
try
{
var versionLine = HttpHelper.DownloadString(_versionURL).Split('\n', StringSplitOptions.RemoveEmptyEntries).Where(x => x.Contains("[assembly: AssemblyVersion(")).First();
var start = versionLine.IndexOf('\"') + 1;
var end = versionLine.LastIndexOf('\"');
var latest = Version.Parse(versionLine.AsSpan(start, end - start));
if (latest <= Version) { return; }
// wait ten minutes for the build to complete
API.SendChatMessage($"New server version found: {latest}, server will update in 10 minutes");
Thread.Sleep(10 * 60 * 1000);
API.SendChatMessage("downloading update...");
var downloadURL = $"https://github.com/RAGECOOP/RAGECOOP-V/releases/download/nightly/RageCoop.Server-{GetRID()}.zip";
if (Directory.Exists("Update")) { Directory.Delete("Update", true); }
HttpHelper.DownloadFile(downloadURL, "Update.zip", null);
Logger?.Info("Installing update");
Directory.CreateDirectory("Update");
new FastZip().ExtractZip("Update.zip", "Update", FastZip.Overwrite.Always, null, null, null, true);
MainNetServer.Shutdown("Server updating");
Logger.Info("Server shutting down!");
Logger.Flush();
Process.Start(Path.Combine("Update", RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "RageCoop.Server.exe": "RageCoop.Server"), "update \"" + AppDomain.CurrentDomain.BaseDirectory[0..^1] + "\"");
Environment.Exit(0);
}
catch(Exception 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()
{
foreach(var c in ClientsByNetHandle.Values.ToArray())
{
if (c.EntitiesCount > 100 && Settings.KickSpamming)
{
c.Kick("Bye bye asshole: spamming");
API.SendChatMessage($"Asshole {c.Username} was kicked: Spamming");
}
else if (Settings.KickGodMode && c.Player.IsInvincible)
{
c.Kick("Bye bye asshole: godmode");
API.SendChatMessage($"Asshole {c.Username} was kicked: GodMode");
}
}
}
}
}
@@ -0,0 +1,251 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
namespace RageCoop.Server
{
public partial class Server
{
private void Listen()
{
NetIncomingMessage msg = null;
while (!_stopping)
{
try
{
msg = MainNetServer.WaitMessage(200);
ProcessMessage(msg);
}
catch (Exception ex)
{
Logger?.Error("Error processing message");
Logger?.Error(ex);
if (msg != null)
{
DisconnectAndLog(msg.SenderConnection, PacketType.Unknown, ex);
}
}
}
Logger?.Info("Server is shutting down!");
MainNetServer.Shutdown("Server is shutting down!");
BaseScript.OnStop();
Resources.UnloadAll();
}
private void ProcessMessage(NetIncomingMessage message)
{
Client sender;
if (message == null) { return; }
switch (message.MessageType)
{
case NetIncomingMessageType.ConnectionApproval:
{
Logger?.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]");
if (message.ReadByte() != (byte)PacketType.Handshake)
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!");
message.SenderConnection.Deny("Wrong packet!");
}
else
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
GetHandshake(message.SenderConnection, data.GetPacket<Packets.Handshake>());
}
catch (Exception e)
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}");
Logger?.Error(e);
message.SenderConnection.Deny(e.Message);
}
}
break;
}
case NetIncomingMessageType.StatusChanged:
{
// Get sender client
if (!ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
break;
}
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
if (status == NetConnectionStatus.Disconnected)
{
PlayerDisconnected(sender);
}
else if (status == NetConnectionStatus.Connected)
{
PlayerConnected(sender);
_worker.QueueJob(() => API.Events.InvokePlayerConnected(sender));
Resources.SendTo(sender);
}
break;
}
case NetIncomingMessageType.Data:
{
// Get sender client
if (ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
// Get packet type
var type = (PacketType)message.ReadByte();
switch (type)
{
case PacketType.Response:
{
int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback))
{
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32()));
PendingResponses.Remove(id);
}
break;
}
case PacketType.Request:
{
int id = message.ReadInt32();
if (RequestHandlers.TryGetValue((PacketType)message.ReadByte(), out var handler))
{
var response = MainNetServer.CreateMessage();
response.Write((byte)PacketType.Response);
response.Write(id);
handler(message.ReadBytes(message.ReadInt32()), sender).Pack(response);
MainNetServer.SendMessage(response, message.SenderConnection, NetDeliveryMethod.ReliableOrdered);
}
break;
}
default:
{
byte[] data = message.ReadBytes(message.ReadInt32());
if (type.IsSyncEvent())
{
// Sync Events
if (Settings.UseP2P) { break; }
try
{
var toSend = MainNetServer.Connections.Exclude(message.SenderConnection);
if (toSend.Count != 0)
{
var outgoingMessage = MainNetServer.CreateMessage();
outgoingMessage.Write((byte)type);
outgoingMessage.Write(data.Length);
outgoingMessage.Write(data);
MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents);
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
else
{
HandlePacket(type, data, sender);
}
break;
}
}
}
break;
}
case NetIncomingMessageType.ErrorMessage:
Logger?.Error(message.ReadString());
break;
case NetIncomingMessageType.WarningMessage:
Logger?.Warning(message.ReadString());
break;
case NetIncomingMessageType.DebugMessage:
case NetIncomingMessageType.VerboseDebugMessage:
Logger?.Debug(message.ReadString());
break;
case NetIncomingMessageType.UnconnectedData:
{
if (message.ReadByte() == (byte)PacketType.PublicKeyRequest)
{
var msg = MainNetServer.CreateMessage();
var p = new Packets.PublicKeyResponse();
Security.GetPublicKey(out p.Modulus, out p.Exponent);
p.Pack(msg);
Logger?.Debug($"Sending public key to {message.SenderEndPoint}, length:{msg.LengthBytes}");
MainNetServer.SendUnconnectedMessage(msg, message.SenderEndPoint);
}
}
break;
default:
Logger?.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel));
break;
}
MainNetServer.Recycle(message);
}
private void HandlePacket(PacketType type, byte[] data, Client sender)
{
try
{
switch (type)
{
case PacketType.PedSync:
PedSync(data.GetPacket<Packets.PedSync>(), sender);
break;
case PacketType.VehicleSync:
VehicleSync(data.GetPacket<Packets.VehicleSync>(), sender);
break;
case PacketType.ProjectileSync:
ProjectileSync(data.GetPacket<Packets.ProjectileSync>(), sender);
break;
case PacketType.ChatMessage:
{
Packets.ChatMessage packet = new((b) =>
{
return Security.Decrypt(b, sender.EndPoint);
});
packet.Deserialize(data);
ChatMessageReceived(packet.Username, packet.Message, sender);
}
break;
case PacketType.Voice:
{
if (Settings.UseVoice && !Settings.UseP2P)
{
Forward(data.GetPacket<Packets.Voice>(), sender, ConnectionChannel.Voice);
}
}
break;
case PacketType.CustomEvent:
{
Packets.CustomEvent packet = new Packets.CustomEvent();
packet.Deserialize(data);
_worker.QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender));
}
break;
default:
Logger?.Error("Unhandled Data / Packet type");
break;
}
}
catch (Exception e)
{
DisconnectAndLog(sender.Connection, type, e);
}
}
}
}
+57 -383
View File
@@ -14,6 +14,7 @@ using Lidgren.Network;
using System.Timers;
using System.Security.Cryptography;
using RageCoop.Server.Scripting;
using Timer = System.Timers.Timer;
using System.Net.Sockets;
using System.Threading.Tasks;
using RageCoop.Core.Scripting;
@@ -44,13 +45,15 @@ namespace RageCoop.Server
private Dictionary<int,FileTransfer> InProgressFileTransfers=new();
internal Resources Resources;
internal Logger Logger;
private Security Security;
internal Security Security;
private bool _stopping = false;
private Thread _listenerThread;
private Thread _announceThread;
private Thread _latencyThread;
private Worker _worker;
private HashSet<char> _allowedCharacterSet;
private readonly Thread _listenerThread;
private readonly Timer _announceTimer = new();
private readonly Timer _playerUpdateTimer = new();
private readonly Timer _antiAssholesTimer = new();
private readonly Timer _updateTimer = new();
private readonly Worker _worker;
private readonly HashSet<char> _allowedCharacterSet;
private Dictionary<int,Action<PacketType,byte[]>> PendingResponses=new();
internal Dictionary<PacketType, Func<byte[],Client,Packet>> RequestHandlers=new();
/// <summary>
@@ -80,134 +83,32 @@ namespace RageCoop.Server
_worker=new Worker("ServerWorker", Logger);
_listenerThread=new Thread(() => Listen());
_latencyThread=new Thread(() =>
_announceTimer.Interval = 1;
_announceTimer.Elapsed += (s, e) =>
{
while (!_stopping)
{
foreach(var c in ClientsByNetHandle.Values.ToArray())
{
try
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID=c.Player.ID,
Username=c.Username,
Latency=c.Latency,
Position=c.Player.Position
}.Pack(outgoingMessage);
MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
}
catch(Exception ex)
{
Logger?.Error(ex);
}
}
Thread.Sleep(1000);
}
});
_announceThread=new Thread(async () =>
_announceTimer.Interval = 10000;
_announceTimer.Stop();
Announce();
_announceTimer.Start();
};
_playerUpdateTimer.Interval = 1000;
_playerUpdateTimer.Elapsed += (s, e) => SendPlayerUpdate();
_antiAssholesTimer.Interval = 5000;
_antiAssholesTimer.Elapsed += (s, e) => KickAssholes();
_updateTimer.Interval = 1;
_updateTimer.Elapsed += (s, e) =>
{
try
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
HttpClient httpClient = new();
IpInfo info;
try
{
info = CoreUtils.GetIPInfo();
Logger?.Info($"Your public IP is {info.Address}, announcing to master server...");
}
catch (Exception ex)
{
Logger?.Error(ex.InnerException?.Message ?? ex.Message);
return;
}
while (!_stopping)
{
HttpResponseMessage response = null;
try
{
Security.GetPublicKey(out var pModulus,out var pExpoenet);
var serverInfo = new ServerInfo
{
address = info.Address,
port=Settings.Port.ToString(),
country=info.Country,
name=Settings.Name,
version=Version.ToString(),
players=MainNetServer.ConnectionsCount.ToString(),
maxPlayers=Settings.MaxPlayers.ToString(),
description=Settings.Description,
website=Settings.Website,
gameMode=Settings.GameMode,
language=Settings.Language,
useP2P=Settings.UseP2P,
useZT=Settings.UseZeroTier,
ztID=Settings.UseZeroTier ? Settings.ZeroTierNetworkID : "",
ztAddress=Settings.UseZeroTier ? ZeroTierHelper.Networks[Settings.ZeroTierNetworkID].Addresses.Where(x => !x.Contains(":")).First() : "0.0.0.0",
publicKeyModulus=Convert.ToBase64String(pModulus),
publicKeyExponent=Convert.ToBase64String(pExpoenet)
};
string msg = JsonConvert.SerializeObject(serverInfo);
var realUrl = Util.GetFinalRedirect(Settings.MasterServer);
response = await httpClient.PostAsync(realUrl, new StringContent(msg, Encoding.UTF8, "application/json"));
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
// Sleep for 5s
Thread.Sleep(5000);
continue;
}
if (response == null)
{
Logger?.Error("MasterServer: Something went wrong!");
}
else if (response.StatusCode != HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
{
string requestContent = await response.Content.ReadAsStringAsync();
Logger?.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}");
}
else
{
Logger?.Error($"MasterServer: [{(int)response.StatusCode}]");
Logger?.Error($"MasterServer: [{await response.Content.ReadAsStringAsync()}]");
}
}
// Sleep for 10s
for (int i = 0; i<10; i++)
{
if (_stopping)
{
break;
}
else
{
Thread.Sleep(1000);
}
}
}
}
catch (HttpRequestException ex)
{
Logger?.Error($"MasterServer: {ex.InnerException.Message}");
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
}
});
_updateTimer.Interval= 1000 * 60 * 10; // 10 minutes
_updateTimer.Stop();
CheckUpdate();
_updateTimer.Start();
};
}
@@ -256,11 +157,17 @@ namespace RageCoop.Server
BaseScript.OnStart();
Resources.LoadAll();
_listenerThread.Start();
_latencyThread.Start();
_playerUpdateTimer.Enabled=true;
if (Settings.AnnounceSelf)
{
_announceThread.Start();
_announceTimer.Enabled=true;
}
if (Settings.AutoUpdate)
{
_updateTimer.Enabled = true;
}
_antiAssholesTimer.Enabled = true;
Logger?.Info("Listening for clients");
}
@@ -269,254 +176,18 @@ namespace RageCoop.Server
/// </summary>
public void Stop()
{
_stopping = true;
Logger?.Flush();
Logger?.Dispose();
_stopping = true;
_listenerThread.Join();
_latencyThread.Join();
if (_announceThread.IsAlive)
{
_announceThread.Join();
}
_playerUpdateTimer.Enabled = false;
_announceTimer.Enabled = false;
_worker.Dispose();
}
private void Listen()
{
NetIncomingMessage msg=null;
while (!_stopping)
{
try
{
msg=MainNetServer.WaitMessage(200);
ProcessMessage(msg);
}
catch(Exception ex)
{
Logger?.Error("Error processing message");
Logger?.Error(ex);
if (msg!=null)
{
DisconnectAndLog(msg.SenderConnection, PacketType.Unknown, ex);
}
}
}
Logger?.Info("Server is shutting down!");
MainNetServer.Shutdown("Server is shutting down!");
BaseScript.OnStop();
Resources.UnloadAll();
}
private void ProcessMessage(NetIncomingMessage message)
{
Client sender;
if (message == null) { return; }
switch (message.MessageType)
{
case NetIncomingMessageType.ConnectionApproval:
{
Logger?.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]");
if (message.ReadByte() != (byte)PacketType.Handshake)
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!");
message.SenderConnection.Deny("Wrong packet!");
}
else
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
GetHandshake(message.SenderConnection, data.GetPacket<Packets.Handshake>());
}
catch (Exception e)
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}");
Logger?.Error(e);
message.SenderConnection.Deny(e.Message);
}
}
break;
}
case NetIncomingMessageType.StatusChanged:
{
// Get sender client
if (!ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
break;
}
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
if (status == NetConnectionStatus.Disconnected)
{
PlayerDisconnected(sender);
}
else if (status == NetConnectionStatus.Connected)
{
PlayerConnected(sender);
_worker.QueueJob(() => API.Events.InvokePlayerConnected(sender));
Resources.SendTo(sender);
}
break;
}
case NetIncomingMessageType.Data:
{
// Get sender client
if (ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
// Get packet type
var type = (PacketType)message.ReadByte();
switch (type)
{
case PacketType.Response:
{
int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback))
{
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32()));
PendingResponses.Remove(id);
}
break;
}
case PacketType.Request:
{
int id = message.ReadInt32();
if (RequestHandlers.TryGetValue((PacketType)message.ReadByte(), out var handler))
{
var response=MainNetServer.CreateMessage();
response.Write((byte)PacketType.Response);
response.Write(id);
handler(message.ReadBytes(message.ReadInt32()),sender).Pack(response);
MainNetServer.SendMessage(response,message.SenderConnection,NetDeliveryMethod.ReliableOrdered);
}
break;
}
default:
{
byte[] data = message.ReadBytes(message.ReadInt32());
if (type.IsSyncEvent())
{
// Sync Events
if (Settings.UseP2P) { break; }
try
{
var toSend = MainNetServer.Connections.Exclude(message.SenderConnection);
if (toSend.Count!=0)
{
var outgoingMessage = MainNetServer.CreateMessage();
outgoingMessage.Write((byte)type);
outgoingMessage.Write(data.Length);
outgoingMessage.Write(data);
MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents);
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
else
{
HandlePacket(type, data, sender);
}
break;
}
}
}
break;
}
case NetIncomingMessageType.ErrorMessage:
Logger?.Error(message.ReadString());
break;
case NetIncomingMessageType.WarningMessage:
Logger?.Warning(message.ReadString());
break;
case NetIncomingMessageType.DebugMessage:
case NetIncomingMessageType.VerboseDebugMessage:
Logger?.Debug(message.ReadString());
break;
case NetIncomingMessageType.UnconnectedData:
{
if (message.ReadByte()==(byte)PacketType.PublicKeyRequest)
{
var msg = MainNetServer.CreateMessage();
var p=new Packets.PublicKeyResponse();
Security.GetPublicKey(out p.Modulus,out p.Exponent);
p.Pack(msg);
Logger?.Debug($"Sending public key to {message.SenderEndPoint}, length:{msg.LengthBytes}");
MainNetServer.SendUnconnectedMessage(msg, message.SenderEndPoint);
}
}
break;
default:
Logger?.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel));
break;
}
MainNetServer.Recycle(message);
}
internal void QueueJob(Action job)
{
_worker.QueueJob(job);
}
private void HandlePacket(PacketType type,byte[] data,Client sender)
{
try
{
switch (type)
{
case PacketType.PedSync:
PedSync(data.GetPacket<Packets.PedSync>(), sender);
break;
case PacketType.VehicleSync:
VehicleSync(data.GetPacket<Packets.VehicleSync>(), sender);
break;
case PacketType.ProjectileSync:
ProjectileSync(data.GetPacket<Packets.ProjectileSync>(), sender);
break;
case PacketType.ChatMessage:
{
Packets.ChatMessage packet = new((b) =>
{
return Security.Decrypt(b,sender.EndPoint);
});
packet.Deserialize(data);
ChatMessageReceived(packet.Username,packet.Message, sender);
}
break;
case PacketType.Voice:
{
if (Settings.UseVoice)
{
Forward(data.GetPacket<Packets.Voice>(), sender, ConnectionChannel.Voice);
}
}
break;
case PacketType.CustomEvent:
{
Packets.CustomEvent packet = new Packets.CustomEvent();
packet.Deserialize(data);
_worker.QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender));
}
break;
default:
Logger?.Error("Unhandled Data / Packet type");
break;
}
}
catch (Exception e)
{
DisconnectAndLog(sender.Connection, type, e);
}
}
// Send a message to targets or all players
internal void ChatMessageReceived(string name, string message,Client sender=null)
@@ -546,8 +217,6 @@ namespace RageCoop.Server
}.Pack(msg);
MainNetServer.SendMessage(msg,c.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat);
}
Logger?.Info(name + ": " + message);
}
internal void SendChatMessage(string name, string message, Client target)
{
@@ -630,14 +299,19 @@ namespace RageCoop.Server
}
internal void SendFile(string path,string name,Client client,Action<float> updateCallback=null)
{
SendFile(File.OpenRead(path), name,client,NewFileID(),updateCallback);
var fs = File.OpenRead(path);
SendFile(fs, name,client,NewFileID(),updateCallback);
fs.Close();
fs.Dispose();
}
internal void SendFile(Stream stream, string name, Client client,int id=default, Action<float> updateCallback = null)
{
id = id ==default? NewFileID(): id ;
stream.Seek(0, SeekOrigin.Begin);
// Logger.Debug("1");
id = id ==default? NewFileID(): id ;
// Logger.Debug("2");
var total = stream.Length;
// Logger.Debug("3");
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferRequest()
{
FileLength= total,
@@ -646,8 +320,8 @@ namespace RageCoop.Server
}, ConnectionChannel.File)?.Response!=FileResponse.NeedToDownload)
{
Logger?.Info($"Skipping file transfer \"{name}\" to {client.Username}");
stream.Close();
stream.Dispose();
// stream.Close();
// stream.Dispose();
return;
}
Logger?.Debug($"Initiating file transfer:{name}, {total}");
@@ -688,8 +362,8 @@ namespace RageCoop.Server
{
Logger.Warning($"File trasfer to {client.Username} failed: "+name);
}
stream.Close();
stream.Dispose();
// stream.Close();
// stream.Dispose();
Logger?.Debug($"All file chunks sent:{name}");
InProgressFileTransfers.Remove(id);
}
+25
View File
@@ -5,6 +5,8 @@ using System.Threading;
using System.Threading.Tasks;
using RageCoop.Core;
using Newtonsoft.Json;
using System.Linq;
namespace RageCoop.Server
{
class Program
@@ -13,6 +15,29 @@ namespace RageCoop.Server
static Logger mainLogger;
static void Main(string[] args)
{
if (args.Length>=2 && args[0]=="update")
{
var target = args[1];
int i =0;
while (i < 10)
{
i++;
try
{
Console.WriteLine("Applying update to "+target);
CoreUtils.CopyFilesRecursively(new(AppDomain.CurrentDomain.BaseDirectory), new(target));
Process.Start(Path.Combine(target, "RageCoop.Server"));
Environment.Exit(0);
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
Thread.Sleep(3000);
}
}
Environment.Exit(i);
}
AppDomain.CurrentDomain.UnhandledException+=UnhandledException;
mainLogger = new Logger()
{
+3 -3
View File
@@ -14,8 +14,8 @@ using System.Resources;
[assembly: AssemblyTrademark("RAGECOOP")]
[assembly: AssemblyCulture("")]
// Version informationr(
[assembly: AssemblyVersion("1.5.1.13")]
[assembly: AssemblyFileVersion("1.5.1.13")]
// Version information
[assembly: AssemblyVersion("1.5.3.107")]
[assembly: AssemblyFileVersion("1.5.3.107")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]
+1 -1
View File
@@ -32,7 +32,7 @@ using System.Resources;
[assembly: AssemblyTrademark("RAGECOOP")]
[assembly: AssemblyCulture("")]
// Version informationr(
// Version information
[assembly: AssemblyVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
[assembly: AssemblyFileVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_LastSelectedProfileId>M:\SandBox-Shared\repo\RageCoop\RageCoop-V\RageCoop.Server\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
<_LastSelectedProfileId>M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Server\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
</PropertyGroup>
</Project>
+6 -7
View File
@@ -209,14 +209,13 @@ namespace RageCoop.Server.Scripting
raiseEvent ??= targets==null;
try
{
if (Server.MainNetServer.ConnectionsCount == 0)
if (Server.MainNetServer.ConnectionsCount != 0)
{
return;
}
targets ??= new(Server.ClientsByNetHandle.Values);
foreach(Client client in targets)
{
Server.SendChatMessage(username, message, client);
targets ??= new(Server.ClientsByNetHandle.Values);
foreach (Client client in targets)
{
Server.SendChatMessage(username, message, client);
}
}
}
catch (Exception e)
+4
View File
@@ -27,6 +27,8 @@ namespace RageCoop.Server.Scripting
{
if (Server.Settings.WeatherTimeSync)
{
if (e.Client != API.Host) { e.Client.SendCustomEvent(CustomEvents.IsHost, false); return; }
foreach (var c in API.GetAllClients().Values)
{
if (c==e.Client)
@@ -41,6 +43,8 @@ namespace RageCoop.Server.Scripting
{
API.SendCustomEventQueued(API.GetAllClients().Values.Where(x=>x!=e.Client).ToList(),CustomEvents.OnPlayerDied,e.Client.Username);
});
API.Events.OnChatMessage+=(s,e) =>
Server.Logger?.Info((e.Client?.Username ?? e.ClaimedSender ?? "Unknown") + ": " + e.Message);
}
public override void OnStop()
{
+83 -30
View File
@@ -22,9 +22,42 @@ namespace RageCoop.Server.Scripting
Server = server;
Logger=server.Logger;
}
private List<string> ClientResourceZips=new List<string>();
private Dictionary<string,Func<Stream>> ClientResources=new();
private Dictionary<string,Func<Stream>> ResourceStreams=new();
private List<MemoryStream> MemStreams = new();
public void LoadAll()
{
// 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");
@@ -45,22 +78,21 @@ namespace RageCoop.Server.Scripting
var zipPath = Path.Combine(tmpDir, Path.GetFileName(resourceFolder))+".res";
try
{
using (ZipFile zip = ZipFile.Create(zipPath))
{
zip.BeginUpdate();
foreach (var dir in Directory.GetDirectories(resourceFolder, "*", SearchOption.AllDirectories))
{
zip.AddDirectory(dir.Substring(resourceFolder.Length+1));
}
foreach (var file in Directory.GetFiles(resourceFolder, "*", SearchOption.AllDirectories))
{
zip.Add(file, file.Substring(resourceFolder.Length+1));
}
zip.CommitUpdate();
zip.Close();
ClientResourceZips.Add(zipPath);
}
}
using ZipFile zip = ZipFile.Create(zipPath);
zip.BeginUpdate();
foreach (var dir in Directory.GetDirectories(resourceFolder, "*", SearchOption.AllDirectories))
{
zip.AddDirectory(dir[(resourceFolder.Length + 1)..]);
}
foreach (var file in Directory.GetFiles(resourceFolder, "*", SearchOption.AllDirectories))
{
if (Path.GetFileName(file).CanBeIgnored()) { continue; }
zip.Add(file, file[(resourceFolder.Length + 1)..]);
}
zip.CommitUpdate();
zip.Close();
ClientResources.Add(Path.GetFileNameWithoutExtension(zipPath), () => File.OpenRead(zipPath));
}
catch (Exception ex)
{
Logger?.Error($"Failed to pack client resource:{resourceFolder}");
@@ -71,7 +103,10 @@ namespace RageCoop.Server.Scripting
var packed = Directory.GetFiles(path, "*.res", SearchOption.TopDirectoryOnly);
if (packed.Length>0)
{
ClientResourceZips.AddRange(packed);
foreach(var file in packed)
{
ClientResources.Add(Path.GetFileNameWithoutExtension(file),()=>File.OpenRead(file));
}
}
}
@@ -101,26 +136,34 @@ namespace RageCoop.Server.Scripting
Logger?.Error(ex);
}
}
foreach (var resource in Directory.GetFiles(path, "*.res", SearchOption.TopDirectoryOnly))
foreach (var res in Directory.GetFiles(path, "*.res", SearchOption.TopDirectoryOnly))
{
try
if (!ResourceStreams.TryAdd(Path.GetFileNameWithoutExtension(res),()=>File.OpenRead(res)))
{
Logger?.Warning($"Resource \"{res}\" cannot be loaded, ignoring...");
continue;
}
}
foreach(var res in ResourceStreams)
{
try
{
var name = Path.GetFileNameWithoutExtension(resource);
var name = res.Key;
if (LoadedResources.ContainsKey(name))
{
Logger?.Warning($"Resource \"{name}\" has already been loaded, ignoring...");
continue;
}
Logger?.Info($"Loading resource: "+name);
var r = ServerResource.LoadFromZip(resource, Path.Combine("Resources", "Temp", "Server"), dataFolder, Logger);
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: {Path.GetFileNameWithoutExtension(resource)}");
Logger?.Error($"Failed to load resource: {res.Key}");
Logger?.Error(ex);
}
}
}
// Start scripts
lock (LoadedResources)
@@ -172,21 +215,31 @@ namespace RageCoop.Server.Scripting
}
LoadedResources.Clear();
}
foreach(var s in MemStreams)
{
try
{
s.Close();
s.Dispose();
}
catch(Exception ex)
{
Logger?.Error("[Resources.CloseMemStream]",ex);
}
}
}
public void SendTo(Client client)
{
Task.Run(() =>
{
if (ClientResourceZips.Count!=0)
if (ClientResources.Count!=0)
{
Logger?.Info($"Sending resources to client:{client.Username}");
foreach (var rs in ClientResourceZips)
foreach (var rs in ClientResources)
{
using (var fs = File.OpenRead(rs))
{
Server.SendFile(rs, Path.GetFileName(rs), client);
}
Logger?.Debug(rs.Key);
Server.SendFile(rs.Value(),rs.Key+".res", client);
}
Logger?.Info($"Resources sent to:{client.Username}");
+30 -4
View File
@@ -211,11 +211,21 @@ namespace RageCoop.Server.Scripting
ped.Owner=sender;
ped.Health=p.Health;
ped._rot=p.Rotation;
ped.Owner=sender;
ped._isInvincible = p.Flags.HasPedFlag(PedDataFlags.IsInvincible);
if (p.Speed>=4 && Vehicles.TryGetValue(p.VehicleID,out var v))
{
ped.LastVehicle=v;
}
if (ped.Owner != sender)
{
if (ped.Owner != null)
{
ped.Owner.EntitiesCount--;
}
ped.Owner = sender;
sender.EntitiesCount++;
}
}
internal void Update(Packets.VehicleSync p, Client sender)
{
@@ -226,8 +236,16 @@ namespace RageCoop.Server.Scripting
veh.ID=p.ID;
}
veh._pos = p.Position+p.Velocity*sender.Latency;
veh.Owner=sender;
veh._quat=p.Quaternion;
if(veh.Owner != sender)
{
if (veh.Owner != null)
{
veh.Owner.EntitiesCount--;
}
veh.Owner = sender;
sender.EntitiesCount++;
}
}
internal void CleanUp(Client left)
{
@@ -252,7 +270,11 @@ namespace RageCoop.Server.Scripting
}
internal void RemoveVehicle(int id)
{
Vehicles.TryRemove(id, out _);
Vehicles.TryRemove(id, out var veh);
if (veh?.Owner != null)
{
veh.Owner.EntitiesCount--;
}
}
internal void RemoveProp(int id)
@@ -265,7 +287,11 @@ namespace RageCoop.Server.Scripting
}
internal void RemovePed(int id)
{
Peds.TryRemove(id, out _);
Peds.TryRemove(id, out var ped);
if (ped?.Owner != null)
{
ped.Owner.EntitiesCount--;
}
}
internal void Add(ServerPed ped)
+10
View File
@@ -192,6 +192,16 @@ namespace RageCoop.Server.Scripting
/// Health
/// </summary>
public int Health { get; internal set; }
internal bool _isInvincible;
/// <summary>
/// Get or set whether this ped is invincible
/// </summary>
public bool IsInvincible {
get => _isInvincible;
set => Owner.SendNativeCall(Hash.SET_ENTITY_INVINCIBLE,Handle,value);
}
}
/// <summary>
/// Represents a vehicle from a client
+3 -3
View File
@@ -81,10 +81,10 @@ namespace RageCoop.Server.Scripting
}
return r;
}
internal static ServerResource LoadFromZip(string zipPath, string tmpDir, string dataFolder, Logger logger = null)
internal static ServerResource LoadFrom(Stream input,string name, string tmpDir, string dataFolder, Logger logger = null)
{
tmpDir=Path.Combine(tmpDir, Path.GetFileNameWithoutExtension(zipPath));
new FastZip().ExtractZip(zipPath, tmpDir, 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>
+17 -2
View File
@@ -63,7 +63,7 @@
/// <summary>
/// See <see cref="Core.Logger.LogLevel"/>.
/// </summary>
public int LogLevel { get; set; }=2;
public int LogLevel { get; set; } = 0;
/// <summary>
/// NPC data won't be sent to a player if their distance is greater than this value. -1 for unlimited.
@@ -103,6 +103,21 @@
/// <summary>
/// The zerotier network id to join, default value is zerotier's public Earth network.
/// </summary>
public string ZeroTierNetworkID = "8056c2e21c000001";
public string ZeroTierNetworkID { get; set; } = "8056c2e21c000001";
/// <summary>
/// Automatically update to nightly build when an update is avalible, check is performed every 10 minutes.
/// </summary>
public bool AutoUpdate { get; set; } = false;
/// <summary>
/// Kick godmode assholes
/// </summary>
public bool KickGodMode { get; set; } = false;
/// <summary>
/// Kick spamming assholes
/// </summary>
public bool KickSpamming { get; set; } = true;
}
}
-12
View File
@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace RageCoop.Server
{
internal static class Updater
{
}
}
Binary file not shown.