Unload fix and small tweaks

This commit is contained in:
sardelka9515
2022-10-15 13:52:49 +08:00
parent b48b15b652
commit 411b199a98
14 changed files with 84 additions and 94 deletions

View File

@ -13,11 +13,11 @@ using System.Windows.Forms;
namespace RageCoop.Client.Loader
{
public class DomainContext : MarshalByRefObject, IDisposable
public class LoaderContext : 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 static ConcurrentDictionary<string, LoaderContext> LoadedDomains => new ConcurrentDictionary<string, LoaderContext>(_loadedDomains);
static readonly ConcurrentDictionary<string, LoaderContext> _loadedDomains = new ConcurrentDictionary<string, LoaderContext>();
public bool UnloadRequested;
public string BaseDirectory => AppDomain.CurrentDomain.BaseDirectory;
@ -40,7 +40,7 @@ namespace RageCoop.Client.Loader
{
return _loadedDomains.ContainsKey(Path.GetFullPath(dir).ToLower());
}
public static DomainContext Load(string dir)
public static LoaderContext Load(string dir)
{
lock (_loadedDomains)
{
@ -52,7 +52,7 @@ namespace RageCoop.Client.Loader
ScriptDomain newDomain = null;
try
{
dir = Path.GetFullPath(dir);
dir = Path.GetFullPath(dir).ToLower();
Directory.CreateDirectory(dir);
Exception e = null;
// Load domain in main thread
@ -72,21 +72,21 @@ namespace RageCoop.Client.Loader
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,
var context = (LoaderContext)newDomain.AppDomain.CreateInstanceFromAndUnwrap(
typeof(LoaderContext).Assembly.Location,
typeof(LoaderContext).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 );
_loadedDomains.TryAdd(dir, context);
}
catch (Exception ex)
{
@ -111,7 +111,7 @@ namespace RageCoop.Client.Loader
}
}
public static void Unload(DomainContext domain)
public static void Unload(LoaderContext domain)
{
lock (_loadedDomains)
{
@ -122,17 +122,23 @@ namespace RageCoop.Client.Loader
{
try
{
if (!_loadedDomains.TryRemove(domain.BaseDirectory, out _))
if (!_loadedDomains.TryRemove(domain.BaseDirectory.ToLower(), out _))
{
throw new Exception("Failed to remove domain from list");
}
domain.Dispose();
ScriptDomain.Unload(domain.CurrentDomain);
}
catch (Exception e) { ex = e; }
catch (Exception e) {
ex = e;
GTA.UI.Notification.Show(ex.ToString());
}
});
GTA.Script.Yield();
if (ex != null) { throw ex; }
if (ex != null)
{
throw ex;
}
Console.Info("Unloaded domain: " + name);
}
}
@ -155,15 +161,15 @@ namespace RageCoop.Client.Loader
#region LOAD-CONTEXT
private DomainContext()
private LoaderContext()
{
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");
public static ScriptDomain PrimaryDomain => AppDomain.CurrentDomain.GetData("Primary") as ScriptDomain;
public static LoaderContext CurrentContext => AppDomain.CurrentDomain.GetData("RageCoop.Client.LoaderContext") as LoaderContext;
/// <summary>
/// Request the current domain to be unloaded
/// </summary>

View File

@ -1,53 +1,61 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using GTA;
using SHVDN;
using Console = GTA.Console;
namespace RageCoop.Client.Loader
{
public class Main : Script
public class Main : GTA.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>();
static int MainThreadID;
public Main()
{
if (LoaderContext.PrimaryDomain != null) { throw new InvalidOperationException("Improperly placed loader assembly, please re-install to fix this issue"); }
Tick += OnTick;
SHVDN.ScriptDomain.CurrentDomain.Tick += DomainTick;
KeyDown += OnKeyDown;
}
private void OnKeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
Aborted += (s, e) => LoaderContext.UnloadAll();
}
private void OnTick(object sender, EventArgs e)
{
while (Game.IsLoading)
{
Script.Yield();
GTA.Script.Yield();
}
DomainContext.CheckForUnloadRequest();
if (!DomainContext.IsLoaded(ScriptsLocation))
LoaderContext.CheckForUnloadRequest();
if (!LoaderContext.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);
LoaderContext.Load(ScriptsLocation);
}
}
internal static void QueueToMainThread(Action task)
{
TaskQueue.Enqueue(task);
if (Thread.CurrentThread.ManagedThreadId != MainThreadID)
{
TaskQueue.Enqueue(task);
}
else
{
task();
}
}
private static void DomainTick(object sender, EventArgs e)
{
if (MainThreadID == default) { MainThreadID=Thread.CurrentThread.ManagedThreadId;}
while (TaskQueue.TryDequeue(out var task))
{
try

View File

@ -15,7 +15,7 @@
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutPutPath>..\..\bin\Debug\Client</OutPutPath>
<OutPutPath>..\..\bin\Debug\Client.Loader</OutPutPath>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
@ -24,7 +24,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutPutPath>..\..\bin\Release\Client</OutPutPath>
<OutPutPath>..\..\bin\Release\Client.Loader</OutPutPath>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
@ -50,7 +50,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DomainLoader.cs" />
<Compile Include="LoaderContext.cs" />
<Compile Include="Main.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View File

@ -19,6 +19,7 @@ namespace RageCoop.Client
public static Script Instance;
public DevTool()
{
Util.StartUpCheck();
Instance = this;
Tick += OnTick;
KeyDown += OnKeyDown;

View File

@ -50,6 +50,7 @@ namespace RageCoop.Client
/// </summary>
public Main()
{
Util.StartUpCheck();
Console.Info($"Starting {typeof(Main).FullName}, domain: {AppDomain.CurrentDomain.Id} {AppDomain.CurrentDomain.FriendlyName}");
try
{
@ -76,7 +77,7 @@ namespace RageCoop.Client
Directory.CreateDirectory(Settings.DataDirectory);
Logger = new Logger()
{
Writers = new List<StreamWriter> { CoreUtils.OpenWriter(LogPath)},
Writers = new List<StreamWriter> { CoreUtils.OpenWriter(LogPath) },
#if DEBUG
LogLevel = 0,
#else

View File

@ -60,7 +60,7 @@ namespace RageCoop.Client
private static void ReloadDomain(object sender, EventArgs e)
{
Loader.DomainContext.RequestUnload();
Loader.LoaderContext.RequestUnload();
}
}
}

View File

@ -76,7 +76,7 @@ namespace RageCoop.Client.Menus
try { File.Delete(Path.Combine("Scripts", "RageCoop.Client.Installer.exe")); } catch { }
API.QueueAction(() =>
{
Util.Reload();
Loader.LoaderContext.RequestUnload();
IsUpdating = false;
});
}

View File

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

View File

@ -120,16 +120,22 @@ namespace RageCoop.Client.Scripting
/// <summary>
/// This is equivalent of <see cref="GTA.Script.Tick"/>.
/// </summary>
/// <remarks>Calling <see cref="GTA.Script.Yield"/> in the handler will interfer other scripts, subscribe to <see cref="GTA.Script.Tick"/> instead.</remarks>
[Obsolete]
public static event EmptyEvent OnTick;
/// <summary>
/// This is equivalent of <see cref="Script.KeyDown"/>
/// </summary>
/// <remarks>Calling <see cref="GTA.Script.Yield"/> in the handler will interfer other scripts, subscribe to <see cref="GTA.Script.KeyDown"/> instead.</remarks>
[Obsolete]
public static KeyEventHandler OnKeyDown;
/// <summary>
/// This is equivalent of <see cref="Script.KeyUp"/>
/// </summary>
/// <remarks>Calling <see cref="GTA.Script.Yield"/> in the handler will interfer other scripts, subscribe to <see cref="GTA.Script.KeyUp"/> instead.</remarks>
[Obsolete]
public static KeyEventHandler OnKeyUp;
#region INVOKE

View File

@ -77,7 +77,7 @@ namespace RageCoop.Client.Scripting
StopScripts();
LoadedResources.Clear();
Loader.DomainContext.RequestUnload();
Loader.LoaderContext.RequestUnload();
}
private void Unpack(string zipPath, string dataFolderRoot)
@ -110,7 +110,18 @@ namespace RageCoop.Client.Scripting
var assemblies = new Dictionary<ResourceFile, Assembly>();
foreach (var file in Directory.GetFiles(scriptsDir, "*", SearchOption.AllDirectories))
{
if (Path.GetFileName(file).CanBeIgnored()) { try { File.Delete(file); } catch { } continue; }
if (Path.GetFileName(file).CanBeIgnored())
{
try
{
File.Delete(file);
}
catch (Exception ex)
{
API.Logger.Warning($"Failed to delete API assembly: {file}. This may or may cause some unexpected behaviours.\n{ex}");
}
continue;
}
var relativeName = file.Substring(scriptsDir.Length + 1).Replace('\\', '/');
var rfile = new ResourceFile()
{

View File

@ -14,12 +14,20 @@ using System.Windows.Forms;
using System.Xml.Serialization;
using Newtonsoft.Json;
using SHVDN;
using System.Runtime.InteropServices.ComTypes;
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
namespace RageCoop.Client
{
internal static class Util
{
public static void StartUpCheck()
{
if (AppDomain.CurrentDomain.GetData("RageCoop.Client.LoaderContext") == null)
{
throw new Exception($"Client not loaded with loader, please re-install using the installer to fix this issue");
}
}
public static SizeF ResolutionMaintainRatio
{
get
@ -207,62 +215,8 @@ namespace RageCoop.Client
Function.Call(Hash.SET_RADIO_TO_STATION_INDEX, index);
}
#region WIN32
private const UInt32 WM_KEYDOWN = 0x0100;
public static void Reload()
{
string reloadKey = "None";
var lines = File.ReadAllLines("ScriptHookVDotNet.ini");
foreach (var l in lines)
{
var ss = l.Split('=');
if (ss.Length > 0 && ss[0] == "ReloadKey")
{
reloadKey = ss[1];
}
}
var lineList = lines.ToList();
if (reloadKey == "None")
{
foreach (var l in lines)
{
var ss = l.Split('=');
if (ss.Length > 0 && ss[0] == "ReloadKey")
{
reloadKey = ss[1];
lineList.Remove(l);
}
}
lineList.Add("ReloadKey=Insert");
File.WriteAllLines("ScriptHookVDotNet.ini", lineList.ToArray());
GTA.UI.Notification.Show("Reload cannot be performed automatically, please type \"Reload()\" manually in the SHVDN console.");
}
Keys key = (Keys)Enum.Parse(typeof(Keys), reloadKey, true);
// Move log file so it doesn't get deleted
Main.Logger.Dispose();
var path = Main.LogPath + ".last.log";
try
{
if (File.Exists(path)) { File.Delete(path); }
if (File.Exists(Main.LogPath)) { File.Move(Main.LogPath, path); }
}
catch (Exception ex)
{
GTA.UI.Notification.Show(ex.Message);
}
PostMessage(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle, WM_KEYDOWN, (int)key, 0);
}
[DllImport("user32.dll")]
private static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
[DllImport("kernel32.dll")]
public static extern ulong GetTickCount64();
#endregion
}
}

View File

@ -22,6 +22,7 @@ namespace RageCoop.Client
/// </summary>
public WorldThread()
{
Util.StartUpCheck();
Instance = this;
Tick += OnTick;
Aborted += (sender, e) =>

View File

@ -26,6 +26,8 @@ namespace RageCoop.Core
private static readonly HashSet<string> ToIgnore = new HashSet<string>()
{
"RageCoop.Client",
"RageCoop.Client.Loader",
"RageCoop.Client.Installer",
"RageCoop.Core",
"RageCoop.Server",
"ScriptHookVDotNet2",

View File

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