Compare commits

...

8 Commits

Author SHA1 Message Date
HRiggs e3e306879a update authours and required mods 2026-10-02 12:30:51 -04:00
HRiggs 5bc4202100 Fix 1.6 build and indexed map clearing 2026-10-02 04:00:27 -04:00
HRiggs 691d97ef2c Merge pull request '1.6 to main' (#1) from rimworld-1.6 into main
Reviewed-on: #1
2026-10-02 06:26:53 +00:00
localavaster 0aeae0d1bd match performance-fish 2026-04-18 15:18:05 -06:00
localavaster 50f4d5378b use reference identity for Thing and fix ListerBuildings type cache fallbacks, possibly fixed rgctx bug 2026-04-15 10:10:50 -06:00
localavaster 124052be77 rebuild dlls 2026-04-14 01:30:37 -06:00
localavaster fdff6b048a fix crash 2026-04-14 00:11:03 -06:00
localavaster 5a3f6c1cc8 add 1.6 support and restore 1.5 compatibility 2026-04-07 04:02:57 -06:00
18 changed files with 226 additions and 63 deletions
+2
View File
@@ -0,0 +1,2 @@
[Oo]bj/
[Bb]in/
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -2
View File
@@ -7,10 +7,11 @@
<li>1.3</li>
<li>1.4</li>
<li>1.5</li>
<li>1.6</li>
</supportedVersions>
<author>bradson</author>
<author>bradson, localavaster, Lionel Colaso, Riggs0</author>
<modIconPath IgnoreIfNoMatchingField="True">FisheryLib/ModIcon</modIconPath>
<url>https://github.com/bbradson/fishery</url>
<url>https://git.hudsonriggs.systems/HRiggs/Fishery</url>
<loadBefore>
<li>Ludeon.RimWorld</li>
<li>me.samboycoding.betterloading</li>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutDir>..\..\1.6\Assemblies\</OutDir>
<Copyright>Copyright (c) 2026 Bradson</Copyright>
<PolySharpUseEmbeddedAttributeForGeneratedTypes>false</PolySharpUseEmbeddedAttributeForGeneratedTypes>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>DEBUG;V1_5;V1_6</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineConstants>V1_5;V1_6</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net48" Version="1.0.3" PrivateAssets="all" />
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.6.4871" />
<PackageReference Include="Lib.Harmony" Version="2.3.6" ExcludeAssets="runtime" />
<PackageReference Update="Zetrith.Prepatcher" Version="1.2.0" />
<PackageReference Update="PolySharp" Version="1.16.0" />
<PackageReference Update="Krafs.Publicizer" Version="2.3.2" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2._buckets" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2._entries" />
</ItemGroup>
</Project>
+134 -52
View File
@@ -140,21 +140,54 @@ public static class CollectionExtensions
array.FillUsingEnumerable(enumerable, startIndex, count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int[]? GetBuckets<TKey, TValue>(Dictionary<TKey, TValue> dictionary)
#if V1_6
=> dictionary._buckets;
#else
=> dictionary.buckets;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Dictionary<TKey, TValue>.Entry[]? GetEntries<TKey, TValue>(Dictionary<TKey, TValue> dictionary)
#if V1_6
=> dictionary._entries;
#else
=> dictionary.entries;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetBucketEntryIndex(int[] buckets, int hashCode)
#if V1_6
=> buckets[hashCode % buckets.Length] - 1;
#else
=> buckets[hashCode % buckets.Length];
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsValidEntryIndex<T>(int index, T[] entries)
=> (uint)index < (uint)entries.Length;
public static ref TValue GetReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(key) & int.MaxValue;
// HashCode.Get can handle null, & int.MaxValue removes any sign
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -163,18 +196,23 @@ public static class CollectionExtensions
public static ref TValue GetReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(ref key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -190,18 +228,23 @@ public static class CollectionExtensions
/// <returns>ref TValue or Unsafe.NullRef{TValue}</returns>
public static ref TValue TryGetReferenceUnsafe<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -210,18 +253,23 @@ public static class CollectionExtensions
public static ref TValue TryGetReferenceUnsafe<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(ref key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -230,18 +278,23 @@ public static class CollectionExtensions
public static TValue? TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(ref key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -251,18 +304,23 @@ public static class CollectionExtensions
[return: MaybeNull]
public static TValue TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -276,16 +334,21 @@ public static class CollectionExtensions
var keyCode = HashCode.Get(key) & int.MaxValue;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -300,16 +363,21 @@ public static class CollectionExtensions
var keyCode = HashCode.Get(ref key) & int.MaxValue;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -352,16 +420,21 @@ public static class CollectionExtensions
var keyCode = HashCode.Get(key) & int.MaxValue;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -376,16 +449,21 @@ public static class CollectionExtensions
var keyCode = HashCode.Get(ref key) & int.MaxValue;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -400,12 +478,15 @@ public static class CollectionExtensions
var result = true;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
{
value = entry.value;
@@ -413,6 +494,8 @@ public static class CollectionExtensions
}
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -427,7 +510,7 @@ public static class CollectionExtensions
Guard.IsNotNull(dictionary);
Guard.IsNotNull(predicate);
var entries = dictionary.entries;
var entries = GetEntries(dictionary);
if (entries is null)
return 0;
@@ -653,13 +736,13 @@ public static class CollectionExtensions
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T UnsafeLoad<T>(this T[] array, int index) => Array.UnsafeLoad(array, index);
public static T UnsafeLoad<T>(this T[] array, int index) => array[index];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnsafeStore<T>(this T[] array, int index, T value) => Array.UnsafeStore(array, index, value);
public static void UnsafeStore<T>(this T[] array, int index, T value) => array[index] = value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TTo UnsafeCast<TFrom, TTo>(this TFrom obj) => Array.UnsafeMov<TFrom, TTo>(obj);
public static TTo UnsafeCast<TFrom, TTo>(this TFrom obj) => Unsafe.As<TFrom, TTo>(ref obj);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddRangeFast<T>(this List<T> list, T[] range)
@@ -687,8 +770,7 @@ public static class CollectionExtensions
return;
list.EnsureCapacity(list._size + value.Length);
UnsafeBlockCopy(ref value.m_firstChar,
ref list._items[list._size], value.Length);
value.CopyTo(0, list._items, list._size, value.Length);
list._size += value.Length;
list._version++;
@@ -764,8 +846,8 @@ public static class CollectionExtensions
public static unsafe uint GetSizeEstimate<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
=> dictionary.GetType().ComputeManagedObjectSizeEstimate()
+ ((uint)sizeof(Dictionary<TKey, TValue>.Entry) * (uint)(dictionary.entries?.Length ?? 0))
+ (sizeof(int) * (uint)(dictionary.buckets?.Length ?? 0));
+ ((uint)sizeof(Dictionary<TKey, TValue>.Entry) * (uint)(GetEntries(dictionary)?.Length ?? 0))
+ (sizeof(int) * (uint)(GetBuckets(dictionary)?.Length ?? 0));
internal static unsafe uint ComputeManagedObjectSizeEstimate(this Type type)
{
@@ -140,6 +140,7 @@ public class IndexedFishMap<TX, TY> : IList<(TX X, TY Y)>, IList, IReadOnlyList<
public void Clear()
{
_xIndices.Clear();
_yIndices.Clear();
_items.Clear();
}
+1 -1
View File
@@ -27,7 +27,7 @@
<ItemGroup>
<PackageReference Include="Zetrith.Prepatcher" Version="1.*" />
<PackageReference Include="PolySharp" Version="1.*" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" />
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
+6
View File
@@ -5,6 +5,8 @@ VisualStudioVersion = 17.1.32120.378
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.5", "1.5.csproj", "{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.6", "1.6.csproj", "{87022FD2-7E2D-45E4-BFE2-8E445B46DF08}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.4", "1.4.csproj", "{BE85EB31-71CD-443C-8C93-20E2B7F3F549}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.3", "1.3.csproj", "{FC5FB1F4-626D-4895-AE5E-922DFE1AE120}"
@@ -21,6 +23,10 @@ Global
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Release|Any CPU.Build.0 = Release|Any CPU
{87022FD2-7E2D-45E4-BFE2-8E445B46DF08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87022FD2-7E2D-45E4-BFE2-8E445B46DF08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87022FD2-7E2D-45E4-BFE2-8E445B46DF08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87022FD2-7E2D-45E4-BFE2-8E445B46DF08}.Release|Any CPU.Build.0 = Release|Any CPU
{BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -7,7 +7,9 @@ using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security;
using Verse;
namespace FisheryLib.FunctionPointers;
@@ -64,12 +66,18 @@ public abstract class MethodFactory(MethodInfo[] methods, string namePrefix, boo
=> type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
.Where(predicate).ToArray();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static bool UseReferenceIdentity(Type type)
=> !type.IsValueType && typeof(Thing).IsAssignableFrom(type);
public new class Equals(Type type, string dynamicMethodNamePrefix, bool byRef)
: MethodFactory(GetMethodInfoArraySafely(type), dynamicMethodNamePrefix, byRef)
{
protected override MethodInfo GetOrMakeMethodForType(Type type)
=> GetSpecializedMethod(type)
?? (type.IsAssignableTo(typeof(IEquatable<>), type)
?? (UseReferenceIdentity(type)
? GetGeneric(nameof(FunctionPointers.Equals.Methods.Reference), type)
: type.IsAssignableTo(typeof(IEquatable<>), type)
? GetGeneric(type.IsValueType
? nameof(FunctionPointers.Equals.Methods.EquatableValueType)
: nameof(FunctionPointers.Equals.Methods.EquatableReferenceType), type)
@@ -235,7 +243,9 @@ public abstract class MethodFactory(MethodInfo[] methods, string namePrefix, boo
{
protected override MethodInfo GetOrMakeMethodForType(Type type)
=> GetSpecializedMethod(type)
?? (type.IsNullable(out var genericArgument)
?? (UseReferenceIdentity(type)
? GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.Reference), type)
: type.IsNullable(out var genericArgument)
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
? GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.Nullable), genericArgument)
: type.IsEnum
+7 -1
View File
@@ -49,7 +49,13 @@ public abstract class SingletonFactory<T_base> where T_base : notnull
}
private static T_base GetInternal(Type type)
=> _instances.GetOrAdd(type, static type => (T_base)Activator.CreateInstance(type, true));
{
if (_instances.TryGetValue(type, out var existing))
return existing;
var instance = _instances.GetOrAdd(type, static type => (T_base)Activator.CreateInstance(type, true));
return instance;
}
private static readonly ConcurrentDictionary<Type, T_base> _instances = new();
}
+25
View File
@@ -0,0 +1,25 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace FisheryLib;
internal static class SpanCompatExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref T DangerousGetPinnableReference<T>(this Span<T> span)
{
if (span.Length == 0)
return ref Unsafe.AsRef<T>((void*)1);
return ref span[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref T DangerousGetPinnableReference<T>(this ReadOnlySpan<T> span)
{
if (span.Length == 0)
return ref Unsafe.AsRef<T>((void*)1);
return ref Unsafe.AsRef(in span[0]);
}
}