From 839e154adcd7e30c36f5d6f03111d9bd399bf9b8 Mon Sep 17 00:00:00 2001 From: Jesse Date: Wed, 23 Jul 2025 12:24:55 +0200 Subject: [PATCH] Make mod loading non nullable, set certain properties to be read only after init (#506) * Make mod loading non nullable, set certain properties to be read only after init - Breaks all mods, will require new nugets * Make mod assembly list IEnumerable * Convert checks to IsNullOrEmpty * Update comment, enforce ModGuid --- .../Models/Spt/Mod/AbstractModMetadata.cs | 14 +++---- .../Models/Spt/Mod/SptMod.cs | 6 +-- Libraries/SPTarkov.Server.Core/Utils/App.cs | 4 +- SPTarkov.Server/Modding/ModDllLoader.cs | 42 ++++++++++--------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Mod/AbstractModMetadata.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Mod/AbstractModMetadata.cs index 8a363e4a..c14ec81a 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Spt/Mod/AbstractModMetadata.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Mod/AbstractModMetadata.cs @@ -2,7 +2,7 @@ /// /// Represents a collection of metadata used to determine things such as author, version, -/// pre-defined load order and incompatibilities. This record is required to be overriden by all mods. +/// pre-defined load order and incompatibilities. This record is required to be overridden by all mods. /// All properties must be overridden. For properties, that you don't need, just assign null. /// public abstract record AbstractModMetadata @@ -13,17 +13,17 @@ public abstract record AbstractModMetadata /// It is recommended (but not mandatory) to use /// reverse domain name notation. /// - public abstract string ModGuid { get; set; } + public abstract string ModGuid { get; init; } /// /// Name of this mod /// - public abstract string Name { get; set; } + public abstract string Name { get; init; } /// /// Your username /// - public abstract string Author { get; set; } + public abstract string Author { get; init; } /// /// People who have contributed to this mod @@ -33,12 +33,12 @@ public abstract record AbstractModMetadata /// /// Semantic version of this mod, this uses the semver standard /// - public abstract string Version { get; set; } + public abstract string Version { get; init; } /// /// SPT version this mod was built for /// - public abstract string SptVersion { get; set; } + public abstract string SptVersion { get; init; } /// /// List of mods this mod should load before @@ -75,5 +75,5 @@ public abstract record AbstractModMetadata /// /// Name of the license this mod uses /// - public abstract string? Licence { get; set; } + public abstract string License { get; init; } } diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Mod/SptMod.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Mod/SptMod.cs index da599405..2e39e0ff 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Spt/Mod/SptMod.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Mod/SptMod.cs @@ -9,11 +9,11 @@ public class SptMod public Dictionary? ExtensionData { get; set; } [JsonPropertyName("directory")] - public string Directory { get; set; } + public required string Directory { get; init; } [JsonPropertyName("modMetadata")] - public AbstractModMetadata? ModMetadata { get; set; } + public required AbstractModMetadata ModMetadata { get; init; } [JsonPropertyName("assemblies")] - public List? Assemblies { get; set; } + public required IEnumerable Assemblies { get; init; } } diff --git a/Libraries/SPTarkov.Server.Core/Utils/App.cs b/Libraries/SPTarkov.Server.Core/Utils/App.cs index c7e05a20..619aec6d 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/App.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/App.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.Hosting; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Extensions; -using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers; @@ -24,8 +23,7 @@ public class App( DatabaseService _databaseService, IHostApplicationLifetime _appLifeTime, IEnumerable _onLoadComponents, - IEnumerable _onUpdateComponents, - HttpServerHelper _httpServerHelper + IEnumerable _onUpdateComponents ) { protected readonly CoreConfig _coreConfig = _configServer.GetConfig(); diff --git a/SPTarkov.Server/Modding/ModDllLoader.cs b/SPTarkov.Server/Modding/ModDllLoader.cs index 71e02ad0..c493cb4b 100644 --- a/SPTarkov.Server/Modding/ModDllLoader.cs +++ b/SPTarkov.Server/Modding/ModDllLoader.cs @@ -53,14 +53,12 @@ public class ModDllLoader /// SptMod private static SptMod LoadMod(string path) { - var result = new SptMod { Directory = path, Assemblies = [] }; - var assemblyCount = 0; + List assemblyList = []; foreach (var file in new DirectoryInfo(path).GetFiles()) // Only search top level { if (string.Equals(file.Extension, ".dll", StringComparison.OrdinalIgnoreCase)) { - assemblyCount++; - result.Assemblies.Add( + assemblyList.Add( AssemblyLoadContext.Default.LoadFromAssemblyPath( Path.GetFullPath(file.FullName) ) @@ -68,30 +66,29 @@ public class ModDllLoader } } - if (assemblyCount == 0) + if (assemblyList.Count == 0) { throw new Exception($"No Assemblies found in path: {Path.GetFullPath(path)}"); } - result.ModMetadata = LoadModMetadata(result.Assemblies, path); - - if (result.ModMetadata == null) + SptMod result = new() { - throw new Exception( - $"Failed to load mod metadata for: {Path.GetFullPath(path)} \ndid you override `AbstractModMetadata`?" - ); - } + Directory = path, + Assemblies = assemblyList, + ModMetadata = LoadModMetadata(assemblyList, path), + }; if ( - result.ModMetadata?.Name == null - || result.ModMetadata?.Author == null - || result.ModMetadata?.Version == null - || result.ModMetadata?.Licence == null - || result.ModMetadata?.SptVersion == null + string.IsNullOrEmpty(result.ModMetadata.ModGuid) + || string.IsNullOrEmpty(result.ModMetadata.Name) + || string.IsNullOrEmpty(result.ModMetadata.Author) + || string.IsNullOrEmpty(result.ModMetadata.Version) + || string.IsNullOrEmpty(result.ModMetadata.License) + || string.IsNullOrEmpty(result.ModMetadata.SptVersion) ) { throw new Exception( - $"The mod metadata for: {Path.GetFullPath(path)} is missing one of these properties: name, author, licence, version or sptVersion" + $"The mod metadata for: {Path.GetFullPath(path)} is missing one of these properties: ModGuid, Name, Author, License, Version or SptVersion" ); } @@ -105,7 +102,7 @@ public class ModDllLoader /// Path of the mod directory /// Mod metadata /// Thrown if duplicate metadata implementations are found - private static AbstractModMetadata? LoadModMetadata(List assemblies, string path) + private static AbstractModMetadata LoadModMetadata(List assemblies, string path) { AbstractModMetadata? result = null; @@ -131,6 +128,13 @@ public class ModDllLoader } } + if (result == null) + { + throw new Exception( + $"Failed to load mod metadata for: {Path.GetFullPath(path)} \ndid you override `AbstractModMetadata`?" + ); + } + return result; } }