mirror of
https://github.com/Pathoschild/SMAPI.git
synced 2026-10-12 02:08:51 +00:00
reduce remaining uses of LINQ on warmer paths
This commit is contained in:
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
using StardewModdingAPI.Toolkit.Utilities;
|
||||
|
||||
namespace StardewModdingAPI.Framework.Content;
|
||||
@@ -117,7 +116,7 @@ internal class ContentCache
|
||||
this.Remove(key, dispose);
|
||||
|
||||
return removed.Count == 0
|
||||
? Enumerable.Empty<string>() // let GC collect the list in gen0 instead of potentially living longer
|
||||
? Array.Empty<string>() // let GC collect the list in gen0 instead of potentially living longer
|
||||
: removed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ internal class GameContentManager : BaseContentManager
|
||||
return null;
|
||||
}
|
||||
|
||||
loader = loadOperations.OrderByDescending(p => p.Priority).FirstOrDefault();
|
||||
loader = loadOperations.MaxBy(p => p.Priority);
|
||||
}
|
||||
if (loader == null)
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StardewValley.Menus;
|
||||
|
||||
namespace StardewModdingAPI.Framework.Extensions;
|
||||
@@ -13,13 +12,12 @@ internal static class ClickableMenuExtensions
|
||||
/// <summary>Get a string representation of the menu chain to the given menu (including the specified menu), in parent to child order.</summary>
|
||||
public string GetMenuChainLabel()
|
||||
{
|
||||
static IEnumerable<IClickableMenu> GetAncestors(IClickableMenu menu)
|
||||
{
|
||||
for (; menu != null; menu = menu.GetParentMenu())
|
||||
yield return menu;
|
||||
}
|
||||
Stack<string> chain = [];
|
||||
|
||||
return string.Join(" > ", GetAncestors(menu).Reverse().Select(p => p.GetType().FullName));
|
||||
for (; menu != null; menu = menu.GetParentMenu())
|
||||
chain.Push(menu.GetType().FullName!);
|
||||
|
||||
return string.Join(" > ", chain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using StardewModdingAPI.Framework.Content;
|
||||
using StardewModdingAPI.Framework.ContentManagers;
|
||||
using StardewModdingAPI.Framework.Exceptions;
|
||||
|
||||
@@ -114,12 +114,14 @@ internal class AssemblyLoader : IDisposable
|
||||
// get referenced local assemblies
|
||||
AssemblyParseResult[] assemblies;
|
||||
{
|
||||
HashSet<string> visitedAssemblyNames = new( // don't try loading assemblies that are already loaded
|
||||
from assembly in AppDomain.CurrentDomain.GetAssemblies()
|
||||
let name = assembly.GetName().Name
|
||||
where name != null
|
||||
select name
|
||||
);
|
||||
// don't try loading assemblies that are already loaded
|
||||
HashSet<string> visitedAssemblyNames = [];
|
||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
string? name = assembly.GetName().Name;
|
||||
if (name != null)
|
||||
visitedAssemblyNames.Add(name);
|
||||
}
|
||||
assemblies = this.GetReferencedLocalAssemblies(assemblyFile, visitedAssemblyNames, this.AssemblyDefinitionResolver).ToArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using StardewModdingAPI.Framework.ModLoading.Framework;
|
||||
@@ -41,13 +40,25 @@ internal class ReferenceToInvalidMemberFinder : BaseInstructionHandler
|
||||
FieldReference? fieldRef = RewriteHelper.AsFieldReference(instruction);
|
||||
if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType))
|
||||
{
|
||||
FieldDefinition? targetField = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name);
|
||||
// resolve field
|
||||
FieldDefinition? targetField = null;
|
||||
if (fieldRef.DeclaringType.Resolve() is { } declaringType)
|
||||
{
|
||||
foreach (FieldDefinition field in declaringType.Fields)
|
||||
{
|
||||
if (field.Name == fieldRef.Name)
|
||||
{
|
||||
targetField = field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wrong return type
|
||||
// detect wrong return type
|
||||
if (targetField != null && !RewriteHelper.LooksLikeSameType(fieldRef.FieldType, targetField.FieldType))
|
||||
this.MarkFlag(InstructionHandleResult.NotCompatible, $"reference to {this.GetMemberDisplayName(fieldRef)} (field returns {this.GetFriendlyTypeName(targetField.FieldType)}, not {this.GetFriendlyTypeName(fieldRef.FieldType)})");
|
||||
|
||||
// missing
|
||||
// detect missing
|
||||
else if (targetField == null || targetField.HasConstant || !RewriteHelper.HasSameNamespaceAndName(fieldRef.DeclaringType, targetField.DeclaringType))
|
||||
this.MarkFlag(InstructionHandleResult.NotCompatible, $"reference to {this.GetMemberDisplayName(fieldRef)} (no such field)");
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Collections.Generic;
|
||||
@@ -292,10 +291,18 @@ internal class RecursiveRewriter
|
||||
if (curChanged)
|
||||
{
|
||||
// get constructor
|
||||
MethodDefinition? constructor = (newAttrType ?? attribute.AttributeType)
|
||||
.Resolve()
|
||||
?.Methods
|
||||
.FirstOrDefault(method => method.IsConstructor && RewriteHelper.HasMatchingSignature(method, attribute.Constructor));
|
||||
MethodDefinition? constructor = null;
|
||||
if ((newAttrType ?? attribute.AttributeType).Resolve() is { } typeDef)
|
||||
{
|
||||
foreach (MethodDefinition method in typeDef.Methods)
|
||||
{
|
||||
if (method.IsConstructor && RewriteHelper.HasMatchingSignature(method, attribute.Constructor))
|
||||
{
|
||||
constructor = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException($"Can't rewrite attribute type '{attribute.AttributeType.FullName}' to '{newAttrType?.FullName}', no equivalent constructor found.");
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using StardewModdingAPI.Framework.ModLoading.Framework;
|
||||
@@ -67,17 +66,30 @@ internal class HeuristicFieldRewriter : BaseInstructionHandler
|
||||
/// <param name="fieldRef">The field reference.</param>
|
||||
/// <param name="declaringType">The type on which the field was defined.</param>
|
||||
/// <param name="isRead">Whether the field is being read; else it's being written to.</param>
|
||||
private bool TryRewriteToProperty(ModuleDefinition module, Instruction instruction, FieldReference fieldRef, TypeDefinition declaringType, bool isRead)
|
||||
private bool TryRewriteToProperty(ModuleDefinition module, Instruction instruction, FieldReference fieldRef, TypeDefinition? declaringType, bool isRead)
|
||||
{
|
||||
// get equivalent property
|
||||
PropertyDefinition? property = declaringType?.Properties.FirstOrDefault(p => p.Name == fieldRef.Name);
|
||||
MethodDefinition? method = isRead ? property?.GetMethod : property?.SetMethod;
|
||||
if (method == null)
|
||||
MethodDefinition? accessor = null;
|
||||
if (declaringType is not null)
|
||||
{
|
||||
PropertyDefinition? targetProperty = null;
|
||||
foreach (PropertyDefinition property in declaringType.Properties)
|
||||
{
|
||||
if (property.Name == fieldRef.Name)
|
||||
{
|
||||
targetProperty = property;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
accessor = isRead ? targetProperty?.GetMethod : targetProperty?.SetMethod;
|
||||
}
|
||||
if (accessor is null)
|
||||
return false;
|
||||
|
||||
// rewrite field to property
|
||||
instruction.OpCode = OpCodes.Call;
|
||||
instruction.Operand = module.ImportReference(method);
|
||||
instruction.Operand = module.ImportReference(accessor);
|
||||
|
||||
this.Phrases.Add($"{fieldRef.DeclaringType.Name}.{fieldRef.Name} (field => property)");
|
||||
return this.MarkRewritten();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Collections.Generic;
|
||||
using StardewModdingAPI.Framework.ModLoading.Framework;
|
||||
|
||||
namespace StardewModdingAPI.Framework.ModLoading.Rewriters;
|
||||
@@ -46,6 +46,7 @@ internal class HeuristicMethodRewriter : BaseInstructionHandler
|
||||
return false;
|
||||
|
||||
// get method definition
|
||||
int firstMissingIndex = methodRef.Parameters.Count;
|
||||
MethodDefinition? method = null;
|
||||
foreach (MethodDefinition match in type.Methods)
|
||||
{
|
||||
@@ -53,11 +54,11 @@ internal class HeuristicMethodRewriter : BaseInstructionHandler
|
||||
continue;
|
||||
|
||||
// reference matches initial parameters of definition
|
||||
if (methodRef.Parameters.Count >= match.Parameters.Count || !this.InitialParametersMatch(methodRef, match))
|
||||
if (firstMissingIndex >= match.Parameters.Count || !this.InitialParametersMatch(methodRef, match))
|
||||
continue;
|
||||
|
||||
// all remaining parameters in definition are optional
|
||||
if (!match.Parameters.Skip(methodRef.Parameters.Count).All(p => p.IsOptional))
|
||||
if (!this.RemainingParametersAreOptional(match.Parameters, startAt: firstMissingIndex))
|
||||
continue;
|
||||
|
||||
method = match;
|
||||
@@ -67,15 +68,14 @@ internal class HeuristicMethodRewriter : BaseInstructionHandler
|
||||
return false;
|
||||
|
||||
// get instructions to inject parameter values
|
||||
var loadInstructions = method.Parameters.Skip(methodRef.Parameters.Count)
|
||||
.Select(p => RewriteHelper.GetLoadValueInstruction(p.Constant))
|
||||
.ToArray();
|
||||
|
||||
// skip if invalid
|
||||
foreach (Instruction? loadInstruction in loadInstructions)
|
||||
Instruction[] loadInstructions = new Instruction[method.Parameters.Count - firstMissingIndex];
|
||||
for (int i = 0; i + firstMissingIndex < method.Parameters.Count; i++)
|
||||
{
|
||||
Instruction? loadInstruction = RewriteHelper.GetLoadValueInstruction(method.Parameters[i + firstMissingIndex].Constant);
|
||||
if (loadInstruction is null)
|
||||
return false; // SMAPI needs to load the value onto the stack before the method call, but the optional parameter type wasn't recognized
|
||||
|
||||
loadInstructions[i] = loadInstruction;
|
||||
}
|
||||
|
||||
// rewrite method reference
|
||||
@@ -114,4 +114,18 @@ internal class HeuristicMethodRewriter : BaseInstructionHandler
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Get whether all parameters starting from the given index are marked optional.</summary>
|
||||
/// <param name="parameters">The parameters to check.</param>
|
||||
/// <param name="startAt">The index of the first parameter to check.</param>
|
||||
private bool RemainingParametersAreOptional(Collection<ParameterDefinition> parameters, int startAt)
|
||||
{
|
||||
for (int i = startAt; i < parameters.Count; i++)
|
||||
{
|
||||
if (!parameters[i].IsOptional)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace StardewModdingAPI.Framework.ModLoading;
|
||||
@@ -73,12 +72,12 @@ internal class TypeReferenceComparer : IEqualityComparer<TypeReference?>
|
||||
typeNameB = this.MapPlaceholder(placeholder: typeNameB, type: typeNameA, map: tokenMap);
|
||||
|
||||
// compare inner tokens
|
||||
string[] symbolsA = this.GetTypeSymbols(typeNameA).ToArray();
|
||||
string[] symbolsB = this.GetTypeSymbols(typeNameB).ToArray();
|
||||
if (symbolsA.Length != symbolsB.Length)
|
||||
IReadOnlyList<string> symbolsA = this.GetTypeSymbols(typeNameA);
|
||||
IReadOnlyList<string> symbolsB = this.GetTypeSymbols(typeNameB);
|
||||
if (symbolsA.Count != symbolsB.Count)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < symbolsA.Length; i++)
|
||||
for (int i = 0; i < symbolsA.Count; i++)
|
||||
{
|
||||
if (!HeuristicallyEqualsImpl(symbolsA[i], symbolsB[i], tokenMap))
|
||||
return false;
|
||||
@@ -106,10 +105,11 @@ internal class TypeReferenceComparer : IEqualityComparer<TypeReference?>
|
||||
|
||||
/// <summary>Get the top-level type symbols in a type name (e.g. <code>List</code> and <code>NetRef<T></code> in <code>List<NetRef<T>></code>)</summary>
|
||||
/// <param name="typeName">The full type name.</param>
|
||||
private IEnumerable<string> GetTypeSymbols(string typeName)
|
||||
private IReadOnlyList<string> GetTypeSymbols(string typeName)
|
||||
{
|
||||
int openGenerics = 0;
|
||||
List<string> symbols = [];
|
||||
|
||||
int openGenerics = 0;
|
||||
Queue<char> queue = new(typeName);
|
||||
string symbol = "";
|
||||
while (queue.Count > 0)
|
||||
@@ -129,7 +129,7 @@ internal class TypeReferenceComparer : IEqualityComparer<TypeReference?>
|
||||
{
|
||||
// start new generic symbol
|
||||
case 0:
|
||||
yield return symbol;
|
||||
symbols.Add(symbol);
|
||||
symbol = "";
|
||||
openGenerics++;
|
||||
break;
|
||||
@@ -152,7 +152,7 @@ internal class TypeReferenceComparer : IEqualityComparer<TypeReference?>
|
||||
|
||||
// start next generic symbol
|
||||
case 1:
|
||||
yield return symbol;
|
||||
symbols.Add(symbol);
|
||||
symbol = "";
|
||||
break;
|
||||
|
||||
@@ -174,7 +174,7 @@ internal class TypeReferenceComparer : IEqualityComparer<TypeReference?>
|
||||
|
||||
// end generic symbol
|
||||
case 1:
|
||||
yield return symbol;
|
||||
symbols.Add(symbol);
|
||||
symbol = "";
|
||||
openGenerics--;
|
||||
break;
|
||||
@@ -195,6 +195,8 @@ internal class TypeReferenceComparer : IEqualityComparer<TypeReference?>
|
||||
}
|
||||
|
||||
if (symbol != "")
|
||||
yield return symbol;
|
||||
symbols.Add(symbol);
|
||||
|
||||
return symbols;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,7 +883,7 @@ internal class SCore : IDisposable
|
||||
string context = $"Context: loaded save '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}, locale set to {this.ContentCore.GetLocale()}.";
|
||||
if (Context.IsMultiplayer)
|
||||
{
|
||||
int onlineCount = Game1.getOnlineFarmers().Count();
|
||||
int onlineCount = Game1.getOnlineFarmers().Count;
|
||||
context += $" {(Context.IsMainPlayer ? "Main player" : "Farmhand")} with {onlineCount} {(onlineCount == 1 ? "player" : "players")} online.";
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using StardewModdingAPI.Enums;
|
||||
@@ -145,9 +144,13 @@ internal class SGameRunner : GameRunner
|
||||
/// <param name="playerId">The player ID to check.</param>
|
||||
public int? GetScreenId(long playerId)
|
||||
{
|
||||
return this.gameInstances
|
||||
.FirstOrDefault(p => ((SGame)p).PlayerId == playerId)
|
||||
?.instanceId;
|
||||
foreach (Game1 gameInstance in this.gameInstances)
|
||||
{
|
||||
if (((SGame)gameInstance).PlayerId == playerId)
|
||||
return gameInstance.instanceId;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers;
|
||||
|
||||
@@ -71,8 +70,16 @@ internal class ComparableListWatcher<TValue> : BaseDisposableWatcher, ICollectio
|
||||
|
||||
// detect changes
|
||||
HashSet<TValue> curValues = new HashSet<TValue>(this.CurrentValues, this.LastValues.Comparer);
|
||||
this.RemovedImpl.AddRange(from value in this.LastValues where !curValues.Contains(value) select value);
|
||||
this.AddedImpl.AddRange(from value in curValues where !this.LastValues.Contains(value) select value);
|
||||
foreach (TValue value in this.LastValues)
|
||||
{
|
||||
if (!curValues.Contains(value))
|
||||
this.RemovedImpl.Add(value);
|
||||
}
|
||||
foreach (TValue value in curValues)
|
||||
{
|
||||
if (!this.LastValues.Contains(value))
|
||||
this.AddedImpl.Add(value);
|
||||
}
|
||||
this.LastValues = curValues;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
|
||||
namespace StardewModdingAPI.Framework.StateTracking.FieldWatchers;
|
||||
|
||||
@@ -86,25 +85,31 @@ internal class ObservableCollectionWatcher<TValue> : BaseDisposableWatcher, ICol
|
||||
/// <param name="e">The event arguments.</param>
|
||||
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
// clear
|
||||
if (e.Action == NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
this.RemovedImpl.AddRange(this.PreviousValues);
|
||||
this.PreviousValues.Clear();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
TValue[]? added = e.NewItems?.Cast<TValue>().ToArray();
|
||||
TValue[]? removed = e.OldItems?.Cast<TValue>().ToArray();
|
||||
|
||||
if (removed != null)
|
||||
// removed items
|
||||
if (e.OldItems != null)
|
||||
{
|
||||
foreach (TValue value in e.OldItems)
|
||||
this.RemovedImpl.Add(value);
|
||||
this.PreviousValues.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
|
||||
}
|
||||
|
||||
// added items
|
||||
if (e.NewItems != null)
|
||||
{
|
||||
int insertAt = e.NewStartingIndex;
|
||||
foreach (TValue value in e.NewItems)
|
||||
{
|
||||
this.RemovedImpl.AddRange(removed);
|
||||
this.PreviousValues.RemoveRange(e.OldStartingIndex, removed.Length);
|
||||
}
|
||||
if (added != null)
|
||||
{
|
||||
this.AddedImpl.AddRange(added);
|
||||
this.PreviousValues.InsertRange(e.NewStartingIndex, added);
|
||||
this.AddedImpl.Add(value);
|
||||
this.PreviousValues.Insert(insertAt, value);
|
||||
insertAt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ internal class PlayerSnapshot
|
||||
/// <summary>Tracks changes to the player's skill levels.</summary>
|
||||
public IDictionary<SkillType, SnapshotDiff<int>> Skills { get; } =
|
||||
Enum
|
||||
.GetValues(typeof(SkillType))
|
||||
.Cast<SkillType>()
|
||||
.GetValues<SkillType>()
|
||||
.ToDictionary(skill => skill, _ => new SnapshotDiff<int>());
|
||||
|
||||
/// <summary>Get a list of inventory changes.</summary>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -163,7 +162,7 @@ internal class JsonSettingsWrapper
|
||||
{
|
||||
oldSet = new HashSet<string>(oldSet, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
bool changed = newValue.Count != oldSet.Count || !oldSet.All(newValue.Contains);
|
||||
bool changed = newValue.Count != oldSet.Count || !oldSet.IsSubsetOf(newValue);
|
||||
if (changed)
|
||||
{
|
||||
this.Properties[fieldName] = JToken.FromObject(newValue);
|
||||
|
||||
@@ -754,8 +754,11 @@ internal class CoreAssetPropagator
|
||||
{
|
||||
List<NPC> characters = [];
|
||||
|
||||
foreach (NPC character in this.GetLocations().SelectMany(p => p.characters))
|
||||
characters.Add(character);
|
||||
foreach (GameLocation location in this.GetLocations())
|
||||
{
|
||||
foreach (NPC character in location.characters)
|
||||
characters.Add(character);
|
||||
}
|
||||
|
||||
if (Game1.CurrentEvent?.actors != null)
|
||||
{
|
||||
@@ -823,9 +826,9 @@ internal class CoreAssetPropagator
|
||||
// get child locations
|
||||
if (buildingInteriors)
|
||||
{
|
||||
foreach (GameLocation location in locations.Select(p => p.Location).ToArray())
|
||||
foreach (LocationInfo location in locations.ToArray())
|
||||
{
|
||||
foreach (Building building in location.buildings)
|
||||
foreach (Building building in location.Location.buildings)
|
||||
{
|
||||
GameLocation indoors = building.indoors.Value;
|
||||
if (indoors is not null)
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using StardewModdingAPI.Framework;
|
||||
|
||||
@@ -152,7 +152,13 @@ public class KeybindList
|
||||
/// <summary>Get the keybind which is currently down, if any. If there are multiple keybinds down, the first one is returned.</summary>
|
||||
public Keybind? GetKeybindCurrentlyDown()
|
||||
{
|
||||
return this.Keybinds.FirstOrDefault(p => p.GetState().IsDown());
|
||||
foreach (Keybind keybind in this.Keybinds)
|
||||
{
|
||||
if (keybind.GetState().IsDown())
|
||||
return keybind;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Get a string representation of the input binding.</summary>
|
||||
@@ -161,6 +167,6 @@ public class KeybindList
|
||||
{
|
||||
return this.Keybinds.Length > 0
|
||||
? string.Join(", ", this.Keybinds.Select(p => p.ToString()))
|
||||
: SButton.None.ToString();
|
||||
: nameof(SButton.None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user