summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/SMAPI.Installer/InteractiveInstaller.cs12
-rw-r--r--src/SMAPI.Installer/assets/unix-launcher.sh34
-rw-r--r--src/SMAPI.ModBuildConfig/build/smapi.targets3
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/manifest.json4
-rw-r--r--src/SMAPI.Mods.ErrorHandler/manifest.json4
-rw-r--r--src/SMAPI.Mods.SaveBackup/manifest.json4
-rw-r--r--src/SMAPI.Web/Controllers/LogParserController.cs16
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/LogParser.cs12
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs22
-rw-r--r--src/SMAPI.Web/Framework/LogParsing/Models/ModType.cs15
-rw-r--r--src/SMAPI.Web/SMAPI.Web.csproj2
-rw-r--r--src/SMAPI.Web/ViewModels/LogParserModel.cs2
-rw-r--r--src/SMAPI.Web/Views/Index/Index.cshtml4
-rw-r--r--src/SMAPI.Web/Views/LogParser/Index.cshtml34
-rw-r--r--src/SMAPI.Web/wwwroot/Content/css/main.css2
-rw-r--r--src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.pngbin1099 -> 0 bytes
-rw-r--r--src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.svg1
-rw-r--r--src/SMAPI.Web/wwwroot/Content/images/pufferchick.pngbin831 -> 0 bytes
-rw-r--r--src/SMAPI.Web/wwwroot/Content/images/pufferchick.svg1
-rw-r--r--src/SMAPI.Web/wwwroot/Content/images/sidebar-bg.gifbin1104 -> 0 bytes
-rw-r--r--src/SMAPI.Web/wwwroot/Content/images/sidebar-bg.svg1
-rw-r--r--src/SMAPI.Web/wwwroot/Content/js/index.js4
-rw-r--r--src/SMAPI.Web/wwwroot/schemas/content-patcher.json4
-rw-r--r--src/SMAPI/Constants.cs8
-rw-r--r--src/SMAPI/Framework/Content/AssetInfo.cs4
-rw-r--r--src/SMAPI/Framework/ContentManagers/ModContentManager.cs4
-rw-r--r--src/SMAPI/Framework/Deprecations/DeprecationManager.cs2
-rw-r--r--src/SMAPI/Framework/ExitState.cs15
-rw-r--r--src/SMAPI/Framework/ModHelpers/CommandHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentHelper.cs8
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs70
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoader.cs13
-rw-r--r--src/SMAPI/Framework/ModLoading/ModResolver.cs6
-rw-r--r--src/SMAPI/Framework/SCore.cs67
-rw-r--r--src/SMAPI/SMAPI.csproj3
-rw-r--r--src/SMAPI/Utilities/PerScreen.cs2
-rw-r--r--src/SMAPI/app.manifest41
-rw-r--r--src/SMAPI/i18n/tr.json2
39 files changed, 315 insertions, 115 deletions
diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs
index fd1a6047..d00a5df4 100644
--- a/src/SMAPI.Installer/InteractiveInstaller.cs
+++ b/src/SMAPI.Installer/InteractiveInstaller.cs
@@ -206,7 +206,7 @@ namespace StardewModdingApi.Installer
Console.WriteLine();
// handle choice
- string choice = this.InteractivelyChoose("Type 1 or 2, then press enter.", new[] { "1", "2" });
+ string choice = this.InteractivelyChoose("Type 1 or 2, then press enter.", new[] { "1", "2" }, printLine: Console.WriteLine);
switch (choice)
{
case "1":
@@ -629,22 +629,22 @@ namespace StardewModdingApi.Installer
}
/// <summary>Interactively ask the user to choose a value.</summary>
- /// <param name="print">A callback which prints a message to the console.</param>
+ /// <param name="printLine">A callback which prints a message to the console.</param>
/// <param name="message">The message to print.</param>
/// <param name="options">The allowed options (not case sensitive).</param>
/// <param name="indent">The indentation to prefix to output.</param>
- private string InteractivelyChoose(string message, string[] options, string indent = "", Action<string>? print = null)
+ private string InteractivelyChoose(string message, string[] options, string indent = "", Action<string>? printLine = null)
{
- print ??= this.PrintInfo;
+ printLine ??= this.PrintInfo;
while (true)
{
- print(indent + message);
+ printLine(indent + message);
Console.Write(indent);
string? input = Console.ReadLine()?.Trim().ToLowerInvariant();
if (input == null || !options.Contains(input))
{
- print($"{indent}That's not a valid option.");
+ printLine($"{indent}That's not a valid option.");
continue;
}
return input;
diff --git a/src/SMAPI.Installer/assets/unix-launcher.sh b/src/SMAPI.Installer/assets/unix-launcher.sh
index ae9624e7..778663d7 100644
--- a/src/SMAPI.Installer/assets/unix-launcher.sh
+++ b/src/SMAPI.Installer/assets/unix-launcher.sh
@@ -54,12 +54,12 @@ if [ "$(uname)" == "Darwin" ]; then
# https://stackoverflow.com/a/29511052/262123
if [ "$USE_CURRENT_SHELL" == "false" ]; then
echo "Reopening in the Terminal app..."
- echo '#!/bin/sh' > /tmp/open-smapi-terminal.sh
- echo "\"$0\" $@ --use-current-shell" >> /tmp/open-smapi-terminal.sh
- chmod +x /tmp/open-smapi-terminal.sh
- cat /tmp/open-smapi-terminal.sh
- open -W -a Terminal /tmp/open-smapi-terminal.sh
- rm /tmp/open-smapi-terminal.sh
+ echo '#!/bin/sh' > /tmp/open-smapi-terminal.command
+ echo "\"$0\" $@ --use-current-shell" >> /tmp/open-smapi-terminal.command
+ chmod +x /tmp/open-smapi-terminal.command
+ cat /tmp/open-smapi-terminal.command
+ open -W /tmp/open-smapi-terminal.command
+ rm /tmp/open-smapi-terminal.command
exit 0
fi
fi
@@ -71,8 +71,8 @@ fi
##########
# script must be run from the game folder
if [ ! -f "Stardew Valley.dll" ]; then
- echo "Oops! SMAPI must be placed in the Stardew Valley game folder.\nSee instructions: https://stardewvalleywiki.com/Modding:Player_Guide";
- read
+ printf "Oops! SMAPI must be placed in the Stardew Valley game folder.\nSee instructions: https://stardewvalleywiki.com/Modding:Player_Guide";
+ read -r
exit 1
fi
@@ -102,37 +102,39 @@ else
# find the true shell behind x-terminal-emulator
if [ "$TERMINAL_NAME" = "x-terminal-emulator" ]; then
- export TERMINAL_NAME="$(basename "$(readlink -f $(command -v x-terminal-emulator))")"
+ TERMINAL_NAME="$(basename "$(readlink -f "$(command -v x-terminal-emulator)")")"
+ export TERMINAL_NAME
fi
# run in selected terminal and account for quirks
- export TERMINAL_PATH="$(command -v $TERMINAL_NAME)"
- if [ -x $TERMINAL_PATH ]; then
+ TERMINAL_PATH="$(command -v "$TERMINAL_NAME")"
+ export TERMINAL_PATH
+ if [ -x "$TERMINAL_PATH" ]; then
case $TERMINAL_NAME in
terminal|termite)
# consumes only one argument after -e
# options containing space characters are unsupported
- exec $TERMINAL_NAME -e "env TERM=xterm $LAUNCH_FILE $@"
+ exec "$TERMINAL_NAME" -e "env TERM=xterm $LAUNCH_FILE $@"
;;
xterm|konsole|alacritty)
# consumes all arguments after -e
- exec $TERMINAL_NAME -e env TERM=xterm $LAUNCH_FILE "$@"
+ exec "$TERMINAL_NAME" -e env TERM=xterm $LAUNCH_FILE "$@"
;;
terminator|xfce4-terminal|mate-terminal)
# consumes all arguments after -x
- exec $TERMINAL_NAME -x env TERM=xterm $LAUNCH_FILE "$@"
+ exec "$TERMINAL_NAME" -x env TERM=xterm $LAUNCH_FILE "$@"
;;
gnome-terminal)
# consumes all arguments after --
- exec $TERMINAL_NAME -- env TERM=xterm $LAUNCH_FILE "$@"
+ exec "$TERMINAL_NAME" -- env TERM=xterm $LAUNCH_FILE "$@"
;;
kitty)
# consumes all trailing arguments
- exec $TERMINAL_NAME env TERM=xterm $LAUNCH_FILE "$@"
+ exec "$TERMINAL_NAME" env TERM=xterm $LAUNCH_FILE "$@"
;;
*)
diff --git a/src/SMAPI.ModBuildConfig/build/smapi.targets b/src/SMAPI.ModBuildConfig/build/smapi.targets
index b66ec27b..12619439 100644
--- a/src/SMAPI.ModBuildConfig/build/smapi.targets
+++ b/src/SMAPI.ModBuildConfig/build/smapi.targets
@@ -8,8 +8,7 @@
** Set build options
**********************************************-->
<PropertyGroup>
- <!-- include PDB file by default to enable line numbers in stack traces -->
- <DebugType>pdbonly</DebugType>
+ <!-- enable line numbers in stack traces -->
<DebugSymbols>true</DebugSymbols>
<!-- don't create the 'refs' folder (which isn't useful for mods) -->
diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json
index 3c2dec19..adc45ec3 100644
--- a/src/SMAPI.Mods.ConsoleCommands/manifest.json
+++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Console Commands",
"Author": "SMAPI",
- "Version": "3.15.1",
+ "Version": "3.16.0",
"Description": "Adds SMAPI console commands that let you manipulate the game.",
"UniqueID": "SMAPI.ConsoleCommands",
"EntryDll": "ConsoleCommands.dll",
- "MinimumApiVersion": "3.15.1"
+ "MinimumApiVersion": "3.16.0"
}
diff --git a/src/SMAPI.Mods.ErrorHandler/manifest.json b/src/SMAPI.Mods.ErrorHandler/manifest.json
index 28b4b149..3a3c9283 100644
--- a/src/SMAPI.Mods.ErrorHandler/manifest.json
+++ b/src/SMAPI.Mods.ErrorHandler/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Error Handler",
"Author": "SMAPI",
- "Version": "3.15.1",
+ "Version": "3.16.0",
"Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.",
"UniqueID": "SMAPI.ErrorHandler",
"EntryDll": "ErrorHandler.dll",
- "MinimumApiVersion": "3.15.1"
+ "MinimumApiVersion": "3.16.0"
}
diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json
index 1944575b..eb98aa32 100644
--- a/src/SMAPI.Mods.SaveBackup/manifest.json
+++ b/src/SMAPI.Mods.SaveBackup/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Save Backup",
"Author": "SMAPI",
- "Version": "3.15.1",
+ "Version": "3.16.0",
"Description": "Automatically backs up all your saves once per day into its folder.",
"UniqueID": "SMAPI.SaveBackup",
"EntryDll": "SaveBackup.dll",
- "MinimumApiVersion": "3.15.1"
+ "MinimumApiVersion": "3.16.0"
}
diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs
index 33af5a81..a3bcf4c3 100644
--- a/src/SMAPI.Web/Controllers/LogParserController.cs
+++ b/src/SMAPI.Web/Controllers/LogParserController.cs
@@ -1,7 +1,9 @@
using System;
-using System.Linq;
+using System.Collections.Specialized;
+using System.IO;
using System.Text;
using System.Threading.Tasks;
+using System.Web;
using Microsoft.AspNetCore.Mvc;
using StardewModdingAPI.Toolkit.Utilities;
using StardewModdingAPI.Web.Framework;
@@ -87,9 +89,15 @@ namespace StardewModdingAPI.Web.Controllers
public async Task<ActionResult> PostAsync()
{
// get raw log text
- string? input = this.Request.Form["input"].FirstOrDefault();
- if (string.IsNullOrWhiteSpace(input))
- return this.View("Index", this.GetModel(null, uploadError: "The log file seems to be empty."));
+ // note: avoid this.Request.Form, which fails if any mod logged a null character.
+ string? input;
+ {
+ using StreamReader reader = new StreamReader(this.Request.Body);
+ NameValueCollection parsed = HttpUtility.ParseQueryString(await reader.ReadToEndAsync());
+ input = parsed["input"];
+ if (string.IsNullOrWhiteSpace(input))
+ return this.View("Index", this.GetModel(null, uploadError: "The log file seems to be empty."));
+ }
// upload log
UploadResult uploadResult = await this.Storage.SaveAsync(input);
diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs
index 0efa62c5..18ba754c 100644
--- a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs
+++ b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs
@@ -36,13 +36,13 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
private readonly Regex ContentPackListStartPattern = new(@"^Loaded \d+ content packs:$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching an entry in SMAPI's content pack list.</summary>
- private readonly Regex ContentPackListEntryPattern = new(@"^ (?<name>.+?) (?<version>[^\s]+)(?: by (?<author>[^\|]+))? \| for (?<for>[^\|]+)(?: \| (?<description>.+))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex ContentPackListEntryPattern = new(@"^ (?<name>.+?) (?<version>[^\s]+)(?: by (?<author>[^\|]+))? \| for (?<for>[^\|]*)(?: \| (?<description>.+))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching the start of SMAPI's mod update list.</summary>
private readonly Regex ModUpdateListStartPattern = new(@"^You can update \d+ mods?:$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching an entry in SMAPI's mod update list.</summary>
- private readonly Regex ModUpdateListEntryPattern = new(@"^ (?<name>.+) (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ private readonly Regex ModUpdateListEntryPattern = new(@"^ (?<name>.+) (?<version>[^\s]+): (?<link>[^\s]+)(?: \(you have [^\)]+\))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>A regex pattern matching SMAPI's update line.</summary>
private readonly Regex SmapiUpdatePattern = new(@"^You can update SMAPI to (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
@@ -77,8 +77,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
};
// parse log messages
- LogModInfo smapiMod = new(name: "SMAPI", author: "Pathoschild", version: "", description: "", loaded: true, isMod: false);
- LogModInfo gameMod = new(name: "game", author: "", version: "", description: "", loaded: true, isMod: false);
+ LogModInfo smapiMod = new(ModType.Special, name: "SMAPI", author: "Pathoschild", version: "", description: "", loaded: true);
+ LogModInfo gameMod = new(ModType.Special, name: "game", author: "", version: "", description: "", loaded: true);
IDictionary<string, List<LogModInfo>> mods = new Dictionary<string, List<LogModInfo>>();
bool inModList = false;
bool inContentPackList = false;
@@ -133,7 +133,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
if (!mods.TryGetValue(name, out List<LogModInfo>? entries))
mods[name] = entries = new List<LogModInfo>();
- entries.Add(new LogModInfo(name: name, author: author, version: version, description: description, loaded: true));
+ entries.Add(new LogModInfo(ModType.CodeMod, name: name, author: author, version: version, description: description, loaded: true));
message.Section = LogSection.ModsList;
}
@@ -156,7 +156,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
if (!mods.TryGetValue(name, out List<LogModInfo>? entries))
mods[name] = entries = new List<LogModInfo>();
- entries.Add(new LogModInfo(name: name, author: author, version: version, description: description, contentPackFor: forMod, loaded: true));
+ entries.Add(new LogModInfo(ModType.ContentPack, name: name, author: author, version: version, description: description, contentPackFor: forMod, loaded: true));
message.Section = LogSection.ContentPackList;
}
diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs b/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs
index 557f08ff..c81942e4 100644
--- a/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs
+++ b/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs
@@ -48,21 +48,24 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models
[MemberNotNullWhen(true, nameof(LogModInfo.UpdateVersion), nameof(LogModInfo.UpdateLink))]
public bool HasUpdate => this.UpdateVersion != null && this.Version != this.UpdateVersion;
+ /// <summary>The mod type.</summary>
+ public ModType ModType { get; }
+
/// <summary>Whether this is an actual mod (rather than a special entry for SMAPI or the game itself).</summary>
- public bool IsMod { get; }
+ public bool IsMod => this.ModType != ModType.Special;
/// <summary>Whether this is a C# code mod.</summary>
- public bool IsCodeMod { get; }
+ public bool IsCodeMod => this.ModType == ModType.CodeMod;
/// <summary>Whether this is a content pack for another mod.</summary>
- [MemberNotNullWhen(true, nameof(LogModInfo.ContentPackFor))]
- public bool IsContentPack { get; }
+ public bool IsContentPack => this.ModType == ModType.ContentPack;
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
+ /// <param name="modType">The mod type.</param>
/// <param name="name">The mod name.</param>
/// <param name="author">The mod author.</param>
/// <param name="version">The mod version.</param>
@@ -72,9 +75,9 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models
/// <param name="contentPackFor">The name of the mod for which this is a content pack (if applicable).</param>
/// <param name="errors">The number of errors logged by this mod.</param>
/// <param name="loaded">Whether the mod was loaded into the game.</param>
- /// <param name="isMod">Whether this is an actual mod (instead of a special entry for SMAPI or the game).</param>
- public LogModInfo(string name, string author, string version, string description, string? updateVersion = null, string? updateLink = null, string? contentPackFor = null, int errors = 0, bool loaded = true, bool isMod = true)
+ public LogModInfo(ModType modType, string name, string author, string version, string description, string? updateVersion = null, string? updateLink = null, string? contentPackFor = null, int errors = 0, bool loaded = true)
{
+ this.ModType = modType;
this.Name = name;
this.Author = author;
this.Description = description;
@@ -84,13 +87,6 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models
this.Errors = errors;
this.Loaded = loaded;
- if (isMod)
- {
- this.IsMod = true;
- this.IsContentPack = !string.IsNullOrWhiteSpace(this.ContentPackFor);
- this.IsCodeMod = !this.IsContentPack;
- }
-
this.OverrideVersion(version);
}
diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/ModType.cs b/src/SMAPI.Web/Framework/LogParsing/Models/ModType.cs
new file mode 100644
index 00000000..363aaaec
--- /dev/null
+++ b/src/SMAPI.Web/Framework/LogParsing/Models/ModType.cs
@@ -0,0 +1,15 @@
+namespace StardewModdingAPI.Web.Framework.LogParsing.Models
+{
+ /// <summary>The type for a <see cref="LogModInfo"/> instance.</summary>
+ public enum ModType
+ {
+ /// <summary>A special non-mod entry (e.g. for SMAPI or the game itself).</summary>
+ Special,
+
+ /// <summary>A C# mod.</summary>
+ CodeMod,
+
+ /// <summary>A content pack loaded by a C# mod.</summary>
+ ContentPack
+ }
+}
diff --git a/src/SMAPI.Web/SMAPI.Web.csproj b/src/SMAPI.Web/SMAPI.Web.csproj
index 0a460e9e..d26cb6f8 100644
--- a/src/SMAPI.Web/SMAPI.Web.csproj
+++ b/src/SMAPI.Web/SMAPI.Web.csproj
@@ -15,7 +15,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Azure.Storage.Blobs" Version="12.12.0" />
+ <PackageReference Include="Azure.Storage.Blobs" Version="12.13.0" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.29" />
<PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.43" />
diff --git a/src/SMAPI.Web/ViewModels/LogParserModel.cs b/src/SMAPI.Web/ViewModels/LogParserModel.cs
index c39a9b0a..34d0b4df 100644
--- a/src/SMAPI.Web/ViewModels/LogParserModel.cs
+++ b/src/SMAPI.Web/ViewModels/LogParserModel.cs
@@ -107,7 +107,7 @@ namespace StardewModdingAPI.Web.ViewModels
// group by mod
return mods
.Where(mod => mod.IsContentPack)
- .GroupBy(mod => mod.ContentPackFor!)
+ .GroupBy(mod => mod.ContentPackFor ?? "")
.ToDictionary(group => group.Key, group => group.ToArray());
}
diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml
index acb8df78..d6472fcb 100644
--- a/src/SMAPI.Web/Views/Index/Index.cshtml
+++ b/src/SMAPI.Web/Views/Index/Index.cshtml
@@ -10,12 +10,12 @@
@section Head {
<link rel="stylesheet" href="~/Content/css/index.css" />
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1" crossorigin="anonymous"></script>
- <script src="~/Content/js/index.js"></script>
+ <script src="~/Content/js/index.js?r=20220708"></script>
}
<h1>
SMAPI
- <img id="pufferchick" src="Content/images/pufferchick.png" />
+ <img id="pufferchick" src="Content/images/pufferchick.svg" />
</h1>
<div id="blurb">
<p>The mod loader for Stardew Valley.</p>
diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml
index b824b768..f71c6ac1 100644
--- a/src/SMAPI.Web/Views/LogParser/Index.cshtml
+++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml
@@ -17,6 +17,7 @@
LogModInfo[] outdatedMods = log?.Mods.Where(mod => mod.HasUpdate).ToArray() ?? Array.Empty<LogModInfo>();
LogModInfo? errorHandler = log?.Mods.FirstOrDefault(p => p.IsCodeMod && p.Name == "Error Handler");
bool hasOlderErrorHandler = errorHandler?.GetParsedVersion() is not null && log?.ApiVersionParsed is not null && log.ApiVersionParsed.IsNewerThan(errorHandler.GetParsedVersion());
+ bool isPyTkCompatibilityMode = log?.ApiVersionParsed?.IsOlderThan("3.15.0") is false && log.Mods.Any(p => p.IsCodeMod && p.Name == "PyTK" && p.GetParsedVersion()?.IsOlderThan("1.23.1") is true);
// get filters
IDictionary<string, bool> defaultFilters = Enum
@@ -242,7 +243,7 @@ else if (log?.IsValid == true)
@if (log?.IsValid == true)
{
<div id="output">
- @if (outdatedMods.Any() || errorHandler is null || hasOlderErrorHandler)
+ @if (outdatedMods.Any() || errorHandler is null || hasOlderErrorHandler || isPyTkCompatibilityMode)
{
<h2>Suggested fixes</h2>
<ul id="fix-list">
@@ -254,6 +255,10 @@ else if (log?.IsValid == true)
{
<li>Your <strong>Error Handler</strong> mod is older than SMAPI. You may be missing some game/mod error fixes. You can <a href="https://stardewvalleywiki.com/Modding:Player_Guide#Install_SMAPI">reinstall SMAPI</a> to update it.</li>
}
+ @if (isPyTkCompatibilityMode)
+ {
+ <li>PyTK 1.23.0 or earlier isn't compatible with newer SMAPI performance optimizations. This may increase loading times or in-game lag.</li>
+ }
@if (outdatedMods.Any())
{
<li>
@@ -347,16 +352,31 @@ else if (log?.IsValid == true)
<span class="notice btn txt" v-on:click="toggleContentPacks">toggle content packs in list</span>
}
</caption>
- @foreach (var mod in log.Mods.Where(p => p.Loaded && !p.IsContentPack))
- {
- if (contentPacks == null || !contentPacks.TryGetValue(mod.Name, out LogModInfo[]? contentPackList))
- contentPackList = null;
+ @{
+ var modsWithContentPacks = log.Mods
+ .Where(mod => mod.Loaded && !mod.IsContentPack)
+ .Select(mod => (
+ Mod: mod,
+ ContentPacks: contentPacks?.TryGetValue(mod.Name, out LogModInfo[]? contentPackList) == true ? contentPackList : Array.Empty<LogModInfo>()
+ ))
+ .ToList();
+ if (contentPacks?.TryGetValue("", out LogModInfo[] invalidPacks) == true)
+ {
+ modsWithContentPacks.Add((
+ Mod: new LogModInfo(ModType.CodeMod, "<invalid content packs>", "", "", ""),
+ ContentPacks: invalidPacks
+ ));
+ }
+ }
+
+ @foreach ((LogModInfo mod, LogModInfo[] contentPackList) in modsWithContentPacks)
+ {
<tr v-on:click="toggleMod('@Model.GetSlug(mod.Name)')" class="mod-entry" v-bind:class="{ hidden: !showMods['@Model.GetSlug(mod.Name)'] }">
<td><input type="checkbox" v-bind:checked="showMods['@Model.GetSlug(mod.Name)']" v-bind:class="{ invisible: !anyModsHidden }" /></td>
<td>
<strong v-pre>@mod.Name</strong> @mod.Version
- @if (contentPackList != null)
+ @if (contentPackList.Any())
{
<div v-if="!hideContentPacks" class="content-packs">
@foreach (var contentPack in contentPackList)
@@ -369,7 +389,7 @@ else if (log?.IsValid == true)
</td>
<td>
@mod.Author
- @if (contentPackList != null)
+ @if (contentPackList.Any())
{
<div v-if="!hideContentPacks" class="content-packs">
@foreach (var contentPack in contentPackList)
diff --git a/src/SMAPI.Web/wwwroot/Content/css/main.css b/src/SMAPI.Web/wwwroot/Content/css/main.css
index a0a407d8..79de9c39 100644
--- a/src/SMAPI.Web/wwwroot/Content/css/main.css
+++ b/src/SMAPI.Web/wwwroot/Content/css/main.css
@@ -68,7 +68,7 @@ a {
margin-top: 3em;
min-height: 75%;
width: 12em;
- background: url("../images/sidebar-bg.gif") no-repeat top right;
+ background: url("../images/sidebar-bg.svg") no-repeat top right;
color: #666;
}
diff --git a/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png b/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png
deleted file mode 100644
index f359146c..00000000
--- a/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png
+++ /dev/null
Binary files differ
diff --git a/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.svg b/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.svg
new file mode 100644
index 00000000..2734c289
--- /dev/null
+++ b/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.svg
@@ -0,0 +1 @@
+<svg width="56" height="56" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 17.684h2.947v8.842H0v-8.842Zm29.474 32.421h2.947V56h-2.947v-5.895ZM18.667 29.474h2.947v5.894h-2.947v-5.894Zm16.701 20.631h2.948V56h-2.948v-5.895ZM2.948 14.737h5.894v2.947H2.947v-2.947Zm5.894 2.947h5.895v2.948H8.842v-2.948Zm0 23.58h5.895v2.947H8.842v-2.948ZM23.58 5.893h5.895v2.948H23.58V5.895Z" fill="#6B3407"/><path d="M14.737 41.263h5.895v2.948h-5.895v-2.948Zm29.474 0h5.894v2.948h-5.894v-2.948ZM32.42 23.58h5.895v2.947H32.42V23.58Z" fill="#8F4039"/><path d="M26.526 2.947h5.895v2.948h-5.895V2.947Z" fill="#FF4D1E"/><path d="M32.421 8.842h5.895v2.947H32.42V8.843Z" fill="#8F4039"/><path d="M23.579 47.158h14.737v2.947H23.579v-2.947Zm-1.965-11.79h11.79v2.948h-11.79v-2.948ZM20.632 0h11.79v2.947h-11.79V0Zm0 2.947h2.947v2.948h-2.947V2.947Zm11.79 0h2.946v2.948h-2.947V2.947Zm2.946 2.948h2.948v2.947h-2.948V5.895ZM2.948 26.526h2.947v2.948H2.947v-2.948Z" fill="#6B3407"/><path d="M2.947 23.579h2.948v2.947H2.947V23.58Zm2.948 2.947h2.947v2.948H5.895v-2.948Zm-2.948 5.895h2.948v2.947H2.947v-2.947Zm5.895 5.895h2.947v2.947H8.843v-2.947Zm9.825-2.948h2.947v2.948h-2.947v-2.948Zm14.736-2.947h2.948v2.947h-2.947v-2.947Z" fill="#9A4E0F"/><path d="M5.895 38.316h2.947v2.947H5.895v-2.947Zm15.719-8.842h2.947v2.947h-2.947v-2.947Z" fill="#6B3407"/><path d="M17.684 44.21h2.948v2.948h-2.948V44.21Zm5.895 0h2.947v2.948H23.58V44.21Zm20.632 0h2.947v2.948H44.21V44.21Zm5.894-5.894h2.948v2.947h-2.948v-2.947Zm-2.947-2.948h2.947v2.948h-2.947v-2.948Zm5.895-8.842H56v2.948h-2.947v-2.948Zm-2.948-2.947h2.948v2.947h-2.948V23.58ZM26.526 8.842h2.948v2.947h-2.948V8.843Z" fill="#8F4039"/><path d="M23.579 2.947h2.947v2.948H23.58V2.947Zm8.842 2.948h2.947v2.947h-2.947V5.895Z" fill="#C31E1E"/><path d="M29.474 5.895h2.947v2.947h-2.947V5.895Z" fill="#FF4D1E"/><path d="M23.579 11.79h2.947v2.947H23.58V11.79Z" fill="#8F4039"/><path d="M2.947 17.684h2.948v2.948H2.947v-2.948Z" fill="#FFE2AF"/><path d="M5.895 17.684h2.947v2.948H5.895v-2.948Zm-2.948 2.948h2.948v2.947H2.947v-2.947Zm8.843 0h2.947v2.947H11.79v-2.947Zm12.771 8.842h2.948v2.947H24.56v-2.947Zm8.843 0h2.947v2.947h-2.947v-2.947Zm0 5.894h2.947v2.948h-2.947v-2.948ZM8.841 26.526h2.947v5.895H8.843v-5.895Zm-5.895 2.948h2.948v2.947H2.947v-2.947Zm11.79 5.894h2.947v2.948h-2.947v-2.948Z" fill="#DEA159"/><path d="M14.737 38.316h2.947v2.947h-2.947v-2.947Zm5.895 2.947h2.947v2.948h-2.947v-2.948Zm23.579-8.842h2.947v2.947H44.21v-2.947Zm0-5.895h2.947v2.948H44.21v-2.948Zm-17.685 0h14.737v2.948H26.526v-2.948Zm0 11.79h5.895v2.947h-5.895v-2.947Zm5.895 2.947h8.842v2.948h-8.842v-2.948Zm2.947-2.947h8.843v2.947h-8.843v-2.947Zm2.948-2.948h8.842v2.948h-8.842v-2.948Zm0-11.79h8.842v2.948h-8.842V23.58Zm-2.948-8.841h8.843v2.947h-8.843v-2.947Zm-11.79 0h8.843v2.947H23.58v-2.947Zm0 8.842h8.843v2.947H23.58V23.58Zm5.896-5.895h11.79v5.895h-11.79v-5.895Zm6.877 14.737h4.912v2.947h-4.912v-2.947Zm0-2.947h13.754v2.947H36.351v-2.947ZM26.526 11.79h8.842v2.947h-8.842V11.79Z" fill="#FCD100"/><path d="M20.632 44.21h2.947v2.948h-2.947V44.21Z" fill="#DF8900"/><path d="M20.632 44.21h2.947v2.948h-2.947V44.21Zm20.631-2.947h2.948v2.948h-2.948v-2.948Zm2.948-2.947h2.947v2.947H44.21v-2.947Zm2.947-5.895h2.947v2.947h-2.947v-2.947Zm-5.895 0h2.948v2.947h-2.948v-2.947Zm2.948-14.737h2.947v2.948H44.21v-2.948Zm-8.843-5.895h2.948v2.948h-2.948V11.79Zm-14.736 2.948h2.947v2.947h-2.947v-2.947Zm-2.948 2.947h2.948v2.948h-2.948v-