Files
RAGECOOP-V/Client/Scripts/Scripting/Resources.cs

191 lines
7.1 KiB
C#
Raw Normal View History

2022-07-20 17:50:01 +08:00
using ICSharpCode.SharpZipLib.Zip;
2022-07-01 12:22:31 +08:00
using RageCoop.Core;
2022-07-20 17:50:01 +08:00
using RageCoop.Core.Scripting;
using SHVDN;
2022-06-30 09:28:13 +08:00
using System;
2022-10-09 23:35:30 +08:00
using System.Collections.Concurrent;
2022-07-01 12:22:31 +08:00
using System.Collections.Generic;
2022-07-20 17:50:01 +08:00
using System.IO;
using System.Linq;
using System.Reflection;
namespace RageCoop.Client.Scripting
{
2022-07-20 17:50:01 +08:00
/// <summary>
///
/// </summary>
public class ClientResource
2022-07-20 17:50:01 +08:00
{
/// <summary>
/// Name of the resource
/// </summary>
public string Name { get; internal set; }
2022-10-10 16:45:58 +08:00
/// <summary>
/// Directory where the scripts is loaded from
/// </summary>
public string ScriptsDirectory { get; internal set; }
2022-07-20 17:50:01 +08:00
/// <summary>
/// A resource-specific folder that can be used to store your files.
/// </summary>
2022-10-10 16:45:58 +08:00
public string DataFolder { get; internal set; }
2022-07-20 17:50:01 +08:00
/// <summary>
/// Get all <see cref="ClientScript"/> instance in this resource.
/// </summary>
public List<ClientScript> Scripts { get; internal set; } = new List<ClientScript>();
/// <summary>
/// Get the <see cref="ResourceFile"/> where this script is loaded from.
/// </summary>
public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>();
/// <summary>
/// A <see cref="Core.Logger"/> instance that can be used to debug your resource.
/// </summary>
public Logger Logger { get; internal set; }
}
internal class Resources
{
2022-10-09 23:35:30 +08:00
internal readonly ConcurrentDictionary<string, ClientResource> LoadedResources = new ConcurrentDictionary<string, ClientResource>();
2022-09-06 21:46:35 +08:00
private Logger Logger { get; set; }
2022-07-20 17:50:01 +08:00
public Resources()
{
Logger = Main.Logger;
}
public void Load(string path, string[] zips)
{
LoadedResources.Clear();
foreach (var zip in zips)
{
var zipPath = Path.Combine(path, zip);
Logger?.Info($"Loading resource: {Path.GetFileNameWithoutExtension(zip)}");
2022-10-09 23:35:30 +08:00
Unpack(zipPath, Path.Combine(path, "Data"));
2022-07-20 17:50:01 +08:00
}
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();
});
});
2022-07-20 17:50:01 +08:00
}
public void Unload()
{
StopScripts();
2022-10-10 16:45:58 +08:00
LoadedResources.Clear();
2022-10-15 13:52:49 +08:00
Loader.LoaderContext.RequestUnload();
}
2022-07-01 13:54:18 +08:00
2022-10-09 23:35:30 +08:00
private void Unpack(string zipPath, string dataFolderRoot)
2022-07-20 17:50:01 +08:00
{
var r = new ClientResource()
{
Logger = API.Logger,
2022-07-20 17:50:01 +08:00
Scripts = new List<ClientScript>(),
2022-10-09 23:35:30 +08:00
Name = Path.GetFileNameWithoutExtension(zipPath),
2022-10-10 16:45:58 +08:00
DataFolder = Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(zipPath)),
ScriptsDirectory = Path.Combine(Directory.GetParent(zipPath).FullName, Path.GetFileNameWithoutExtension(zipPath))
2022-07-20 17:50:01 +08:00
};
Directory.CreateDirectory(r.DataFolder);
2022-10-10 16:45:58 +08:00
var scriptsDir = r.ScriptsDirectory;
if (Directory.Exists(scriptsDir)) { Directory.Delete(scriptsDir, true); }
else if (File.Exists(scriptsDir)) { File.Delete(scriptsDir); }
Directory.CreateDirectory(scriptsDir);
2022-10-09 23:35:30 +08:00
2022-10-10 16:45:58 +08:00
new FastZip().ExtractZip(zipPath, scriptsDir, null);
2022-07-01 12:22:31 +08:00
2022-10-10 16:45:58 +08:00
foreach (var dir in Directory.GetDirectories(scriptsDir, "*", SearchOption.AllDirectories))
2022-07-20 17:50:01 +08:00
{
2022-10-09 23:35:30 +08:00
r.Files.Add(dir, new ResourceFile()
2022-07-20 17:50:01 +08:00
{
2022-10-09 23:35:30 +08:00
IsDirectory = true,
2022-10-10 16:45:58 +08:00
Name = dir.Substring(scriptsDir.Length + 1).Replace('\\', '/')
2022-10-09 23:35:30 +08:00
});
2022-07-20 17:50:01 +08:00
}
2022-10-09 23:35:30 +08:00
var assemblies = new Dictionary<ResourceFile, Assembly>();
2022-10-10 16:45:58 +08:00
foreach (var file in Directory.GetFiles(scriptsDir, "*", SearchOption.AllDirectories))
2022-07-20 17:50:01 +08:00
{
2022-10-15 13:52:49 +08:00
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;
}
2022-10-10 16:45:58 +08:00
var relativeName = file.Substring(scriptsDir.Length + 1).Replace('\\', '/');
2022-10-09 23:35:30 +08:00
var rfile = new ResourceFile()
2022-07-20 17:50:01 +08:00
{
2022-10-09 23:35:30 +08:00
GetStream = () => { return new FileStream(file, FileMode.Open, FileAccess.Read); },
IsDirectory = false,
Name = relativeName
};
r.Files.Add(relativeName, rfile);
2022-07-20 17:50:01 +08:00
}
2022-07-01 12:22:31 +08:00
LoadedResources.TryAdd(r.Name, r);
}
2022-10-15 17:06:19 +08:00
private 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);
}
}
2022-10-15 17:06:19 +08:00
private 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();
2022-07-20 17:50:01 +08:00
}
}
}