fix log parsing for invalid content packs (#860)

This commit is contained in:
Jesse Plamondon-Willard 2022-08-08 22:27:07 -04:00
parent e376386d25
commit d813c4e2c8
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
6 changed files with 52 additions and 25 deletions

View File

@ -29,6 +29,7 @@
* Added log parser warning about performance of PyTK 1.23.0 or earlier. * Added log parser warning about performance of PyTK 1.23.0 or earlier.
* Converted images to SVG (thanks to ishan!). * Converted images to SVG (thanks to ishan!).
* Updated log parser for new update alert format in SMAPI 3.15.1. * Updated log parser for new update alert format in SMAPI 3.15.1.
* Fixed parsing for invalid content packs.
## 3.15.1 ## 3.15.1
Released 06 July 2022 for Stardew Valley 1.5.6 or later. Released 06 July 2022 for Stardew Valley 1.5.6 or later.

View File

@ -36,7 +36,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
private readonly Regex ContentPackListStartPattern = new(@"^Loaded \d+ content packs:$", RegexOptions.Compiled | RegexOptions.IgnoreCase); 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> /// <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> /// <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); private readonly Regex ModUpdateListStartPattern = new(@"^You can update \d+ mods?:$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
@ -77,8 +77,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
}; };
// parse log messages // parse log messages
LogModInfo smapiMod = new(name: "SMAPI", author: "Pathoschild", version: "", description: "", loaded: true, isMod: false); LogModInfo smapiMod = new(ModType.Special, name: "SMAPI", author: "Pathoschild", version: "", description: "", loaded: true);
LogModInfo gameMod = new(name: "game", author: "", version: "", description: "", loaded: true, isMod: false); LogModInfo gameMod = new(ModType.Special, name: "game", author: "", version: "", description: "", loaded: true);
IDictionary<string, List<LogModInfo>> mods = new Dictionary<string, List<LogModInfo>>(); IDictionary<string, List<LogModInfo>> mods = new Dictionary<string, List<LogModInfo>>();
bool inModList = false; bool inModList = false;
bool inContentPackList = false; bool inContentPackList = false;
@ -133,7 +133,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
if (!mods.TryGetValue(name, out List<LogModInfo>? entries)) if (!mods.TryGetValue(name, out List<LogModInfo>? entries))
mods[name] = entries = new List<LogModInfo>(); 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; message.Section = LogSection.ModsList;
} }
@ -156,7 +156,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing
if (!mods.TryGetValue(name, out List<LogModInfo>? entries)) if (!mods.TryGetValue(name, out List<LogModInfo>? entries))
mods[name] = entries = new List<LogModInfo>(); 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; message.Section = LogSection.ContentPackList;
} }

View File

@ -48,21 +48,24 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models
[MemberNotNullWhen(true, nameof(LogModInfo.UpdateVersion), nameof(LogModInfo.UpdateLink))] [MemberNotNullWhen(true, nameof(LogModInfo.UpdateVersion), nameof(LogModInfo.UpdateLink))]
public bool HasUpdate => this.UpdateVersion != null && this.Version != this.UpdateVersion; 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> /// <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> /// <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> /// <summary>Whether this is a content pack for another mod.</summary>
[MemberNotNullWhen(true, nameof(LogModInfo.ContentPackFor))] public bool IsContentPack => this.ModType == ModType.ContentPack;
public bool IsContentPack { get; }
/********* /*********
** Public methods ** Public methods
*********/ *********/
/// <summary>Construct an instance.</summary> /// <summary>Construct an instance.</summary>
/// <param name="modType">The mod type.</param>
/// <param name="name">The mod name.</param> /// <param name="name">The mod name.</param>
/// <param name="author">The mod author.</param> /// <param name="author">The mod author.</param>
/// <param name="version">The mod version.</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="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="errors">The number of errors logged by this mod.</param>
/// <param name="loaded">Whether the mod was loaded into the game.</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(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)
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)
{ {
this.ModType = modType;
this.Name = name; this.Name = name;
this.Author = author; this.Author = author;
this.Description = description; this.Description = description;
@ -84,13 +87,6 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models
this.Errors = errors; this.Errors = errors;
this.Loaded = loaded; this.Loaded = loaded;
if (isMod)
{
this.IsMod = true;
this.IsContentPack = !string.IsNullOrWhiteSpace(this.ContentPackFor);
this.IsCodeMod = !this.IsContentPack;
}
this.OverrideVersion(version); this.OverrideVersion(version);
} }

View File

@ -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
}
}

View File

@ -107,7 +107,7 @@ namespace StardewModdingAPI.Web.ViewModels
// group by mod // group by mod
return mods return mods
.Where(mod => mod.IsContentPack) .Where(mod => mod.IsContentPack)
.GroupBy(mod => mod.ContentPackFor!) .GroupBy(mod => mod.ContentPackFor ?? "")
.ToDictionary(group => group.Key, group => group.ToArray()); .ToDictionary(group => group.Key, group => group.ToArray());
} }

View File

@ -352,16 +352,31 @@ else if (log?.IsValid == true)
<span class="notice btn txt" v-on:click="toggleContentPacks">toggle content packs in list</span> <span class="notice btn txt" v-on:click="toggleContentPacks">toggle content packs in list</span>
} }
</caption> </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)'] }"> <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><input type="checkbox" v-bind:checked="showMods['@Model.GetSlug(mod.Name)']" v-bind:class="{ invisible: !anyModsHidden }" /></td>
<td> <td>
<strong v-pre>@mod.Name</strong> @mod.Version <strong v-pre>@mod.Name</strong> @mod.Version
@if (contentPackList != null) @if (contentPackList.Any())
{ {
<div v-if="!hideContentPacks" class="content-packs"> <div v-if="!hideContentPacks" class="content-packs">
@foreach (var contentPack in contentPackList) @foreach (var contentPack in contentPackList)
@ -374,7 +389,7 @@ else if (log?.IsValid == true)
</td> </td>
<td> <td>
@mod.Author @mod.Author
@if (contentPackList != null) @if (contentPackList.Any())
{ {
<div v-if="!hideContentPacks" class="content-packs"> <div v-if="!hideContentPacks" class="content-packs">
@foreach (var contentPack in contentPackList) @foreach (var contentPack in contentPackList)