From 0c191eb32c41ffedd321951cda70b521e9b51c96 Mon Sep 17 00:00:00 2001
From: atravita-mods <94934860+atravita-mods@users.noreply.github.com>
Date: Sat, 15 Oct 2022 08:36:24 -0400
Subject: make asset name comparing lazy.
---
src/SMAPI/Framework/Content/AssetName.cs | 108 ++++++++++++++++++++++---------
1 file changed, 78 insertions(+), 30 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs
index 148354a1..05e1d1c2 100644
--- a/src/SMAPI/Framework/Content/AssetName.cs
+++ b/src/SMAPI/Framework/Content/AssetName.cs
@@ -1,7 +1,11 @@
using System;
using StardewModdingAPI.Toolkit.Utilities;
+using StardewModdingAPI.Utilities.AssetPathUtilities;
+
using StardewValley;
+using ToolkitPathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities;
+
namespace StardewModdingAPI.Framework.Content
{
/// An asset name that can be loaded through the content pipeline.
@@ -94,10 +98,28 @@ namespace StardewModdingAPI.Framework.Content
if (string.IsNullOrWhiteSpace(assetName))
return false;
- assetName = PathUtilities.NormalizeAssetName(assetName);
+ AssetPartYielder compareTo = new(useBaseName ? this.BaseName : this.Name);
+ AssetPartYielder compareFrom = new(assetName);
+
+ while (true)
+ {
+ bool otherHasMore = compareFrom.MoveNext();
+ bool iHaveMore = compareTo.MoveNext();
+
+ // neither of us have any more to yield, I'm done.
+ if (!otherHasMore && !iHaveMore)
+ return true;
+
+ // One of us has more but the other doesn't, this isn't a match.
+ if (otherHasMore ^ iHaveMore)
+ return false;
- string compareTo = useBaseName ? this.BaseName : this.Name;
- return compareTo.Equals(assetName, StringComparison.OrdinalIgnoreCase);
+ // My next bit doesn't match their next bit, this isn't a match.
+ if (!compareTo.Current.Equals(compareFrom.Current, StringComparison.OrdinalIgnoreCase))
+ return false;
+
+ // continue checking.
+ }
}
///
@@ -119,43 +141,69 @@ namespace StardewModdingAPI.Framework.Content
if (prefix is null)
return false;
- string rawTrimmed = prefix.Trim();
+ ReadOnlySpan trimmed = prefix.AsSpan().Trim();
+
+ // just because most ReadOnlySpan/Span APIs expect a ReadOnlySpan/Span, easier to read.
+ ReadOnlySpan seperators = new(ToolkitPathUtilities.PossiblePathSeparators);
- // asset keys can't have a leading slash, but NormalizeAssetName will trim them
- if (rawTrimmed.StartsWith('/') || rawTrimmed.StartsWith('\\'))
+ // asset keys can't have a leading slash, but AssetPathYielder won't yield that.
+ if (seperators.Contains(trimmed[0]))
return false;
- // normalize prefix
+ if (trimmed.Length == 0)
+ return true;
+
+ AssetPartYielder compareTo = new(this.Name);
+ AssetPartYielder compareFrom = new(trimmed);
+
+ while (true)
{
- string normalized = PathUtilities.NormalizeAssetName(prefix);
+ bool otherHasMore = compareFrom.MoveNext();
+ bool iHaveMore = compareTo.MoveNext();
- // keep trailing slash
- if (rawTrimmed.EndsWith('/') || rawTrimmed.EndsWith('\\'))
- normalized += PathUtilities.PreferredAssetSeparator;
+ // Neither of us have any more to yield, I'm done.
+ if (!otherHasMore && !iHaveMore)
+ return true;
- prefix = normalized;
- }
+ // the prefix is actually longer than the asset name, this can't be true.
+ if (otherHasMore && !iHaveMore)
+ return false;
- // compare
- if (prefix.Length == 0)
- return true;
+ // they're done, I have more. (These are going to be word boundaries, I don't need to check that).
+ if (!otherHasMore && iHaveMore)
+ {
+ return allowSubfolder || !compareTo.Remainder.Contains(seperators, StringComparison.Ordinal);
+ }
- return
- this.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
- && (
- allowPartialWord
- || this.Name.Length == prefix.Length
- || !char.IsLetterOrDigit(prefix[^1]) // last character in suffix is word separator
- || !char.IsLetterOrDigit(this.Name[prefix.Length]) // or first character after it is
- )
- && (
- allowSubfolder
- || this.Name.Length == prefix.Length
- || !this.Name[prefix.Length..].Contains(PathUtilities.PreferredAssetSeparator)
- );
+ // check my next segment against theirs.
+ if (otherHasMore && iHaveMore)
+ {
+ // my next segment doesn't match theirs.
+ if (!compareTo.Current.StartsWith(compareFrom.Current, StringComparison.OrdinalIgnoreCase))
+ return false;
+
+ // my next segment starts with theirs but isn't an exact match.
+ if (compareTo.Current.Length != compareFrom.Current.Length)
+ {
+ // something like "Maps/" would require an exact match.
+ if (seperators.Contains(trimmed[^1]))
+ return false;
+
+ // check for partial word.
+ if (!allowPartialWord
+ && char.IsLetterOrDigit(compareFrom.Current[^1]) // last character in suffix is not word separator
+ && char.IsLetterOrDigit(compareTo.Current[compareFrom.Current.Length]) // and the first character after it isn't either.
+ )
+ return false;
+
+ return allowSubfolder || !compareTo.Remainder.Contains(seperators, StringComparison.Ordinal);
+ }
+
+ // exact matches should continue checking.
+ }
+ }
}
-
///
public bool IsDirectlyUnderPath(string? assetFolder)
{
--
cgit
From 70cde89480e43bb1369c1063c7b19f757784f269 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Sun, 16 Oct 2022 14:41:45 -0400
Subject: tweak naming in new code
---
src/SMAPI/Framework/Content/AssetName.cs | 52 +++++++++++++++-----------------
1 file changed, 25 insertions(+), 27 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs
index 05e1d1c2..d7ee6dba 100644
--- a/src/SMAPI/Framework/Content/AssetName.cs
+++ b/src/SMAPI/Framework/Content/AssetName.cs
@@ -1,9 +1,7 @@
using System;
using StardewModdingAPI.Toolkit.Utilities;
using StardewModdingAPI.Utilities.AssetPathUtilities;
-
using StardewValley;
-
using ToolkitPathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities;
namespace StardewModdingAPI.Framework.Content
@@ -98,24 +96,24 @@ namespace StardewModdingAPI.Framework.Content
if (string.IsNullOrWhiteSpace(assetName))
return false;
- AssetPartYielder compareTo = new(useBaseName ? this.BaseName : this.Name);
- AssetPartYielder compareFrom = new(assetName);
-
+ AssetNamePartEnumerator curParts = new(useBaseName ? this.BaseName : this.Name);
+ AssetNamePartEnumerator otherParts = new(assetName);
+
while (true)
{
- bool otherHasMore = compareFrom.MoveNext();
- bool iHaveMore = compareTo.MoveNext();
+ bool otherHasMore = otherParts.MoveNext();
+ bool curHasMore = curParts.MoveNext();
// neither of us have any more to yield, I'm done.
- if (!otherHasMore && !iHaveMore)
+ if (!otherHasMore && !curHasMore)
return true;
// One of us has more but the other doesn't, this isn't a match.
- if (otherHasMore ^ iHaveMore)
+ if (otherHasMore ^ curHasMore)
return false;
// My next bit doesn't match their next bit, this isn't a match.
- if (!compareTo.Current.Equals(compareFrom.Current, StringComparison.OrdinalIgnoreCase))
+ if (!curParts.Current.Equals(otherParts.Current, StringComparison.OrdinalIgnoreCase))
return false;
// continue checking.
@@ -144,59 +142,59 @@ namespace StardewModdingAPI.Framework.Content
ReadOnlySpan trimmed = prefix.AsSpan().Trim();
// just because most ReadOnlySpan/Span APIs expect a ReadOnlySpan/Span, easier to read.
- ReadOnlySpan seperators = new(ToolkitPathUtilities.PossiblePathSeparators);
+ ReadOnlySpan pathSeparators = new(ToolkitPathUtilities.PossiblePathSeparators);
// asset keys can't have a leading slash, but AssetPathYielder won't yield that.
- if (seperators.Contains(trimmed[0]))
+ if (pathSeparators.Contains(trimmed[0]))
return false;
if (trimmed.Length == 0)
return true;
- AssetPartYielder compareTo = new(this.Name);
- AssetPartYielder compareFrom = new(trimmed);
+ AssetNamePartEnumerator curParts = new(this.Name);
+ AssetNamePartEnumerator prefixParts = new(trimmed);
while (true)
{
- bool otherHasMore = compareFrom.MoveNext();
- bool iHaveMore = compareTo.MoveNext();
+ bool prefixHasMore = prefixParts.MoveNext();
+ bool curHasMore = curParts.MoveNext();
// Neither of us have any more to yield, I'm done.
- if (!otherHasMore && !iHaveMore)
+ if (!prefixHasMore && !curHasMore)
return true;
// the prefix is actually longer than the asset name, this can't be true.
- if (otherHasMore && !iHaveMore)
+ if (prefixHasMore && !curHasMore)
return false;
// they're done, I have more. (These are going to be word boundaries, I don't need to check that).
- if (!otherHasMore && iHaveMore)
+ if (!prefixHasMore && curHasMore)
{
- return allowSubfolder || !compareTo.Remainder.Contains(seperators, StringComparison.Ordinal);
+ return allowSubfolder || !curParts.Remainder.Contains(pathSeparators, StringComparison.Ordinal);
}
// check my next segment against theirs.
- if (otherHasMore && iHaveMore)
+ if (prefixHasMore && curHasMore)
{
// my next segment doesn't match theirs.
- if (!compareTo.Current.StartsWith(compareFrom.Current, StringComparison.OrdinalIgnoreCase))
+ if (!curParts.Current.StartsWith(prefixParts.Current, StringComparison.OrdinalIgnoreCase))
return false;
// my next segment starts with theirs but isn't an exact match.
- if (compareTo.Current.Length != compareFrom.Current.Length)
+ if (curParts.Current.Length != prefixParts.Current.Length)
{
// something like "Maps/" would require an exact match.
- if (seperators.Contains(trimmed[^1]))
+ if (pathSeparators.Contains(trimmed[^1]))
return false;
// check for partial word.
if (!allowPartialWord
- && char.IsLetterOrDigit(compareFrom.Current[^1]) // last character in suffix is not word separator
- && char.IsLetterOrDigit(compareTo.Current[compareFrom.Current.Length]) // and the first character after it isn't either.
+ && char.IsLetterOrDigit(prefixParts.Current[^1]) // last character in suffix is not word separator
+ && char.IsLetterOrDigit(curParts.Current[prefixParts.Current.Length]) // and the first character after it isn't either.
)
return false;
- return allowSubfolder || !compareTo.Remainder.Contains(seperators, StringComparison.Ordinal);
+ return allowSubfolder || !curParts.Remainder.Contains(pathSeparators, StringComparison.Ordinal);
}
// exact matches should continue checking.
--
cgit
From 4e3b2810e6951b72bdf5c5cbdd23a079d53a4c96 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Sun, 16 Oct 2022 14:41:45 -0400
Subject: fix index-out-of-range error when StartsWith prefix is empty
---
src/SMAPI/Framework/Content/AssetName.cs | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs
index d7ee6dba..c0572105 100644
--- a/src/SMAPI/Framework/Content/AssetName.cs
+++ b/src/SMAPI/Framework/Content/AssetName.cs
@@ -139,21 +139,19 @@ namespace StardewModdingAPI.Framework.Content
if (prefix is null)
return false;
+ // get initial values
ReadOnlySpan trimmed = prefix.AsSpan().Trim();
+ if (trimmed.Length == 0)
+ return true;
+ ReadOnlySpan pathSeparators = new(ToolkitPathUtilities.PossiblePathSeparators); // just to simplify calling other span APIs
- // just because most ReadOnlySpan/Span APIs expect a ReadOnlySpan/Span, easier to read.
- ReadOnlySpan pathSeparators = new(ToolkitPathUtilities.PossiblePathSeparators);
-
- // asset keys can't have a leading slash, but AssetPathYielder won't yield that.
+ // asset keys can't have a leading slash, but AssetPathYielder will trim them
if (pathSeparators.Contains(trimmed[0]))
return false;
- if (trimmed.Length == 0)
- return true;
-
+ // compare segments
AssetNamePartEnumerator curParts = new(this.Name);
AssetNamePartEnumerator prefixParts = new(trimmed);
-
while (true)
{
bool prefixHasMore = prefixParts.MoveNext();
--
cgit
From 5d30b47e1e903f7ceb53116528255934c238e5ba Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Sun, 16 Oct 2022 14:41:46 -0400
Subject: fix IsEquivalentTo no longer ignoring surrounding whitespace
---
src/SMAPI/Framework/Content/AssetName.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs
index c0572105..6220ea61 100644
--- a/src/SMAPI/Framework/Content/AssetName.cs
+++ b/src/SMAPI/Framework/Content/AssetName.cs
@@ -97,7 +97,7 @@ namespace StardewModdingAPI.Framework.Content
return false;
AssetNamePartEnumerator curParts = new(useBaseName ? this.BaseName : this.Name);
- AssetNamePartEnumerator otherParts = new(assetName);
+ AssetNamePartEnumerator otherParts = new(assetName.AsSpan().Trim());
while (true)
{
--
cgit
From 573f732c2a2118d7a4848151764df6bef1a47008 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Sun, 16 Oct 2022 14:41:46 -0400
Subject: reduce sequential bool checks a bit
---
src/SMAPI/Framework/Content/AssetName.cs | 77 +++++++++++++++-----------------
1 file changed, 37 insertions(+), 40 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs
index 6220ea61..bdb79dde 100644
--- a/src/SMAPI/Framework/Content/AssetName.cs
+++ b/src/SMAPI/Framework/Content/AssetName.cs
@@ -101,22 +101,20 @@ namespace StardewModdingAPI.Framework.Content
while (true)
{
- bool otherHasMore = otherParts.MoveNext();
bool curHasMore = curParts.MoveNext();
+ bool otherHasMore = otherParts.MoveNext();
- // neither of us have any more to yield, I'm done.
- if (!otherHasMore && !curHasMore)
- return true;
-
- // One of us has more but the other doesn't, this isn't a match.
- if (otherHasMore ^ curHasMore)
+ // mismatch: lengths differ
+ if (otherHasMore != curHasMore)
return false;
- // My next bit doesn't match their next bit, this isn't a match.
+ // match: both reached the end without a mismatch
+ if (!curHasMore)
+ return true;
+
+ // mismatch: current segment is different
if (!curParts.Current.Equals(otherParts.Current, StringComparison.OrdinalIgnoreCase))
return false;
-
- // continue checking.
}
}
@@ -154,48 +152,47 @@ namespace StardewModdingAPI.Framework.Content
AssetNamePartEnumerator prefixParts = new(trimmed);
while (true)
{
- bool prefixHasMore = prefixParts.MoveNext();
bool curHasMore = curParts.MoveNext();
+ bool prefixHasMore = prefixParts.MoveNext();
- // Neither of us have any more to yield, I'm done.
- if (!prefixHasMore && !curHasMore)
- return true;
-
- // the prefix is actually longer than the asset name, this can't be true.
- if (prefixHasMore && !curHasMore)
- return false;
-
- // they're done, I have more. (These are going to be word boundaries, I don't need to check that).
- if (!prefixHasMore && curHasMore)
+ // reached end of prefix or asset name
+ if (prefixHasMore != curHasMore)
{
+ // mismatch: prefix is longer
+ if (prefixHasMore)
+ return false;
+
+ // possible match: all prefix segments matched
return allowSubfolder || !curParts.Remainder.Contains(pathSeparators, StringComparison.Ordinal);
}
- // check my next segment against theirs.
- if (prefixHasMore && curHasMore)
+ // match: previous segments matched exactly and both reached the end
+ if (!prefixHasMore)
+ return true;
+
+ // compare segment
+ if (curParts.Current.Length == prefixParts.Current.Length)
+ {
+ // mismatch: segments aren't equivalent
+ if (!curParts.Current.Equals(prefixParts.Current, StringComparison.OrdinalIgnoreCase))
+ return false;
+ }
+ else
{
- // my next segment doesn't match theirs.
+ // mismatch: cur segment doesn't start with prefix
if (!curParts.Current.StartsWith(prefixParts.Current, StringComparison.OrdinalIgnoreCase))
return false;
- // my next segment starts with theirs but isn't an exact match.
- if (curParts.Current.Length != prefixParts.Current.Length)
- {
- // something like "Maps/" would require an exact match.
- if (pathSeparators.Contains(trimmed[^1]))
- return false;
-
- // check for partial word.
- if (!allowPartialWord
- && char.IsLetterOrDigit(prefixParts.Current[^1]) // last character in suffix is not word separator
- && char.IsLetterOrDigit(curParts.Current[prefixParts.Current.Length]) // and the first character after it isn't either.
- )
- return false;
+ // mismatch: something like "Maps/" would need an exact match
+ if (pathSeparators.Contains(trimmed[^1]))
+ return false;
- return allowSubfolder || !curParts.Remainder.Contains(pathSeparators, StringComparison.Ordinal);
- }
+ // mismatch: partial word match not allowed, and the first or last letter of the suffix isn't a word separator
+ if (!allowPartialWord && char.IsLetterOrDigit(prefixParts.Current[^1]) && char.IsLetterOrDigit(curParts.Current[prefixParts.Current.Length]))
+ return false;
- // exact matches should continue checking.
+ // possible match
+ return allowSubfolder || !curParts.Remainder.Contains(pathSeparators, StringComparison.Ordinal);
}
}
}
--
cgit
From 4dcc6904b9e72ac3567dfafe3824c2de48218b58 Mon Sep 17 00:00:00 2001
From: atravita-mods <94934860+atravita-mods@users.noreply.github.com>
Date: Sun, 16 Oct 2022 18:04:19 -0400
Subject: fix issues with subfolders
---
src/SMAPI/Framework/Content/AssetName.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs
index bdb79dde..9d59f222 100644
--- a/src/SMAPI/Framework/Content/AssetName.cs
+++ b/src/SMAPI/Framework/Content/AssetName.cs
@@ -162,8 +162,8 @@ namespace StardewModdingAPI.Framework.Content
if (prefixHasMore)
return false;
- // possible match: all prefix segments matched
- return allowSubfolder || !curParts.Remainder.Contains(pathSeparators, StringComparison.Ordinal);
+ // possible match: all prefix segments matched.
+ return allowSubfolder || (pathSeparators.Contains(trimmed[^1]) ? curParts.Remainder.Length == 0 : curParts.Current.Length == 0);
}
// match: previous segments matched exactly and both reached the end
@@ -192,7 +192,7 @@ namespace StardewModdingAPI.Framework.Content
return false;
// possible match
- return allowSubfolder || !curParts.Remainder.Contains(pathSeparators, StringComparison.Ordinal);
+ return allowSubfolder || (pathSeparators.Contains(trimmed[^1]) ? curParts.Remainder.IndexOfAny(ToolkitPathUtilities.PossiblePathSeparators) < 0 : curParts.Remainder.Length == 0);
}
}
}
@@ -203,7 +203,7 @@ namespace StardewModdingAPI.Framework.Content
if (assetFolder is null)
return false;
- return this.StartsWith(assetFolder + "/", allowPartialWord: false, allowSubfolder: false);
+ return this.StartsWith(assetFolder + ToolkitPathUtilities.PreferredPathSeparator, allowPartialWord: false, allowSubfolder: false);
}
///
--
cgit
From b99dbf53bda9dc1178a3b6e8cbafea609f3ee6dc Mon Sep 17 00:00:00 2001
From: atravita-mods <94934860+atravita-mods@users.noreply.github.com>
Date: Tue, 18 Oct 2022 18:58:41 -0400
Subject: fix this case.
---
src/SMAPI/Framework/Content/AssetName.cs | 4 ++++
1 file changed, 4 insertions(+)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs
index 9d59f222..7b87c0c5 100644
--- a/src/SMAPI/Framework/Content/AssetName.cs
+++ b/src/SMAPI/Framework/Content/AssetName.cs
@@ -179,6 +179,10 @@ namespace StardewModdingAPI.Framework.Content
}
else
{
+ // mismatch: prefix has more beyond this, and this segment isn't an exact match
+ if (prefixParts.Remainder.Length != 0)
+ return false;
+
// mismatch: cur segment doesn't start with prefix
if (!curParts.Current.StartsWith(prefixParts.Current, StringComparison.OrdinalIgnoreCase))
return false;
--
cgit
From 61d6ec12daee843f758e5f828a713a72a767a94b Mon Sep 17 00:00:00 2001
From: Tyler
Date: Tue, 18 Oct 2022 20:03:28 -0500
Subject: add detailed manifest validation errors at build time
---
src/SMAPI/Framework/ModLoading/ModResolver.cs | 98 +++------------------------
1 file changed, 10 insertions(+), 88 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index fe56f4d2..352c22cc 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -8,7 +8,6 @@ using StardewModdingAPI.Toolkit.Framework.ModData;
using StardewModdingAPI.Toolkit.Framework.ModScanning;
using StardewModdingAPI.Toolkit.Framework.UpdateData;
using StardewModdingAPI.Toolkit.Serialization.Models;
-using StardewModdingAPI.Toolkit.Utilities;
using StardewModdingAPI.Toolkit.Utilities.PathLookups;
namespace StardewModdingAPI.Framework.ModLoading
@@ -126,100 +125,23 @@ namespace StardewModdingAPI.Framework.ModLoading
continue;
}
- // validate DLL / content pack fields
+ // check for dll if it's supposed to have one
+ if (!string.IsNullOrEmpty(mod.Manifest.EntryDll) && validateFilesExist)
{
- bool hasDll = !string.IsNullOrWhiteSpace(mod.Manifest.EntryDll);
- bool isContentPack = mod.Manifest.ContentPackFor != null;
-
- // validate field presence
- if (!hasDll && !isContentPack)
+ IFileLookup pathLookup = getFileLookup(mod.DirectoryPath);
+ FileInfo file = pathLookup.GetFile(mod.Manifest.EntryDll!);
+ if (!file.Exists)
{
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest has no {nameof(IManifest.EntryDll)} or {nameof(IManifest.ContentPackFor)} field; must specify one.");
- continue;
- }
- if (hasDll && isContentPack)
- {
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest sets both {nameof(IManifest.EntryDll)} and {nameof(IManifest.ContentPackFor)}, which are mutually exclusive.");
- continue;
- }
-
- // validate DLL
- if (hasDll)
- {
- // invalid filename format
- if (mod.Manifest.EntryDll!.Intersect(Path.GetInvalidFileNameChars()).Any())
- {
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest has invalid filename '{mod.Manifest.EntryDll}' for the EntryDLL field.");
- continue;
- }
-
- // file doesn't exist
- if (validateFilesExist)
- {
- IFileLookup pathLookup = getFileLookup(mod.DirectoryPath);
- FileInfo file = pathLookup.GetFile(mod.Manifest.EntryDll!);
- if (!file.Exists)
- {
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its DLL '{mod.Manifest.EntryDll}' doesn't exist.");
- continue;
- }
- }
- }
-
- // validate content pack
- else
- {
- // invalid content pack ID
- if (string.IsNullOrWhiteSpace(mod.Manifest.ContentPackFor!.UniqueID))
- {
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest declares {nameof(IManifest.ContentPackFor)} without its required {nameof(IManifestContentPackFor.UniqueID)} field.");
- continue;
- }
- }
- }
-
- // validate required fields
- {
- List missingFields = new List(3);
-
- if (string.IsNullOrWhiteSpace(mod.Manifest.Name))
- missingFields.Add(nameof(IManifest.Name));
- if (mod.Manifest.Version == null || mod.Manifest.Version.ToString() == "0.0.0")
- missingFields.Add(nameof(IManifest.Version));
- if (string.IsNullOrWhiteSpace(mod.Manifest.UniqueID))
- missingFields.Add(nameof(IManifest.UniqueID));
-
- if (missingFields.Any())
- {
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest is missing required fields ({string.Join(", ", missingFields)}).");
+ mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its DLL '{mod.Manifest.EntryDll}' doesn't exist.");
continue;
}
}
- // validate ID format
- if (!PathUtilities.IsSlug(mod.Manifest.UniqueID))
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, "its manifest specifies an invalid ID (IDs must only contain letters, numbers, underscores, periods, or hyphens).");
-
- // validate dependencies
- foreach (IManifestDependency? dependency in mod.Manifest.Dependencies)
+ // validate manifest
+ if (mod.Manifest is Manifest manifest && !manifest.TryValidate(out string manifestError))
{
- // null dependency
- if (dependency == null)
- {
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest has a null entry under {nameof(IManifest.Dependencies)}.");
- continue;
- }
-
- // missing ID
- if (string.IsNullOrWhiteSpace(dependency.UniqueID))
- {
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest has a {nameof(IManifest.Dependencies)} entry with no {nameof(IManifestDependency.UniqueID)} field.");
- continue;
- }
-
- // invalid ID
- if (!PathUtilities.IsSlug(dependency.UniqueID))
- mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest has a {nameof(IManifest.Dependencies)} entry with an invalid {nameof(IManifestDependency.UniqueID)} field (IDs must only contain letters, numbers, underscores, periods, or hyphens).");
+ mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its {manifestError}");
+ continue;
}
}
--
cgit
From bb2fde18292352471501887013ca2b7f60a9dc25 Mon Sep 17 00:00:00 2001
From: Michał Dolaś
Date: Wed, 9 Nov 2022 17:25:25 +0100
Subject: Added ModsToLoadFirst/Last to SMAPI config, along with the
implementation
---
src/SMAPI/Framework/ModLoading/ModResolver.cs | 22 +++++++++++++++++-----
src/SMAPI/Framework/Models/SConfig.cs | 12 +++++++++++-
src/SMAPI/Framework/SCore.cs | 10 +++++++++-
3 files changed, 37 insertions(+), 7 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index fe56f4d2..b90f9ba5 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -245,7 +245,9 @@ namespace StardewModdingAPI.Framework.ModLoading
/// Sort the given mods by the order they should be loaded.
/// The mods to process.
/// Handles access to SMAPI's internal mod metadata list.
- public IEnumerable ProcessDependencies(IEnumerable mods, ModDatabase modDatabase)
+ /// The mod IDs SMAPI should try to load first, before any other mods not included in this list.
+ /// The mod IDs SMAPI should try to load last, after all other mods not included in this list.
+ public IEnumerable ProcessDependencies(IEnumerable mods, IReadOnlyList modIdsToLoadFirst, IReadOnlyList modIdsToLoadLast, ModDatabase modDatabase)
{
// initialize metadata
mods = mods.ToArray();
@@ -260,8 +262,18 @@ namespace StardewModdingAPI.Framework.ModLoading
}
// sort mods
- foreach (IModMetadata mod in mods)
- this.ProcessDependencies(mods.ToArray(), modDatabase, mod, states, sortedMods, new List());
+ IModMetadata[] allMods = mods.ToArray();
+ IModMetadata[] modsToLoadFirst = allMods.Where(m => modIdsToLoadFirst.Contains(m.Manifest.UniqueID)).ToArray();
+ IModMetadata[] modsToLoadLast = allMods.Where(m => modIdsToLoadLast.Contains(m.Manifest.UniqueID)).ToArray();
+ IModMetadata[] modsToLoadAsUsual = allMods.Where(m => !modsToLoadFirst.Contains(m) && !modsToLoadLast.Contains(m)).ToArray();
+
+ List orderSortedMods = new();
+ orderSortedMods.AddRange(modsToLoadFirst);
+ orderSortedMods.AddRange(modsToLoadAsUsual);
+ orderSortedMods.AddRange(modsToLoadLast);
+
+ foreach (IModMetadata mod in orderSortedMods)
+ this.ProcessDependencies(orderSortedMods, modDatabase, mod, states, sortedMods, new List());
return sortedMods.Reverse();
}
@@ -278,7 +290,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// The list in which to save mods sorted by dependency order.
/// The current change of mod dependencies.
/// Returns the mod dependency status.
- private ModDependencyStatus ProcessDependencies(IModMetadata[] mods, ModDatabase modDatabase, IModMetadata mod, IDictionary states, Stack sortedMods, ICollection currentChain)
+ private ModDependencyStatus ProcessDependencies(IReadOnlyList mods, ModDatabase modDatabase, IModMetadata mod, IDictionary states, Stack sortedMods, ICollection currentChain)
{
// check if already visited
switch (states[mod])
@@ -409,7 +421,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// Get the dependencies declared in a manifest.
/// The mod manifest.
/// The loaded mods.
- private IEnumerable GetDependenciesFrom(IManifest manifest, IModMetadata[] loadedMods)
+ private IEnumerable GetDependenciesFrom(IManifest manifest, IReadOnlyList loadedMods)
{
IModMetadata? FindMod(string id) => loadedMods.FirstOrDefault(m => m.HasID(id));
diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs
index bceb0940..ddd721d5 100644
--- a/src/SMAPI/Framework/Models/SConfig.cs
+++ b/src/SMAPI/Framework/Models/SConfig.cs
@@ -82,6 +82,12 @@ namespace StardewModdingAPI.Framework.Models
/// The mod IDs SMAPI should ignore when performing update checks or validating update keys.
public HashSet SuppressUpdateChecks { get; set; }
+ /// The mod IDs SMAPI should try to load first, before any other mods not included in this list.
+ public List ModsToLoadFirst { get; set; }
+
+ /// The mod IDs SMAPI should try to load last, after all other mods not included in this list.
+ public List ModsToLoadLast { get; set; }
+
/********
** Public methods
@@ -100,7 +106,9 @@ namespace StardewModdingAPI.Framework.Models
/// The colors to use for text written to the SMAPI console.
/// Whether to prevent mods from enabling Harmony's debug mode, which impacts performance and creates a file on your desktop. Debug mode should never be enabled by a released mod.
/// The mod IDs SMAPI should ignore when performing update checks or validating update keys.
- public SConfig(bool developerMode, bool? checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, ColorSchemeConfig consoleColors, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks)
+ /// The mod IDs SMAPI should try to load first, before any other mods not included in this list.
+ /// The mod IDs SMAPI should try to load last, after all other mods not included in this list.
+ public SConfig(bool developerMode, bool? checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, ColorSchemeConfig consoleColors, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks, string[]? modsToLoadFirst, string[]? modsToLoadLast)
{
this.DeveloperMode = developerMode;
this.CheckForUpdates = checkForUpdates ?? (bool)SConfig.DefaultValues[nameof(this.CheckForUpdates)];
@@ -115,6 +123,8 @@ namespace StardewModdingAPI.Framework.Models
this.ConsoleColors = consoleColors;
this.SuppressHarmonyDebugMode = suppressHarmonyDebugMode ?? (bool)SConfig.DefaultValues[nameof(this.SuppressHarmonyDebugMode)];
this.SuppressUpdateChecks = new HashSet(suppressUpdateChecks ?? Array.Empty(), StringComparer.OrdinalIgnoreCase);
+ this.ModsToLoadFirst = new List(modsToLoadFirst ?? Array.Empty());
+ this.ModsToLoadLast = new List(modsToLoadLast ?? Array.Empty());
}
/// Override the value of .
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 40979b09..7bd60490 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -423,9 +423,17 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($" Skipped {mod.GetRelativePathWithRoot()} (folder name starts with a dot).");
mods = mods.Where(p => !p.IsIgnored).ToArray();
+ // warn about mods that should load first or last which are not found at all
+ foreach (string modId in this.Settings.ModsToLoadFirst)
+ if (!mods.Any(m => m.Manifest.UniqueID == modId))
+ this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load first, but it could not be found.", LogLevel.Warn);
+ foreach (string modId in this.Settings.ModsToLoadLast)
+ if (!mods.Any(m => m.Manifest.UniqueID == modId))
+ this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load last, but it could not be found.", LogLevel.Warn);
+
// load mods
resolver.ValidateManifests(mods, Constants.ApiVersion, toolkit.GetUpdateUrl, getFileLookup: this.GetFileLookup);
- mods = resolver.ProcessDependencies(mods, modDatabase).ToArray();
+ mods = resolver.ProcessDependencies(mods, this.Settings.ModsToLoadFirst, this.Settings.ModsToLoadLast, modDatabase).ToArray();
this.LoadMods(mods, this.Toolkit.JsonHelper, this.ContentCore, modDatabase);
// check for software likely to cause issues
--
cgit
From 42b4b6b6a4ae1bb59182857b383539b24b063215 Mon Sep 17 00:00:00 2001
From: Michał Dolaś
Date: Wed, 9 Nov 2022 19:50:32 +0100
Subject: Renamed first/last to early/late; ignoring mods declared as both and
warning about those
---
src/SMAPI/Framework/ModLoading/ModResolver.cs | 16 ++++++++--------
src/SMAPI/Framework/Models/SConfig.cs | 18 +++++++++---------
src/SMAPI/Framework/SCore.cs | 15 +++++++++------
3 files changed, 26 insertions(+), 23 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index b90f9ba5..f9ba73c4 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -245,9 +245,9 @@ namespace StardewModdingAPI.Framework.ModLoading
/// Sort the given mods by the order they should be loaded.
/// The mods to process.
/// Handles access to SMAPI's internal mod metadata list.
- /// The mod IDs SMAPI should try to load first, before any other mods not included in this list.
- /// The mod IDs SMAPI should try to load last, after all other mods not included in this list.
- public IEnumerable ProcessDependencies(IEnumerable mods, IReadOnlyList modIdsToLoadFirst, IReadOnlyList modIdsToLoadLast, ModDatabase modDatabase)
+ /// The mod IDs SMAPI should try to load early, before any other mods not included in this list.
+ /// The mod IDs SMAPI should try to load late, after all other mods not included in this list.
+ public IEnumerable ProcessDependencies(IEnumerable mods, IReadOnlyList modIdsToLoadEarly, IReadOnlyList modIdsToLoadLate, ModDatabase modDatabase)
{
// initialize metadata
mods = mods.ToArray();
@@ -263,14 +263,14 @@ namespace StardewModdingAPI.Framework.ModLoading
// sort mods
IModMetadata[] allMods = mods.ToArray();
- IModMetadata[] modsToLoadFirst = allMods.Where(m => modIdsToLoadFirst.Contains(m.Manifest.UniqueID)).ToArray();
- IModMetadata[] modsToLoadLast = allMods.Where(m => modIdsToLoadLast.Contains(m.Manifest.UniqueID)).ToArray();
- IModMetadata[] modsToLoadAsUsual = allMods.Where(m => !modsToLoadFirst.Contains(m) && !modsToLoadLast.Contains(m)).ToArray();
+ IModMetadata[] modsToLoadEarly = allMods.Where(m => modIdsToLoadEarly.Contains(m.Manifest.UniqueID) && !modIdsToLoadLate.Contains(m.Manifest.UniqueID)).ToArray();
+ IModMetadata[] modsToLoadLate = allMods.Where(m => modIdsToLoadLate.Contains(m.Manifest.UniqueID) && !modIdsToLoadEarly.Contains(m.Manifest.UniqueID)).ToArray();
+ IModMetadata[] modsToLoadAsUsual = allMods.Where(m => !modsToLoadEarly.Contains(m) && !modsToLoadLate.Contains(m)).ToArray();
List orderSortedMods = new();
- orderSortedMods.AddRange(modsToLoadFirst);
+ orderSortedMods.AddRange(modsToLoadEarly);
orderSortedMods.AddRange(modsToLoadAsUsual);
- orderSortedMods.AddRange(modsToLoadLast);
+ orderSortedMods.AddRange(modsToLoadLate);
foreach (IModMetadata mod in orderSortedMods)
this.ProcessDependencies(orderSortedMods, modDatabase, mod, states, sortedMods, new List());
diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs
index ddd721d5..40d3450f 100644
--- a/src/SMAPI/Framework/Models/SConfig.cs
+++ b/src/SMAPI/Framework/Models/SConfig.cs
@@ -82,11 +82,11 @@ namespace StardewModdingAPI.Framework.Models
/// The mod IDs SMAPI should ignore when performing update checks or validating update keys.
public HashSet SuppressUpdateChecks { get; set; }
- /// The mod IDs SMAPI should try to load first, before any other mods not included in this list.
- public List ModsToLoadFirst { get; set; }
+ /// The mod IDs SMAPI should try to load early, before any other mods not included in this list.
+ public List ModsToLoadEarly { get; set; }
- /// The mod IDs SMAPI should try to load last, after all other mods not included in this list.
- public List ModsToLoadLast { get; set; }
+ /// The mod IDs SMAPI should try to load late, after all other mods not included in this list.
+ public List ModsToLoadLate { get; set; }
/********
@@ -106,9 +106,9 @@ namespace StardewModdingAPI.Framework.Models
/// The colors to use for text written to the SMAPI console.
/// Whether to prevent mods from enabling Harmony's debug mode, which impacts performance and creates a file on your desktop. Debug mode should never be enabled by a released mod.
/// The mod IDs SMAPI should ignore when performing update checks or validating update keys.
- /// The mod IDs SMAPI should try to load first, before any other mods not included in this list.
- /// The mod IDs SMAPI should try to load last, after all other mods not included in this list.
- public SConfig(bool developerMode, bool? checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, ColorSchemeConfig consoleColors, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks, string[]? modsToLoadFirst, string[]? modsToLoadLast)
+ /// The mod IDs SMAPI should try to load early, before any other mods not included in this list.
+ /// The mod IDs SMAPI should try to load late, after all other mods not included in this list.
+ public SConfig(bool developerMode, bool? checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, ColorSchemeConfig consoleColors, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks, string[]? modsToLoadEarly, string[]? modsToLoadLate)
{
this.DeveloperMode = developerMode;
this.CheckForUpdates = checkForUpdates ?? (bool)SConfig.DefaultValues[nameof(this.CheckForUpdates)];
@@ -123,8 +123,8 @@ namespace StardewModdingAPI.Framework.Models
this.ConsoleColors = consoleColors;
this.SuppressHarmonyDebugMode = suppressHarmonyDebugMode ?? (bool)SConfig.DefaultValues[nameof(this.SuppressHarmonyDebugMode)];
this.SuppressUpdateChecks = new HashSet(suppressUpdateChecks ?? Array.Empty(), StringComparer.OrdinalIgnoreCase);
- this.ModsToLoadFirst = new List(modsToLoadFirst ?? Array.Empty());
- this.ModsToLoadLast = new List(modsToLoadLast ?? Array.Empty());
+ this.ModsToLoadEarly = new List(modsToLoadEarly ?? Array.Empty());
+ this.ModsToLoadLate = new List(modsToLoadLate ?? Array.Empty());
}
/// Override the value of .
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 7bd60490..9e91924e 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -423,17 +423,20 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($" Skipped {mod.GetRelativePathWithRoot()} (folder name starts with a dot).");
mods = mods.Where(p => !p.IsIgnored).ToArray();
- // warn about mods that should load first or last which are not found at all
- foreach (string modId in this.Settings.ModsToLoadFirst)
+ // warn about mods that should load early or late which are not found at all, or both
+ foreach (string modId in this.Settings.ModsToLoadEarly)
if (!mods.Any(m => m.Manifest.UniqueID == modId))
- this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load first, but it could not be found.", LogLevel.Warn);
- foreach (string modId in this.Settings.ModsToLoadLast)
+ this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load early, but it could not be found.", LogLevel.Warn);
+ foreach (string modId in this.Settings.ModsToLoadLate)
if (!mods.Any(m => m.Manifest.UniqueID == modId))
- this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load last, but it could not be found.", LogLevel.Warn);
+ this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load late, but it could not be found.", LogLevel.Warn);
+ foreach (string modId in this.Settings.ModsToLoadEarly)
+ if (this.Settings.ModsToLoadLate.Contains(modId))
+ this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load both early and late - this will be ignored.", LogLevel.Warn);
// load mods
resolver.ValidateManifests(mods, Constants.ApiVersion, toolkit.GetUpdateUrl, getFileLookup: this.GetFileLookup);
- mods = resolver.ProcessDependencies(mods, this.Settings.ModsToLoadFirst, this.Settings.ModsToLoadLast, modDatabase).ToArray();
+ mods = resolver.ProcessDependencies(mods, this.Settings.ModsToLoadEarly, this.Settings.ModsToLoadLate, modDatabase).ToArray();
this.LoadMods(mods, this.Toolkit.JsonHelper, this.ContentCore, modDatabase);
// check for software likely to cause issues
--
cgit
From 9fd8c35b462bc19efb520da21cda66f83559a66e Mon Sep 17 00:00:00 2001
From: Michał Dolaś
Date: Wed, 9 Nov 2022 20:26:50 +0100
Subject: Actually taking order into consideration
---
src/SMAPI/Framework/ModLoading/ModResolver.cs | 14 ++++++++++++--
src/SMAPI/Framework/SCore.cs | 4 ++--
2 files changed, 14 insertions(+), 4 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index f9ba73c4..8ef5e4a8 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -263,8 +263,18 @@ namespace StardewModdingAPI.Framework.ModLoading
// sort mods
IModMetadata[] allMods = mods.ToArray();
- IModMetadata[] modsToLoadEarly = allMods.Where(m => modIdsToLoadEarly.Contains(m.Manifest.UniqueID) && !modIdsToLoadLate.Contains(m.Manifest.UniqueID)).ToArray();
- IModMetadata[] modsToLoadLate = allMods.Where(m => modIdsToLoadLate.Contains(m.Manifest.UniqueID) && !modIdsToLoadEarly.Contains(m.Manifest.UniqueID)).ToArray();
+ IModMetadata[] modsToLoadEarly = modIdsToLoadEarly
+ .Where(modId => !modIdsToLoadLate.Contains(modId))
+ .Select(modId => allMods.FirstOrDefault(m => m.Manifest.UniqueID == modId))
+ .Where(m => m != null)
+ .Select(m => m!)
+ .ToArray();
+ IModMetadata[] modsToLoadLate = modIdsToLoadLate
+ .Where(modId => !modIdsToLoadEarly.Contains(modId))
+ .Select(modId => allMods.FirstOrDefault(m => m.Manifest.UniqueID == modId))
+ .Where(m => m != null)
+ .Select(m => m!)
+ .ToArray();
IModMetadata[] modsToLoadAsUsual = allMods.Where(m => !modsToLoadEarly.Contains(m) && !modsToLoadLate.Contains(m)).ToArray();
List orderSortedMods = new();
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 9e91924e..4d1eb959 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -426,10 +426,10 @@ namespace StardewModdingAPI.Framework
// warn about mods that should load early or late which are not found at all, or both
foreach (string modId in this.Settings.ModsToLoadEarly)
if (!mods.Any(m => m.Manifest.UniqueID == modId))
- this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load early, but it could not be found.", LogLevel.Warn);
+ this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load early, but it could not be found or was skipped.", LogLevel.Warn);
foreach (string modId in this.Settings.ModsToLoadLate)
if (!mods.Any(m => m.Manifest.UniqueID == modId))
- this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load late, but it could not be found.", LogLevel.Warn);
+ this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load late, but it could not be found or was skipped.", LogLevel.Warn);
foreach (string modId in this.Settings.ModsToLoadEarly)
if (this.Settings.ModsToLoadLate.Contains(modId))
this.Monitor.Log($" SMAPI configuration specifies a mod {modId} that should load both early and late - this will be ignored.", LogLevel.Warn);
--
cgit
From 76e5588f02b247204969efb488c3fb293601faeb Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Wed, 9 Nov 2022 21:41:04 -0500
Subject: add option to disable console input
---
src/SMAPI/Framework/Models/SConfig.cs | 34 ++++++++++++++++++++--------------
src/SMAPI/Framework/SCore.cs | 19 +++++++++++--------
2 files changed, 31 insertions(+), 22 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs
index bceb0940..825ebb44 100644
--- a/src/SMAPI/Framework/Models/SConfig.cs
+++ b/src/SMAPI/Framework/Models/SConfig.cs
@@ -16,6 +16,7 @@ namespace StardewModdingAPI.Framework.Models
private static readonly IDictionary DefaultValues = new Dictionary
{
[nameof(CheckForUpdates)] = true,
+ [nameof(ListenForConsoleInput)] = true,
[nameof(ParanoidWarnings)] = Constants.IsDebugBuild,
[nameof(UseBetaChannel)] = Constants.ApiVersion.IsPrerelease(),
[nameof(GitHubProjectName)] = "Pathoschild/SMAPI",
@@ -48,6 +49,9 @@ namespace StardewModdingAPI.Framework.Models
/// Whether to check for newer versions of SMAPI and mods on startup.
public bool CheckForUpdates { get; set; }
+ /// Whether SMAPI should listen for console input to support console commands.
+ public bool ListenForConsoleInput { get; set; }
+
/// Whether to add a section to the 'mod issues' list for mods which which directly use potentially sensitive .NET APIs like file or shell access.
public bool ParanoidWarnings { get; set; }
@@ -87,23 +91,25 @@ namespace StardewModdingAPI.Framework.Models
** Public methods
********/
/// Construct an instance.
- /// Whether to enable development features.
- /// Whether to check for newer versions of SMAPI and mods on startup.
- /// Whether to add a section to the 'mod issues' list for mods which which directly use potentially sensitive .NET APIs like file or shell access.
- /// Whether to show beta versions as valid updates.
- /// SMAPI's GitHub project name, used to perform update checks.
- /// The base URL for SMAPI's web API, used to perform update checks.
- /// The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting.
- /// Whether SMAPI should rewrite mods for compatibility.
- /// >Whether to make SMAPI file APIs case-insensitive, even on Linux.
- /// Whether SMAPI should log network traffic.
- /// The colors to use for text written to the SMAPI console.
- /// Whether to prevent mods from enabling Harmony's debug mode, which impacts performance and creates a file on your desktop. Debug mode should never be enabled by a released mod.
- /// The mod IDs SMAPI should ignore when performing update checks or validating update keys.
- public SConfig(bool developerMode, bool? checkForUpdates, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, ColorSchemeConfig consoleColors, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SConfig(bool developerMode, bool? checkForUpdates, bool? listenForConsoleInput, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, ColorSchemeConfig consoleColors, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks)
{
this.DeveloperMode = developerMode;
this.CheckForUpdates = checkForUpdates ?? (bool)SConfig.DefaultValues[nameof(this.CheckForUpdates)];
+ this.ListenForConsoleInput = listenForConsoleInput ?? (bool)SConfig.DefaultValues[nameof(this.ListenForConsoleInput)];
this.ParanoidWarnings = paranoidWarnings ?? (bool)SConfig.DefaultValues[nameof(this.ParanoidWarnings)];
this.UseBetaChannel = useBetaChannel ?? (bool)SConfig.DefaultValues[nameof(this.UseBetaChannel)];
this.GitHubProjectName = gitHubProjectName;
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 40979b09..7bb54653 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -447,14 +447,17 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log("SMAPI found problems in your game's content files which are likely to cause errors or crashes. Consider uninstalling XNB mods or reinstalling the game.", LogLevel.Error);
// start SMAPI console
- new Thread(
- () => this.LogManager.RunConsoleInputLoop(
- commandManager: this.CommandManager,
- reloadTranslations: this.ReloadTranslations,
- handleInput: input => this.RawCommandQueue.Add(input),
- continueWhile: () => this.IsGameRunning && !this.IsExiting
- )
- ).Start();
+ if (this.Settings.ListenForConsoleInput)
+ {
+ new Thread(
+ () => this.LogManager.RunConsoleInputLoop(
+ commandManager: this.CommandManager,
+ reloadTranslations: this.ReloadTranslations,
+ handleInput: input => this.RawCommandQueue.Add(input),
+ continueWhile: () => this.IsGameRunning && !this.IsExiting
+ )
+ ).Start();
+ }
}
/// Raised after an instance finishes loading its initial content.
--
cgit
From 303b3924ae3ef905d77b9d7ef0f9efc70e58c2b8 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard
Date: Thu, 10 Nov 2022 21:50:01 -0500
Subject: fix case where prefix ends with a path separator
---
src/SMAPI/Framework/Content/AssetName.cs | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
(limited to 'src/SMAPI/Framework')
diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs
index 7b87c0c5..99968299 100644
--- a/src/SMAPI/Framework/Content/AssetName.cs