mirror of
https://github.com/Pathoschild/SMAPI.git
synced 2026-10-12 02:08:51 +00:00
avoid Where() enumerators when possible
This commit is contained in:
@@ -377,8 +377,11 @@ internal class InteractiveInstaller
|
||||
{
|
||||
// copy SMAPI files to game dir
|
||||
this.PrintDebug("Adding SMAPI files...");
|
||||
foreach (FileSystemInfo sourceEntry in paths.BundleDir.EnumerateFileSystemInfos().Where(this.ShouldCopy))
|
||||
foreach (FileSystemInfo sourceEntry in paths.BundleDir.EnumerateFileSystemInfos())
|
||||
{
|
||||
if (!this.ShouldCopy(sourceEntry))
|
||||
continue;
|
||||
|
||||
this.InteractivelyDelete(Path.Combine(paths.GameDir.FullName, sourceEntry.Name), allowUserInput);
|
||||
this.RecursiveCopy(sourceEntry, paths.GameDir);
|
||||
}
|
||||
|
||||
@@ -329,8 +329,11 @@ public class ModScanner
|
||||
{
|
||||
bool hasXnbFile = false;
|
||||
|
||||
foreach (FileInfo file in files.Where(this.IsRelevant))
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
if (!this.IsRelevant(file))
|
||||
continue;
|
||||
|
||||
if (this.StrictXnbModExtensions.Contains(file.Extension))
|
||||
{
|
||||
hasXnbFile = true;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewModdingAPI.Toolkit.Framework.UpdateData;
|
||||
|
||||
namespace StardewModdingAPI.Web.Framework.Caching.Mods;
|
||||
@@ -13,7 +12,7 @@ internal class ModCacheMemoryRepository : BaseCacheRepository, IModCacheReposito
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The cached mod data indexed by <c>{site key}:{ID}</c>.</summary>
|
||||
private readonly IDictionary<string, Cached<IModPage>> Mods = new Dictionary<string, Cached<IModPage>>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<string, Cached<IModPage>> Mods = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
|
||||
/*********
|
||||
@@ -57,13 +56,11 @@ internal class ModCacheMemoryRepository : BaseCacheRepository, IModCacheReposito
|
||||
{
|
||||
DateTimeOffset minDate = DateTimeOffset.UtcNow.Subtract(age);
|
||||
|
||||
string[] staleKeys = this.Mods
|
||||
.Where(p => p.Value.LastRequested < minDate)
|
||||
.Select(p => p.Key)
|
||||
.ToArray();
|
||||
|
||||
foreach (string key in staleKeys)
|
||||
this.Mods.Remove(key);
|
||||
foreach ((string key, var modPage) in this.Mods)
|
||||
{
|
||||
if (modPage.LastRequested < minDate)
|
||||
this.Mods.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -499,8 +499,11 @@ internal class ContentCoordinator : IDisposable
|
||||
return this.ContentManagerLock.InReadLock(() =>
|
||||
{
|
||||
List<object> values = [];
|
||||
foreach (IContentManager content in this.ContentManagers.Where(p => !p.IsNamespaced && p.IsLoaded(assetName)))
|
||||
foreach (IContentManager content in this.ContentManagers)
|
||||
{
|
||||
if (content.IsNamespaced || !content.IsLoaded(assetName))
|
||||
continue;
|
||||
|
||||
object value = content.LoadExact<object>(assetName, useCache: true);
|
||||
values.Add(value);
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ internal class LogManager : IDisposable
|
||||
/// <param name="logParanoidWarnings">Whether to log issues for mods which directly use potentially sensitive .NET APIs like file or shell access.</param>
|
||||
/// <param name="logTechnicalDetailsForBrokenMods">Whether to include more technical details about broken mods in the TRACE logs. This is mainly useful for creating compatibility rewriters.</param>
|
||||
/// <param name="hasHarmonyFix">Whether Harmony was fixed to work with Stardew Valley.</param>
|
||||
public void LogModInfo(IModMetadata[] loaded, IModMetadata[] loadedContentPacks, IModMetadata[] loadedMods, IReadOnlyList<IModMetadata> skippedMods, bool logParanoidWarnings, bool logTechnicalDetailsForBrokenMods, bool hasHarmonyFix)
|
||||
public void LogModInfo(IReadOnlyList<IModMetadata> loaded, IReadOnlyList<IModMetadata> loadedContentPacks, IReadOnlyList<IModMetadata> loadedMods, IReadOnlyList<IModMetadata> skippedMods, bool logParanoidWarnings, bool logTechnicalDetailsForBrokenMods, bool hasHarmonyFix)
|
||||
{
|
||||
// report malicious mod
|
||||
foreach (IModMetadata mod in skippedMods)
|
||||
@@ -300,7 +300,7 @@ internal class LogManager : IDisposable
|
||||
}
|
||||
|
||||
// log loaded mods
|
||||
this.Monitor.Log($"Loaded {loadedMods.Length} mods" + (loadedMods.Length > 0 ? ":" : "."), LogLevel.Info);
|
||||
this.Monitor.Log($"Loaded {loadedMods.Count} mods" + (loadedMods.Count > 0 ? ":" : "."), LogLevel.Info);
|
||||
foreach (IModMetadata metadata in loadedMods.OrderBy(p => p.DisplayName))
|
||||
{
|
||||
IManifest manifest = metadata.Manifest;
|
||||
@@ -319,7 +319,7 @@ internal class LogManager : IDisposable
|
||||
{
|
||||
string? GetModDisplayName(string id) => loadedMods.FirstOrDefault(p => p.HasId(id))?.DisplayName;
|
||||
|
||||
this.Monitor.Log($"Loaded {loadedContentPacks.Length} content packs:", LogLevel.Info);
|
||||
this.Monitor.Log($"Loaded {loadedContentPacks.Count} content packs:", LogLevel.Info);
|
||||
foreach (IModMetadata metadata in loadedContentPacks.OrderBy(p => p.DisplayName))
|
||||
{
|
||||
IManifest manifest = metadata.Manifest;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
@@ -69,16 +68,19 @@ internal class RecursiveRewriter
|
||||
/// <returns>Returns whether the module was modified.</returns>
|
||||
public bool RewriteModule()
|
||||
{
|
||||
IEnumerable<TypeDefinition> types = this.Module.GetTypes().Where(type => type.BaseType != null); // skip special types like <Module>
|
||||
|
||||
bool changed = false;
|
||||
|
||||
try
|
||||
{
|
||||
changed |= this.RewriteModuleImpl(this.Module);
|
||||
|
||||
foreach (TypeDefinition type in types)
|
||||
foreach (TypeDefinition type in this.Module.GetTypes())
|
||||
{
|
||||
if (type.BaseType is null)
|
||||
continue; // skip special types like <Module>
|
||||
|
||||
changed |= this.RewriteTypeDefinition(type);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -293,8 +295,7 @@ internal class RecursiveRewriter
|
||||
MethodDefinition? constructor = (newAttrType ?? attribute.AttributeType)
|
||||
.Resolve()
|
||||
?.Methods
|
||||
.Where(method => method.IsConstructor)
|
||||
.FirstOrDefault(ctor => RewriteHelper.HasMatchingSignature(ctor, attribute.Constructor));
|
||||
.FirstOrDefault(method => method.IsConstructor && RewriteHelper.HasMatchingSignature(method, attribute.Constructor));
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException($"Can't rewrite attribute type '{attribute.AttributeType.FullName}' to '{newAttrType?.FullName}', no equivalent constructor found.");
|
||||
|
||||
|
||||
@@ -225,10 +225,13 @@ internal class ModResolver
|
||||
var states = mods.ToDictionary(mod => mod, _ => ModDependencyStatus.Queued);
|
||||
|
||||
// handle failed mods
|
||||
foreach (IModMetadata mod in mods.Where(m => m.Status == ModMetadataStatus.Failed))
|
||||
foreach (IModMetadata mod in mods)
|
||||
{
|
||||
states[mod] = ModDependencyStatus.Failed;
|
||||
sortedMods.Push(mod);
|
||||
if (mod.Status is ModMetadataStatus.Failed)
|
||||
{
|
||||
states[mod] = ModDependencyStatus.Failed;
|
||||
sortedMods.Push(mod);
|
||||
}
|
||||
}
|
||||
|
||||
// sort mods
|
||||
|
||||
@@ -47,8 +47,11 @@ internal class HeuristicMethodRewriter : BaseInstructionHandler
|
||||
|
||||
// get method definition
|
||||
MethodDefinition? method = null;
|
||||
foreach (MethodDefinition match in type.Methods.Where(p => p.Name == methodRef.Name))
|
||||
foreach (MethodDefinition match in type.Methods)
|
||||
{
|
||||
if (match.Name != methodRef.Name)
|
||||
continue;
|
||||
|
||||
// reference matches initial parameters of definition
|
||||
if (methodRef.Parameters.Count >= match.Parameters.Count || !this.InitialParametersMatch(methodRef, match))
|
||||
continue;
|
||||
|
||||
@@ -484,10 +484,13 @@ internal class SCore : IDisposable
|
||||
{
|
||||
bool anyIgnored = false;
|
||||
|
||||
foreach (IModMetadata mod in mods.Where(p => p.IsIgnored))
|
||||
foreach (IModMetadata mod in mods)
|
||||
{
|
||||
anyIgnored = true;
|
||||
this.Monitor.Log($" Skipped {mod.GetRelativePathWithRoot()} (folder name starts with a dot).");
|
||||
if (mod.IsIgnored)
|
||||
{
|
||||
anyIgnored = true;
|
||||
this.Monitor.Log($" Skipped {mod.GetRelativePathWithRoot()} (folder name starts with a dot).");
|
||||
}
|
||||
}
|
||||
|
||||
if (anyIgnored)
|
||||
@@ -1982,9 +1985,18 @@ internal class SCore : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
IModMetadata[] loaded = this.ModRegistry.GetAll().ToArray();
|
||||
IModMetadata[] loadedContentPacks = loaded.Where(p => p.IsContentPack).ToArray();
|
||||
IModMetadata[] loadedMods = loaded.Where(p => !p.IsContentPack).ToArray();
|
||||
List<IModMetadata> loaded = [];
|
||||
List<IModMetadata> loadedContentPacks = [];
|
||||
List<IModMetadata> loadedMods = [];
|
||||
foreach (IModMetadata mod in this.ModRegistry.GetAll())
|
||||
{
|
||||
loaded.Add(mod);
|
||||
|
||||
if (mod.IsContentPack)
|
||||
loadedContentPacks.Add(mod);
|
||||
else
|
||||
loadedMods.Add(mod);
|
||||
}
|
||||
|
||||
// unlock content packs
|
||||
this.ModRegistry.AreAllModsLoaded = true;
|
||||
@@ -2103,10 +2115,10 @@ internal class SCore : IDisposable
|
||||
|
||||
// validate dependencies
|
||||
// Although dependencies are validated before mods are loaded, a dependency may have failed to load.
|
||||
foreach (IManifestDependency dependency in manifest!.Dependencies.Where(p => p.IsRequired))
|
||||
foreach (IManifestDependency dependency in manifest!.Dependencies)
|
||||
{
|
||||
// not missing
|
||||
if (this.ModRegistry.Get(dependency.UniqueID) != null)
|
||||
if (!dependency.IsRequired || this.ModRegistry.Get(dependency.UniqueID) != null)
|
||||
continue;
|
||||
|
||||
// ignored in compatibility list (e.g. fully replaced by the game code)
|
||||
@@ -2319,23 +2331,34 @@ internal class SCore : IDisposable
|
||||
mod = null;
|
||||
|
||||
// find type
|
||||
TypeInfo[] modEntries = modAssembly.DefinedTypes.Where(type => typeof(Mod).IsAssignableFrom(type) && !type.IsAbstract).Take(2).ToArray();
|
||||
if (modEntries.Length == 0)
|
||||
TypeInfo? modType = null;
|
||||
foreach (TypeInfo type in modAssembly.DefinedTypes)
|
||||
{
|
||||
// skip: not a Mod type
|
||||
if (!typeof(Mod).IsAssignableFrom(type) || type.IsAbstract)
|
||||
continue;
|
||||
|
||||
// fail: multiple Mod types found
|
||||
if (modType is not null)
|
||||
{
|
||||
error = $"its DLL contains multiple '{nameof(Mod)}' subclasses.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// else save mod type
|
||||
modType = type;
|
||||
}
|
||||
if (modType is null)
|
||||
{
|
||||
error = $"its DLL has no '{nameof(Mod)}' subclass.";
|
||||
return false;
|
||||
}
|
||||
if (modEntries.Length > 1)
|
||||
{
|
||||
error = $"its DLL contains multiple '{nameof(Mod)}' subclasses.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// get implementation
|
||||
Context.HeuristicModsRunningCode.Push(metadata);
|
||||
try
|
||||
{
|
||||
mod = (Mod?)modAssembly.CreateInstance(modEntries[0].ToString());
|
||||
mod = (Mod?)modAssembly.CreateInstance(modType.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -192,8 +192,11 @@ internal class SMultiplayer : Multiplayer
|
||||
newPeer.SendMessage(new OutgoingMessage((byte)MessageType.ModContext, Game1.player.UniqueMultiplayerID, this.GetContextSyncMessageFields()));
|
||||
|
||||
// reply with other players' context
|
||||
foreach (MultiplayerPeer otherPeer in this.Peers.Values.Where(p => p.PlayerID != newPeer.PlayerID))
|
||||
foreach (MultiplayerPeer otherPeer in this.Peers.Values)
|
||||
{
|
||||
if (otherPeer.PlayerID == newPeer.PlayerID)
|
||||
continue;
|
||||
|
||||
this.Monitor.VerboseLog($" Replying with context for player {otherPeer.PlayerID}...");
|
||||
newPeer.SendMessage(new OutgoingMessage((byte)MessageType.ModContext, otherPeer.PlayerID, this.GetContextSyncMessageFields(otherPeer)));
|
||||
}
|
||||
@@ -202,8 +205,11 @@ internal class SMultiplayer : Multiplayer
|
||||
if (this.Peers.Count > 1)
|
||||
{
|
||||
object[] fields = this.GetContextSyncMessageFields(newPeer);
|
||||
foreach (MultiplayerPeer otherPeer in this.Peers.Values.Where(p => p.PlayerID != newPeer.PlayerID))
|
||||
foreach (MultiplayerPeer otherPeer in this.Peers.Values)
|
||||
{
|
||||
if (otherPeer.PlayerID == newPeer.PlayerID)
|
||||
continue;
|
||||
|
||||
this.Monitor.VerboseLog($" Forwarding context to player {otherPeer.PlayerID}...");
|
||||
otherPeer.SendMessage(new OutgoingMessage((byte)MessageType.ModContext, newPeer.PlayerID, fields));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using StardewModdingAPI.Framework.StateTracking.Comparers;
|
||||
using StardewModdingAPI.Framework.StateTracking.FieldWatchers;
|
||||
using StardewValley;
|
||||
@@ -16,7 +15,7 @@ internal class ChestTracker : IDisposable
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The item stack sizes as of the last update.</summary>
|
||||
private readonly IDictionary<Item, int> StackSizes;
|
||||
private readonly Dictionary<Item, int> StackSizes = [];
|
||||
|
||||
/// <summary>Items added since the last update.</summary>
|
||||
private readonly HashSet<Item> Added = new(new ObjectReferenceComparer<Item>());
|
||||
@@ -46,9 +45,11 @@ internal class ChestTracker : IDisposable
|
||||
this.Chest = chest;
|
||||
this.InventoryWatcher = WatcherFactory.ForInventory($"{name}.{nameof(chest.Items)}", chest.Items);
|
||||
|
||||
this.StackSizes = this.Chest.Items
|
||||
.Where(n => n != null)
|
||||
.ToDictionary(n => n, n => n.Stack);
|
||||
foreach (Item? item in this.Chest.Items)
|
||||
{
|
||||
if (item is not null)
|
||||
this.StackSizes[item] = item.Stack;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Update the current values if needed.</summary>
|
||||
|
||||
@@ -16,10 +16,10 @@ internal class PlayerTracker : IDisposable
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The player's inventory as of the last reset.</summary>
|
||||
private IDictionary<Item, int> PreviousInventory;
|
||||
private readonly Dictionary<Item, int> PreviousInventory;
|
||||
|
||||
/// <summary>The player's inventory change as of the last update.</summary>
|
||||
private IDictionary<Item, int> CurrentInventory;
|
||||
private readonly Dictionary<Item, int> CurrentInventory = [];
|
||||
|
||||
/// <summary>The player's last valid location.</summary>
|
||||
private GameLocation? LastValidLocation;
|
||||
@@ -50,7 +50,7 @@ internal class PlayerTracker : IDisposable
|
||||
{
|
||||
// init player data
|
||||
this.Player = player;
|
||||
this.CurrentInventory = this.GetInventory();
|
||||
this.SaveInventoryTo(this.CurrentInventory);
|
||||
this.PreviousInventory = new Dictionary<Item, int>(this.CurrentInventory);
|
||||
|
||||
// init trackers
|
||||
@@ -81,16 +81,21 @@ internal class PlayerTracker : IDisposable
|
||||
watcher.Update();
|
||||
|
||||
// update inventory
|
||||
this.CurrentInventory = this.GetInventory();
|
||||
this.CurrentInventory.Clear();
|
||||
this.SaveInventoryTo(this.CurrentInventory);
|
||||
}
|
||||
|
||||
/// <summary>Reset all trackers so their current values are the baseline.</summary>
|
||||
public void Reset()
|
||||
{
|
||||
// reset watchers
|
||||
foreach (IWatcher watcher in this.Watchers)
|
||||
watcher.Reset();
|
||||
|
||||
this.PreviousInventory = this.CurrentInventory;
|
||||
// reset PreviousInventory to match current
|
||||
this.PreviousInventory.Clear();
|
||||
foreach ((Item item, int stack) in this.CurrentInventory)
|
||||
this.PreviousInventory.Add(item, stack);
|
||||
}
|
||||
|
||||
/// <summary>Get the player's current location, ignoring temporary null values.</summary>
|
||||
@@ -105,8 +110,8 @@ internal class PlayerTracker : IDisposable
|
||||
/// <returns>Returns whether anything changed.</returns>
|
||||
public bool TryGetInventoryChanges([NotNullWhen(true)] out SnapshotItemListDiff? changes)
|
||||
{
|
||||
ISet<Item> added = new HashSet<Item>(new ObjectReferenceComparer<Item>());
|
||||
ISet<Item> removed = new HashSet<Item>(new ObjectReferenceComparer<Item>());
|
||||
HashSet<Item> added = new(new ObjectReferenceComparer<Item>());
|
||||
HashSet<Item> removed = new(new ObjectReferenceComparer<Item>());
|
||||
foreach (Item item in this.PreviousInventory.Keys.Union(this.CurrentInventory.Keys))
|
||||
{
|
||||
if (!this.PreviousInventory.ContainsKey(item))
|
||||
@@ -133,10 +138,13 @@ internal class PlayerTracker : IDisposable
|
||||
** Private methods
|
||||
*********/
|
||||
/// <summary>Get the player's current inventory.</summary>
|
||||
private IDictionary<Item, int> GetInventory()
|
||||
/// <param name="inventory">The dictionary to fill with the player's inventory.</param>
|
||||
private void SaveInventoryTo(Dictionary<Item, int> inventory)
|
||||
{
|
||||
return this.Player.Items
|
||||
.Where(n => n != null)
|
||||
.ToDictionary(n => n, n => n.Stack);
|
||||
foreach (Item? item in this.Player.Items)
|
||||
{
|
||||
if (item is not null)
|
||||
inventory[item] = item.Stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user