diff --git a/ExampleMods/Package.json b/ExampleMods/Package.json new file mode 100644 index 00000000..95474cad --- /dev/null +++ b/ExampleMods/Package.json @@ -0,0 +1,34 @@ +{ + "Name": "ChangeMe", + "Author": "ChangeMe", + "Version": "ChangeMe", + "SptVersion": "ChangeMe", + "Licence": "ChangeMe", + "IsBundleMod": false, + "Url": "ChangeMe", + "Contributors": [ + "ChamgeMe" + ], + "Main": "ChangeMe", + "DevDependencies": { + "ChangeMe": "ChangeMe" + }, + "Scripts": { + "ChangeMe": "ChangeMe" + }, + "Dependencies": { + "ChangeMe": "ChangeMe" + }, + "ModDependencies": { + "ChangeMe": "ChangeMe" + }, + "LoadAfter": [ + "ChangeMe" + ], + "LoadBefore": [ + "ChangeMe" + ], + "Incompatibilities": [ + "ChangeMe" + ] +} diff --git a/Libraries/Core/Models/Spt/Mod/PackageJsonData.cs b/Libraries/Core/Models/Spt/Mod/PackageJsonData.cs index bcaf7194..fc40ca7a 100644 --- a/Libraries/Core/Models/Spt/Mod/PackageJsonData.cs +++ b/Libraries/Core/Models/Spt/Mod/PackageJsonData.cs @@ -4,118 +4,99 @@ namespace Core.Models.Spt.Mod; public record PackageJsonData { - [JsonPropertyName("incompatibilities")] public List? Incompatibilities { get; set; } - [JsonPropertyName("loadBefore")] public List? LoadBefore { get; set; } - [JsonPropertyName("loadAfter")] public List? LoadAfter { get; set; } - [JsonPropertyName("dependencies")] public Dictionary? Dependencies { get; set; } - [JsonPropertyName("modDependencies")] public Dictionary? ModDependencies { get; set; } - [JsonPropertyName("name")] public string? Name { get; set; } - [JsonPropertyName("url")] public string? Url { get; set; } - [JsonPropertyName("author")] public string? Author { get; set; } - [JsonPropertyName("version")] public string? Version { get; set; } - [JsonPropertyName("sptVersion")] public string? SptVersion { get; set; } - // We deliberately purge this data - [JsonPropertyName("scripts")] public Dictionary? Scripts { get; set; } - [JsonPropertyName("devDependencies")] public Dictionary? DevDependencies { get; set; } - [JsonPropertyName("licence")] public string? Licence { get; set; } - [JsonPropertyName("main")] public string? Main { get; set; } - [JsonPropertyName("isBundleMod")] public bool? IsBundleMod { get; set; } - [JsonPropertyName("contributors")] public List? Contributors { get; set; } } - -// TODO: this will need changing to however we implement it in this project diff --git a/Libraries/Core/Models/Spt/Mod/SptMod.cs b/Libraries/Core/Models/Spt/Mod/SptMod.cs new file mode 100644 index 00000000..f47e1577 --- /dev/null +++ b/Libraries/Core/Models/Spt/Mod/SptMod.cs @@ -0,0 +1,13 @@ +using System.Reflection; +using System.Text.Json.Serialization; + +namespace Core.Models.Spt.Mod; + +public class SptMod +{ + [JsonPropertyName("PackageJson")] + public PackageJsonData PackageJson { get; set; } + + [JsonPropertyName("Assembly")] + public Assembly Assembly { get; set; } +} diff --git a/Server/ModDllLoader.cs b/Server/ModDllLoader.cs index 6717d370..854945f2 100644 --- a/Server/ModDllLoader.cs +++ b/Server/ModDllLoader.cs @@ -1,33 +1,101 @@ using System.Reflection; +using System.Text.Json; +using Core.Models.Spt.Mod; +using Core.Models.Utils; namespace Server; public class ModDllLoader { private const string ModPath = "./user/mods/"; + private static ISptLogger _logger; - public static List LoadAllMods() + public ModDllLoader(ISptLogger logger) + { + _logger = logger; + } + + + public static List LoadAllMods() { if (!Directory.Exists(ModPath)) { Directory.CreateDirectory(ModPath); } - var mods = new List(); - foreach (var file in Directory.GetFiles(ModPath, "*.dll", SearchOption.AllDirectories)) + var mods = new List(); + + // foreach directory in /user/mods/ + // treat this as the MOD + // should contain a dll and Package.json + // load both + // if either is missing Throw Warning and skip + + var modDirectories = Directory.GetDirectories(ModPath); + + foreach (var modDirectory in modDirectories) { try { - mods.Add(Assembly.LoadFile(Path.GetFullPath(file))); - var name = file.Split(Path.DirectorySeparatorChar); - Console.WriteLine($"Loaded {name[^1]}"); + mods.Add(LoadMod(modDirectory)); } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine(e.Message); } } return mods; } + + private static SptMod? LoadMod(string path) + { + var result = new SptMod(); + var asmCount = 0; + var packCount = 0; + foreach (var file in new DirectoryInfo(path).GetFiles()) // only search top level + { + if (file.Name.ToLower() == "package.json") + { + packCount++; + + // deal with package.json + var jjson = File.ReadAllText(file.FullName); + var json = JsonSerializer.Deserialize(jjson); + result.PackageJson = json; + if (packCount > 1) + { + throw new Exception($"More than one package.json file found in path: {path}"); + } + } + + if (file.Extension.ToLower() == ".dll") + { + asmCount++; + + result.Assembly = Assembly.LoadFile(Path.GetFullPath(file.FullName)); + if (asmCount > 1) + { + throw new Exception($"More than one Assembly found in {path}"); + } + } + } + + if (packCount == 0) + { + throw new Exception($"No package.json found in path: {Path.GetFullPath(path)}"); + } + + if (asmCount == 0) + { + throw new Exception($"No Assemblies found in path: {Path.GetFullPath(path)}"); + } + + if (result.Assembly is not null && result.PackageJson is not null) + { + Console.WriteLine($"Loaded {result.PackageJson.Name}"); + } + + return result; + } } diff --git a/Server/Program.cs b/Server/Program.cs index 11bcce4d..8c7ec0e9 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -1,5 +1,6 @@ using System.Runtime; using Core.Context; +using Core.Models.Enums; using Core.Models.External; using Core.Models.Spt.Config; using Core.Servers; @@ -16,8 +17,8 @@ public static class Program { public static void Main(string[] args) { - var assemblies = ModDllLoader.LoadAllMods(); - HarmonyBootstrapper.LoadAllPatches(assemblies); + var mods = ModDllLoader.LoadAllMods(); + // HarmonyBootstrapper.LoadAllPatches(assemblies); var builder = WebApplication.CreateBuilder(args); builder.Host.UseSerilog(); @@ -28,7 +29,7 @@ public static class Program ProgramStatics.Initialize(); DependencyInjectionRegistrator.RegisterSptComponents(typeof(Program).Assembly, typeof(App).Assembly, builder.Services); - DependencyInjectionRegistrator.RegisterModOverrideComponents(builder.Services, assemblies); + DependencyInjectionRegistrator.RegisterModOverrideComponents(builder.Services, mods.Select(a => a.Assembly).ToList()); var logger = new SerilogLoggerProvider(registeredLogger).CreateLogger("Server"); try { @@ -46,7 +47,7 @@ public static class Program var appContext = serviceProvider.GetService(); // Add the Loaded Mod Assemblies for later - appContext?.AddValue(ContextVariableType.LOADED_MOD_ASSEMBLIES, assemblies); + appContext?.AddValue(ContextVariableType.LOADED_MOD_ASSEMBLIES, mods); // This is the builder that will get use by the HttpServer to start up the web application appContext?.AddValue(ContextVariableType.APP_BUILDER, builder);