diff --git a/1.6/Assemblies/PerformanceFish.dll b/1.6/Assemblies/PerformanceFish.dll index d3dbcb5..f546adc 100644 Binary files a/1.6/Assemblies/PerformanceFish.dll and b/1.6/Assemblies/PerformanceFish.dll differ diff --git a/1.6/Assemblies/PerformanceFish.pdb b/1.6/Assemblies/PerformanceFish.pdb index c9d922a..689dc06 100644 Binary files a/1.6/Assemblies/PerformanceFish.pdb and b/1.6/Assemblies/PerformanceFish.pdb differ diff --git a/Source/PerformanceFish/Listers/Buildings.cs b/Source/PerformanceFish/Listers/Buildings.cs index 753f3cc..3ad0c09 100644 --- a/Source/PerformanceFish/Listers/Buildings.cs +++ b/Source/PerformanceFish/Listers/Buildings.cs @@ -6,6 +6,7 @@ using Mono.Cecil; using Mono.Cecil.Cil; using PerformanceFish.Prepatching; +using PerformanceFish.Utility; namespace PerformanceFish.Listers; @@ -40,7 +41,21 @@ public sealed class Buildings : ClassWithFishPrepatches => ilProcessor.ReplaceBodyWith(ReplacementBody); public static IEnumerable ReplacementBody(ListerBuildings __instance) - => (IEnumerable)__instance.Cache().ColonistBuildingsByType.GetOrAdd(typeof(T)); + => typeof(Building).IsAssignableFrom(typeof(T)) + ? (IEnumerable)__instance.Cache().ColonistBuildingsByType.GetOrAdd(typeof(T)) + : FallbackLoop(__instance); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static IEnumerable FallbackLoop(ListerBuildings __instance) + { + var allBuildingsColonist = __instance.allBuildingsColonist; + + for (var i = 0; i < allBuildingsColonist.Count; i++) + { + if (allBuildingsColonist[i] is T val) + yield return val; + } + } } #endif @@ -256,14 +271,14 @@ public sealed class Buildings : ClassWithFishPrepatches var buildingType = b.GetType(); - do + while (buildingType != null && typeof(Building).IsAssignableFrom(buildingType)) { - if (buildingType == null) + cache.ColonistBuildingsByType.GetOrAdd(buildingType).Add(b); + if (buildingType == typeof(Building)) break; - cache.ColonistBuildingsByType.GetOrAdd(buildingType).Add(b); + buildingType = buildingType.BaseType; } - while ((buildingType = buildingType.BaseType) != typeof(Building)); } else { @@ -313,14 +328,14 @@ public sealed class Buildings : ClassWithFishPrepatches var buildingType = b.GetType(); - do + while (buildingType != null && typeof(Building).IsAssignableFrom(buildingType)) { - if (buildingType == null) + cache.ColonistBuildingsByType.GetOrAdd(buildingType).Remove(b); + if (buildingType == typeof(Building)) break; - cache.ColonistBuildingsByType.GetOrAdd(buildingType).Remove(b); + buildingType = buildingType.BaseType; } - while ((buildingType = buildingType.BaseType) != typeof(Building)); } else { @@ -332,17 +347,43 @@ public sealed class Buildings : ClassWithFishPrepatches public readonly record struct Cache() { public readonly FishTable> - ColonistBuildingsByDef = [], - NonColonistBuildingsByDef = []; + ColonistBuildingsByDef = new() { ValueInitializer = static _ => InitializeBuildingsList() }, + NonColonistBuildingsByDef = new() { ValueInitializer = static _ => InitializeBuildingsList() }; - public readonly IndexedFishSet ColonistResearchBenches = []; + public readonly IndexedFishSet ColonistResearchBenches = InitializeBuildingsList(); - public readonly IndexedFishSet ColonistDeepScanners = []; + public readonly IndexedFishSet ColonistDeepScanners = InitializeBuildingsList(); public readonly FishTable ColonistBuildingsByType = new() { ValueInitializer = static type - => (IList)Activator.CreateInstance(typeof(IndexedFishSet<>).MakeGenericType(type)) + => (IList)_buildingsListInitializerDefinition.MakeGenericMethod(type).Invoke(null, null) }; + + private static readonly MethodInfo _buildingsListInitializerDefinition + = methodof(InitializeBuildingsList).GetGenericMethodDefinition(); + + public static unsafe IndexedFishSet InitializeBuildingsList() where T : Building +#pragma warning disable CS8622 + => new(static () => new(0, + keyHashCodeByRefGetter: &GetKeyByRef, + keyHashCodeGetter: &ThingHelper.GetKey, + keyEqualityByRefComparer: &EqualsByKeyByRef, + keyEqualityComparer: &EqualsByKey)); +#pragma warning restore CS8622 + + private static int GetKeyByRef(ref T thing) where T : Building => thing.GetKey(); + + private static bool EqualsByKey(T? left, T? right) where T : Building + => ReferenceEquals(left, right) + || left is not null + && right is not null + && left.GetKey() == right.GetKey(); + + private static bool EqualsByKeyByRef(ref T? left, ref T? right) where T : Building + => ReferenceEquals(left, right) + || left is not null + && right is not null + && left.GetKey() == right.GetKey(); } }