move basic mod scanning into the toolkit (#532)
This commit is contained in:
parent
34b0fd2870
commit
c12777ad53
|
@ -7,8 +7,8 @@ using Newtonsoft.Json;
|
|||
using NUnit.Framework;
|
||||
using StardewModdingAPI.Framework;
|
||||
using StardewModdingAPI.Framework.ModLoading;
|
||||
using StardewModdingAPI.Toolkit;
|
||||
using StardewModdingAPI.Toolkit.Framework.ModData;
|
||||
using StardewModdingAPI.Toolkit.Serialisation;
|
||||
using StardewModdingAPI.Toolkit.Serialisation.Models;
|
||||
|
||||
namespace StardewModdingAPI.Tests.Core
|
||||
|
@ -31,7 +31,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
Directory.CreateDirectory(rootFolder);
|
||||
|
||||
// act
|
||||
IModMetadata[] mods = new ModResolver().ReadManifests(rootFolder, new JsonHelper(), new ModDatabase()).ToArray();
|
||||
IModMetadata[] mods = new ModResolver().ReadManifests(new ModToolkit(), rootFolder, new ModDatabase()).ToArray();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(0, mods.Length, 0, $"Expected to find zero manifests, found {mods.Length} instead.");
|
||||
|
@ -46,7 +46,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
Directory.CreateDirectory(modFolder);
|
||||
|
||||
// act
|
||||
IModMetadata[] mods = new ModResolver().ReadManifests(rootFolder, new JsonHelper(), new ModDatabase()).ToArray();
|
||||
IModMetadata[] mods = new ModResolver().ReadManifests(new ModToolkit(), rootFolder, new ModDatabase()).ToArray();
|
||||
IModMetadata mod = mods.FirstOrDefault();
|
||||
|
||||
// assert
|
||||
|
@ -85,7 +85,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
File.WriteAllText(filename, JsonConvert.SerializeObject(original));
|
||||
|
||||
// act
|
||||
IModMetadata[] mods = new ModResolver().ReadManifests(rootFolder, new JsonHelper(), new ModDatabase()).ToArray();
|
||||
IModMetadata[] mods = new ModResolver().ReadManifests(new ModToolkit(), rootFolder, new ModDatabase()).ToArray();
|
||||
IModMetadata mod = mods.FirstOrDefault();
|
||||
|
||||
// assert
|
||||
|
|
|
@ -3,8 +3,9 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using StardewModdingAPI.Toolkit;
|
||||
using StardewModdingAPI.Toolkit.Framework.ModData;
|
||||
using StardewModdingAPI.Toolkit.Serialisation;
|
||||
using StardewModdingAPI.Toolkit.Framework.ModScanning;
|
||||
using StardewModdingAPI.Toolkit.Serialisation.Models;
|
||||
using StardewModdingAPI.Toolkit.Utilities;
|
||||
|
||||
|
@ -17,38 +18,15 @@ namespace StardewModdingAPI.Framework.ModLoading
|
|||
** Public methods
|
||||
*********/
|
||||
/// <summary>Get manifest metadata for each folder in the given root path.</summary>
|
||||
/// <param name="toolkit">The mod toolkit.</param>
|
||||
/// <param name="rootPath">The root path to search for mods.</param>
|
||||
/// <param name="jsonHelper">The JSON helper with which to read manifests.</param>
|
||||
/// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param>
|
||||
/// <returns>Returns the manifests by relative folder.</returns>
|
||||
public IEnumerable<IModMetadata> ReadManifests(string rootPath, JsonHelper jsonHelper, ModDatabase modDatabase)
|
||||
public IEnumerable<IModMetadata> ReadManifests(ModToolkit toolkit, string rootPath, ModDatabase modDatabase)
|
||||
{
|
||||
foreach (DirectoryInfo modDir in this.GetModFolders(rootPath))
|
||||
foreach (ModFolder folder in toolkit.GetModFolders(rootPath))
|
||||
{
|
||||
// read file
|
||||
Manifest manifest = null;
|
||||
string error = null;
|
||||
{
|
||||
string path = Path.Combine(modDir.FullName, "manifest.json");
|
||||
try
|
||||
{
|
||||
manifest = jsonHelper.ReadJsonFile<Manifest>(path);
|
||||
if (manifest == null)
|
||||
{
|
||||
error = File.Exists(path)
|
||||
? "its manifest is invalid."
|
||||
: "it doesn't have a manifest.";
|
||||
}
|
||||
}
|
||||
catch (SParseException ex)
|
||||
{
|
||||
error = $"parsing its manifest failed: {ex.Message}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = $"parsing its manifest failed:\n{ex.GetLogSummary()}";
|
||||
}
|
||||
}
|
||||
Manifest manifest = folder.Manifest;
|
||||
|
||||
// parse internal data record (if any)
|
||||
ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest);
|
||||
|
@ -58,7 +36,7 @@ namespace StardewModdingAPI.Framework.ModLoading
|
|||
if (string.IsNullOrWhiteSpace(displayName))
|
||||
displayName = dataRecord?.DisplayName;
|
||||
if (string.IsNullOrWhiteSpace(displayName))
|
||||
displayName = PathUtilities.GetRelativePath(rootPath, modDir.FullName);
|
||||
displayName = PathUtilities.GetRelativePath(rootPath, folder.ActualDirectory?.FullName ?? folder.SearchDirectory.FullName);
|
||||
|
||||
// apply defaults
|
||||
if (manifest != null && dataRecord != null)
|
||||
|
@ -68,10 +46,10 @@ namespace StardewModdingAPI.Framework.ModLoading
|
|||
}
|
||||
|
||||
// build metadata
|
||||
ModMetadataStatus status = error == null
|
||||
ModMetadataStatus status = folder.ManifestParseError == null
|
||||
? ModMetadataStatus.Found
|
||||
: ModMetadataStatus.Failed;
|
||||
yield return new ModMetadata(displayName, modDir.FullName, manifest, dataRecord).SetStatus(status, error);
|
||||
yield return new ModMetadata(displayName, folder.ActualDirectory?.FullName, manifest, dataRecord).SetStatus(status, folder.ManifestParseError);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,8 +104,8 @@ namespace StardewModdingAPI
|
|||
new Regex(@"^DebugOutput: (?:added CLOUD|dismount tile|Ping|playerPos)", RegexOptions.Compiled | RegexOptions.CultureInvariant)
|
||||
};
|
||||
|
||||
/// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
|
||||
private readonly JsonHelper JsonHelper = new JsonHelper();
|
||||
/// <summary>The mod toolkit used for generic mod interactions.</summary>
|
||||
private readonly ModToolkit Toolkit = new ModToolkit();
|
||||
|
||||
|
||||
/*********
|
||||
|
@ -205,7 +205,7 @@ namespace StardewModdingAPI
|
|||
new RectangleConverter()
|
||||
};
|
||||
foreach (JsonConverter converter in converters)
|
||||
this.JsonHelper.JsonSettings.Converters.Add(converter);
|
||||
this.Toolkit.JsonHelper.JsonSettings.Converters.Add(converter);
|
||||
|
||||
// add error handlers
|
||||
#if SMAPI_FOR_WINDOWS
|
||||
|
@ -423,14 +423,14 @@ namespace StardewModdingAPI
|
|||
ModResolver resolver = new ModResolver();
|
||||
|
||||
// load manifests
|
||||
IModMetadata[] mods = resolver.ReadManifests(Constants.ModPath, this.JsonHelper, modDatabase).ToArray();
|
||||
IModMetadata[] mods = resolver.ReadManifests(toolkit, Constants.ModPath, modDatabase).ToArray();
|
||||
resolver.ValidateManifests(mods, Constants.ApiVersion, toolkit.GetUpdateUrl);
|
||||
|
||||
// process dependencies
|
||||
mods = resolver.ProcessDependencies(mods, modDatabase).ToArray();
|
||||
|
||||
// load mods
|
||||
this.LoadMods(mods, this.JsonHelper, this.ContentCore, modDatabase);
|
||||
this.LoadMods(mods, this.Toolkit.JsonHelper, this.ContentCore, modDatabase);
|
||||
|
||||
// write metadata file
|
||||
if (this.Settings.DumpMetadata)
|
||||
|
@ -443,7 +443,7 @@ namespace StardewModdingAPI
|
|||
ModFolderPath = Constants.ModPath,
|
||||
Mods = mods
|
||||
};
|
||||
this.JsonHelper.WriteJsonFile(Path.Combine(Constants.LogDir, $"{Constants.LogNamePrefix}.metadata-dump.json"), export);
|
||||
this.Toolkit.JsonHelper.WriteJsonFile(Path.Combine(Constants.LogDir, $"{Constants.LogNamePrefix}.metadata-dump.json"), export);
|
||||
}
|
||||
|
||||
// check for updates
|
||||
|
@ -875,7 +875,7 @@ namespace StardewModdingAPI
|
|||
{
|
||||
IMonitor packMonitor = this.GetSecondaryMonitor(packManifest.Name);
|
||||
IContentHelper packContentHelper = new ContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, packMonitor);
|
||||
return new ContentPack(packDirPath, packManifest, packContentHelper, this.JsonHelper);
|
||||
return new ContentPack(packDirPath, packManifest, packContentHelper, this.Toolkit.JsonHelper);
|
||||
}
|
||||
|
||||
modHelper = new ModHelper(manifest.UniqueID, metadata.DirectoryPath, jsonHelper, this.GameInstance.Input, events, contentHelper, commandHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper, contentPacks, CreateTransitionalContentPack, this.DeprecationManager);
|
||||
|
@ -1117,7 +1117,7 @@ namespace StardewModdingAPI
|
|||
/// <param name="mods">The mods for which to reload translations.</param>
|
||||
private void ReloadTranslations(IEnumerable<IModMetadata> mods)
|
||||
{
|
||||
JsonHelper jsonHelper = this.JsonHelper;
|
||||
JsonHelper jsonHelper = this.Toolkit.JsonHelper;
|
||||
foreach (IModMetadata metadata in mods)
|
||||
{
|
||||
if (metadata.IsContentPack)
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using StardewModdingAPI.Toolkit.Serialisation.Models;
|
||||
|
||||
namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||
{
|
||||
/// <summary>The info about a mod read from its folder.</summary>
|
||||
public class ModFolder
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The Mods subfolder containing this mod.</summary>
|
||||
public DirectoryInfo SearchDirectory { get; }
|
||||
|
||||
/// <summary>The folder containing manifest.json.</summary>
|
||||
public DirectoryInfo ActualDirectory { get; }
|
||||
|
||||
/// <summary>The mod manifest.</summary>
|
||||
public Manifest Manifest { get; }
|
||||
|
||||
/// <summary>The error which occurred parsing the manifest, if any.</summary>
|
||||
public string ManifestParseError { get; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance when a mod wasn't found in a folder.</summary>
|
||||
/// <param name="searchDirectory">The directory that was searched.</param>
|
||||
public ModFolder(DirectoryInfo searchDirectory)
|
||||
{
|
||||
this.SearchDirectory = searchDirectory;
|
||||
}
|
||||
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="searchDirectory">The Mods subfolder containing this mod.</param>
|
||||
/// <param name="actualDirectory">The folder containing manifest.json.</param>
|
||||
/// <param name="manifest">The mod manifest.</param>
|
||||
/// <param name="manifestParseError">The error which occurred parsing the manifest, if any.</param>
|
||||
public ModFolder(DirectoryInfo searchDirectory, DirectoryInfo actualDirectory, Manifest manifest, string manifestParseError = null)
|
||||
{
|
||||
this.SearchDirectory = searchDirectory;
|
||||
this.ActualDirectory = actualDirectory;
|
||||
this.Manifest = manifest;
|
||||
this.ManifestParseError = manifestParseError;
|
||||
}
|
||||
|
||||
/// <summary>Get the update keys for a mod.</summary>
|
||||
/// <param name="manifest">The mod manifest.</param>
|
||||
public IEnumerable<string> GetUpdateKeys(Manifest manifest)
|
||||
{
|
||||
return
|
||||
(manifest.UpdateKeys ?? new string[0])
|
||||
.Where(p => !string.IsNullOrWhiteSpace(p))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using StardewModdingAPI.Toolkit.Serialisation;
|
||||
using StardewModdingAPI.Toolkit.Serialisation.Models;
|
||||
|
||||
namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||
{
|
||||
/// <summary>Scans folders for mod data.</summary>
|
||||
public class ModScanner
|
||||
{
|
||||
/*********
|
||||
** Properties
|
||||
*********/
|
||||
/// <summary>The JSON helper with which to read manifests.</summary>
|
||||
private readonly JsonHelper JsonHelper;
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="jsonHelper">The JSON helper with which to read manifests.</param>
|
||||
public ModScanner(JsonHelper jsonHelper)
|
||||
{
|
||||
this.JsonHelper = jsonHelper;
|
||||
}
|
||||
|
||||
/// <summary>Extract information about all mods in the given folder.</summary>
|
||||
/// <param name="rootPath">The root folder containing mods.</param>
|
||||
public IEnumerable<ModFolder> GetModFolders(string rootPath)
|
||||
{
|
||||
foreach (DirectoryInfo folder in new DirectoryInfo(rootPath).EnumerateDirectories())
|
||||
yield return this.ReadFolder(rootPath, folder);
|
||||
}
|
||||
|
||||
/// <summary>Extract information from a mod folder.</summary>
|
||||
/// <param name="rootPath">The root folder containing mods.</param>
|
||||
/// <param name="searchFolder">The folder to search for a mod.</param>
|
||||
public ModFolder ReadFolder(string rootPath, DirectoryInfo searchFolder)
|
||||
{
|
||||
// find manifest.json
|
||||
FileInfo manifestFile = this.FindManifest(searchFolder);
|
||||
if (manifestFile == null)
|
||||
return new ModFolder(searchFolder);
|
||||
|
||||
// read mod info
|
||||
Manifest manifest = null;
|
||||
string manifestError = null;
|
||||
{
|
||||
try
|
||||
{
|
||||
manifest = this.JsonHelper.ReadJsonFile<Manifest>(manifestFile.FullName);
|
||||
if (manifest == null)
|
||||
{
|
||||
manifestError = File.Exists(manifestFile.FullName)
|
||||
? "its manifest is invalid."
|
||||
: "it doesn't have a manifest.";
|
||||
}
|
||||
}
|
||||
catch (SParseException ex)
|
||||
{
|
||||
manifestError = $"parsing its manifest failed: {ex.Message}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
manifestError = $"parsing its manifest failed:\n{ex}";
|
||||
}
|
||||
}
|
||||
|
||||
return new ModFolder(searchFolder, manifestFile.Directory, manifest, manifestError);
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** Private methods
|
||||
*********/
|
||||
/// <summary>Find the manifest for a mod folder.</summary>
|
||||
/// <param name="folder">The folder to search.</param>
|
||||
private FileInfo FindManifest(DirectoryInfo folder)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// check for manifest in current folder
|
||||
FileInfo file = new FileInfo(Path.Combine(folder.FullName, "manifest.json"));
|
||||
if (file.Exists)
|
||||
return file;
|
||||
|
||||
// check for single subfolder
|
||||
FileSystemInfo[] entries = folder.EnumerateFileSystemInfos().Take(2).ToArray();
|
||||
if (entries.Length == 1 && entries[0] is DirectoryInfo subfolder)
|
||||
{
|
||||
folder = subfolder;
|
||||
continue;
|
||||
}
|
||||
|
||||
// not found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ using System.Threading.Tasks;
|
|||
using Newtonsoft.Json;
|
||||
using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
|
||||
using StardewModdingAPI.Toolkit.Framework.ModData;
|
||||
using StardewModdingAPI.Toolkit.Framework.ModScanning;
|
||||
using StardewModdingAPI.Toolkit.Serialisation;
|
||||
|
||||
namespace StardewModdingAPI.Toolkit
|
||||
{
|
||||
|
@ -27,6 +29,13 @@ namespace StardewModdingAPI.Toolkit
|
|||
};
|
||||
|
||||
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>Encapsulates SMAPI's JSON parsing.</summary>
|
||||
public JsonHelper JsonHelper { get; } = new JsonHelper();
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
|
@ -53,6 +62,13 @@ namespace StardewModdingAPI.Toolkit
|
|||
return new ModDatabase(records, this.GetUpdateUrl);
|
||||
}
|
||||
|
||||
/// <summary>Extract information about all mods in the given folder.</summary>
|
||||
/// <param name="rootPath">The root folder containing mods.</param>
|
||||
public IEnumerable<ModFolder> GetModFolders(string rootPath)
|
||||
{
|
||||
return new ModScanner(this.JsonHelper).GetModFolders(rootPath);
|
||||
}
|
||||
|
||||
/// <summary>Get an update URL for an update key (if valid).</summary>
|
||||
/// <param name="updateKey">The update key.</param>
|
||||
public string GetUpdateUrl(string updateKey)
|
||||
|
|
Loading…
Reference in New Issue