Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 89ebd0305c | |||
| b209202292 | |||
| f199241ed8 | |||
| 50229116a7 | |||
| f192caeae1 | |||
| 6005b2ba11 | |||
| 6e213611cc | |||
| 347d65af62 | |||
| 636ee3a33f | |||
| 8ff08e0804 | |||
| 6d7fe58719 | |||
| c7e14ef191 | |||
| 39eb5ef80a | |||
| d25c2539e0 | |||
| eb31a33104 | |||
| 8b3f8ffd9b | |||
| cca09c4178 | |||
| 871bb7e64e | |||
| 02da3ce8af | |||
| 711ba62fc2 | |||
| 8f7d3135db | |||
| cb563a1bf8 | |||
| a84ab42a42 | |||
| b3e285f92f | |||
| 01433f0de6 | |||
| c034cd8aa9 | |||
| 363c2ccb00 | |||
| 7380c162be | |||
| f1c7192029 | |||
| 6720636d48 | |||
| 2af9482da9 | |||
| bf22c17bba | |||
| 01492a0c55 | |||
| c17b234057 | |||
| 739577bbf1 | |||
| 2d2da624e4 | |||
| 9e66762061 | |||
| 84d578366a | |||
| 03bc3a7742 | |||
| 5bfc7f836e | |||
| 54977c79f1 | |||
| 0352bfa328 | |||
| 6fbc386b45 | |||
| 203e3b2528 | |||
| c9aa995224 | |||
| c201b94897 | |||
| 87f873d1a5 | |||
| 6c355b109d | |||
| 2ef3012672 | |||
| 7229574ff4 | |||
| eff9fd50b1 | |||
| e2cbf26360 | |||
| b0f448fd5f | |||
| 721c4d3dee | |||
| 87dfc18d91 | |||
| fa4fe2c10f | |||
| 3a088ddbfc | |||
| 52cc9b647c | |||
| 1aa23901d5 | |||
| 52584d5774 | |||
| 62bf15dede | |||
| 722ca16f57 | |||
| 3e61498366 | |||
| 1ff2fa5691 | |||
| 5088973474 | |||
| b63378f4d0 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
)]
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
Binary file not shown.
|
After Width: | Height: | Size: 2.5 MiB |
+17
-22
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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" )]
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -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 --
|
||||
+18
-36
@@ -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()
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,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
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]; }
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ namespace RageCoop.Core
|
||||
IsInLowCover = 1 << 11,
|
||||
IsInCoverFacingLeft = 1 << 12,
|
||||
IsBlindFiring = 1 << 13,
|
||||
IsInvincible = 1 << 14,
|
||||
IsFullSync = 1<<15 ,
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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" )]
|
||||
|
||||
|
||||
@@ -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,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>
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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}");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
Reference in New Issue
Block a user