diff --git a/CKAN.schema b/CKAN.schema index defe003f0f..ee02809e0d 100644 --- a/CKAN.schema +++ b/CKAN.schema @@ -439,6 +439,16 @@ "description" : "Spec v1.25 Missions path", "type" : "string", "pattern" : "^Missions" + }, + { + "description" : "KSP2 BepInEx plugins folder", + "type" : "string", + "pattern" : "BepInEx/plugins" + }, + { + "description" : "KSP2 SpaceWarp plugins folder", + "type" : "string", + "pattern" : "SpaceWarp/Mods" } ] }, diff --git a/Core/GameInstanceManager.cs b/Core/GameInstanceManager.cs index a0df024e20..ac706ff585 100644 --- a/Core/GameInstanceManager.cs +++ b/Core/GameInstanceManager.cs @@ -19,7 +19,8 @@ public class GameInstanceManager : IDisposable { private static IGame[] knownGames = new IGame[] { - new KerbalSpaceProgram() + new KerbalSpaceProgram(), + new KerbalSpaceProgram2(), }; /// @@ -642,5 +643,8 @@ public IGame DetermineGame(DirectoryInfo path, IUser user) } } + public static IGame GameByShortName(string shortName) + => knownGames.FirstOrDefault(g => g.ShortName == shortName); + } } diff --git a/Core/Games/KerbalSpaceProgram2.cs b/Core/Games/KerbalSpaceProgram2.cs new file mode 100644 index 0000000000..93b3342706 --- /dev/null +++ b/Core/Games/KerbalSpaceProgram2.cs @@ -0,0 +1,200 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.IO; +using System.Collections.Generic; + +using Autofac; +using log4net; + +using CKAN.Versioning; + +namespace CKAN.Games +{ + public class KerbalSpaceProgram2 : IGame + { + public string ShortName => "KSP2"; + + public bool GameInFolder(DirectoryInfo where) + => where.EnumerateFiles().Any(f => f.Name == "KSP2_x64.exe") + && where.EnumerateDirectories().Any(d => d.Name == "KSP2_x64_Data"); + + /// + /// Finds the Steam KSP path. Returns null if the folder cannot be located. + /// + /// The KSP path. + public string SteamPath() + { + // Attempt to get the Steam path. + string steamPath = CKANPathUtils.SteamPath(); + + if (steamPath == null) + { + return null; + } + + // Default steam library + string installPath = GameDirectory(steamPath); + if (installPath != null) + { + return installPath; + } + + // Attempt to find through config file + string configPath = Path.Combine(steamPath, "config", "config.vdf"); + if (File.Exists(configPath)) + { + log.InfoFormat("Found Steam config file at {0}", configPath); + StreamReader reader = new StreamReader(configPath); + string line; + while ((line = reader.ReadLine()) != null) + { + // Found Steam library + if (line.Contains("BaseInstallFolder")) + { + // This assumes config file is valid, we just skip it if it looks funny. + string[] split_line = line.Split('"'); + + if (split_line.Length > 3) + { + log.DebugFormat("Found a Steam Libary Location at {0}", split_line[3]); + + installPath = GameDirectory(split_line[3]); + if (installPath != null) + { + log.InfoFormat("Found a KSP install at {0}", installPath); + return installPath; + } + } + } + } + } + + // Could not locate the folder. + return null; + } + + /// + /// Get the default non-Steam path to KSP on macOS + /// + /// + /// "/Applications/Kerbal Space Program" if it exists and we're on a Mac, else null + /// + public string MacPath() + { + if (Platform.IsMac) + { + string installPath = Path.Combine( + // This is "/Applications" in Mono on Mac + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), + "Kerbal Space Program 2" + ); + return Directory.Exists(installPath) ? installPath : null; + } + return null; + } + + public string PrimaryModDirectoryRelative => "BepInEx/plugins"; + + public string PrimaryModDirectory(GameInstance inst) + => CKANPathUtils.NormalizePath( + Path.Combine(inst.GameDir(), PrimaryModDirectoryRelative)); + + public string[] StockFolders => new string[] + { + "KSP2_x64_Data", + "MonoBleedingEdge", + "PDLauncher", + }; + + public string[] ReservedPaths => new string[] + { + }; + + public string[] CreateableDirs => new string[] + { + "BepInEx", + "BepInEx/plugins", + }; + + /// + /// Checks the path against a list of reserved game directories + /// + /// + /// + public bool IsReservedDirectory(GameInstance inst, string path) + => path == inst.GameDir() || path == inst.CkanDir() + || path == PrimaryModDirectory(inst); + + public bool AllowInstallationIn(string name, out string path) + => allowedFolders.TryGetValue(name, out path); + + public void RebuildSubdirectories(GameInstance inst) + { + } + + public string DefaultCommandLine => + Platform.IsUnix ? "./KSP2.x86_64 -single-instance" + : Platform.IsMac ? "./KSP2.app/Contents/MacOS/KSP" + : "KSP2_x64.exe -single-instance"; + + public string[] AdjustCommandLine(string[] args, GameVersion installedVersion) + => args; + + public List KnownVersions + => new List + { + new GameVersion(0, 1, 0, 0), + }; + + public GameVersion DetectVersion(DirectoryInfo where) + => GameVersion.Parse( + FileVersionInfo.GetVersionInfo( + Path.Combine(where.FullName, "KSP2_x64.exe")).ProductVersion); + + public string CompatibleVersionsFile => "compatible_game_versions.json"; + + public string[] BuildIDFiles => new string[] + { + "KSP2_x64.exe", + }; + + public Uri DefaultRepositoryURL => new Uri("https://github.com/KSP-CKAN/KSP2-CKAN-meta/archive/master.tar.gz"); + + public Uri RepositoryListURL => new Uri("https://raw.githubusercontent.com/KSP-CKAN/KSP2-CKAN-meta/master/repositories.json"); + + private readonly Dictionary allowedFolders = new Dictionary + { + { "BepInEx", "BepInEx" }, + { "BepInEx/plugins", "BepInEx/plugins" }, + }; + + /// + /// Finds the KSP path under a Steam Library. Returns null if the folder cannot be located. + /// + /// Steam Library Path + /// The KSP path. + private static string GameDirectory(string steamPath) + { + // There are several possibilities for the path under Linux. + // Try with the uppercase version. + string installPath = Path.Combine(steamPath, "SteamApps", "common", "Kerbal Space Program 2"); + + if (Directory.Exists(installPath)) + { + return installPath; + } + + // Try with the lowercase version. + installPath = Path.Combine(steamPath, "steamapps", "common", "Kerbal Space Program 2"); + + if (Directory.Exists(installPath)) + { + return installPath; + } + return null; + } + + private static readonly ILog log = LogManager.GetLogger(typeof(KerbalSpaceProgram2)); + } +} diff --git a/Dockerfile.netkan b/Dockerfile.netkan index f2715ce594..259298be75 100644 --- a/Dockerfile.netkan +++ b/Dockerfile.netkan @@ -5,6 +5,6 @@ RUN useradd -ms /bin/bash netkan USER netkan WORKDIR /home/netkan ADD netkan.exe . -ENTRYPOINT /usr/bin/mono netkan.exe --queues $QUEUES \ +ENTRYPOINT /usr/bin/mono netkan.exe --game $GAME --queues $QUEUES \ --net-useragent 'Mozilla/5.0 (compatible; Netkanbot/1.0; CKAN; +https://github.com/KSP-CKAN/NetKAN-Infra)' \ --github-token $GH_Token --gitlab-token "$GL_Token" --cachedir ckan_cache -v diff --git a/Netkan/CKAN-netkan.csproj b/Netkan/CKAN-netkan.csproj index 7d65873867..b0e8c02e48 100644 --- a/Netkan/CKAN-netkan.csproj +++ b/Netkan/CKAN-netkan.csproj @@ -108,6 +108,7 @@ + @@ -128,6 +129,7 @@ + @@ -180,4 +182,4 @@ - \ No newline at end of file + diff --git a/Netkan/CmdLineOptions.cs b/Netkan/CmdLineOptions.cs index 8c6eeafd1b..e412274dda 100644 --- a/Netkan/CmdLineOptions.cs +++ b/Netkan/CmdLineOptions.cs @@ -55,6 +55,9 @@ internal class CmdLineOptions [Option("version", HelpText = "Display the netkan version number and exit")] public bool Version { get; set; } + [Option("game", DefaultValue = "KSP", HelpText = "Short name of the game for which to inflate mods")] + public string Game { get; set; } + [ValueOption(0)] public string File { get; set; } } diff --git a/Netkan/Processors/Inflator.cs b/Netkan/Processors/Inflator.cs index 3d0baf3bb6..64254f61a0 100644 --- a/Netkan/Processors/Inflator.cs +++ b/Netkan/Processors/Inflator.cs @@ -4,18 +4,20 @@ using System.Linq; using Autofac; using log4net; + using CKAN.Configuration; using CKAN.Versioning; using CKAN.NetKAN.Model; using CKAN.NetKAN.Services; using CKAN.NetKAN.Transformers; using CKAN.NetKAN.Validators; +using CKAN.Games; namespace CKAN.NetKAN.Processors { public class Inflator { - public Inflator(string cacheDir, bool overwriteCache, string githubToken, string gitlabToken, bool prerelease) + public Inflator(string cacheDir, bool overwriteCache, string githubToken, string gitlabToken, bool prerelease, IGame game) { log.Debug("Initializing inflator"); cache = FindCache( @@ -24,11 +26,11 @@ public Inflator(string cacheDir, bool overwriteCache, string githubToken, string cacheDir ); - IModuleService moduleService = new ModuleService(); + IModuleService moduleService = new ModuleService(game); IFileService fileService = new FileService(cache); http = new CachingHttpService(cache, overwriteCache); - ckanValidator = new CkanValidator(http, moduleService); - transformer = new NetkanTransformer(http, fileService, moduleService, githubToken, gitlabToken, prerelease, netkanValidator); + ckanValidator = new CkanValidator(http, moduleService, game); + transformer = new NetkanTransformer(http, fileService, moduleService, githubToken, gitlabToken, prerelease, game, netkanValidator); } internal IEnumerable Inflate(string filename, Metadata netkan, TransformOptions opts) diff --git a/Netkan/Processors/QueueHandler.cs b/Netkan/Processors/QueueHandler.cs index ee8c842528..e25fab8a92 100644 --- a/Netkan/Processors/QueueHandler.cs +++ b/Netkan/Processors/QueueHandler.cs @@ -13,22 +13,24 @@ using log4net.Repository.Hierarchy; using Newtonsoft.Json; using Newtonsoft.Json.Linq; + using CKAN.Versioning; using CKAN.NetKAN.Transformers; using CKAN.NetKAN.Model; using CKAN.NetKAN.Extensions; +using CKAN.Games; namespace CKAN.NetKAN.Processors { public class QueueHandler { - public QueueHandler(string inputQueueName, string outputQueueName, string cacheDir, bool overwriteCache, string githubToken, string gitlabToken, bool prerelease) + public QueueHandler(string inputQueueName, string outputQueueName, string cacheDir, bool overwriteCache, string githubToken, string gitlabToken, bool prerelease, IGame game) { warningAppender = GetQueueLogAppender(); (LogManager.GetRepository() as Hierarchy)?.Root.AddAppender(warningAppender); log.Debug("Initializing SQS queue handler"); - inflator = new Inflator(cacheDir, overwriteCache, githubToken, gitlabToken, prerelease); + inflator = new Inflator(cacheDir, overwriteCache, githubToken, gitlabToken, prerelease, game); inputQueueURL = getQueueUrl(inputQueueName); outputQueueURL = getQueueUrl(outputQueueName); diff --git a/Netkan/Program.cs b/Netkan/Program.cs index 84378fcb4d..124bdd77c5 100644 --- a/Netkan/Program.cs +++ b/Netkan/Program.cs @@ -16,6 +16,7 @@ using CKAN.NetKAN.Processors; using CKAN.NetKAN.Transformers; using CKAN.NetKAN.Extensions; +using CKAN.Games; namespace CKAN.NetKAN { @@ -47,6 +48,8 @@ public static int Main(string[] args) return ExitOk; } + var game = GameInstanceManager.GameByShortName(Options.Game); + if (!string.IsNullOrEmpty(Options.ValidateCkan)) { var ckan = new Metadata(JObject.Parse(File.ReadAllText(Options.ValidateCkan))); @@ -55,7 +58,8 @@ public static int Main(string[] args) Options.OverwriteCache, Options.GitHubToken, Options.GitLabToken, - Options.PreRelease + Options.PreRelease, + game ); inf.ValidateCkan(ckan); Console.WriteLine(QueueHandler.serializeCkan( @@ -74,7 +78,8 @@ public static int Main(string[] args) Options.OverwriteCache, Options.GitHubToken, Options.GitLabToken, - Options.PreRelease + Options.PreRelease, + game ); qh.Process(); return ExitOk; @@ -92,7 +97,8 @@ public static int Main(string[] args) Options.OverwriteCache, Options.GitHubToken, Options.GitLabToken, - Options.PreRelease + Options.PreRelease, + game ); var ckans = inf.Inflate( Options.File, diff --git a/Netkan/Services/IModuleService.cs b/Netkan/Services/IModuleService.cs index ed3ce3000b..0793649cbb 100644 --- a/Netkan/Services/IModuleService.cs +++ b/Netkan/Services/IModuleService.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json.Linq; using CKAN.NetKAN.Sources.Avc; +using CKAN.NetKAN.Sources.SpaceWarp; namespace CKAN.NetKAN.Services { @@ -19,6 +20,8 @@ internal interface IModuleService IEnumerable GetPlugins(CkanModule module, ZipFile zip, GameInstance inst); IEnumerable GetCrafts(CkanModule module, ZipFile zip, GameInstance inst); + SpaceWarpInfo GetSpaceWarpInfo(CkanModule module, ZipFile zip, GameInstance inst); + IEnumerable FileSources(CkanModule module, ZipFile zip, GameInstance inst); IEnumerable FileDestinations(CkanModule module, string filePath); } diff --git a/Netkan/Services/ModuleService.cs b/Netkan/Services/ModuleService.cs index 4e091c2e08..857055d32c 100644 --- a/Netkan/Services/ModuleService.cs +++ b/Netkan/Services/ModuleService.cs @@ -12,12 +12,20 @@ using CKAN.Versioning; using CKAN.Extensions; using CKAN.NetKAN.Sources.Avc; +using CKAN.NetKAN.Sources.SpaceWarp; using CKAN.Games; namespace CKAN.NetKAN.Services { internal sealed class ModuleService : IModuleService { + public ModuleService(IGame game) + { + this.game = game; + } + + private IGame game; + private static readonly ILog Log = LogManager.GetLogger(typeof(ModuleService)); public AvcVersion GetInternalAvc(CkanModule module, string zipFilePath, string internalFilePath = null) @@ -66,7 +74,7 @@ public bool HasInstallableFiles(CkanModule module, string filePath) try { ModuleInstaller.FindInstallableFiles(module, filePath, - new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser())); + new GameInstance(game, "/", "dummy", new NullUser())); } catch (BadMetadataKraken) { @@ -97,7 +105,7 @@ public IEnumerable FileSources(CkanModule module, ZipFile zip, GameIns public IEnumerable FileDestinations(CkanModule module, string filePath) { - var inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", null, false); + var inst = new GameInstance(game, "/", "dummy", null, false); return ModuleInstaller .FindInstallableFiles(module, filePath, inst) .Where(f => !f.source.IsDirectory) @@ -230,7 +238,7 @@ public Tuple FindInternalAvc(CkanModule module, ZipFile zipfile, const string versionExt = ".version"; // Get all our version files - var ksp = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); + var ksp = new GameInstance(game, "/", "dummy", new NullUser()); var files = ModuleInstaller.FindInstallableFiles(module, zipfile, ksp) .Select(x => x.source) .Where(source => source.Name.EndsWith(versionExt, @@ -315,5 +323,15 @@ public static AvcVersion GetInternalAvc(ZipFile zipfile, ZipEntry avcEntry) } } } + + private const string SpaceWarpInfoFilename = "swinfo.json"; + + public SpaceWarpInfo GetSpaceWarpInfo(CkanModule module, ZipFile zip, GameInstance inst) + => GetFilesBySuffix(module, zip, SpaceWarpInfoFilename, inst) + .Select(instF => instF.source) + .Select(entry => + JsonConvert.DeserializeObject( + new StreamReader(zip.GetInputStream(entry)).ReadToEnd())) + .FirstOrDefault(); } } diff --git a/Netkan/Sources/SpaceWarp/SpaceWarpInfo.cs b/Netkan/Sources/SpaceWarp/SpaceWarpInfo.cs new file mode 100644 index 0000000000..7e76913063 --- /dev/null +++ b/Netkan/Sources/SpaceWarp/SpaceWarpInfo.cs @@ -0,0 +1,50 @@ +using System; + +using Newtonsoft.Json; + +// https://github.com/SpaceWarpDev/SpaceWarp/blob/main/example_mod_info.json + +namespace CKAN.NetKAN.Sources.SpaceWarp +{ + public class SpaceWarpInfo + { + public string mod_id; + public string name; + public string author; + public string description; + public string source; + public string version; + public Dependency[] dependencies; + public VersionMinMax ksp2_version; + } + + public class Dependency + { + public string id; + public VersionMinMax version; + } + + public class VersionMinMax + { + [JsonConverter(typeof(SpaceWarpGameVersionConverter))] + public string min; + [JsonConverter(typeof(SpaceWarpGameVersionConverter))] + public string max; + } + + public class SpaceWarpGameVersionConverter : JsonConverter + { + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + => (reader.Value as string)?.Replace("*", "any"); + + public override bool CanConvert(Type objectType) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } +} diff --git a/Netkan/Transformers/InstallSizeTransformer.cs b/Netkan/Transformers/InstallSizeTransformer.cs index 2bec4cb65e..ea2bcfceb6 100644 --- a/Netkan/Transformers/InstallSizeTransformer.cs +++ b/Netkan/Transformers/InstallSizeTransformer.cs @@ -13,10 +13,11 @@ internal sealed class InstallSizeTransformer : ITransformer { public string Name { get { return "install_size"; } } - public InstallSizeTransformer(IHttpService http, IModuleService moduleService) + public InstallSizeTransformer(IHttpService http, IModuleService moduleService, IGame game) { _http = http; _moduleService = moduleService; + _game = game; } public IEnumerable Transform(Metadata metadata, TransformOptions opts) @@ -26,7 +27,7 @@ public IEnumerable Transform(Metadata metadata, TransformOptions opts) var json = metadata.Json(); CkanModule mod = CkanModule.FromJson(json.ToString()); ZipFile zip = new ZipFile(_http.DownloadModule(metadata)); - GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); + GameInstance inst = new GameInstance(_game, "/", "dummy", new NullUser()); json["install_size"] = _moduleService.FileSources(mod, zip, inst) .Select(ze => ze.Size) .Sum(); @@ -40,5 +41,6 @@ public IEnumerable Transform(Metadata metadata, TransformOptions opts) private readonly IHttpService _http; private readonly IModuleService _moduleService; + private readonly IGame _game; } } diff --git a/Netkan/Transformers/InternalCkanTransformer.cs b/Netkan/Transformers/InternalCkanTransformer.cs index c66c479874..5d30d06054 100644 --- a/Netkan/Transformers/InternalCkanTransformer.cs +++ b/Netkan/Transformers/InternalCkanTransformer.cs @@ -19,13 +19,15 @@ internal sealed class InternalCkanTransformer : ITransformer private readonly IHttpService _http; private readonly IModuleService _moduleService; + private readonly IGame _game; public string Name { get { return "internal_ckan"; } } - public InternalCkanTransformer(IHttpService http, IModuleService moduleService) + public InternalCkanTransformer(IHttpService http, IModuleService moduleService, IGame game) { _http = http; _moduleService = moduleService; + _game = game; } public IEnumerable Transform(Metadata metadata, TransformOptions opts) @@ -39,7 +41,7 @@ public IEnumerable Transform(Metadata metadata, TransformOptions opts) var moduleJson = metadata.Json(); moduleJson.SafeAdd("version", "1"); CkanModule mod = CkanModule.FromJson(moduleJson.ToString()); - GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); + GameInstance inst = new GameInstance(_game, "/", "dummy", new NullUser()); var internalJson = _moduleService.GetInternalCkan(mod, _http.DownloadModule(metadata), inst); diff --git a/Netkan/Transformers/LocalizationsTransformer.cs b/Netkan/Transformers/LocalizationsTransformer.cs index 731d897dee..88b87089f0 100644 --- a/Netkan/Transformers/LocalizationsTransformer.cs +++ b/Netkan/Transformers/LocalizationsTransformer.cs @@ -21,10 +21,11 @@ internal sealed class LocalizationsTransformer : ITransformer /// /// HTTP service /// Module service - public LocalizationsTransformer(IHttpService http, IModuleService moduleService) + public LocalizationsTransformer(IHttpService http, IModuleService moduleService, IGame game) { _http = http; _moduleService = moduleService; + _game = game; } /// @@ -52,7 +53,7 @@ public IEnumerable Transform(Metadata metadata, TransformOptions opts) { CkanModule mod = CkanModule.FromJson(json.ToString()); ZipFile zip = new ZipFile(_http.DownloadModule(metadata)); - GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); + GameInstance inst = new GameInstance(_game, "/", "dummy", new NullUser()); log.Debug("Extracting locales"); // Extract the locale names from the ZIP's cfg files @@ -86,6 +87,7 @@ public IEnumerable Transform(Metadata metadata, TransformOptions opts) private readonly IHttpService _http; private readonly IModuleService _moduleService; + private readonly IGame _game; private static readonly ILog log = LogManager.GetLogger(typeof(LocalizationsTransformer)); diff --git a/Netkan/Transformers/NetkanTransformer.cs b/Netkan/Transformers/NetkanTransformer.cs index bc81d8f081..1c64100e95 100644 --- a/Netkan/Transformers/NetkanTransformer.cs +++ b/Netkan/Transformers/NetkanTransformer.cs @@ -9,6 +9,7 @@ using CKAN.NetKAN.Sources.Gitlab; using CKAN.NetKAN.Sources.Jenkins; using CKAN.NetKAN.Sources.Spacedock; +using CKAN.Games; namespace CKAN.NetKAN.Transformers { @@ -29,6 +30,7 @@ public NetkanTransformer( string githubToken, string gitlabToken, bool prerelease, + IGame game, IValidator validator ) { @@ -37,7 +39,7 @@ IValidator validator var glApi = new GitlabApi(http, gitlabToken); _transformers = InjectVersionedOverrideTransformers(new List { - new StagingTransformer(), + new StagingTransformer(game), new MetaNetkanTransformer(http, ghApi), new SpacedockTransformer(new SpacedockApi(http), ghApi), new CurseTransformer(new CurseApi(http)), @@ -46,9 +48,10 @@ IValidator validator new HttpTransformer(), new JenkinsTransformer(new JenkinsApi(http)), new AvcKrefTransformer(http, ghApi), - new InternalCkanTransformer(http, moduleService), + new InternalCkanTransformer(http, moduleService, game), + new SpaceWarpInfoTransformer(http, moduleService, game), new AvcTransformer(http, moduleService, ghApi), - new LocalizationsTransformer(http, moduleService), + new LocalizationsTransformer(http, moduleService, game), new VersionEditTransformer(), new ForcedVTransformer(), new EpochTransformer(), @@ -57,7 +60,7 @@ IValidator validator // specify a before or after property. new VersionedOverrideTransformer(before: new string[] { null }, after: new string[] { null }), new DownloadAttributeTransformer(http, fileService), - new InstallSizeTransformer(http, moduleService), + new InstallSizeTransformer(http, moduleService, game), new GeneratedByTransformer(), new OptimusPrimeTransformer(), new StripNetkanMetadataTransformer(), diff --git a/Netkan/Transformers/SpaceWarpInfoTransformer.cs b/Netkan/Transformers/SpaceWarpInfoTransformer.cs new file mode 100644 index 0000000000..ae284e87a3 --- /dev/null +++ b/Netkan/Transformers/SpaceWarpInfoTransformer.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; + +using ICSharpCode.SharpZipLib.Zip; +using log4net; + +using CKAN.NetKAN.Model; +using CKAN.NetKAN.Sources.SpaceWarp; +using CKAN.NetKAN.Services; +using CKAN.NetKAN.Extensions; +using CKAN.Versioning; +using CKAN.Games; + +namespace CKAN.NetKAN.Transformers +{ + internal sealed class SpaceWarpInfoTransformer : ITransformer + { + public SpaceWarpInfoTransformer(IHttpService httpSvc, IModuleService modSvc, IGame game) + { + this.httpSvc = httpSvc; + this.modSvc = modSvc; + this.game = game; + } + + public string Name => "space_warp_info"; + + public IEnumerable Transform(Metadata metadata, TransformOptions opts) + { + if (metadata.Download != null) + { + var moduleJson = metadata.Json(); + moduleJson.SafeAdd("version", "1"); + CkanModule mod = CkanModule.FromJson(moduleJson.ToString()); + GameInstance inst = new GameInstance(game, "/", "dummy", new NullUser()); + ZipFile zip = new ZipFile(httpSvc.DownloadModule(metadata)); + SpaceWarpInfo modinfo = modSvc.GetSpaceWarpInfo(mod, zip, inst); + if (modinfo != null) + { + var json = metadata.Json(); + json.SafeAdd("name", modinfo.name); + json.SafeAdd("author", modinfo.author); + json.SafeAdd("abstract", modinfo.description); + json.SafeAdd("version", modinfo.version); + if (GameVersion.TryParse(modinfo.ksp2_version.min, out GameVersion _)) + { + json.SafeAdd("ksp_version_min", modinfo.ksp2_version.min); + } + if (GameVersion.TryParse(modinfo.ksp2_version.max, out GameVersion _)) + { + json.SafeAdd("ksp_version_max", modinfo.ksp2_version.max); + } + yield return new Metadata(json); + } + } + yield return metadata; + } + + private readonly IHttpService httpSvc; + private readonly IModuleService modSvc; + private readonly IGame game; + + private static readonly ILog log = LogManager.GetLogger(typeof(SpaceWarpInfoTransformer)); + } +} diff --git a/Netkan/Transformers/SpacedockTransformer.cs b/Netkan/Transformers/SpacedockTransformer.cs index d863770925..1ee9bcd387 100644 --- a/Netkan/Transformers/SpacedockTransformer.cs +++ b/Netkan/Transformers/SpacedockTransformer.cs @@ -76,7 +76,7 @@ private Metadata TransformOne(Metadata metadata, JObject json, SpacedockMod sdMo if (json["ksp_version_min"] == null && json["ksp_version_max"] == null && json["ksp_version"] == null) { Log.DebugFormat("Writing ksp_version from SpaceDock: {0}", latestVersion.KSP_version); - json["ksp_version"] = latestVersion.KSP_version.ToString(); + json["ksp_version"] = latestVersion.KSP_version.WithoutBuild.ToString(); } json.SafeAdd("name", sdMod.name); diff --git a/Netkan/Transformers/StagingTransformer.cs b/Netkan/Transformers/StagingTransformer.cs index be4835fef2..ec17214216 100644 --- a/Netkan/Transformers/StagingTransformer.cs +++ b/Netkan/Transformers/StagingTransformer.cs @@ -13,9 +13,10 @@ namespace CKAN.NetKAN.Transformers { internal sealed class StagingTransformer : ITransformer { - public StagingTransformer() + public StagingTransformer(IGame game) { - currentRelease = new KerbalSpaceProgram().KnownVersions.Max().ToVersionRange(); + this.game = game; + currentRelease = game.KnownVersions.Max().ToVersionRange(); } public string Name { get { return "staging"; } } @@ -41,7 +42,6 @@ private bool VersionsNeedManualReview(Metadata metadata, out string reason) var maxVer = maxStr == null ? GameVersion.Any : GameVersion.Parse((string)maxStr); if (currentRelease.IntersectWith(new GameVersionRange(minVer, maxVer)) == null) { - var game = new KerbalSpaceProgram(); reason = $"Hard-coded game versions not compatible with current release: {GameVersionRange.VersionSpan(game, minVer, maxVer)}\r\nPlease check that they match the forum thread."; return true; } @@ -53,7 +53,9 @@ private bool VersionsNeedManualReview(Metadata metadata, out string reason) } } - private static GameVersionRange currentRelease; + private readonly GameVersionRange currentRelease; + private readonly IGame game; + private static readonly ILog Log = LogManager.GetLogger(typeof(StagingTransformer)); } } diff --git a/Netkan/Validators/CkanValidator.cs b/Netkan/Validators/CkanValidator.cs index 53018e8f2e..9b6fb9e19a 100644 --- a/Netkan/Validators/CkanValidator.cs +++ b/Netkan/Validators/CkanValidator.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; + using CKAN.NetKAN.Model; using CKAN.NetKAN.Services; +using CKAN.Games; namespace CKAN.NetKAN.Validators { @@ -8,7 +10,7 @@ internal sealed class CkanValidator : IValidator { private readonly List _validators; - public CkanValidator(IHttpService downloader, IModuleService moduleService) + public CkanValidator(IHttpService downloader, IModuleService moduleService, IGame game) { this.downloader = downloader; this.moduleService = moduleService; @@ -16,14 +18,14 @@ public CkanValidator(IHttpService downloader, IModuleService moduleService) { new IsCkanModuleValidator(), new TagsValidator(), - new InstallsFilesValidator(downloader, moduleService), - new MatchesKnownGameVersionsValidator(), + new InstallsFilesValidator(downloader, moduleService, game), + new MatchesKnownGameVersionsValidator(game), new ObeysCKANSchemaValidator(), new KindValidator(), - new HarmonyValidator(downloader, moduleService), - new ModuleManagerDependsValidator(downloader, moduleService), - new PluginsValidator(downloader, moduleService), - new CraftsInShipsValidator(downloader, moduleService), + new HarmonyValidator(downloader, moduleService, game), + new ModuleManagerDependsValidator(downloader, moduleService, game), + new PluginsValidator(downloader, moduleService, game), + new CraftsInShipsValidator(downloader, moduleService, game), }; } diff --git a/Netkan/Validators/CraftsInShipsValidator.cs b/Netkan/Validators/CraftsInShipsValidator.cs index 7f3fd9ebe2..28ad5b6aec 100644 --- a/Netkan/Validators/CraftsInShipsValidator.cs +++ b/Netkan/Validators/CraftsInShipsValidator.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json.Linq; using ICSharpCode.SharpZipLib.Zip; using log4net; + using CKAN.NetKAN.Services; using CKAN.NetKAN.Model; using CKAN.Games; @@ -12,10 +13,11 @@ namespace CKAN.NetKAN.Validators { internal sealed class CraftsInShipsValidator : IValidator { - public CraftsInShipsValidator(IHttpService http, IModuleService moduleService) + public CraftsInShipsValidator(IHttpService http, IModuleService moduleService, IGame game) { _http = http; _moduleService = moduleService; + _game = game; } public void Validate(Metadata metadata) @@ -30,7 +32,7 @@ public void Validate(Metadata metadata) if (!string.IsNullOrEmpty(package)) { var zip = new ZipFile(package); - var inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", null, false); + var inst = new GameInstance(_game, "/", "dummy", null, false); var badCrafts = _moduleService.GetCrafts(mod, zip, inst) .Where(f => !AllowedCraftPath(inst.ToRelativeGameDir(f.destination))) .ToList(); @@ -55,6 +57,7 @@ private bool AllowedCraftPath(string path) private readonly IHttpService _http; private readonly IModuleService _moduleService; + private readonly IGame _game; private static readonly ILog Log = LogManager.GetLogger(typeof(CraftsInShipsValidator)); } diff --git a/Netkan/Validators/HarmonyValidator.cs b/Netkan/Validators/HarmonyValidator.cs index 1eb5950cfe..13cfa89fc0 100644 --- a/Netkan/Validators/HarmonyValidator.cs +++ b/Netkan/Validators/HarmonyValidator.cs @@ -13,10 +13,11 @@ namespace CKAN.NetKAN.Validators { internal sealed class HarmonyValidator : IValidator { - public HarmonyValidator(IHttpService http, IModuleService moduleService) + public HarmonyValidator(IHttpService http, IModuleService moduleService, IGame game) { _http = http; _moduleService = moduleService; + _game = game; } public void Validate(Metadata metadata) @@ -25,14 +26,14 @@ public void Validate(Metadata metadata) CkanModule mod = CkanModule.FromJson(json.ToString()); // The Harmony2 module is allowed to install a Harmony DLL; // anybody else must have "provides":["Harmony1"] to do so - if (!mod.IsDLC && mod.identifier != "Harmony2") + if (_game.ShortName == "KSP" && !mod.IsDLC && mod.identifier != "Harmony2") { // Need to peek at the mod's files var package = _http.DownloadModule(metadata); if (!string.IsNullOrEmpty(package)) { ZipFile zip = new ZipFile(package); - GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); + GameInstance inst = new GameInstance(_game, "/", "dummy", new NullUser()); var harmonyDLLs = _moduleService.GetPlugins(mod, zip, inst) .Select(instF => instF.source.Name) @@ -55,6 +56,7 @@ public void Validate(Metadata metadata) private readonly IHttpService _http; private readonly IModuleService _moduleService; + private readonly IGame _game; private static readonly ILog Log = LogManager.GetLogger(typeof(HarmonyValidator)); } diff --git a/Netkan/Validators/InstallsFilesValidator.cs b/Netkan/Validators/InstallsFilesValidator.cs index bb86fef45f..68448b4070 100644 --- a/Netkan/Validators/InstallsFilesValidator.cs +++ b/Netkan/Validators/InstallsFilesValidator.cs @@ -12,11 +12,13 @@ internal sealed class InstallsFilesValidator : IValidator { private readonly IHttpService _http; private readonly IModuleService _moduleService; + private readonly IGame _game; - public InstallsFilesValidator(IHttpService http, IModuleService moduleService) + public InstallsFilesValidator(IHttpService http, IModuleService moduleService, IGame game) { _http = http; _moduleService = moduleService; + _game = game; } public void Validate(Metadata metadata) @@ -31,7 +33,7 @@ public void Validate(Metadata metadata) { throw new Kraken(string.Format( "Module contains no files matching: {0}", - mod.DescribeInstallStanzas(new KerbalSpaceProgram()) + mod.DescribeInstallStanzas(_game) )); } diff --git a/Netkan/Validators/MatchesKnownGameVersionsValidator.cs b/Netkan/Validators/MatchesKnownGameVersionsValidator.cs index cc309198d4..44d419dc44 100644 --- a/Netkan/Validators/MatchesKnownGameVersionsValidator.cs +++ b/Netkan/Validators/MatchesKnownGameVersionsValidator.cs @@ -1,17 +1,19 @@ using System.Collections.Generic; using Autofac; + using CKAN.GameVersionProviders; using CKAN.Versioning; -using CKAN.Games; using CKAN.NetKAN.Model; +using CKAN.Games; namespace CKAN.NetKAN.Validators { internal sealed class MatchesKnownGameVersionsValidator : IValidator { - public MatchesKnownGameVersionsValidator() + public MatchesKnownGameVersionsValidator(IGame game) { - knownVersions = new KerbalSpaceProgram().KnownVersions; + this.game = game; + knownVersions = game.KnownVersions; } public void Validate(Metadata metadata) @@ -21,11 +23,11 @@ public void Validate(Metadata metadata) { GameVersion minKsp = null, maxKsp = null; Registry.GetMinMaxVersions(new List() {mod}, out _, out _, out minKsp, out maxKsp); - var game = new KerbalSpaceProgram(); throw new Kraken($"{metadata.Identifier} doesn't match any valid game version: {GameVersionRange.VersionSpan(game, minKsp, maxKsp)}"); } } private List knownVersions; + private IGame game; } } diff --git a/Netkan/Validators/ModuleManagerDependsValidator.cs b/Netkan/Validators/ModuleManagerDependsValidator.cs index 58d1e9cbda..bbd495e9af 100644 --- a/Netkan/Validators/ModuleManagerDependsValidator.cs +++ b/Netkan/Validators/ModuleManagerDependsValidator.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json.Linq; using ICSharpCode.SharpZipLib.Zip; using log4net; + using CKAN.NetKAN.Services; using CKAN.NetKAN.Model; using CKAN.Extensions; @@ -13,10 +14,11 @@ namespace CKAN.NetKAN.Validators { internal sealed class ModuleManagerDependsValidator : IValidator { - public ModuleManagerDependsValidator(IHttpService http, IModuleService moduleService) + public ModuleManagerDependsValidator(IHttpService http, IModuleService moduleService, IGame game) { _http = http; _moduleService = moduleService; + _game = game; } public void Validate(Metadata metadata) @@ -31,7 +33,7 @@ public void Validate(Metadata metadata) if (!string.IsNullOrEmpty(package)) { ZipFile zip = new ZipFile(package); - GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); + GameInstance inst = new GameInstance(_game, "/", "dummy", new NullUser()); var mmConfigs = _moduleService.GetConfigFiles(mod, zip, inst) .Where(cfg => moduleManagerRegex.IsMatch( new StreamReader(zip.GetInputStream(cfg.source)).ReadToEnd())) @@ -63,6 +65,7 @@ public void Validate(Metadata metadata) private readonly IHttpService _http; private readonly IModuleService _moduleService; + private readonly IGame _game; private static readonly ILog Log = LogManager.GetLogger(typeof(ModuleManagerDependsValidator)); } diff --git a/Netkan/Validators/PluginsValidator.cs b/Netkan/Validators/PluginsValidator.cs index c4e25f3dc1..1fcc5c4a61 100644 --- a/Netkan/Validators/PluginsValidator.cs +++ b/Netkan/Validators/PluginsValidator.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json.Linq; using ICSharpCode.SharpZipLib.Zip; using log4net; + using CKAN.Extensions; using CKAN.NetKAN.Services; using CKAN.NetKAN.Model; @@ -13,10 +14,11 @@ namespace CKAN.NetKAN.Validators { internal sealed class PluginsValidator : IValidator { - public PluginsValidator(IHttpService http, IModuleService moduleService) + public PluginsValidator(IHttpService http, IModuleService moduleService, IGame game) { _http = http; _moduleService = moduleService; + _game = game; } public void Validate(Metadata metadata) @@ -31,7 +33,7 @@ public void Validate(Metadata metadata) if (!string.IsNullOrEmpty(package)) { ZipFile zip = new ZipFile(package); - GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); + GameInstance inst = new GameInstance(_game, "/", "dummy", new NullUser()); var plugins = _moduleService.GetPlugins(mod, zip, inst).ToList(); bool hasPlugin = plugins.Any(); @@ -76,6 +78,7 @@ public void Validate(Metadata metadata) private readonly IHttpService _http; private readonly IModuleService _moduleService; + private readonly IGame _game; private static readonly ILog Log = LogManager.GetLogger(typeof(PluginsValidator)); } diff --git a/Spec.md b/Spec.md index 4e15854afc..04f2c4ec5a 100644 --- a/Spec.md +++ b/Spec.md @@ -293,8 +293,9 @@ three source directives: In addition a destination directive *must* be provided: - `install_to`: The target location where the matched file or directory should be installed. - - Valid values for this entry are `GameData`, `Missions`(**v1.25**), `Ships`, `Ships/SPH`(**v1.12**), `Ships/VAB`(**v1.12**), `Ships/@thumbs/VAB`(**v1.16**), `Ships/@thumbs/SPH`(**v1.16**), `Ships/Script`(**v1.29**), `Tutorial`, `Scenarios` (**v1.14**) + - Valid values for this entry for KSP1 mods are `GameData`, `Missions`(**v1.25**), `Ships`, `Ships/SPH`(**v1.12**), `Ships/VAB`(**v1.12**), `Ships/@thumbs/VAB`(**v1.16**), `Ships/@thumbs/SPH`(**v1.16**), `Ships/Script`(**v1.29**), `Tutorial`, `Scenarios` (**v1.14**), and `GameRoot` (which should be used sparingly, if at all). + - Valid values for this entry for KSP2 mods are `GameRoot`, `BepInEx/plugins` (**v1.32**), and `SpaceWarp/Mods` (**v1.32**) - A path to a given subfolder location can be specified *only* under `GameData` (**v1.2**); for example: `GameData/MyMod/Plugins`. The client *must* check this path and abort the install if any attempts to traverse up directories are found (eg: `GameData/../Example`). diff --git a/Tests/NetKAN/Services/ModuleServiceTests.cs b/Tests/NetKAN/Services/ModuleServiceTests.cs index 0264b49f35..fbea93058e 100644 --- a/Tests/NetKAN/Services/ModuleServiceTests.cs +++ b/Tests/NetKAN/Services/ModuleServiceTests.cs @@ -21,7 +21,7 @@ public void HasInstallableFilesReturnsFalseWhenNoInstallableFiles() var json = JObject.Parse(TestData.DogeCoinFlag_101()); json["install"][0]["file"] = "DOES_NOT_EXIST"; - var sut = new ModuleService(); + var sut = new ModuleService(new KerbalSpaceProgram()); // Act var result = sut.HasInstallableFiles(CkanModule.FromJson(json.ToString()), zip); @@ -39,7 +39,7 @@ public void HasInstallableFilesReturnsTrueWhenInstallableFiles() var zip = TestData.DogeCoinFlagZip(); var json = JObject.Parse(TestData.DogeCoinFlag_101()); - var sut = new ModuleService(); + var sut = new ModuleService(new KerbalSpaceProgram()); // Act var result = sut.HasInstallableFiles(CkanModule.FromJson(json.ToString()), zip); @@ -54,7 +54,7 @@ public void HasInstallableFilesReturnsTrueWhenInstallableFiles() public void GetsInternalCkanCorrectly() { // Arrange - var sut = new ModuleService(); + var sut = new ModuleService(new KerbalSpaceProgram()); CkanModule mod = CkanModule.FromJson(TestData.DogeCoinFlag_101()); GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); @@ -80,7 +80,7 @@ public void GetsInternalAvcCorrectly() json["version"] = "1.0.0"; json["download"] = "https://awesomemod.example/AwesomeMod.zip"; - var sut = new ModuleService(); + var sut = new ModuleService(new KerbalSpaceProgram()); // Act var result = sut.GetInternalAvc(CkanModule.FromJson(json.ToString()), TestData.DogeCoinFlagAvcZip()); diff --git a/Tests/NetKAN/Transformers/InstallSizeTransformerTests.cs b/Tests/NetKAN/Transformers/InstallSizeTransformerTests.cs index 8896b322aa..a41fe4d686 100644 --- a/Tests/NetKAN/Transformers/InstallSizeTransformerTests.cs +++ b/Tests/NetKAN/Transformers/InstallSizeTransformerTests.cs @@ -7,6 +7,7 @@ using CKAN.NetKAN.Model; using CKAN.NetKAN.Services; using CKAN.NetKAN.Transformers; +using CKAN.Games; using Tests.Data; namespace Tests.NetKAN.Transformers @@ -24,9 +25,9 @@ public void Transform_NormalModule_CorrectInstallSize() mHttp.Setup(i => i.DownloadModule(It.IsAny())) .Returns(TestData.DogeCoinFlagZip()); - var modSvc = new ModuleService(); + var modSvc = new ModuleService(new KerbalSpaceProgram()); - ITransformer sut = new InstallSizeTransformer(mHttp.Object, modSvc); + ITransformer sut = new InstallSizeTransformer(mHttp.Object, modSvc, new KerbalSpaceProgram()); // Act var result = sut.Transform(new Metadata(json), opts).First(); diff --git a/Tests/NetKAN/Transformers/InternalCkanTransformerTests.cs b/Tests/NetKAN/Transformers/InternalCkanTransformerTests.cs index b0b68d283a..ce117bdb67 100644 --- a/Tests/NetKAN/Transformers/InternalCkanTransformerTests.cs +++ b/Tests/NetKAN/Transformers/InternalCkanTransformerTests.cs @@ -10,6 +10,7 @@ using CKAN.NetKAN.Model; using CKAN.NetKAN.Services; using CKAN.NetKAN.Transformers; +using CKAN.Games; namespace Tests.NetKAN.Transformers { @@ -39,7 +40,7 @@ public void AddsMissingProperties() It.IsAny())) .Returns(internalCkan); - var sut = new InternalCkanTransformer(mHttp.Object, mModuleService.Object); + var sut = new InternalCkanTransformer(mHttp.Object, mModuleService.Object, new KerbalSpaceProgram()); var json = new JObject(); json["spec_version"] = 1; @@ -78,7 +79,7 @@ public void DoesNotOverrideExistingProperties() It.IsAny())) .Returns(internalCkan); - var sut = new InternalCkanTransformer(mHttp.Object, mModuleService.Object); + var sut = new InternalCkanTransformer(mHttp.Object, mModuleService.Object, new KerbalSpaceProgram()); var json = new JObject(); json["spec_version"] = 1; @@ -120,7 +121,7 @@ public void HigherOfTwoSpecVersionsIsChosen( It.IsAny())) .Returns(internalCkan); - var sut = new InternalCkanTransformer(mHttp.Object, mModuleService.Object); + var sut = new InternalCkanTransformer(mHttp.Object, mModuleService.Object, new KerbalSpaceProgram()); var json = new JObject(); json["spec_version"] = specVersion; diff --git a/Tests/NetKAN/Validators/CkanValidatorTests.cs b/Tests/NetKAN/Validators/CkanValidatorTests.cs index 5a27a64565..2c1ef3aadb 100644 --- a/Tests/NetKAN/Validators/CkanValidatorTests.cs +++ b/Tests/NetKAN/Validators/CkanValidatorTests.cs @@ -1,10 +1,12 @@ -using CKAN; +using Moq; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +using CKAN; using CKAN.NetKAN.Model; using CKAN.NetKAN.Services; using CKAN.NetKAN.Validators; -using Moq; -using Newtonsoft.Json.Linq; -using NUnit.Framework; +using CKAN.Games; namespace Tests.NetKAN.Validators { @@ -37,7 +39,7 @@ public void DoesNotThrowOnValidCkan() mModuleService.Setup(i => i.HasInstallableFiles(It.IsAny(), It.IsAny())) .Returns(true); - var sut = new CkanValidator(mHttp.Object, mModuleService.Object); + var sut = new CkanValidator(mHttp.Object, mModuleService.Object, new KerbalSpaceProgram()); var json = (JObject)ValidCkan.DeepClone(); // Act @@ -62,7 +64,7 @@ public void DoesThrowWhenMissingProperty(string propertyName) mModuleService.Setup(i => i.HasInstallableFiles(It.IsAny(), It.IsAny())) .Returns(true); - var sut = new CkanValidator(mHttp.Object, mModuleService.Object); + var sut = new CkanValidator(mHttp.Object, mModuleService.Object, new KerbalSpaceProgram()); var json = (JObject)ValidCkan.DeepClone(); json.Remove(propertyName); @@ -85,7 +87,7 @@ public void DoesThrowWhenIdentifiersDoNotMatch() mModuleService.Setup(i => i.HasInstallableFiles(It.IsAny(), It.IsAny())) .Returns(true); - var sut = new CkanValidator(mHttp.Object, mModuleService.Object); + var sut = new CkanValidator(mHttp.Object, mModuleService.Object, new KerbalSpaceProgram()); var json = new JObject(); json["spec_version"] = 1; json["identifier"] = "AmazingMod"; @@ -113,7 +115,7 @@ public void DoesThrowWhenNoInstallableFiles() netkan["spec_version"] = 1; netkan["identifier"] = "AwesomeMod"; - var sut = new CkanValidator(mHttp.Object, mModuleService.Object); + var sut = new CkanValidator(mHttp.Object, mModuleService.Object, new KerbalSpaceProgram()); var json = (JObject)ValidCkan.DeepClone(); // Act diff --git a/Tests/NetKAN/Validators/InstallsFilesValidatorTests.cs b/Tests/NetKAN/Validators/InstallsFilesValidatorTests.cs index 320f190d79..93a4680400 100644 --- a/Tests/NetKAN/Validators/InstallsFilesValidatorTests.cs +++ b/Tests/NetKAN/Validators/InstallsFilesValidatorTests.cs @@ -1,10 +1,12 @@ -using CKAN; +using Moq; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +using CKAN; using CKAN.NetKAN.Model; using CKAN.NetKAN.Services; using CKAN.NetKAN.Validators; -using Moq; -using Newtonsoft.Json.Linq; -using NUnit.Framework; +using CKAN.Games; namespace Tests.NetKAN.Validators { @@ -27,7 +29,7 @@ public void DoesNotThrowWhenInstallableFiles() json["version"] = "1.0.0"; json["download"] = "https://www.awesome-mod.example/AwesomeMod.zip"; - var sut = new InstallsFilesValidator(mHttp.Object, mModuleService.Object); + var sut = new InstallsFilesValidator(mHttp.Object, mModuleService.Object, new KerbalSpaceProgram()); // Act TestDelegate act = () => sut.Validate(new Metadata(json)); @@ -54,7 +56,7 @@ public void DoesThrowWhenNoInstallableFiles() json["version"] = "1.0.0"; json["download"] = "https://www.awesome-mod.example/AwesomeMod.zip"; - var sut = new InstallsFilesValidator(mHttp.Object, mModuleService.Object); + var sut = new InstallsFilesValidator(mHttp.Object, mModuleService.Object, new KerbalSpaceProgram()); // Act TestDelegate act = () => sut.Validate(new Metadata(json)); diff --git a/Tests/NetKAN/Validators/MatchesKnownGameVersionsValidatorTests.cs b/Tests/NetKAN/Validators/MatchesKnownGameVersionsValidatorTests.cs index fecd7f4c5c..8dac393ceb 100644 --- a/Tests/NetKAN/Validators/MatchesKnownGameVersionsValidatorTests.cs +++ b/Tests/NetKAN/Validators/MatchesKnownGameVersionsValidatorTests.cs @@ -1,7 +1,9 @@ using Newtonsoft.Json.Linq; using NUnit.Framework; + using CKAN.NetKAN.Model; using CKAN.NetKAN.Validators; +using CKAN.Games; namespace Tests.NetKAN.Validators { @@ -55,7 +57,7 @@ private void TryVersion(string ksp_version, string ksp_version_min, string ksp_v } // Act - var val = new MatchesKnownGameVersionsValidator(); + var val = new MatchesKnownGameVersionsValidator(new KerbalSpaceProgram()); val.Validate(new Metadata(json)); } }