Load in client in seperate domain

Rewrote logger for more flexible options
Complete client resource loading
This commit is contained in:
sardelka9515
2022-10-15 11:09:17 +08:00
parent 8701ac703e
commit 42c0ef2159
34 changed files with 1090 additions and 960 deletions

View File

@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "RageCoop
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client.Installer", "RageCoop.Client.Installer\RageCoop.Client.Installer.csproj", "{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client.Loader", "RageCoop.Client.Loader\RageCoop.Client.Loader.csproj", "{D2A39C1C-2454-4411-8BA1-BE34EEB9042F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -51,6 +53,14 @@ Global
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|Any CPU.Build.0 = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.ActiveCfg = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.Build.0 = Release|Any CPU
{D2A39C1C-2454-4411-8BA1-BE34EEB9042F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2A39C1C-2454-4411-8BA1-BE34EEB9042F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2A39C1C-2454-4411-8BA1-BE34EEB9042F}.Debug|x64.ActiveCfg = Debug|Any CPU
{D2A39C1C-2454-4411-8BA1-BE34EEB9042F}.Debug|x64.Build.0 = Debug|Any CPU
{D2A39C1C-2454-4411-8BA1-BE34EEB9042F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D2A39C1C-2454-4411-8BA1-BE34EEB9042F}.Release|Any CPU.Build.0 = Release|Any CPU
{D2A39C1C-2454-4411-8BA1-BE34EEB9042F}.Release|x64.ActiveCfg = Release|Any CPU
{D2A39C1C-2454-4411-8BA1-BE34EEB9042F}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,200 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Console = GTA.Console;
using SHVDN;
using System.Windows.Forms;
namespace RageCoop.Client.Loader
{
public class DomainContext : MarshalByRefObject, IDisposable
{
#region PRIMARY-LOADING-LOGIC
public static ConcurrentDictionary<string, DomainContext> LoadedDomains => new ConcurrentDictionary<string, DomainContext>(_loadedDomains);
static readonly ConcurrentDictionary<string, DomainContext> _loadedDomains = new ConcurrentDictionary<string, DomainContext>();
public bool UnloadRequested;
public string BaseDirectory => AppDomain.CurrentDomain.BaseDirectory;
private ScriptDomain CurrentDomain => ScriptDomain.CurrentDomain;
public static void CheckForUnloadRequest()
{
lock (_loadedDomains)
{
foreach (var p in _loadedDomains.Values)
{
if (p.UnloadRequested)
{
Unload(p);
}
}
}
}
public static bool IsLoaded(string dir)
{
return _loadedDomains.ContainsKey(Path.GetFullPath(dir).ToLower());
}
public static DomainContext Load(string dir)
{
lock (_loadedDomains)
{
dir = Path.GetFullPath(dir).ToLower();
if (IsLoaded(dir))
{
throw new Exception("Already loaded");
}
ScriptDomain newDomain = null;
try
{
dir = Path.GetFullPath(dir);
Directory.CreateDirectory(dir);
Exception e = null;
// Load domain in main thread
Main.QueueToMainThread(() =>
{
try
{
/*
var assemblies = new List<string>();
assemblies.Add(typeof(DomainLoader).Assembly.Location);
assemblies.AddRange(typeof(DomainLoader).Assembly.GetReferencedAssemblies()
.Select(x => Assembly.Load(x.FullName).Location)
.Where(x => !string.IsNullOrEmpty(x)));
*/
// Delete API assemblies
Directory.GetFiles(dir, "ScriptHookVDotNet*", SearchOption.AllDirectories).ToList().ForEach(x => File.Delete(x));
var ctxAsm = Path.Combine(dir, "RageCoop.Client.Loader.dll");
if (File.Exists(ctxAsm)) { File.Delete(ctxAsm); }
newDomain = ScriptDomain.Load(
Directory.GetParent(typeof(ScriptDomain).Assembly.Location).FullName, dir);
newDomain.AppDomain.SetData("Primary", ScriptDomain.CurrentDomain);
newDomain.AppDomain.SetData("Console", ScriptDomain.CurrentDomain.AppDomain.GetData("Console"));
var context = (DomainContext)newDomain.AppDomain.CreateInstanceFromAndUnwrap(
typeof(DomainContext).Assembly.Location,
typeof(DomainContext).FullName, ignoreCase: false,
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new object[] { }
, null, null);
newDomain.AppDomain.SetData("RageCoop.Client.LoaderContext", context);
newDomain.Start();
_loadedDomains.TryAdd(dir,context );
}
catch (Exception ex)
{
e = ex;
}
});
// Wait till next tick
GTA.Script.Yield();
if (e != null) { throw e; }
return _loadedDomains[dir];
}
catch (Exception ex)
{
GTA.UI.Notification.Show(ex.ToString());
Console.Error(ex);
if (newDomain != null)
{
ScriptDomain.Unload(newDomain);
}
throw;
}
}
}
public static void Unload(DomainContext domain)
{
lock (_loadedDomains)
{
Exception ex = null;
var name = domain.CurrentDomain.Name;
Console.Info("Unloading domain: " + name);
Main.QueueToMainThread(() =>
{
try
{
if (!_loadedDomains.TryRemove(domain.BaseDirectory, out _))
{
throw new Exception("Failed to remove domain from list");
}
domain.Dispose();
ScriptDomain.Unload(domain.CurrentDomain);
}
catch (Exception e) { ex = e; }
});
GTA.Script.Yield();
if (ex != null) { throw ex; }
Console.Info("Unloaded domain: " + name);
}
}
public static void Unload(string dir)
{
Unload(_loadedDomains[Path.GetFullPath(dir).ToLower()]);
}
public static void UnloadAll()
{
lock (_loadedDomains)
{
foreach (var d in _loadedDomains.Values.ToArray())
{
Unload(d);
}
}
}
#endregion
#region LOAD-CONTEXT
private DomainContext()
{
AppDomain.CurrentDomain.DomainUnload += (s, e) => Dispose();
PrimaryDomain.Tick += Tick;
PrimaryDomain.KeyEvent += KeyEvent;
Console.Info($"Loaded domain: {AppDomain.CurrentDomain.FriendlyName}, {AppDomain.CurrentDomain.BaseDirectory}");
}
public static ScriptDomain PrimaryDomain=> (ScriptDomain)AppDomain.CurrentDomain.GetData("Primary");
public static DomainContext CurrentContext => (DomainContext)AppDomain.CurrentDomain.GetData("RageCoop.Client.LoaderContext");
/// <summary>
/// Request the current domain to be unloaded
/// </summary>
public static void RequestUnload()
{
if (PrimaryDomain == null)
{
throw new NotSupportedException("Current domain not loaded by the loader therfore cannot be unloaded automatically");
}
CurrentContext.UnloadRequested = true;
}
private void Tick(object sender, EventArgs args)
{
CurrentDomain.DoTick();
}
private void KeyEvent(Keys keys, bool status)
{
CurrentDomain.DoKeyEvent(keys, status);
}
public void Dispose()
{
lock (this)
{
if (PrimaryDomain == null) { return; }
PrimaryDomain.Tick -= Tick;
PrimaryDomain.KeyEvent -= KeyEvent;
AppDomain.CurrentDomain.SetData("Primary", null);
}
}
#endregion
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Windows.Forms;
using GTA;
using Console = GTA.Console;
namespace RageCoop.Client.Loader
{
public class Main : Script
{
static readonly string GameDir = Directory.GetParent(typeof(SHVDN.ScriptDomain).Assembly.Location).FullName;
static readonly string ScriptsLocation = Path.Combine(GameDir, "RageCoop", "Scripts");
private static readonly ConcurrentQueue<Action> TaskQueue = new ConcurrentQueue<Action>();
public Main()
{
Tick += OnTick;
SHVDN.ScriptDomain.CurrentDomain.Tick += DomainTick;
KeyDown += OnKeyDown;
}
private void OnKeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
}
private void OnTick(object sender, EventArgs e)
{
while (Game.IsLoading)
{
Script.Yield();
}
DomainContext.CheckForUnloadRequest();
if (!DomainContext.IsLoaded(ScriptsLocation))
{
if (!File.Exists(Path.Combine(ScriptsLocation, "RageCoop.Client.dll")))
{
GTA.UI.Notification.Show("~r~Main assembly is missing, please re-install the client");
Abort();
}
DomainContext.Load(ScriptsLocation);
}
}
internal static void QueueToMainThread(Action task)
{
TaskQueue.Enqueue(task);
}
private static void DomainTick(object sender, EventArgs e)
{
while (TaskQueue.TryDequeue(out var task))
{
try
{
task.Invoke();
}
catch (Exception ex)
{
Console.Error(ex.ToString());
}
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("RageCoop.Client.Loader")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RageCoop.Client.Loader")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("fc8cbdbb-6dc3-43af-b34d-092e476410a5")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{FC8CBDBB-6DC3-43AF-B34D-092E476410A5}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>RageCoop.Client.Loader</RootNamespace>
<AssemblyName>RageCoop.Client.Loader</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutDir>..\bin\Debug\Client.Loader</OutDir>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutDir>..\bin\Release\Client.Loader</OutDir>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="ScriptHookVDotNet">
<HintPath>..\libs\ScriptHookVDotNet.dll</HintPath>
</Reference>
<Reference Include="ScriptHookVDotNet3">
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DomainLoader.cs" />
<Compile Include="Main.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -20,7 +20,6 @@ namespace RageCoop.Client
public DevTool()
{
Instance = this;
if (!Util.IsPrimaryDomain) { Abort(); return; }
Tick += OnTick;
KeyDown += OnKeyDown;
Pause();

View File

@ -25,7 +25,6 @@ namespace RageCoop.Client
[ScriptAttributes(Author = "RageCoop", NoDefaultInstance = false, SupportURL = "https://github.com/RAGECOOP/RAGECOOP-V")]
internal class Main : Script
{
public static API API = API.GetInstance();
private static bool _gameLoaded = false;
internal static Version Version = typeof(Main).Assembly.GetName().Version;
@ -45,21 +44,20 @@ namespace RageCoop.Client
internal static Vector3 PlayerPosition;
internal static Resources Resources = null;
private static readonly ConcurrentQueue<Action> TaskQueue = new ConcurrentQueue<Action>();
public static Worker Worker;
public static string LogPath => $"{Settings.DataDirectory}\\RageCoop.Client.log";
/// <summary>
/// Don't use it!
/// </summary>
public Main()
{
Console.WriteLine($"Starting {typeof(Main).FullName}, domain: {AppDomain.CurrentDomain.Id}, {Util.IsPrimaryDomain}");
if (!Util.IsPrimaryDomain) { Console.WriteLine("Ignored loading in scondary domain"); Abort(); return; }
Console.Info($"Starting {typeof(Main).FullName}, domain: {AppDomain.CurrentDomain.Id} {AppDomain.CurrentDomain.FriendlyName}");
try
{
Settings = Util.ReadSettings();
if (Settings.DataDirectory.StartsWith("Scripts"))
{
var defaultDir = new Settings().DataDirectory;
Console.Warning.WriteLine("Data directory must be outside scripts folder, migrating to default direcoty: " + defaultDir);
Console.Warning("Data directory must be outside scripts folder, migrating to default direcoty: " + defaultDir);
if (Directory.Exists(Settings.DataDirectory))
{
CoreUtils.CopyFilesRecursively(new DirectoryInfo(Settings.DataDirectory), new DirectoryInfo(defaultDir));
@ -78,16 +76,34 @@ namespace RageCoop.Client
Directory.CreateDirectory(Settings.DataDirectory);
Logger = new Logger()
{
LogPath = $"{Settings.DataDirectory}\\RageCoop.Client.log",
UseConsole = false,
Writers = new List<StreamWriter> { CoreUtils.OpenWriter(LogPath)},
#if DEBUG
LogLevel = 0,
#else
LogLevel = Settings.LogLevel,
#endif
};
Logger.OnFlush += (s, d) => Console.WriteLine(d);
Worker = new Worker("RageCoop.Client.Main.Worker", Logger);
Logger.OnFlush += (line, formatted) =>
{
switch (line.LogLevel)
{
#if DEBUG
// case LogLevel.Trace:
case LogLevel.Debug:
Console.Info(line.Message);
break;
#endif
case LogLevel.Info:
Console.Info(line.Message);
break;
case LogLevel.Warning:
Console.Warning(line.Message);
break;
case LogLevel.Error:
Console.Error(line.Message);
break;
}
};
ScriptDomain.CurrentDomain.Tick += DomainTick;
Resources = new Resources();
if (Game.Version < GameVersion.v1_0_1290_1_Steam)
@ -163,9 +179,17 @@ namespace RageCoop.Client
}
}
}
internal static void QueueToMainThread(Action task)
/// <summary>
/// Queue an action to main thread and wait for execution to complete, must be called from script thread.
/// </summary>
/// <param name="task"></param>
internal static void QueueToMainThreadAndWait(Action task)
{
TaskQueue.Enqueue(task);
Exception e = null;
TaskQueue.Enqueue(() => { try { task(); } catch (Exception ex) { e = ex; } });
Yield();
if (e != null) { throw e; }
}
public static Ped P;

View File

@ -9,7 +9,6 @@ namespace RageCoop.Client
{
internal static class DebugMenu
{
public static string DebugLocation => Path.Combine(Main.Settings.DataDirectory, @"Debug");
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Debug", "Debug settings")
{
UseMouse = false,
@ -20,7 +19,7 @@ namespace RageCoop.Client
UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
public static NativeItem DebugScriptsItem = new NativeItem("Load debug scripts", $"Load scripts from {DebugLocation}");
public static NativeItem ReloadItem = new NativeItem("Reload","Reload RAGECOOP and associated scripts");
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);
@ -50,29 +49,18 @@ namespace RageCoop.Client
};
ShowNetworkInfoItem.CheckboxChanged += (s, e) => { Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked; };
ShowOwnerItem.CheckboxChanged += (s, e) => { Main.Settings.ShowEntityOwnerName = ShowOwnerItem.Checked; Util.SaveSettings(); };
DebugScriptsItem.Activated += ToggleScripts;
ReloadItem.Activated += ReloadDomain;
Menu.Add(SimulatedLatencyItem);
Menu.Add(ShowNetworkInfoItem);
Menu.Add(ShowOwnerItem);
Menu.Add(DebugScriptsItem);
Menu.Add(ReloadItem);
Menu.AddSubMenu(DiagnosticMenu);
}
private static void ToggleScripts(object sender, EventArgs e)
private static void ReloadDomain(object sender, EventArgs e)
{
if (ResourceDomain.IsLoaded(DebugLocation))
{
ResourceDomain.Unload(DebugLocation);
DebugScriptsItem.Title = "Load debug scripts";
DebugScriptsItem.Description = $"Load scripts from {DebugLocation}";
}
else
{
ResourceDomain.Load(DebugLocation);
DebugScriptsItem.Title = "Unload debug scripts";
DebugScriptsItem.Description = $"Unload scripts from {DebugLocation}";
}
Loader.DomainContext.RequestUnload();
}
}
}

View File

@ -17,7 +17,6 @@ namespace RageCoop.Client.Menus
/// </summary>
internal static class ServersMenu
{
static API API = Main.API;
private static Thread GetServersThread;
internal static NativeMenu Menu = new NativeMenu("RAGECOOP", "Servers", "Go to the server list")
{

View File

@ -11,7 +11,6 @@ namespace RageCoop.Client.Menus
{
internal class UpdateMenu
{
static readonly API API = Main.API;
public static bool IsUpdating { get; private set; } = false;
private static readonly NativeItem _updatingItem = new NativeItem("Updating...");
private static readonly NativeItem _downloadItem = new NativeItem("Download", "Download and update to latest nightly");

View File

@ -13,7 +13,6 @@ namespace RageCoop.Client
{
internal static partial class Networking
{
static API API = Main.API;
public static CoopPeer Peer;
public static float Latency => ServerConnection.AverageRoundtripTime / 2;
public static bool ShowNetworkInfo = false;

View File

@ -1,6 +1,7 @@
using GTA;
using Lidgren.Network;
using RageCoop.Client.Menus;
using RageCoop.Client.Scripting;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using System;

View File

@ -3,6 +3,7 @@ using GTA.Math;
using GTA.Native;
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Client.Scripting;
using System;
using System.Collections.Generic;

View File

@ -12,7 +12,6 @@ namespace RageCoop.Client
{
internal static class PlayerList
{
static readonly API API = Main.API;
private const float LEFT_POSITION = 0.122f;
private const float RIGHT_POSITION = 0.9f;
private static readonly Scaleform _mainScaleform = new Scaleform("mp_mm_card_freemode");

View File

@ -16,7 +16,7 @@ using System.Resources;
// Version informationr(
[assembly: AssemblyVersion("1.5.4.244")]
[assembly: AssemblyFileVersion("1.5.4.244")]
[assembly: AssemblyVersion("1.5.4.291")]
[assembly: AssemblyFileVersion("1.5.4.291")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]

View File

@ -1,314 +1,317 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>RageCoop.Client</RootNamespace>
<AssemblyName>RageCoop.Client</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<OutPutPath>..\bin\Debug\Client</OutPutPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>DEBUG</DefineConstants>
<WarningLevel>4</WarningLevel>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<OutPutPath>..\bin\Release\Client</OutPutPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="Debug.cs" />
<Compile Include="DevTools\DevTool.cs" />
<Compile Include="Main.cs" />
<Compile Include="Menus\CoopMenu.cs" />
<Compile Include="Menus\Sub\DebugMenu.cs" />
<Compile Include="Menus\Sub\DevToolMenu.cs" />
<Compile Include="Menus\Sub\ServersMenu.cs" />
<Compile Include="Menus\Sub\SettingsMenu.cs" />
<Compile Include="Menus\Sub\UpdateMenu.cs" />
<Compile Include="Networking\Chat.cs" />
<Compile Include="Networking\DownloadManager.cs" />
<Compile Include="Networking\HolePunch.cs" />
<Compile Include="Networking\Networking.cs" />
<Compile Include="Networking\Receive.cs" />
<Compile Include="Networking\Send.cs" />
<Compile Include="Networking\Statistics.cs" />
<Compile Include="PlayerList.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AssemblyInfo.tt</DependentUpon>
</Compile>
<Compile Include="Scripting\API.cs" />
<Compile Include="Scripting\BaseScript.cs" />
<Compile Include="Scripting\ClientScript.cs" />
<Compile Include="Scripting\ResourceDomain.cs" />
<Compile Include="Scripting\Resources.cs" />
<Compile Include="Security.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.Members.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.Animations.cs" />
<Compile Include="Sync\Entities\SyncedEntity.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.cs" />
<Compile Include="Sync\Entities\SyncedProjectile.cs" />
<Compile Include="Sync\Entities\SyncedProp.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" />
<Compile Include="Util\AddOnDataProvider.cs" />
<Compile Include="Util\Memory.cs" />
<Compile Include="Util\NativeCaller.cs" />
<Compile Include="Util\PedConfigFlags.cs" />
<Compile Include="Util\PedExtensions.cs" />
<Compile Include="Util\TaskType.cs" />
<Compile Include="Util\Util.cs" />
<Compile Include="Util\VehicleExtensions.cs" />
<Compile Include="Util\WeaponUtil.cs" />
<Compile Include="WorldThread.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj">
<Project>{cc2e8102-e568-4524-aa1f-f8e0f1cfe58a}</Project>
<Name>RageCoop.Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="Costura, Version=5.7.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=1.3.3.11, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="LemonUI.SHVDN3, Version=1.7.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath>
</Reference>
<Reference Include="Lidgren.Network, Version=2012.1.7.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\Lidgren.Network.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.ObjectPool, Version=6.0.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.ObjectPool.6.0.8\lib\net461\Microsoft.Extensions.ObjectPool.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Win32.Registry, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Win32.Registry.4.7.0\lib\net461\Microsoft.Win32.Registry.dll</HintPath>
</Reference>
<Reference Include="NAudio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.2.1.0\lib\net472\NAudio.dll</HintPath>
</Reference>
<Reference Include="NAudio.Asio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Asio.2.1.0\lib\netstandard2.0\NAudio.Asio.dll</HintPath>
</Reference>
<Reference Include="NAudio.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Core.2.1.0\lib\netstandard2.0\NAudio.Core.dll</HintPath>
</Reference>
<Reference Include="NAudio.Midi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Midi.2.1.0\lib\netstandard2.0\NAudio.Midi.dll</HintPath>
</Reference>
<Reference Include="NAudio.Wasapi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Wasapi.2.1.0\lib\netstandard2.0\NAudio.Wasapi.dll</HintPath>
</Reference>
<Reference Include="NAudio.WinForms, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.WinForms.2.1.0\lib\net472\NAudio.WinForms.dll</HintPath>
</Reference>
<Reference Include="NAudio.WinMM, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.WinMM.2.1.0\lib\netstandard2.0\NAudio.WinMM.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="ScriptHookVDotNet">
<HintPath>..\libs\ScriptHookVDotNet.dll</HintPath>
</Reference>
<Reference Include="ScriptHookVDotNet3">
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.AppContext, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.Tracing, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.4.3.0\lib\net462\System.IO.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Linq, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Linq.Expressions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Reflection, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.Extensions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.AccessControl, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.AccessControl.4.7.0\lib\net461\System.Security.AccessControl.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Principal.Windows.4.7.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="System.Text.RegularExpressions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.RegularExpressions.4.3.0\lib\net463\System.Text.RegularExpressions.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Serialization" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="FodyWeavers.xml" />
<Content Include="Properties\AssemblyInfo.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
</Content>
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Fody.6.6.3\build\Fody.targets" Condition="Exists('..\packages\Fody.6.6.3\build\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Fody.6.6.3\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.6.3\build\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets'))" />
</Target>
<Import Project="..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" />
<PropertyGroup Condition=" '$(DevEnvDir)' != '*Undefined*'">
<PostBuildEvent>"$(DevEnvDir)TextTransform.exe" -a !!BuildConfiguration!$(Configuration) "$(ProjectDir)Properties\AssemblyInfo.tt"</PostBuildEvent>
</PropertyGroup>
<Import Project="..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>RageCoop.Client</RootNamespace>
<AssemblyName>RageCoop.Client</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<OutPutPath>..\bin\Debug\Client</OutPutPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>DEBUG</DefineConstants>
<WarningLevel>4</WarningLevel>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<OutPutPath>..\bin\Release\Client</OutPutPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="Debug.cs" />
<Compile Include="DevTools\DevTool.cs" />
<Compile Include="Main.cs" />
<Compile Include="Menus\CoopMenu.cs" />
<Compile Include="Menus\Sub\DebugMenu.cs" />
<Compile Include="Menus\Sub\DevToolMenu.cs" />
<Compile Include="Menus\Sub\ServersMenu.cs" />
<Compile Include="Menus\Sub\SettingsMenu.cs" />
<Compile Include="Menus\Sub\UpdateMenu.cs" />
<Compile Include="Networking\Chat.cs" />
<Compile Include="Networking\DownloadManager.cs" />
<Compile Include="Networking\HolePunch.cs" />
<Compile Include="Networking\Networking.cs" />
<Compile Include="Networking\Receive.cs" />
<Compile Include="Networking\Send.cs" />
<Compile Include="Networking\Statistics.cs" />
<Compile Include="PlayerList.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AssemblyInfo.tt</DependentUpon>
</Compile>
<Compile Include="Scripting\API.cs" />
<Compile Include="Scripting\BaseScript.cs" />
<Compile Include="Scripting\ClientScript.cs" />
<Compile Include="Scripting\Resources.cs" />
<Compile Include="Security.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.Members.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.Animations.cs" />
<Compile Include="Sync\Entities\SyncedEntity.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.cs" />
<Compile Include="Sync\Entities\SyncedProjectile.cs" />
<Compile Include="Sync\Entities\SyncedProp.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" />
<Compile Include="Util\AddOnDataProvider.cs" />
<Compile Include="Util\Memory.cs" />
<Compile Include="Util\NativeCaller.cs" />
<Compile Include="Util\PedConfigFlags.cs" />
<Compile Include="Util\PedExtensions.cs" />
<Compile Include="Util\TaskType.cs" />
<Compile Include="Util\Util.cs" />
<Compile Include="Util\VehicleExtensions.cs" />
<Compile Include="Util\WeaponUtil.cs" />
<Compile Include="WorldThread.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RageCoop.Client.Loader\RageCoop.Client.Loader.csproj">
<Project>{d2a39c1c-2454-4411-8ba1-be34eeb9042f}</Project>
<Name>RageCoop.Client.Loader</Name>
</ProjectReference>
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj">
<Project>{cc2e8102-e568-4524-aa1f-f8e0f1cfe58a}</Project>
<Name>RageCoop.Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="Costura, Version=5.7.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=1.3.3.11, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="LemonUI.SHVDN3, Version=1.7.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath>
</Reference>
<Reference Include="Lidgren.Network, Version=2012.1.7.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\Lidgren.Network.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.ObjectPool, Version=6.0.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.ObjectPool.6.0.8\lib\net461\Microsoft.Extensions.ObjectPool.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Win32.Registry, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Win32.Registry.4.7.0\lib\net461\Microsoft.Win32.Registry.dll</HintPath>
</Reference>
<Reference Include="NAudio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.2.1.0\lib\net472\NAudio.dll</HintPath>
</Reference>
<Reference Include="NAudio.Asio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Asio.2.1.0\lib\netstandard2.0\NAudio.Asio.dll</HintPath>
</Reference>
<Reference Include="NAudio.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Core.2.1.0\lib\netstandard2.0\NAudio.Core.dll</HintPath>
</Reference>
<Reference Include="NAudio.Midi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Midi.2.1.0\lib\netstandard2.0\NAudio.Midi.dll</HintPath>
</Reference>
<Reference Include="NAudio.Wasapi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Wasapi.2.1.0\lib\netstandard2.0\NAudio.Wasapi.dll</HintPath>
</Reference>
<Reference Include="NAudio.WinForms, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.WinForms.2.1.0\lib\net472\NAudio.WinForms.dll</HintPath>
</Reference>
<Reference Include="NAudio.WinMM, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.WinMM.2.1.0\lib\netstandard2.0\NAudio.WinMM.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="ScriptHookVDotNet">
<HintPath>..\libs\ScriptHookVDotNet.dll</HintPath>
</Reference>
<Reference Include="ScriptHookVDotNet3">
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.AppContext, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.Tracing, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.4.3.0\lib\net462\System.IO.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Linq, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Linq.Expressions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Reflection, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.Extensions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.AccessControl, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.AccessControl.4.7.0\lib\net461\System.Security.AccessControl.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Principal.Windows.4.7.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="System.Text.RegularExpressions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.RegularExpressions.4.3.0\lib\net463\System.Text.RegularExpressions.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Serialization" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="FodyWeavers.xml" />
<Content Include="Properties\AssemblyInfo.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
</Content>
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Fody.6.6.3\build\Fody.targets" Condition="Exists('..\packages\Fody.6.6.3\build\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Fody.6.6.3\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.6.3\build\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets'))" />
</Target>
<Import Project="..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" />
<PropertyGroup Condition=" '$(DevEnvDir)' != '*Undefined*'">
<PostBuildEvent>"$(DevEnvDir)TextTransform.exe" -a !!BuildConfiguration!$(Configuration) "$(ProjectDir)Properties\AssemblyInfo.tt"</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@ -26,160 +26,137 @@ namespace RageCoop.Client.Scripting
/// </summary>
public object[] Args { get; set; }
}
/// <summary>
/// Client configuration, this will conflict with server-side config.
/// </summary>
public class ClientConfig : MarshalByRefObject
{
/// <summary>
/// Get or set local player's username, set won't be effective if already connected to a server.
/// </summary>
public string Username
{
get => Main.Settings.Username;
set
{
if (Networking.IsOnServer || string.IsNullOrEmpty(value))
{
return;
}
Main.Settings.Username = value;
}
}
/// <summary>
/// Enable automatic respawn for this player.
/// </summary>
public bool EnableAutoRespawn { get; set; } = true;
/// <summary>
/// Get or set player's blip color
/// </summary>
public BlipColor BlipColor { get; set; } = BlipColor.White;
/// <summary>
/// Get or set player's blip sprite
/// </summary>
public BlipSprite BlipSprite { get; set; } = BlipSprite.Standard;
/// <summary>
/// Get or set scale of player's blip
/// </summary>
public float BlipScale { get; set; } = 1;
}
/// <summary>
/// Base events for RageCoop
/// </summary>
public class ClientEvents : MarshalByRefObject
{
internal Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new Dictionary<int, List<Action<CustomEventReceivedArgs>>>();
#region DELEGATES
/// <summary>
///
/// </summary>
public delegate void EmptyEvent();
/// <summary>
///
/// </summary>
/// <param name="hash"></param>
/// <param name="args"></param>
public delegate void CustomEvent(int hash, List<object> args);
#endregion
/// <summary>
/// The local player is dead
/// </summary>
public event EmptyEvent OnPlayerDied;
/// <summary>
/// A local vehicle is spawned
/// </summary>
public event EventHandler<SyncedVehicle> OnVehicleSpawned;
/// <summary>
/// A local vehicle is deleted
/// </summary>
public event EventHandler<SyncedVehicle> OnVehicleDeleted;
/// <summary>
/// A local ped is spawned
/// </summary>
public event EventHandler<SyncedPed> OnPedSpawned;
/// <summary>
/// A local ped is deleted
/// </summary>
public event EventHandler<SyncedPed> OnPedDeleted;
#region INVOKE
internal void InvokeVehicleSpawned(SyncedVehicle v) { OnVehicleSpawned?.Invoke(null, v); }
internal void InvokeVehicleDeleted(SyncedVehicle v) { OnVehicleDeleted?.Invoke(null, v); }
internal void InvokePedSpawned(SyncedPed p) { OnPedSpawned?.Invoke(null, p); }
internal void InvokePedDeleted(SyncedPed p) { OnPedDeleted?.Invoke(null, p); }
internal void InvokePlayerDied() { OnPlayerDied?.Invoke(); }
internal void InvokeCustomEventReceived(Packets.CustomEvent p)
{
var args = new CustomEventReceivedArgs() { Hash = p.Hash, Args = p.Args };
// Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
if (CustomEventHandlers.TryGetValue(p.Hash, out List<Action<CustomEventReceivedArgs>> handlers))
{
handlers.ForEach((x) => { x.Invoke(args); });
}
if (Util.IsPrimaryDomain)
{
ResourceDomain.DoCallBack("CustomEvent",p);
}
}
#endregion
}
/// <summary>
/// Provides vital functionality to interact with RAGECOOP
/// </summary>
public class API : MarshalByRefObject
public static class API
{
static API()
{
if (!Util.IsPrimaryDomain)
{
ResourceDomain.RegisterCallBackForCurrentDomain("CustomEvent",
(data) => {
Events.InvokeCustomEventReceived(data as Packets.CustomEvent);
});
}
}
static API Instance;
private API() { }
#region INTERNAL
internal static Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new Dictionary<int, List<Action<CustomEventReceivedArgs>>>();
#endregion
/// <summary>
/// Get an instance to bridge data between domains
/// Client configuration, this will conflict with server-side config.
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static API GetInstance()
public static class Config
{
if (Instance != null) { return Instance; }
if (Util.IsPrimaryDomain)
/// <summary>
/// Get or set local player's username, set won't be effective if already connected to a server.
/// </summary>
public static string Username
{
Instance = new API();
get => Main.Settings.Username;
set
{
if (Networking.IsOnServer || string.IsNullOrEmpty(value))
{
return;
}
Main.Settings.Username = value;
}
}
else
{
Instance = AppDomain.CurrentDomain.GetData("RageCoop.Client.API") as API;
}
return Instance;
}
/// <summary>
/// Enable automatic respawn for this player.
/// </summary>
public static bool EnableAutoRespawn { get; set; } = true;
public static ClientEvents Events = new ClientEvents();
public ClientConfig Config = new ClientConfig();
/// <summary>
/// Get or set player's blip color
/// </summary>
public static BlipColor BlipColor { get; set; } = BlipColor.White;
/// <summary>
/// Get or set player's blip sprite
/// </summary>
public static BlipSprite BlipSprite { get; set; } = BlipSprite.Standard;
/// <summary>
/// Get or set scale of player's blip
/// </summary>
public static float BlipScale { get; set; } = 1;
}
/// <summary>
/// Base events for RageCoop
/// </summary>
public static class Events
{
#region DELEGATES
/// <summary>
///
/// </summary>
public delegate void EmptyEvent();
/// <summary>
///
/// </summary>
/// <param name="hash"></param>
/// <param name="args"></param>
public delegate void CustomEvent(int hash, List<object> args);
#endregion
/// <summary>
/// The local player is dead
/// </summary>
public static event EmptyEvent OnPlayerDied;
/// <summary>
/// A local vehicle is spawned
/// </summary>
public static event EventHandler<SyncedVehicle> OnVehicleSpawned;
/// <summary>
/// A local vehicle is deleted
/// </summary>
public static event EventHandler<SyncedVehicle> OnVehicleDeleted;
/// <summary>
/// A local ped is spawned
/// </summary>
public static event EventHandler<SyncedPed> OnPedSpawned;
/// <summary>
/// A local ped is deleted
/// </summary>
public static event EventHandler<SyncedPed> OnPedDeleted;
/// <summary>
/// This is equivalent of <see cref="GTA.Script.Tick"/>.
/// </summary>
public static event EmptyEvent OnTick;
/// <summary>
/// This is equivalent of <see cref="Script.KeyDown"/>
/// </summary>
public static KeyEventHandler OnKeyDown;
/// <summary>
/// This is equivalent of <see cref="Script.KeyUp"/>
/// </summary>
public static KeyEventHandler OnKeyUp;
#region INVOKE
internal static void InvokeVehicleSpawned(SyncedVehicle v) { OnVehicleSpawned?.Invoke(null, v); }
internal static void InvokeVehicleDeleted(SyncedVehicle v) { OnVehicleDeleted?.Invoke(null, v); }
internal static void InvokePedSpawned(SyncedPed p) { OnPedSpawned?.Invoke(null, p); }
internal static void InvokePedDeleted(SyncedPed p) { OnPedDeleted?.Invoke(null, p); }
internal static void InvokePlayerDied() { OnPlayerDied?.Invoke(); }
internal static void InvokeTick() { OnTick?.Invoke(); }
internal static void InvokeKeyDown(object s, KeyEventArgs e) { OnKeyDown?.Invoke(s, e); }
internal static void InvokeKeyUp(object s, KeyEventArgs e) { OnKeyUp?.Invoke(s, e); }
internal static void InvokeCustomEventReceived(Packets.CustomEvent p)
{
var args = new CustomEventReceivedArgs() { Hash = p.Hash, Args = p.Args };
// Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
if (CustomEventHandlers.TryGetValue(p.Hash, out List<Action<CustomEventReceivedArgs>> handlers))
{
handlers.ForEach((x) => { x.Invoke(args); });
}
}
#endregion
}
#region PROPERTIES
@ -187,65 +164,57 @@ namespace RageCoop.Client.Scripting
/// Get the local player's ID
/// </summary>
/// <returns>PlayerID</returns>
public int LocalPlayerID => Main.LocalPlayerID;
public static int LocalPlayerID => Main.LocalPlayerID;
/// <summary>
/// Check if player is connected to a server
/// </summary>
public bool IsOnServer => Networking.IsOnServer;
public static bool IsOnServer => Networking.IsOnServer;
/// <summary>
/// Get an <see cref="System.Net.IPEndPoint"/> that the player is currently connected to, or null if not connected to the server
/// </summary>
public System.Net.IPEndPoint ServerEndPoint => Networking.IsOnServer ? Networking.ServerConnection?.RemoteEndPoint : null;
public static System.Net.IPEndPoint ServerEndPoint => Networking.IsOnServer ? Networking.ServerConnection?.RemoteEndPoint : null;
/// <summary>
/// Check if a RAGECOOP menu is visible
/// </summary>
public bool IsMenuVisible => Menus.CoopMenu.MenuPool.AreAnyVisible;
public static bool IsMenuVisible => Menus.CoopMenu.MenuPool.AreAnyVisible;
/// <summary>
/// Check if the RAGECOOP chat is visible
/// </summary>
public bool IsChatFocused => Main.MainChat.Focused;
public static bool IsChatFocused => Main.MainChat.Focused;
/// <summary>
/// Check if the RAGECOOP list of players is visible
/// </summary>
public bool IsPlayerListVisible => Util.GetTickCount64() - PlayerList.Pressed < 5000;
public static bool IsPlayerListVisible => Util.GetTickCount64() - PlayerList.Pressed < 5000;
/// <summary>
/// Get the version of RAGECOOP
/// </summary>
public Version CurrentVersion => Main.Version;
public static Version CurrentVersion => Main.Version;
/// <summary>
/// Get a <see cref="Core.Logger"/> that RAGECOOP is currently using.
/// </summary>
/// <returns></returns>
public Logger Logger => Main.Logger;
public static Logger Logger => Main.Logger;
/// <summary>
/// Get all players indexed by their ID
/// </summary>
public Dictionary<int, Player> Players => new Dictionary<int, Player>(PlayerList.Players);
public static Dictionary<int, Player> Players => new Dictionary<int, Player>(PlayerList.Players);
#endregion
#region FUNCTIONS
public ClientResource GetResource(string name)
{
if (Main.Resources.LoadedResources.TryGetValue(name.ToLower(), out var res))
{
return res;
}
return null;
}
/// <summary>
/// Connect to a server
/// </summary>
/// <param name="address">Address of the server, e.g. 127.0.0.1:4499</param>
/// <exception cref="InvalidOperationException">When a connection is active or being established</exception>
public void Connect(string address)
public static void Connect(string address)
{
if (Networking.IsOnServer || Networking.IsConnecting)
{
@ -256,7 +225,7 @@ namespace RageCoop.Client.Scripting
/// <summary>
/// Disconnect from current server or cancel the connection attempt.
/// </summary>
public void Disconnect()
public static void Disconnect()
{
if (Networking.IsOnServer || Networking.IsConnecting)
{
@ -268,7 +237,7 @@ namespace RageCoop.Client.Scripting
/// List all servers from master server address
/// </summary>
/// <returns></returns>
public List<ServerInfo> ListServers()
public static List<ServerInfo> ListServers()
{
return JsonConvert.DeserializeObject<List<ServerInfo>>(HttpHelper.DownloadString(Main.Settings.MasterServer));
}
@ -278,7 +247,7 @@ namespace RageCoop.Client.Scripting
/// </summary>
/// <param name="from">Name of the sender</param>
/// <param name="message">The player's message</param>
public void LocalChatMessage(string from, string message)
public static void LocalChatMessage(string from, string message)
{
Main.MainChat.AddMessage(from, message);
}
@ -287,20 +256,97 @@ namespace RageCoop.Client.Scripting
/// Send a chat message or command to server/other players
/// </summary>
/// <param name="message"></param>
public void SendChatMessage(string message)
public static void SendChatMessage(string message)
{
Networking.SendChatMessage(message);
}
/// <summary>
/// Send an event and data to the server.
/// </summary>
/// <param name="eventHash">An unique identifier of the event</param>
/// <param name="args">The objects conataing your data, see <see cref="CustomEventReceivedArgs"/> for a list of supported types</param>
public static void SendCustomEvent(CustomEventHash eventHash, params object[] args)
{
Networking.Peer.SendTo(new Packets.CustomEvent()
{
Args = args,
Hash = eventHash
}, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
}
/// <summary>
/// Send an event and data to the server
/// </summary>
/// <param name="flags"></param>
/// <param name="eventHash">An unique identifier of the event</param>
/// <param name="args">The objects conataing your data, see <see cref="CustomEventReceivedArgs"/> for a list of supported types</param>
public static void SendCustomEvent(CustomEventFlags flags, CustomEventHash eventHash, params object[] args)
{
Networking.Peer.SendTo(new Packets.CustomEvent(flags)
{
Args = args,
Hash = eventHash
}, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
}
/// <summary>
/// Register an handler to the specifed event hash, one event can have multiple handlers. This will be invoked from backgound thread, use <see cref="QueueAction(Action)"/> in the handler to dispatch code to script thread.
/// </summary>
/// <param name="hash">An unique identifier of the event, you can hash your event name with <see cref="Core.Scripting.CustomEvents.Hash(string)"/></param>
/// <param name="handler">An handler to be invoked when the event is received from the server. </param>
public static void RegisterCustomEventHandler(CustomEventHash hash, Action<CustomEventReceivedArgs> handler)
{
lock (CustomEventHandlers)
{
if (!CustomEventHandlers.TryGetValue(hash, out List<Action<CustomEventReceivedArgs>> handlers))
{
CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>());
}
handlers.Add(handler);
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public static void RequestSharedFile(string name, Action<string> callback)
{
EventHandler<string> handler = (s, e) =>
{
if (e.EndsWith(name))
{
callback(e);
}
};
DownloadManager.DownloadCompleted += handler;
Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest()
{
Name = name,
},
(p) =>
{
if (p.Response != FileResponse.Loaded)
{
DownloadManager.DownloadCompleted -= handler;
throw new ArgumentException("Requested file was not found on the server: " + name);
}
});
}
#endregion
/// <summary>
/// Queue an action to be executed on next tick.
/// </summary>
/// <param name="a"></param>
public void QueueAction(Action a)
public static void QueueAction(Action a)
{
WorldThread.QueueAction(a);
}
public void QueueActionAndWait(Action a, int timeout = 15000)
public static void QueueActionAndWait(Action a, int timeout = 15000)
{
var done = new AutoResetEvent(false);
Exception e = null;
@ -325,75 +371,5 @@ namespace RageCoop.Client.Scripting
{
WorldThread.QueueAction(a);
}
/// <summary>
/// Send an event and data to the server.
/// </summary>
/// <param name="eventHash">An unique identifier of the event</param>
/// <param name="args">The objects conataing your data, see <see cref="CustomEventReceivedArgs"/> for a list of supported types</param>
public void SendCustomEvent(CustomEventHash eventHash, params object[] args)
{
Networking.Peer.SendTo(new Packets.CustomEvent()
{
Args = args,
Hash = eventHash
}, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
}
/// <summary>
/// Send an event and data to the server
/// </summary>
/// <param name="flags"></param>
/// <param name="eventHash">An unique identifier of the event</param>
/// <param name="args">The objects conataing your data, see <see cref="CustomEventReceivedArgs"/> for a list of supported types</param>
public void SendCustomEvent(CustomEventFlags flags, CustomEventHash eventHash, params object[] args)
{
Networking.Peer.SendTo(new Packets.CustomEvent(flags)
{
Args = args,
Hash = eventHash
}, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public void RequestSharedFile(string name, Action<string> callback)
{
EventHandler<string> handler = (s, e) =>
{
if (e.EndsWith(name))
{
callback(e);
}
};
DownloadManager.DownloadCompleted += handler;
Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest()
{
Name = name,
},
(p) =>
{
if (p.Response != FileResponse.Loaded)
{
DownloadManager.DownloadCompleted -= handler;
throw new ArgumentException("Requested file was not found on the server: " + name);
}
});
}
public static void RegisterCustomEventHandler(CustomEventHash hash, Action<CustomEventReceivedArgs> handler)
{
lock (Events.CustomEventHandlers)
{
if (!Events.CustomEventHandlers.TryGetValue(hash, out List<Action<CustomEventReceivedArgs>> handlers))
{
Events.CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>());
}
handlers.Add(handler);
}
}
#endregion
}
}

View File

@ -8,13 +8,9 @@ namespace RageCoop.Client.Scripting
/// </summary>
public abstract class ClientScript : GTA.Script
{
/// <summary>
/// An <see cref="Scripting.API"/> instance to communicate with RageCoop
/// </summary>
protected static API API => Main.API;
/// <summary>
/// This method would be called from main thread, right after the constructor.
/// This method would be called from main thread, right after all script constructors are invoked.
/// </summary>
public abstract void OnStart();

View File

@ -1,218 +0,0 @@
using RageCoop.Core;
using Console = GTA.Console;
using SHVDN;
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Concurrent;
using static System.Net.WebRequestMethods;
namespace RageCoop.Client.Scripting
{
internal class ResourceDomain : MarshalByRefObject, IDisposable
{
public static ConcurrentDictionary<string, ResourceDomain> LoadedDomains => new ConcurrentDictionary<string, ResourceDomain>(_loadedDomains);
static readonly ConcurrentDictionary<string, ResourceDomain> _loadedDomains = new ConcurrentDictionary<string, ResourceDomain>();
public static ScriptDomain PrimaryDomain;
public string BaseDirectory => AppDomain.CurrentDomain.BaseDirectory;
private ScriptDomain CurrentDomain => ScriptDomain.CurrentDomain;
private static ConcurrentDictionary<string, Action<object>> _callBacks = new ConcurrentDictionary<string, Action<object>>();
API API => API.GetInstance();
private ResourceDomain(ScriptDomain primary, string[] apiPaths)
{
AppDomain.CurrentDomain.SetData("Primary", primary);
foreach (var apiPath in apiPaths)
{
try
{
Assembly.LoadFrom(apiPath);
}
catch
{
}
}
PrimaryDomain = primary;
primary.Tick += Tick;
primary.KeyEvent += KeyEvent;
CurrentDomain.Start();
SetupScripts();
Console.WriteLine($"Loaded domain: {AppDomain.CurrentDomain.FriendlyName}, {AppDomain.CurrentDomain.BaseDirectory}");
}
public static bool IsLoaded(string dir)
{
return _loadedDomains.ContainsKey(Path.GetFullPath(dir).ToLower());
}
public void SetupScripts()
{
foreach (var s in GetClientScripts())
{
try
{
API.Logger.Debug("Starting script: " + s.GetType().FullName);
var script = (ClientScript)s;
var res = API.GetResource(Path.GetFileName(Directory.GetParent(script.Filename).FullName));
if (res == null) { API.Logger.Warning("Failed to locate resource for script: " + script.Filename); continue; }
script.CurrentResource = res;
script.CurrentFile = res.Files.Values.Where(x => x.Name.ToLower() == script.Filename.Substring(res.ScriptsDirectory.Length + 1).Replace('\\', '/')).FirstOrDefault();
res.Scripts.Add(script);
s.GetType().Assembly.GetReferencedAssemblies().ForEach(x => Assembly.Load(x.FullName));
script.OnStart();
}
catch (Exception ex)
{
API.Logger.Error($"Failed to start {s.GetType().FullName}", ex);
}
}
}
public object[] GetClientScripts()
{
Console.WriteLine("Running scripts: " + ScriptDomain.CurrentDomain.RunningScripts.Select(x => x.ScriptInstance.GetType().FullName).Dump());
return ScriptDomain.CurrentDomain.RunningScripts.Where(x =>
x.ScriptInstance.GetType().IsSubclassOf(typeof(ClientScript)) &&
!x.ScriptInstance.GetType().IsAbstract).Select(x => x.ScriptInstance).ToArray();
}
public static void RegisterCallBackForCurrentDomain(string name, Action<object> callback)
{
if (!_callBacks.TryAdd(name, callback))
{
throw new Exception("Failed to add callback");
}
}
public void DoCallback(string name,object data)
{
if(_callBacks.TryGetValue(name, out var callBack))
{
callBack(data);
}
}
public static void DoCallBack(string name,object data)
{
foreach(var d in _loadedDomains)
{
d.Value.DoCallback(name, data);
}
}
public static ResourceDomain Load(string dir = @"RageCoop\Scripts\Debug")
{
lock (_loadedDomains)
{
dir = Path.GetFullPath(dir).ToLower();
if (!Util.IsPrimaryDomain)
{
throw new InvalidOperationException("Cannot load in another domain");
}
if (IsLoaded(dir))
{
throw new Exception("Already loaded");
}
ScriptDomain sDomain = null;
try
{
dir = Path.GetFullPath(dir);
Directory.CreateDirectory(dir);
// Copy test script
// File.Copy(@"M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\bin\Debug\TestScript.dll", Path.Combine(dir, Path.GetFileName("TestScript.dll")), true);
// Load domain in main thread
Main.QueueToMainThread(() =>
{
var api = new List<string>();
api.Add(typeof(ResourceDomain).Assembly.Location);
api.AddRange(typeof(ResourceDomain).Assembly.GetReferencedAssemblies()
.Select(x => Assembly.Load(x.FullName).Location)
.Where(x => !string.IsNullOrEmpty(x)));
sDomain = ScriptDomain.Load(Directory.GetParent(typeof(ScriptDomain).Assembly.Location).FullName, dir);
sDomain.AppDomain.SetData("Console", ScriptDomain.CurrentDomain.AppDomain.GetData("Console"));
sDomain.AppDomain.SetData("RageCoop.Client.API", API.GetInstance());
_loadedDomains.TryAdd(dir, (ResourceDomain)sDomain.AppDomain.CreateInstanceFromAndUnwrap(typeof(ResourceDomain).Assembly.Location, typeof(ResourceDomain).FullName, false, BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { ScriptDomain.CurrentDomain, api.ToArray() }, null, null));
});
// Wait till next tick
GTA.Script.Yield();
return _loadedDomains[dir];
}
catch (Exception ex)
{
GTA.UI.Notification.Show(ex.ToString());
Main.Logger.Error(ex);
if (sDomain != null)
{
ScriptDomain.Unload(sDomain);
}
throw;
}
}
}
public static void Unload(ResourceDomain domain)
{
lock (_loadedDomains)
{
Exception ex = null;
Main.QueueToMainThread(() =>
{
try
{
domain.Dispose();
ScriptDomain.Unload(domain.CurrentDomain);
_loadedDomains.TryRemove(domain.BaseDirectory, out _);
}
catch (Exception e) { ex = e; }
});
GTA.Script.Yield();
if (ex != null) { throw ex; }
}
}
public static void Unload(string dir)
{
Unload(_loadedDomains[Path.GetFullPath(dir).ToLower()]);
}
public static void UnloadAll()
{
lock (_loadedDomains)
{
foreach (var d in _loadedDomains.Values.ToArray())
{
Unload(d);
}
}
}
private void Tick(object sender, EventArgs args)
{
CurrentDomain.DoTick();
}
private void KeyEvent(Keys keys, bool status)
{
CurrentDomain.DoKeyEvent(keys, status);
}
public void Dispose()
{
PrimaryDomain.Tick -= Tick;
PrimaryDomain.KeyEvent -= KeyEvent;
foreach (var s in GetClientScripts())
{
try
{
API.Logger.Debug("Stopping script: " + s.GetType().FullName);
((ClientScript)s).OnStop();
}
catch (Exception ex)
{
API.Logger.Error($"Failed to stop {s.GetType().FullName}", ex);
}
}
}
}
}

View File

@ -1,6 +1,7 @@
using ICSharpCode.SharpZipLib.Zip;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using SHVDN;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -14,7 +15,7 @@ namespace RageCoop.Client.Scripting
/// <summary>
///
/// </summary>
public class ClientResource : MarshalByRefObject
public class ClientResource
{
/// <summary>
/// Name of the resource
@ -59,19 +60,31 @@ namespace RageCoop.Client.Scripting
Logger?.Info($"Loading resource: {Path.GetFileNameWithoutExtension(zip)}");
Unpack(zipPath, Path.Combine(path, "Data"));
}
Main.API.QueueActionAndWait(() => ResourceDomain.Load(path));
Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories).Where(x => x.CanBeIgnored()).ForEach(x => File.Delete(x));
// Load it in main thread
API.QueueActionAndWait(() =>
{
Main.QueueToMainThreadAndWait(() =>
{
Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories).ForEach(x => ScriptDomain.CurrentDomain.StartScripts(x));
SetupScripts();
});
});
}
public void Unload()
{
ResourceDomain.UnloadAll();
StopScripts();
LoadedResources.Clear();
Loader.DomainContext.RequestUnload();
}
private void Unpack(string zipPath, string dataFolderRoot)
{
var r = new ClientResource()
{
Logger = Main.API.Logger,
Logger = API.Logger,
Scripts = new List<ClientScript>(),
Name = Path.GetFileNameWithoutExtension(zipPath),
DataFolder = Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(zipPath)),
@ -108,7 +121,58 @@ namespace RageCoop.Client.Scripting
r.Files.Add(relativeName, rfile);
}
LoadedResources.TryAdd(r.Name.ToLower(), r);
LoadedResources.TryAdd(r.Name, r);
}
void SetupScripts()
{
foreach (var s in GetClientScripts())
{
try
{
API.Logger.Debug("Starting script: " + s.GetType().FullName);
var script = (ClientScript)s;
if (LoadedResources.TryGetValue(Directory.GetParent(script.Filename).Name, out var r))
{
script.CurrentResource = r;
}
else
{
API.Logger.Warning("Failed to locate resource for script: " + script.Filename);
}
var res = script.CurrentResource;
script.CurrentFile = res?.Files.Values.Where(x => x.Name.ToLower() == script.Filename.Substring(res.ScriptsDirectory.Length + 1).Replace('\\', '/')).FirstOrDefault();
res?.Scripts.Add(script);
script.OnStart();
}
catch (Exception ex)
{
API.Logger.Error($"Failed to start {s.GetType().FullName}", ex);
}
API.Logger.Debug("Started script: " + s.GetType().FullName);
}
}
void StopScripts()
{
foreach (var s in GetClientScripts())
{
try
{
API.Logger.Debug("Stopping script: " + s.GetType().FullName);
((ClientScript)s).OnStop();
}
catch (Exception ex)
{
API.Logger.Error($"Failed to stop {s.GetType().FullName}", ex);
}
}
}
public static object[] GetClientScripts()
{
return ScriptDomain.CurrentDomain.RunningScripts.Where(x =>
x.ScriptInstance.GetType().IsScript(typeof(ClientScript))).Select(x => x.ScriptInstance).ToArray();
}
}

View File

@ -11,7 +11,6 @@ namespace RageCoop.Client
{
internal class EntityPool
{
static readonly API API = Main.API;
public static object PedsLock = new object();
#if BENCHMARK
private static Stopwatch PerfCounter=new Stopwatch();

View File

@ -8,7 +8,6 @@ namespace RageCoop.Client
{
internal static class SyncEvents
{
static readonly Scripting.API API = Main.API;
#region TRIGGER
public static void TriggerPedKilled(SyncedPed victim)

View File

@ -20,12 +20,6 @@ namespace RageCoop.Client
{
internal static class Util
{
public static bool IsPrimaryDomain => AppDomain.CurrentDomain.GetData("Primary") == null;
public static ScriptDomain GetPrimaryDomain()
{
if (IsPrimaryDomain) { return ScriptDomain.CurrentDomain; }
else { return AppDomain.CurrentDomain.GetData("Primary") as ScriptDomain; }
}
public static SizeF ResolutionMaintainRatio
{
get
@ -249,11 +243,11 @@ namespace RageCoop.Client
// Move log file so it doesn't get deleted
Main.Logger.Dispose();
var path = Main.Logger.LogPath + ".last.log";
var path = Main.LogPath + ".last.log";
try
{
if (File.Exists(path)) { File.Delete(path); }
if (File.Exists(Main.Logger.LogPath)) { File.Move(Main.Logger.LogPath, path); }
if (File.Exists(Main.LogPath)) { File.Move(Main.LogPath, path); }
}
catch (Exception ex)
{

View File

@ -23,7 +23,6 @@ namespace RageCoop.Client
public WorldThread()
{
Instance = this;
if (!Util.IsPrimaryDomain) { Abort(); return; }
Tick += OnTick;
Aborted += (sender, e) =>
{

View File

@ -231,6 +231,11 @@ namespace RageCoop.Core
}
return addresses;
}
public static StreamWriter OpenWriter(string path, FileMode mode = FileMode.Create, FileAccess access = FileAccess.Write, FileShare share = FileShare.ReadWrite)
{
return new StreamWriter(File.Open(path, mode, access, share));
}
}
internal class IpInfo
{
@ -412,13 +417,11 @@ namespace RageCoop.Core
return output;
}
public static bool IsSubclassOf(this Type type, string baseTypeName)
public static bool IsScript(this Type type,Type scriptType)
{
for (Type t = type.BaseType; t != null; t = t.BaseType)
if (t.FullName == baseTypeName)
return true;
return false;
return !type.IsAbstract && type.IsSubclassOf(scriptType);
}
}
/// <summary>

View File

@ -1,4 +1,7 @@
using System;
using GTA.NaturalMotion;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
@ -6,12 +9,28 @@ using System.Threading;
namespace RageCoop.Core
{
public enum LogLevel
{
Trace = 0,
Debug = 1,
Info = 2,
Warning = 3,
Error = 4
}
/// <summary>
///
/// </summary>
public class Logger : MarshalByRefObject,IDisposable
public class Logger : MarshalByRefObject, IDisposable
{
public class LogLine
{
internal LogLine() { }
public DateTime TimeStamp;
public LogLevel LogLevel;
public string Message;
}
/// <summary>
/// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
/// </summary>
@ -19,40 +38,29 @@ namespace RageCoop.Core
/// <summary>
/// Name of this logger
/// </summary>
public string Name { get; set; }
/// <summary>
/// Path to log file.
/// </summary>
public string LogPath;
/// <summary>
/// Whether to flush messages to console instead of log file
/// </summary>
public bool UseConsole = false;
private StreamWriter logWriter;
public string Name = "Logger";
public readonly string DateTimeFormat = "HH:mm:ss";
/// <summary>
/// Whether to use UTC time for timestamping the log
/// </summary>
public readonly bool UseUtc = false;
public List<StreamWriter> Writers = new List<StreamWriter> { new StreamWriter(Console.OpenStandardOutput()) };
public int FlushInterval = 1000;
public event FlushDelegate OnFlush;
public bool FlushImmediately = false;
public delegate void FlushDelegate(LogLine line, string fomatted);
private string Buffer = "";
private readonly Thread LoggerThread;
private bool Stopping = false;
private readonly bool FlushImmediately;
public event EventHandler<string> OnFlush;
internal Logger(bool flushImmediately = false, bool overwrite = true)
private readonly ConcurrentQueue<LogLine> _queuedLines = new ConcurrentQueue<LogLine>();
internal Logger()
{
FlushImmediately = flushImmediately;
if (File.Exists(LogPath) && overwrite) { File.Delete(LogPath); }
Name = Process.GetCurrentProcess().Id.ToString();
if (!flushImmediately)
if (!FlushImmediately)
{
LoggerThread = new Thread(() =>
{
if (!UseConsole)
{
while (LogPath == default)
{
Thread.Sleep(100);
}
if (File.Exists(LogPath) && overwrite) { File.Delete(LogPath); }
}
while (!Stopping)
{
Flush();
@ -69,17 +77,7 @@ namespace RageCoop.Core
/// <param name="message"></param>
public void Info(string message)
{
if (LogLevel > 2) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [INF] {1}", Date(), message, Name);
Buffer += msg + "\r\n";
}
if (FlushImmediately)
{
Flush();
}
Enqueue(2, message);
}
/// <summary>
///
@ -87,18 +85,7 @@ namespace RageCoop.Core
/// <param name="message"></param>
public void Warning(string message)
{
if (LogLevel > 3) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [WRN] {1}", Date(), message, Name);
Buffer += msg + "\r\n";
}
if (FlushImmediately)
{
Flush();
}
Enqueue(3, message);
}
/// <summary>
///
@ -106,17 +93,7 @@ namespace RageCoop.Core
/// <param name="message"></param>
public void Error(string message)
{
if (LogLevel > 4) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), message, Name);
Buffer += msg + "\r\n";
}
if (FlushImmediately)
{
Flush();
}
Enqueue(4, message);
}
/// <summary>
///
@ -125,18 +102,7 @@ namespace RageCoop.Core
/// <param name="error"></param>
public void Error(string message, Exception error)
{
if (LogLevel > 4) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [ERR] {1}:{3}", Date(), message, Name, error.Message);
Buffer += msg + "\r\n";
Trace(error.ToString());
}
if (FlushImmediately)
{
Flush();
}
Enqueue(4, $"{message}:\n {error}");
}
/// <summary>
///
@ -144,17 +110,7 @@ namespace RageCoop.Core
/// <param name="ex"></param>
public void Error(Exception ex)
{
if (LogLevel > 4) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n" + ex.Message, Name);
Buffer += msg + "\r\n";
Trace(ex.ToString());
}
if (FlushImmediately)
{
Flush();
}
Enqueue(4, ex.ToString());
}
/// <summary>
///
@ -162,18 +118,7 @@ namespace RageCoop.Core
/// <param name="message"></param>
public void Debug(string message)
{
if (LogLevel > 1) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [DBG] {1}", Date(), message, Name);
Buffer += msg + "\r\n";
}
if (FlushImmediately)
{
Flush();
}
Enqueue(1, message);
}
/// <summary>
///
@ -181,51 +126,43 @@ namespace RageCoop.Core
/// <param name="message"></param>
public void Trace(string message)
{
if (LogLevel > 0) { return; }
lock (Buffer)
Enqueue(0, message);
}
public void Enqueue(int level, string message)
{
if (level < LogLevel) { return; }
_queuedLines.Enqueue(new LogLine()
{
string msg = string.Format("[{0}][{2}] [TRC] {1}", Date(), message, Name);
Buffer += msg + "\r\n";
}
Message = message,
TimeStamp = UseUtc ? DateTime.UtcNow : DateTime.Now,
LogLevel = (LogLevel)level
});
if (FlushImmediately)
{
Flush();
}
}
private string Date()
string Format(LogLine line)
{
return DateTime.Now.ToString("HH:mm:ss");
return string.Format("[{0}][{2}] [{3}] {1}", line.TimeStamp.ToString(DateTimeFormat), line.Message, Name, line.LogLevel.ToString());
}
/// <summary>
///
/// </summary>
public void Flush()
{
lock (Buffer)
lock (_queuedLines)
{
if (Buffer != "")
try
{
if (UseConsole)
while (_queuedLines.TryDequeue(out var line))
{
Console.Write(Buffer);
Buffer = "";
}
else
{
try
{
logWriter = new StreamWriter(LogPath, true, Encoding.UTF8);
logWriter.Write(Buffer);
logWriter.Close();
OnFlush?.Invoke(this,Buffer);
Buffer = "";
}
catch { }
var formatted = Format(line);
Writers.ForEach(x => { x.WriteLine(formatted); x.Flush(); });
OnFlush?.Invoke(line, formatted);
}
}
catch { }
}
}
/// <summary>

View File

@ -144,7 +144,7 @@ namespace RageCoop.Core
void Deserialize(NetIncomingMessage m);
}
internal abstract class Packet : MarshalByRefObject, IPacket
internal abstract class Packet : IPacket
{
public abstract PacketType Type { get; }
public void Pack(NetOutgoingMessage m)

View File

@ -27,7 +27,7 @@ namespace RageCoop.Core.Scripting
/// <summary>
/// Struct to identify different event using hash
/// </summary>
public class CustomEventHash : MarshalByRefObject
public struct CustomEventHash
{
private static readonly MD5 Hasher = MD5.Create();
private static readonly Dictionary<int, string> Hashed = new Dictionary<int, string>();

View File

@ -6,7 +6,7 @@ namespace RageCoop.Core.Scripting
/// <summary>
///
/// </summary>
public class ResourceFile : MarshalByRefObject
public class ResourceFile
{
/// <summary>
/// Full name with relative path of this file

View File

@ -38,10 +38,9 @@ namespace RageCoop.Server
AppDomain.CurrentDomain.UnhandledException += UnhandledException;
mainLogger = new Logger()
{
LogPath = "RageCoop.Server.log",
UseConsole = true,
Name = "Server"
};
mainLogger.Writers.Add(CoreUtils.OpenWriter("RageCoop.Server.log"));
try
{
Console.Title = "RAGECOOP";

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.