From 100e303b488a36e8410ff67e32c35bff80f21ba2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 19 Aug 2018 20:27:28 -0400 Subject: add recursive mod search (#583) --- .../Framework/ModScanning/ModFolder.cs | 15 ++--- .../Framework/ModScanning/ModScanner.cs | 64 ++++++++++++++++++++-- 2 files changed, 63 insertions(+), 16 deletions(-) (limited to 'src/StardewModdingAPI.Toolkit/Framework/ModScanning') diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs index 4aaa3f83..83c9c44d 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs @@ -11,11 +11,8 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /********* ** Accessors *********/ - /// The Mods subfolder containing this mod. - public DirectoryInfo SearchDirectory { get; } - - /// The folder containing manifest.json. - public DirectoryInfo ActualDirectory { get; } + /// The folder containing the mod's manifest.json. + public DirectoryInfo Directory { get; } /// The mod manifest. public Manifest Manifest { get; } @@ -28,14 +25,12 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning ** Public methods *********/ /// Construct an instance. - /// The Mods subfolder containing this mod. - /// The folder containing manifest.json. + /// The folder containing the mod's manifest.json. /// The mod manifest. /// The error which occurred parsing the manifest, if any. - public ModFolder(DirectoryInfo searchDirectory, DirectoryInfo actualDirectory, Manifest manifest, string manifestParseError = null) + public ModFolder(DirectoryInfo directory, Manifest manifest, string manifestParseError = null) { - this.SearchDirectory = searchDirectory; - this.ActualDirectory = actualDirectory; + this.Directory = directory; this.Manifest = manifest; this.ManifestParseError = manifestParseError; } diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs index f1cce4a4..063ec2f4 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs @@ -16,6 +16,14 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /// The JSON helper with which to read manifests. private readonly JsonHelper JsonHelper; + /// A list of filesystem entry names to ignore when checking whether a folder should be treated as a mod. + private readonly HashSet IgnoreFilesystemEntries = new HashSet(StringComparer.InvariantCultureIgnoreCase) + { + ".DS_Store", + "mcs", + "Thumbs.db" + }; + /********* ** Public methods @@ -31,19 +39,23 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /// The root folder containing mods. public IEnumerable GetModFolders(string rootPath) { - foreach (DirectoryInfo folder in new DirectoryInfo(rootPath).EnumerateDirectories()) - yield return this.ReadFolder(rootPath, folder); + DirectoryInfo root = new DirectoryInfo(rootPath); + return this.GetModFolders(root, root); } /// Extract information from a mod folder. - /// The root folder containing mods. /// The folder to search for a mod. - public ModFolder ReadFolder(string rootPath, DirectoryInfo searchFolder) + public ModFolder ReadFolder(DirectoryInfo searchFolder) { // find manifest.json FileInfo manifestFile = this.FindManifest(searchFolder); if (manifestFile == null) - return new ModFolder(searchFolder, null, null, "it doesn't have a manifest."); + { + bool isEmpty = !searchFolder.GetFileSystemInfos().Where(this.IsRelevant).Any(); + if (isEmpty) + return new ModFolder(searchFolder, null, "it's an empty folder."); + return new ModFolder(searchFolder, null, "it contains files, but none of them are manifest.json."); + } // read mod info Manifest manifest = null; @@ -64,13 +76,33 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning } } - return new ModFolder(searchFolder, manifestFile.Directory, manifest, manifestError); + return new ModFolder(manifestFile.Directory, manifest, manifestError); } /********* ** Private methods *********/ + /// Recursively extract information about all mods in the given folder. + /// The root mod folder. + /// The folder to search for mods. + public IEnumerable GetModFolders(DirectoryInfo root, DirectoryInfo folder) + { + // recurse into subfolders + if (this.IsModSearchFolder(root, folder)) + { + foreach (DirectoryInfo subfolder in folder.EnumerateDirectories()) + { + foreach (ModFolder match in this.GetModFolders(root, subfolder)) + yield return match; + } + } + + // treat as mod folder + else + yield return this.ReadFolder(folder); + } + /// Find the manifest for a mod folder. /// The folder to search. private FileInfo FindManifest(DirectoryInfo folder) @@ -94,5 +126,25 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning return null; } } + + /// Get whether a given folder should be treated as a search folder (i.e. look for subfolders containing mods). + /// The root mod folder. + /// The folder to search for mods. + private bool IsModSearchFolder(DirectoryInfo root, DirectoryInfo folder) + { + if (root.FullName == folder.FullName) + return true; + + DirectoryInfo[] subfolders = folder.GetDirectories().Where(this.IsRelevant).ToArray(); + FileInfo[] files = folder.GetFiles().Where(this.IsRelevant).ToArray(); + return subfolders.Any() && !files.Any(); + } + + /// Get whether a file or folder is relevant when deciding how to process a mod folder. + /// The file or folder. + private bool IsRelevant(FileSystemInfo entry) + { + return !this.IgnoreFilesystemEntries.Contains(entry.Name); + } } } -- cgit From ca8699c68f238f3092966a550643859bce357a86 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 19 Aug 2018 21:22:48 -0400 Subject: add display name field to ModFolder (#583) --- src/SMAPI/Framework/ModLoading/ModResolver.cs | 9 +-------- .../Framework/ModScanning/ModFolder.cs | 13 ++++++++++++- .../Framework/ModScanning/ModScanner.cs | 11 ++++++----- 3 files changed, 19 insertions(+), 14 deletions(-) (limited to 'src/StardewModdingAPI.Toolkit/Framework/ModScanning') diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index 11518444..65a311dc 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -30,13 +30,6 @@ namespace StardewModdingAPI.Framework.ModLoading // parse internal data record (if any) ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest); - // get display name - string displayName = manifest?.Name; - if (string.IsNullOrWhiteSpace(displayName)) - displayName = dataRecord?.DisplayName; - if (string.IsNullOrWhiteSpace(displayName)) - displayName = PathUtilities.GetRelativePath(rootPath, folder.Directory.FullName); - // apply defaults if (manifest != null && dataRecord != null) { @@ -48,7 +41,7 @@ namespace StardewModdingAPI.Framework.ModLoading ModMetadataStatus status = folder.ManifestParseError == null ? ModMetadataStatus.Found : ModMetadataStatus.Failed; - yield return new ModMetadata(displayName, folder.Directory.FullName, manifest, dataRecord).SetStatus(status, folder.ManifestParseError); + yield return new ModMetadata(folder.DisplayName, folder.Directory.FullName, manifest, dataRecord).SetStatus(status, folder.ManifestParseError); } } diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs index 83c9c44d..d2fea9e2 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using StardewModdingAPI.Toolkit.Serialisation.Models; +using StardewModdingAPI.Toolkit.Utilities; namespace StardewModdingAPI.Toolkit.Framework.ModScanning { @@ -11,6 +12,9 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /********* ** Accessors *********/ + /// A suggested display name for the mod folder. + public string DisplayName { get; } + /// The folder containing the mod's manifest.json. public DirectoryInfo Directory { get; } @@ -25,14 +29,21 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning ** Public methods *********/ /// Construct an instance. + /// The root folder containing mods. /// The folder containing the mod's manifest.json. /// The mod manifest. /// The error which occurred parsing the manifest, if any. - public ModFolder(DirectoryInfo directory, Manifest manifest, string manifestParseError = null) + public ModFolder(DirectoryInfo root, DirectoryInfo directory, Manifest manifest, string manifestParseError = null) { + // save info this.Directory = directory; this.Manifest = manifest; this.ManifestParseError = manifestParseError; + + // set display name + this.DisplayName = manifest?.Name; + if (string.IsNullOrWhiteSpace(this.DisplayName)) + this.DisplayName = PathUtilities.GetRelativePath(root.FullName, directory.FullName); } /// Get the update keys for a mod. diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs index 063ec2f4..71dc0cb3 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs @@ -44,8 +44,9 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning } /// Extract information from a mod folder. + /// The root folder containing mods. /// The folder to search for a mod. - public ModFolder ReadFolder(DirectoryInfo searchFolder) + public ModFolder ReadFolder(DirectoryInfo root, DirectoryInfo searchFolder) { // find manifest.json FileInfo manifestFile = this.FindManifest(searchFolder); @@ -53,8 +54,8 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning { bool isEmpty = !searchFolder.GetFileSystemInfos().Where(this.IsRelevant).Any(); if (isEmpty) - return new ModFolder(searchFolder, null, "it's an empty folder."); - return new ModFolder(searchFolder, null, "it contains files, but none of them are manifest.json."); + return new ModFolder(root, searchFolder, null, "it's an empty folder."); + return new ModFolder(root, searchFolder, null, "it contains files, but none of them are manifest.json."); } // read mod info @@ -76,7 +77,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning } } - return new ModFolder(manifestFile.Directory, manifest, manifestError); + return new ModFolder(root, manifestFile.Directory, manifest, manifestError); } @@ -100,7 +101,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning // treat as mod folder else - yield return this.ReadFolder(folder); + yield return this.ReadFolder(root, folder); } /// Find the manifest for a mod folder. -- cgit From d2b6a71aa4cc3383d55350e346e27c1ab2ff134b Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 22 Aug 2018 01:36:11 -0400 Subject: fix crash when a mod manifest is corrupted --- docs/release-notes.md | 1 + src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src/StardewModdingAPI.Toolkit/Framework/ModScanning') diff --git a/docs/release-notes.md b/docs/release-notes.md index 3ce7d6bb..34f7404e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,6 +5,7 @@ * Moved most SMAPI files into a `smapi-internal` subfolder. * Moved save backups into a `save-backups` subfolder (instead of `Mods/SaveBackup/backups`). Note that previous backups will be deleted when you update. * Fixed installer duplicating bundled mods if you moved them after the last install. + * Fixed crash when a mod manifest is corrupted. * Updated compatibility list. * For modders: diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs index 71dc0cb3..7512d5cb 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs @@ -64,7 +64,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning { try { - if (!this.JsonHelper.ReadJsonFileIfExists(manifestFile.FullName, out manifest)) + if (!this.JsonHelper.ReadJsonFileIfExists(manifestFile.FullName, out manifest) || manifest == null) manifestError = "its manifest is invalid."; } catch (SParseException ex) -- cgit From 074f730329659d0db4be3a99fa8e5c09383ca3e6 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 27 Sep 2018 00:36:31 -0400 Subject: add separate error when player puts an XNB mod in Mods --- docs/release-notes.md | 1 + .../Framework/ModScanning/ModScanner.cs | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'src/StardewModdingAPI.Toolkit/Framework/ModScanning') diff --git a/docs/release-notes.md b/docs/release-notes.md index 3fea347e..12886019 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,6 +5,7 @@ * Moved most SMAPI files into a `smapi-internal` subfolder. * Moved save backups into a `save-backups` subfolder (instead of `Mods/SaveBackup/backups`). Note that previous backups will be deleted when you update. * Update checks now work even when the mod has no update keys in most cases. + * Improved error when you put an XNB mod in `Mods`. * Fixed error when mods add an invalid location with no name. * Fixed compatibility issues for some Linux players. SMAPI will now always use xterm if it's available. * Fixed some game install paths not detected on Windows. diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs index 7512d5cb..2c23a3ce 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs @@ -24,6 +24,15 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning "Thumbs.db" }; + /// The extensions for files which an XNB mod may contain. If a mod contains *only* these file extensions, it should be considered an XNB mod. + private readonly HashSet PotentialXnbModExtensions = new HashSet(StringComparer.InvariantCultureIgnoreCase) + { + ".md", + ".png", + ".txt", + ".xnb" + }; + /********* ** Public methods @@ -50,11 +59,15 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning { // find manifest.json FileInfo manifestFile = this.FindManifest(searchFolder); + + // set appropriate invalid-mod error if (manifestFile == null) { - bool isEmpty = !searchFolder.GetFileSystemInfos().Where(this.IsRelevant).Any(); - if (isEmpty) + FileInfo[] files = searchFolder.GetFiles("*", SearchOption.AllDirectories).Where(this.IsRelevant).ToArray(); + if (!files.Any()) return new ModFolder(root, searchFolder, null, "it's an empty folder."); + if (files.All(file => this.PotentialXnbModExtensions.Contains(file.Extension))) + return new ModFolder(root, searchFolder, null, "it's an older XNB mod which replaces game files (not run through SMAPI)."); return new ModFolder(root, searchFolder, null, "it contains files, but none of them are manifest.json."); } -- cgit From 88ea1eae13f3c5e3bfcedfb2ac9139c6dc829bac Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 27 Oct 2018 22:08:00 -0400 Subject: add support for ignored mod folders --- docs/release-notes.md | 4 +++- src/SMAPI/Framework/IModMetadata.cs | 3 +++ src/SMAPI/Framework/ModLoading/ModMetadata.cs | 7 ++++++- src/SMAPI/Framework/ModLoading/ModResolver.cs | 4 ++-- src/SMAPI/Framework/SCore.cs | 9 ++++++--- src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs | 7 ++++++- .../Framework/ModScanning/ModScanner.cs | 6 +++++- 7 files changed, 31 insertions(+), 9 deletions(-) (limited to 'src/StardewModdingAPI.Toolkit/Framework/ModScanning') diff --git a/docs/release-notes.md b/docs/release-notes.md index 22c483c4..acbaef99 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -3,9 +3,11 @@ * For players: * Update checks now work even for mods without update keys in most cases. * Reorganised SMAPI files: - * You can now group mods into subfolders to organise them. * Most SMAPI files are now tucked into a `smapi-internal` subfolder. * Save backups are now in a `save-backups` subfolder, so they're easier to access. Note that previous backups will be deleted when you update. + * Added support for organising mods: + * You can now group mods into subfolders to organise them. + * You can now mark a mod folder ignored by starting the name with a dot (like `.disabled mods`). * Improved various error messages to be more clear and intuitive. * SMAPI now prevents a crash caused by mods adding dialogue the game can't parse. * When you have an older game version, SMAPI now recommends a compatible SMAPI version in its error. diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs index a62c9950..bda9429f 100644 --- a/src/SMAPI/Framework/IModMetadata.cs +++ b/src/SMAPI/Framework/IModMetadata.cs @@ -33,6 +33,9 @@ namespace StardewModdingAPI.Framework /// The reason the metadata is invalid, if any. string Error { get; } + /// Whether the mod folder should be ignored. This is true if it was found within a folder whose name starts with a dot. + bool IsIgnored { get; } + /// The mod instance (if loaded and is false). IMod Mod { get; } diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs index 0a5f5d3f..04aa679b 100644 --- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs +++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs @@ -37,6 +37,9 @@ namespace StardewModdingAPI.Framework.ModLoading /// The reason the metadata is invalid, if any. public string Error { get; private set; } + /// Whether the mod folder should be ignored. This is true if it was found within a folder whose name starts with a dot. + public bool IsIgnored { get; } + /// The mod instance (if loaded and is false). public IMod Mod { get; private set; } @@ -65,13 +68,15 @@ namespace StardewModdingAPI.Framework.ModLoading /// The relative to the game's Mods folder. /// The mod manifest. /// Metadata about the mod from SMAPI's internal data (if any). - public ModMetadata(string displayName, string directoryPath, string relativeDirectoryPath, IManifest manifest, ModDataRecordVersionedFields dataRecord) + /// Whether the mod folder should be ignored. This should be true if it was found within a folder whose name starts with a dot. + public ModMetadata(string displayName, string directoryPath, string relativeDirectoryPath, IManifest manifest, ModDataRecordVersionedFields dataRecord, bool isIgnored) { this.DisplayName = displayName; this.DirectoryPath = directoryPath; this.RelativeDirectoryPath = relativeDirectoryPath; this.Manifest = manifest; this.DataRecord = dataRecord; + this.IsIgnored = isIgnored; } /// Set the mod status. diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index 26ec82d7..9992cc78 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -38,11 +38,11 @@ namespace StardewModdingAPI.Framework.ModLoading } // build metadata - ModMetadataStatus status = folder.ManifestParseError == null + ModMetadataStatus status = folder.ManifestParseError == null || !folder.ShouldBeLoaded ? ModMetadataStatus.Found : ModMetadataStatus.Failed; string relativePath = PathUtilities.GetRelativePath(rootPath, folder.Directory.FullName); - yield return new ModMetadata(folder.DisplayName, folder.Directory.FullName, relativePath, manifest, dataRecord).SetStatus(status, folder.ManifestParseError); + yield return new ModMetadata(folder.DisplayName, folder.Directory.FullName, relativePath, manifest, dataRecord, isIgnored: !folder.ShouldBeLoaded).SetStatus(status, folder.ManifestParseError ?? "disabled by dot convention"); } } diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 6c897382..a17af91e 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -373,12 +373,15 @@ namespace StardewModdingAPI.Framework // load manifests IModMetadata[] mods = resolver.ReadManifests(toolkit, this.ModsPath, modDatabase).ToArray(); - resolver.ValidateManifests(mods, Constants.ApiVersion, toolkit.GetUpdateUrl); - // process dependencies - mods = resolver.ProcessDependencies(mods, modDatabase).ToArray(); + // filter out ignored mods + foreach (IModMetadata mod in mods.Where(p => p.IsIgnored)) + this.Monitor.Log($" Skipped {mod.RelativeDirectoryPath} (folder name starts with a dot).", LogLevel.Trace); + mods = mods.Where(p => !p.IsIgnored).ToArray(); // load mods + resolver.ValidateManifests(mods, Constants.ApiVersion, toolkit.GetUpdateUrl); + mods = resolver.ProcessDependencies(mods, modDatabase).ToArray(); this.LoadMods(mods, this.Toolkit.JsonHelper, this.ContentCore, modDatabase); // write metadata file diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs index d2fea9e2..bb467b36 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModFolder.cs @@ -24,6 +24,9 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /// The error which occurred parsing the manifest, if any. public string ManifestParseError { get; } + /// Whether the mod should be loaded by default. This is false if it was found within a folder whose name starts with a dot. + public bool ShouldBeLoaded { get; } + /********* ** Public methods @@ -33,12 +36,14 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /// The folder containing the mod's manifest.json. /// The mod manifest. /// The error which occurred parsing the manifest, if any. - public ModFolder(DirectoryInfo root, DirectoryInfo directory, Manifest manifest, string manifestParseError = null) + /// Whether the mod should be loaded by default. This should be false if it was found within a folder whose name starts with a dot. + public ModFolder(DirectoryInfo root, DirectoryInfo directory, Manifest manifest, string manifestParseError = null, bool shouldBeLoaded = true) { // save info this.Directory = directory; this.Manifest = manifest; this.ManifestParseError = manifestParseError; + this.ShouldBeLoaded = shouldBeLoaded; // set display name this.DisplayName = manifest?.Name; diff --git a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs index 2c23a3ce..106c294f 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/ModScanning/ModScanner.cs @@ -102,8 +102,12 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning /// The folder to search for mods. public IEnumerable GetModFolders(DirectoryInfo root, DirectoryInfo folder) { + // skip + if (folder.FullName != root.FullName && folder.Name.StartsWith(".")) + yield return new ModFolder(root, folder, null, "ignored folder because its name starts with a dot.", shouldBeLoaded: false); + // recurse into subfolders - if (this.IsModSearchFolder(root, folder)) + else if (this.IsModSearchFolder(root, folder)) { foreach (DirectoryInfo subfolder in folder.EnumerateDirectories()) { -- cgit