add 1.6 support and fix prepatch regressions
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
-1
@@ -5,6 +5,7 @@
|
|||||||
<supportedVersions>
|
<supportedVersions>
|
||||||
<li>1.4</li>
|
<li>1.4</li>
|
||||||
<li>1.5</li>
|
<li>1.5</li>
|
||||||
|
<li>1.6</li>
|
||||||
</supportedVersions>
|
</supportedVersions>
|
||||||
<author>bradson</author>
|
<author>bradson</author>
|
||||||
<modIconPath IgnoreIfNoMatchingField="True">PerformanceFish/ModIcon</modIconPath>
|
<modIconPath IgnoreIfNoMatchingField="True">PerformanceFish/ModIcon</modIconPath>
|
||||||
@@ -56,4 +57,4 @@
|
|||||||
<li>vanillaexpanded.storytellers.rick</li> <!-- sussy -->
|
<li>vanillaexpanded.storytellers.rick</li> <!-- sussy -->
|
||||||
</incompatibleWith>
|
</incompatibleWith>
|
||||||
<description>download more tps</description>
|
<description>download more tps</description>
|
||||||
</ModMetaData>
|
</ModMetaData>
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutDir>..\..\1.5\Assemblies\</OutDir>
|
<OutDir>..\..\1.5\Assemblies\</OutDir>
|
||||||
<Copyright>Copyright (c) 2024 Bradson</Copyright>
|
<Copyright>Copyright (c) 2024 Bradson</Copyright>
|
||||||
|
<FisheryRepoRoot Condition="Exists('$(MSBuildThisFileDirectory)..\..\..\Fishery\1.5\Assemblies\1Fishery.dll')">$(MSBuildThisFileDirectory)..\..\..\Fishery</FisheryRepoRoot>
|
||||||
|
<FisheryRepoRoot Condition="'$(FisheryRepoRoot)' == '' and Exists('$(MSBuildThisFileDirectory)..\..\..\Fishery-clean\1.5\Assemblies\1Fishery.dll')">$(MSBuildThisFileDirectory)..\..\..\Fishery-clean</FisheryRepoRoot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DefineConstants>DEBUG;V1_5</DefineConstants>
|
<DefineConstants>DEBUG;V1_5</DefineConstants>
|
||||||
@@ -14,7 +16,7 @@
|
|||||||
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.5.*" />
|
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.5.*" />
|
||||||
<PackageReference Include="Lib.Harmony" Version="2.3.3" ExcludeAssets="runtime" />
|
<PackageReference Include="Lib.Harmony" Version="2.3.3" ExcludeAssets="runtime" />
|
||||||
<Reference Include="1Fishery">
|
<Reference Include="1Fishery">
|
||||||
<HintPath>..\..\..\Fishery\1.5\Assemblies\1Fishery.dll</HintPath>
|
<HintPath>$(FisheryRepoRoot)\1.5\Assemblies\1Fishery.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutDir>..\..\1.6\Assemblies\</OutDir>
|
||||||
|
<Copyright>Copyright (c) 2026 Bradson</Copyright>
|
||||||
|
<FisheryRepoRoot Condition="Exists('$(MSBuildThisFileDirectory)..\..\..\Fishery\1.6\Assemblies\1Fishery.dll')">$(MSBuildThisFileDirectory)..\..\..\Fishery</FisheryRepoRoot>
|
||||||
|
<FisheryRepoRoot Condition="'$(FisheryRepoRoot)' == '' and Exists('$(MSBuildThisFileDirectory)..\..\..\Fishery-clean\1.6\Assemblies\1Fishery.dll')">$(MSBuildThisFileDirectory)..\..\..\Fishery-clean</FisheryRepoRoot>
|
||||||
|
</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="Krafs.Rimworld.Ref" Version="1.6.*" />
|
||||||
|
<PackageReference Include="Lib.Harmony" Version="2.3.3" ExcludeAssets="runtime" />
|
||||||
|
<Reference Include="1Fishery">
|
||||||
|
<HintPath>$(FisheryRepoRoot)\1.6\Assemblies\1Fishery.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -7,13 +7,15 @@ namespace PerformanceFish.Cache;
|
|||||||
|
|
||||||
public sealed class BitCellGrid
|
public sealed class BitCellGrid
|
||||||
{
|
{
|
||||||
|
private readonly Map _map;
|
||||||
private long[] _innerArray;
|
private long[] _innerArray;
|
||||||
private CellIndices _cellIndices;
|
private CellIndices _cellIndices;
|
||||||
|
|
||||||
public BitCellGrid(Map map)
|
public BitCellGrid(Map map)
|
||||||
{
|
{
|
||||||
_innerArray = null!;
|
_map = map;
|
||||||
_cellIndices = null!;
|
_innerArray = Array.Empty<long>();
|
||||||
|
_cellIndices = default;
|
||||||
map.InvokeWhenCellIndicesReady(Initialize);
|
map.InvokeWhenCellIndicesReady(Initialize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,12 +25,26 @@ public sealed class BitCellGrid
|
|||||||
_innerArray = new long[(Math.Max(0, _cellIndices.NumGridCells - 1) >> 6) + 1];
|
_innerArray = new long[(Math.Max(0, _cellIndices.NumGridCells - 1) >> 6) + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void EnsureInitialized()
|
||||||
|
{
|
||||||
|
if (_cellIndices.NumGridCells != _map.cellIndices.NumGridCells)
|
||||||
|
Initialize(_map);
|
||||||
|
}
|
||||||
|
|
||||||
public bool this[int index]
|
public bool this[int index]
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => (_innerArray[index >> 6] & (1L << (index & 63))) != 0L;
|
get
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
ValidateIndex(index);
|
||||||
|
return (_innerArray[index >> 6] & (1L << (index & 63))) != 0L;
|
||||||
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
ValidateIndex(index);
|
||||||
ref var bucket = ref _innerArray[index >> 6];
|
ref var bucket = ref _innerArray[index >> 6];
|
||||||
bucket ^= (-(long)value.AsInt() ^ bucket) & (1L << (index & 63));
|
bucket ^= (-(long)value.AsInt() ^ bucket) & (1L << (index & 63));
|
||||||
}
|
}
|
||||||
@@ -37,7 +53,24 @@ public sealed class BitCellGrid
|
|||||||
public bool this[in IntVec3 c]
|
public bool this[in IntVec3 c]
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => this[c.CellToIndex(_cellIndices)];
|
get
|
||||||
set => this[c.CellToIndex(_cellIndices)] = value;
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
return this[c.CellToIndex(_cellIndices)];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
this[c.CellToIndex(_cellIndices)] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private void ValidateIndex(int index)
|
||||||
|
{
|
||||||
|
if ((uint)index >= (uint)_cellIndices.NumGridCells)
|
||||||
|
{
|
||||||
|
throw new IndexOutOfRangeException($"BitCellGrid index {index} out of bounds for {_cellIndices.NumGridCells} cells on map size {_map.Size}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,38 +9,60 @@ namespace PerformanceFish.Cache;
|
|||||||
|
|
||||||
public sealed class CellGrid<T>
|
public sealed class CellGrid<T>
|
||||||
{
|
{
|
||||||
|
private readonly Map _map;
|
||||||
private T[] _innerArray;
|
private T[] _innerArray;
|
||||||
private CellIndices _cellIndices;
|
private CellIndices _cellIndices;
|
||||||
|
|
||||||
public CellGrid(Map map)
|
public CellGrid(Map map)
|
||||||
{
|
{
|
||||||
_innerArray = null!;
|
_map = map;
|
||||||
_cellIndices = null!;
|
_innerArray = Array.Empty<T>();
|
||||||
|
_cellIndices = default;
|
||||||
map.InvokeWhenCellIndicesReady(Initialize);
|
map.InvokeWhenCellIndicesReady(Initialize);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MemberNotNull(nameof(_innerArray), nameof(_cellIndices))]
|
[MemberNotNull(nameof(_innerArray))]
|
||||||
private void Initialize(Map map)
|
private void Initialize(Map map)
|
||||||
{
|
{
|
||||||
_cellIndices = map.cellIndices;
|
_cellIndices = map.cellIndices;
|
||||||
_innerArray = new T[_cellIndices.NumGridCells];
|
_innerArray = new T[_cellIndices.NumGridCells];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void EnsureInitialized()
|
||||||
|
{
|
||||||
|
if (_cellIndices.NumGridCells != _map.cellIndices.NumGridCells)
|
||||||
|
Initialize(_map);
|
||||||
|
}
|
||||||
|
|
||||||
public ref T this[int index]
|
public ref T this[int index]
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => ref _innerArray[index];
|
get
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
return ref _innerArray[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref T this[in IntVec3 c]
|
public ref T this[in IntVec3 c]
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => ref _innerArray[c.CellToIndex(_cellIndices)];
|
get
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
var index = c.CellToIndex(_cellIndices);
|
||||||
|
return ref _innerArray[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref T this[CellIndex cellIndex]
|
public ref T this[CellIndex cellIndex]
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => ref _innerArray[cellIndex.Value];
|
get
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
return ref _innerArray[cellIndex.Value];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,20 +10,25 @@ public record struct CellIndex(int Value)
|
|||||||
public int Value = Value;
|
public int Value = Value;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public CellIndex(IntVec3 cell, Map map) : this(cell, map.cellIndices.mapSizeX) {}
|
public CellIndex(IntVec3 cell, Map map) : this(cell, map.Size.x) {}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public CellIndex(IntVec3 cell, int mapSizeX) : this(cell.CellToIndex(mapSizeX)) {}
|
public CellIndex(IntVec3 cell, int mapSizeX) : this(cell.CellToIndex(mapSizeX)) {}
|
||||||
|
|
||||||
public IntVec3 ToCell(Map map) => ToCell(map.cellIndices.mapSizeX);
|
public IntVec3 ToCell(Map map) => ToCell(map.Size.x);
|
||||||
|
|
||||||
public IntVec3 ToCell(int mapSizeX) => CellIndicesUtility.IndexToCell(Value, mapSizeX);
|
public IntVec3 ToCell(int mapSizeX) => CellIndicesUtility.IndexToCell(Value, mapSizeX);
|
||||||
|
|
||||||
public List<Thing> GetThingList(Map map) => map.thingGrid.thingGrid[Value];
|
public List<Thing> GetThingList(Map map) => map.thingGrid.thingGrid[Value];
|
||||||
|
|
||||||
public bool IsFogged(Map map) => map.fogGrid.fogGrid[Value];
|
public bool IsFogged(Map map)
|
||||||
|
#if V1_6
|
||||||
|
=> map.fogGrid.fogGrid.IsSet(Value);
|
||||||
|
#else
|
||||||
|
=> map.fogGrid.fogGrid[Value];
|
||||||
|
#endif
|
||||||
|
|
||||||
public float GetSnowDepth(Map map) => map.snowGrid.depthGrid[Value];
|
public float GetSnowDepth(Map map) => map.snowGrid.depthGrid[Value];
|
||||||
|
|
||||||
public Building? GetEdifice(Map map) => map.edificeGrid[Value];
|
public Building? GetEdifice(Map map) => map.edificeGrid[Value];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,25 @@ public sealed class ThingDefPatches : ClassWithFishPrepatches
|
|||||||
public sealed class GetHashCodePatch : FishClassPrepatch
|
public sealed class GetHashCodePatch : FishClassPrepatch
|
||||||
{
|
{
|
||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
|
#if V1_6
|
||||||
|
= "Disabled on 1.6 because Verse.Def now already caches defNameHash and overrides GetHashCode itself.";
|
||||||
|
#else
|
||||||
= "Def.GetHashCode is overridden to return defName.GetHashCode, which is rather slow as that's a string. "
|
= "Def.GetHashCode is overridden to return defName.GetHashCode, which is rather slow as that's a string. "
|
||||||
+ "This patch caches the hashcode in a field added through prepatcher and simply returns that instead.";
|
+ "This patch caches the hashcode in a field added through prepatcher and simply returns that instead.";
|
||||||
|
#endif
|
||||||
|
|
||||||
public override Type Type { get; } = typeof(BuildableDef);
|
public override Type Type { get; } =
|
||||||
|
#if V1_6
|
||||||
|
typeof(Def);
|
||||||
|
#else
|
||||||
|
typeof(BuildableDef);
|
||||||
|
#endif
|
||||||
|
|
||||||
public override void FreePatch(TypeDefinition typeDefinition)
|
public override void FreePatch(TypeDefinition typeDefinition)
|
||||||
{
|
{
|
||||||
|
#if V1_6
|
||||||
|
return;
|
||||||
|
#else
|
||||||
var module = typeDefinition.Module;
|
var module = typeDefinition.Module;
|
||||||
|
|
||||||
var fishHashField = new FieldDefinition(FISH_HASH_FIELD_NAME, FieldAttributes.Private,
|
var fishHashField = new FieldDefinition(FISH_HASH_FIELD_NAME, FieldAttributes.Private,
|
||||||
@@ -74,6 +86,7 @@ public sealed class ThingDefPatches : ClassWithFishPrepatches
|
|||||||
getHashCodeBody.MaxStackSize = 2;
|
getHashCodeBody.MaxStackSize = 2;
|
||||||
getHashCodeBody.Method.ImplAttributes |= MethodImplAttributes.AggressiveInlining;
|
getHashCodeBody.Method.ImplAttributes |= MethodImplAttributes.AggressiveInlining;
|
||||||
getHashCodeBody.OptimizeMacros();
|
getHashCodeBody.OptimizeMacros();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -295,4 +308,4 @@ public sealed class ThingDefPatches : ClassWithFishPrepatches
|
|||||||
Flammability,
|
Flammability,
|
||||||
MaxHitPoints
|
MaxHitPoints
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,13 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Zetrith.Prepatcher" Version="1.*" ExcludeAssets="runtime" />
|
<PackageReference Include="Zetrith.Prepatcher" Version="1.*" ExcludeAssets="runtime" />
|
||||||
<PackageReference Include="PolySharp" Version="1.*" />
|
<PackageReference Include="PolySharp" Version="1.*" />
|
||||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.*" ExcludeAssets="runtime" />
|
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" ExcludeAssets="runtime" />
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2023.*" Aliases="nuget" ExcludeAssets="runtime" />
|
<PackageReference Include="JetBrains.Annotations" Version="2023.*" Aliases="nuget" ExcludeAssets="runtime" />
|
||||||
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
|
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
|
||||||
|
|
||||||
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
|
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
|
||||||
<Publicize Include="Assembly-CSharp:RimWorld.JobGiver_Haul.TryGiveJob" />
|
<Publicize Include="Assembly-CSharp:RimWorld.JobGiver_Haul.TryGiveJob" />
|
||||||
|
<Publicize Include="Assembly-CSharp:RimWorld.CompRottable.TickInterval" />
|
||||||
<!-- DoNotPublicize Include="0Harmony:Mono.Collections.Generic.Collection`1" / -->
|
<!-- DoNotPublicize Include="0Harmony:Mono.Collections.Generic.Collection`1" / -->
|
||||||
<DoNotPublicize Include="0Harmony:System.Memory`1" />
|
<DoNotPublicize Include="0Harmony:System.Memory`1" />
|
||||||
<DoNotPublicize Include="0Harmony:System.ReadOnlyMemory`1" />
|
<DoNotPublicize Include="0Harmony:System.ReadOnlyMemory`1" />
|
||||||
@@ -93,4 +94,4 @@
|
|||||||
<PackagePath />
|
<PackagePath />
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
= "Required by the gas grid optimization";
|
= "Required by the gas grid optimization";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= AccessTools.DeclaredMethod(typeof(GasGrid), nameof(GasGrid.SetDirect));
|
= AccessTools.DeclaredMethod(typeof(GasGrid), nameof(GasGrid.SetDirect),
|
||||||
|
[typeof(int), typeof(byte), typeof(byte), typeof(byte), typeof(byte)])!;
|
||||||
|
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||||
@@ -415,7 +416,7 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int GetMapSizeX(Map map) => map.cellIndices.mapSizeX;
|
public static int GetMapSizeX(Map map) => map.Size.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ExposeDataPatch : FishPrepatch
|
public sealed class ExposeDataPatch : FishPrepatch
|
||||||
@@ -1075,7 +1076,7 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private IntVec3 IndexToCell(int index)
|
private IntVec3 IndexToCell(int index)
|
||||||
=> CellIndicesUtility.IndexToCell(index, Map.cellIndices.mapSizeX);
|
=> CellIndicesUtility.IndexToCell(index, Map.Size.x);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For modders. Performance Fish doesn't automatically scribe added custom defs.
|
/// For modders. Performance Fish doesn't automatically scribe added custom defs.
|
||||||
@@ -1136,4 +1137,4 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
}
|
}
|
||||||
// ReSharper restore UnusedMember.Local
|
// ReSharper restore UnusedMember.Local
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,14 +43,15 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
|||||||
|
|
||||||
public static List<Type> ReplacementBody<TAttr>() where TAttr : Attribute
|
public static List<Type> ReplacementBody<TAttr>() where TAttr : Attribute
|
||||||
{
|
{
|
||||||
|
var attributeType = typeof(TAttr);
|
||||||
lock (Lock)
|
lock (Lock)
|
||||||
{
|
{
|
||||||
var typesWithAttribute = GenTypes.cachedTypesWithAttribute.TryGetValue(typeof(TAttr));
|
var typesWithAttribute = GenTypes.cachedTypesWithAttribute.TryGetValue(attributeType);
|
||||||
|
|
||||||
if (typesWithAttribute is null)
|
if (typesWithAttribute is null)
|
||||||
{
|
{
|
||||||
GenTypes.cachedTypesWithAttribute.Add(typeof(TAttr),
|
GenTypes.cachedTypesWithAttribute.Add(attributeType,
|
||||||
typesWithAttribute = GenTypes.AllTypes.AsParallel()
|
typesWithAttribute = GenTypes.AllTypes
|
||||||
.Where(Predicate<TAttr>()).ToList());
|
.Where(Predicate<TAttr>()).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,23 +83,14 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
|||||||
|
|
||||||
if (subclasses is null)
|
if (subclasses is null)
|
||||||
{
|
{
|
||||||
((PredicateClass)Predicate.Target).BaseType = baseType;
|
|
||||||
GenTypes.cachedSubclasses.Add(baseType,
|
GenTypes.cachedSubclasses.Add(baseType,
|
||||||
subclasses = GenTypes.AllTypes.AsParallel()
|
subclasses = GenTypes.AllTypes
|
||||||
.Where(Predicate).ToList());
|
.Where(type => type.IsSubclassOf(baseType)).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
return subclasses;
|
return subclasses;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Func<Type, bool> Predicate = new PredicateClass().Invoke;
|
|
||||||
|
|
||||||
public sealed class PredicateClass
|
|
||||||
{
|
|
||||||
public Type? BaseType;
|
|
||||||
public bool Invoke(Type type) => type.IsSubclassOf(BaseType!);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class AllSubclassesNonAbstract : FishPrepatch
|
public sealed class AllSubclassesNonAbstract : FishPrepatch
|
||||||
@@ -123,7 +115,7 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
|||||||
if (subclassesNonAbstract is null)
|
if (subclassesNonAbstract is null)
|
||||||
{
|
{
|
||||||
GenTypes.cachedSubclassesNonAbstract.Add(baseType,
|
GenTypes.cachedSubclassesNonAbstract.Add(baseType,
|
||||||
subclassesNonAbstract = baseType.AllSubclasses().AsParallel()
|
subclassesNonAbstract = baseType.AllSubclasses()
|
||||||
.Where(Predicate).ToList());
|
.Where(Predicate).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,4 +125,4 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
|||||||
|
|
||||||
public static Func<Type, bool> Predicate = static subClass => !subClass.IsAbstract;
|
public static Func<Type, bool> Predicate = static subClass => !subClass.IsAbstract;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ public sealed class GeneTrackerOptimization : ClassWithFishPrepatches
|
|||||||
{
|
{
|
||||||
public sealed class GeneTrackerTickPatch : FishPrepatch
|
public sealed class GeneTrackerTickPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
|
private static readonly FishTable<Type, bool> _skippableTypes;
|
||||||
|
|
||||||
|
static GeneTrackerTickPatch()
|
||||||
|
{
|
||||||
|
_skippableTypes = new()
|
||||||
|
{
|
||||||
|
ValueInitializer = static type => ComputeIsSkippableType(type)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
= "Every pawn has a gene tracker, which is responsible for ticking each of their genes. Normally it ticks "
|
= "Every pawn has a gene tracker, which is responsible for ticking each of their genes. Normally it ticks "
|
||||||
+ "all of them equally, including those don't change or affect anything through ticking, like skin colors "
|
+ "all of them equally, including those don't change or affect anything through ticking, like skin colors "
|
||||||
@@ -74,7 +84,7 @@ public sealed class GeneTrackerOptimization : ClassWithFishPrepatches
|
|||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var gene = genes[i];
|
var gene = genes[i];
|
||||||
if (SkippableTypes.Contains(gene.GetType())
|
if (IsSkippableType(gene.GetType())
|
||||||
&& (gene.def.mentalBreakMtbDays <= 0f || gene.def.mentalBreakDef == null))
|
&& (gene.def.mentalBreakMtbDays <= 0f || gene.def.mentalBreakDef == null))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -83,8 +93,15 @@ public sealed class GeneTrackerOptimization : ClassWithFishPrepatches
|
|||||||
genesToTick.Add(gene);
|
genesToTick.Add(gene);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HashSet<Type> SkippableTypes
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
= typeof(Gene).SubclassesWithNoMethodOverrideAndSelf(nameof(Gene.Tick)).ToHashSet();
|
private static bool IsSkippableType(Type type) => _skippableTypes.GetOrAdd(type);
|
||||||
|
|
||||||
|
private static bool ComputeIsSkippableType(Type type)
|
||||||
|
=> type == typeof(Gene)
|
||||||
|
|| type.GetMethod(nameof(Gene.Tick), global::System.Reflection.BindingFlags.Instance
|
||||||
|
| global::System.Reflection.BindingFlags.Public
|
||||||
|
| global::System.Reflection.BindingFlags.NonPublic
|
||||||
|
| global::System.Reflection.BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null) == null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
global using HashCode = FisheryLib.HashCode;
|
||||||
@@ -237,6 +237,11 @@ public sealed class Buildings : ClassWithFishPrepatches
|
|||||||
if (def.building is { isNaturalRock: true })
|
if (def.building is { isNaturalRock: true })
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
UpdateCacheOnAdd(__instance, b, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateCacheOnAdd(ListerBuildings __instance, Building b, ThingDef def)
|
||||||
|
{
|
||||||
var cache = __instance.Cache();
|
var cache = __instance.Cache();
|
||||||
|
|
||||||
if (b.Faction == Faction.OfPlayer)
|
if (b.Faction == Faction.OfPlayer)
|
||||||
@@ -290,6 +295,11 @@ public sealed class Buildings : ClassWithFishPrepatches
|
|||||||
if (def.building is { isNaturalRock: true })
|
if (def.building is { isNaturalRock: true })
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
UpdateCacheOnRemove(__instance, b, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateCacheOnRemove(ListerBuildings __instance, Building b, ThingDef def)
|
||||||
|
{
|
||||||
var cache = __instance.Cache();
|
var cache = __instance.Cache();
|
||||||
|
|
||||||
if (b.Faction == Faction.OfPlayer)
|
if (b.Faction == Faction.OfPlayer)
|
||||||
@@ -334,4 +344,4 @@ public sealed class Buildings : ClassWithFishPrepatches
|
|||||||
=> (IList)Activator.CreateInstance(typeof(IndexedFishSet<>).MakeGenericType(type))
|
=> (IList)Activator.CreateInstance(typeof(IndexedFishSet<>).MakeGenericType(type))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,22 +183,22 @@ public sealed class Haulables : ClassWithFishPrepatches
|
|||||||
public readonly HashSet<Thing> ThingsQueuedToRemove = [];
|
public readonly HashSet<Thing> ThingsQueuedToRemove = [];
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void CheckUpdate(List<Thing> haulables)
|
public void CheckUpdate(ICollection<Thing> haulables)
|
||||||
{
|
{
|
||||||
if (IsDirty(haulables))
|
if (IsDirty(haulables))
|
||||||
Update(haulables);
|
Update(haulables);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool IsDirty(List<Thing> haulables) => haulables.Count != ContainedThings.Count;
|
public bool IsDirty(ICollection<Thing> haulables) => haulables.Count != ContainedThings.Count;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public void Update(List<Thing> haulables)
|
public void Update(ICollection<Thing> haulables)
|
||||||
{
|
{
|
||||||
ContainedThings.Clear();
|
ContainedThings.Clear();
|
||||||
|
|
||||||
for (var i = haulables.Count; i-- > 0;)
|
foreach (var haulable in haulables)
|
||||||
ContainedThings.Add(haulables[i].GetKey());
|
ContainedThings.Add(haulable.GetKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,4 +232,4 @@ public static class ListerHaulablesExtensions
|
|||||||
if (cache.ContainedThings.Remove(t.GetKey()))
|
if (cache.ContainedThings.Remove(t.GetKey()))
|
||||||
haulables.Remove(t);
|
haulables.Remove(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -568,9 +568,25 @@ public sealed class Things : ClassWithFishPatches
|
|||||||
|
|
||||||
public static unsafe IndexedFishSet<T> InitializeThingsList<T>() where T : Thing
|
public static unsafe IndexedFishSet<T> InitializeThingsList<T>() where T : Thing
|
||||||
#pragma warning disable CS8622
|
#pragma warning disable CS8622
|
||||||
=> new(static () => new(0, keyHashCodeByRefGetter: &GetKeyByRef, keyHashCodeGetter: &ThingHelper.GetKey));
|
=> new(static () => new(0,
|
||||||
|
keyHashCodeByRefGetter: &GetKeyByRef,
|
||||||
|
keyHashCodeGetter: &ThingHelper.GetKey,
|
||||||
|
keyEqualityByRefComparer: &EqualsByKeyByRef,
|
||||||
|
keyEqualityComparer: &EqualsByKey));
|
||||||
#pragma warning restore CS8622
|
#pragma warning restore CS8622
|
||||||
|
|
||||||
private static int GetKeyByRef<T>(ref T thing) where T : Thing => thing.GetKey();
|
private static int GetKeyByRef<T>(ref T thing) where T : Thing => thing.GetKey();
|
||||||
|
|
||||||
|
private static bool EqualsByKey<T>(T? left, T? right) where T : Thing
|
||||||
|
=> ReferenceEquals(left, right)
|
||||||
|
|| left is not null
|
||||||
|
&& right is not null
|
||||||
|
&& left.GetKey() == right.GetKey();
|
||||||
|
|
||||||
|
private static bool EqualsByKeyByRef<T>(ref T? left, ref T? right) where T : Thing
|
||||||
|
=> ReferenceEquals(left, right)
|
||||||
|
|| left is not null
|
||||||
|
&& right is not null
|
||||||
|
&& left.GetKey() == right.GetKey();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,17 +114,30 @@ public sealed class MiscOptimizations : ClassWithFishPatches
|
|||||||
= "Throttles rotting on misconfigured defs to only recalculate every 256 ticks, instead of constantly.";
|
= "Throttles rotting on misconfigured defs to only recalculate every 256 ticks, instead of constantly.";
|
||||||
|
|
||||||
public override MethodBase TargetMethodInfo { get; }
|
public override MethodBase TargetMethodInfo { get; }
|
||||||
|
#if V1_6
|
||||||
|
= AccessTools.DeclaredMethod(typeof(RimWorld.CompRottable), nameof(RimWorld.CompRottable.CompTickInterval),
|
||||||
|
[typeof(int)])!;
|
||||||
|
#else
|
||||||
= AccessTools.Method(typeof(RimWorld.CompRottable), nameof(RimWorld.CompRottable.CompTick));
|
= AccessTools.Method(typeof(RimWorld.CompRottable), nameof(RimWorld.CompRottable.CompTick));
|
||||||
|
#endif
|
||||||
|
|
||||||
public static CodeInstructions Transpiler(CodeInstructions codes, ILGenerator generator)
|
public static CodeInstructions Transpiler(CodeInstructions codes, ILGenerator generator)
|
||||||
=> Reflection.GetCodeInstructions(Replacement, generator);
|
=> Reflection.GetCodeInstructions(Replacement, generator);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
#if V1_6
|
||||||
|
public static void Replacement(RimWorld.CompRottable instance, int delta)
|
||||||
|
{
|
||||||
|
if ((TickHelper.TicksGame & 255) == (instance.parent.thingIDNumber & 255))
|
||||||
|
instance.TickInterval(256);
|
||||||
|
}
|
||||||
|
#else
|
||||||
public static void Replacement(RimWorld.CompRottable instance)
|
public static void Replacement(RimWorld.CompRottable instance)
|
||||||
{
|
{
|
||||||
if ((TickHelper.TicksGame & 255) == (instance.parent.thingIDNumber & 255))
|
if ((TickHelper.TicksGame & 255) == (instance.parent.thingIDNumber & 255))
|
||||||
instance.Tick(256);
|
instance.Tick(256);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class RitualObligationTrigger_Date : FirstPriorityFishPatch
|
public sealed class RitualObligationTrigger_Date : FirstPriorityFishPatch
|
||||||
@@ -344,4 +357,4 @@ public sealed class MiscOptimizations : ClassWithFishPatches
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ public sealed class ModsConfigPatches : ClassWithFishPrepatches
|
|||||||
= "Fixes a MayRequire bug causing it to normally not recognize steam versions of mods if another local "
|
= "Fixes a MayRequire bug causing it to normally not recognize steam versions of mods if another local "
|
||||||
+ "copy exists.";
|
+ "copy exists.";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; } = methodof(ModsConfig.AreAllActive);
|
public override MethodBase TargetMethodBase { get; }
|
||||||
|
= AccessTools.DeclaredMethod(typeof(ModsConfig), nameof(ModsConfig.AreAllActive), [typeof(string)])!;
|
||||||
|
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> IsActiveFix(ilProcessor, module);
|
=> IsActiveFix(ilProcessor, module);
|
||||||
@@ -29,7 +30,9 @@ public sealed class ModsConfigPatches : ClassWithFishPrepatches
|
|||||||
= "Fixes a MayRequireAnyOf bug causing it to normally not recognize steam versions of mods if another "
|
= "Fixes a MayRequireAnyOf bug causing it to normally not recognize steam versions of mods if another "
|
||||||
+ "local copy exists, as well as its inability to strip whitespace.";
|
+ "local copy exists, as well as its inability to strip whitespace.";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; } = methodof(ModsConfig.IsAnyActiveOrEmpty);
|
public override MethodBase TargetMethodBase { get; }
|
||||||
|
= AccessTools.DeclaredMethod(typeof(ModsConfig), nameof(ModsConfig.IsAnyActiveOrEmpty),
|
||||||
|
[typeof(IEnumerable<string>), typeof(bool)])!;
|
||||||
|
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> IsActiveFix(ilProcessor, module);
|
=> IsActiveFix(ilProcessor, module);
|
||||||
@@ -59,4 +62,4 @@ public sealed class ModsConfigPatches : ClassWithFishPrepatches
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsActiveReplacement(string id) => ModLister.GetActiveModWithIdentifier(id, true) != null;
|
public static bool IsActiveReplacement(string id) => ModLister.GetActiveModWithIdentifier(id, true) != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ public static class OnAssembliesLoaded
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!patchClass.RequiresLoadedGameForPatching)
|
if (!patchClass.RequiresLoadedGameForPatching)
|
||||||
|
{
|
||||||
patchClass.Patches.PatchAll();
|
patchClass.Patches.PatchAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -122,4 +124,4 @@ public static class OnAssembliesLoaded
|
|||||||
action.Method.FullDescription()}:\n{ex}");
|
action.Method.FullDescription()}:\n{ex}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ VisualStudioVersion = 17.1.31911.260
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.5", "1.5.csproj", "{E8A3D63E-5850-4866-AB06-DBD44B11F67B}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.5", "1.5.csproj", "{E8A3D63E-5850-4866-AB06-DBD44B11F67B}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.6", "1.6.csproj", "{8B299603-8E75-4C35-AFC8-89748599E689}"
|
||||||
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.4", "1.4.csproj", "{0257ABCD-7009-4C7D-9BD8-599FC47595DB}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.4", "1.4.csproj", "{0257ABCD-7009-4C7D-9BD8-599FC47595DB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6948F7E6-7ED7-4716-87EF-CFF1F465F561}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6948F7E6-7ED7-4716-87EF-CFF1F465F561}"
|
||||||
@@ -24,6 +26,10 @@ Global
|
|||||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8B299603-8E75-4C35-AFC8-89748599E689}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8B299603-8E75-4C35-AFC8-89748599E689}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8B299603-8E75-4C35-AFC8-89748599E689}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8B299603-8E75-4C35-AFC8-89748599E689}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{0257ABCD-7009-4C7D-9BD8-599FC47595DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{0257ABCD-7009-4C7D-9BD8-599FC47595DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{0257ABCD-7009-4C7D-9BD8-599FC47595DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0257ABCD-7009-4C7D-9BD8-599FC47595DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{0257ABCD-7009-4C7D-9BD8-599FC47595DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0257ABCD-7009-4C7D-9BD8-599FC47595DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
|
|||||||
{
|
{
|
||||||
public sealed class WorldObjectsHolderTickPatch : FishPrepatch
|
public sealed class WorldObjectsHolderTickPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
|
private static FishTable<Type, bool> _skippableCompTypes = null!;
|
||||||
|
private static FishTable<Type, bool> _skippableWorldObjectTypes = null!;
|
||||||
|
|
||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
= "The world objects holder is responsible for ticking every world object. This includes settlements, "
|
= "The world objects holder is responsible for ticking every world object. This includes settlements, "
|
||||||
+ "caravans, outposts and any other object placed in the world, instead of maps. Normally it ticks "
|
+ "caravans, outposts and any other object placed in the world, instead of maps. Normally it ticks "
|
||||||
@@ -35,7 +38,11 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
var worldObjects = WorldObjectsHolder.tmpWorldObjects;
|
var worldObjects = WorldObjectsHolder.tmpWorldObjects;
|
||||||
for (var i = worldObjects.Count; i-- > 0;)
|
for (var i = worldObjects.Count; i-- > 0;)
|
||||||
|
#if V1_6
|
||||||
|
worldObjects[i].DoTick();
|
||||||
|
#else
|
||||||
worldObjects[i].Tick();
|
worldObjects[i].Tick();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UpdateCache(WorldObjectsHolder instance)
|
public static void UpdateCache(WorldObjectsHolder instance)
|
||||||
@@ -50,7 +57,7 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
|
|||||||
var worldObject = instanceWorldObjects[i];
|
var worldObject = instanceWorldObjects[i];
|
||||||
|
|
||||||
if (worldObject is not MapParent { HasMap: true }
|
if (worldObject is not MapParent { HasMap: true }
|
||||||
&& SkippableWorldObjects.Contains(worldObject.GetType())
|
&& CanSkipWorldObjectTick(worldObject)
|
||||||
&& (worldObject is not Settlement settlement || settlement.trader?.stock is null)
|
&& (worldObject is not Settlement settlement || settlement.trader?.stock is null)
|
||||||
&& CanSkipCompTick(worldObject))
|
&& CanSkipCompTick(worldObject))
|
||||||
{
|
{
|
||||||
@@ -64,6 +71,15 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
|
|||||||
CachedMapsVersion = Current.gameInt.maps._version;
|
CachedMapsVersion = Current.gameInt.maps._version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool CanSkipWorldObjectTick(WorldObject worldObject)
|
||||||
|
{
|
||||||
|
#if V1_6
|
||||||
|
if (worldObject is IThingHolder)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
return IsSkippableWorldObjectType(worldObject.GetType());
|
||||||
|
}
|
||||||
|
|
||||||
private static bool CanSkipCompTick(WorldObject worldObject)
|
private static bool CanSkipCompTick(WorldObject worldObject)
|
||||||
{
|
{
|
||||||
var comps = worldObject.comps;
|
var comps = worldObject.comps;
|
||||||
@@ -74,7 +90,7 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
|
|||||||
if (comp is EnterCooldownComp { Active: true })
|
if (comp is EnterCooldownComp { Active: true })
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!SkippableComps.Contains(comp.GetType()))
|
if (!IsSkippableCompType(comp.GetType()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
comp.CompTick();
|
comp.CompTick();
|
||||||
@@ -106,28 +122,69 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
|
|||||||
public static void AddCompToWhiteList(Type compType)
|
public static void AddCompToWhiteList(Type compType)
|
||||||
{
|
{
|
||||||
_whitelistedTickingCompTypes = _whitelistedTickingCompTypes.Add(compType);
|
_whitelistedTickingCompTypes = _whitelistedTickingCompTypes.Add(compType);
|
||||||
SkippableComps = InitializeSkippableComps();
|
ResetSkippableCompTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddWorldObjectToWhiteList(Type worldObjectType)
|
public static void AddWorldObjectToWhiteList(Type worldObjectType)
|
||||||
{
|
{
|
||||||
_whitelistedWorldObjectTypes = _whitelistedWorldObjectTypes.Add(worldObjectType);
|
_whitelistedWorldObjectTypes = _whitelistedWorldObjectTypes.Add(worldObjectType);
|
||||||
SkippableWorldObjects = InitializeSkippableWorldObjects();
|
ResetSkippableWorldObjectTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HashSet<Type>
|
private static void ResetSkippableCompTypes()
|
||||||
SkippableComps = InitializeSkippableComps(),
|
{
|
||||||
SkippableWorldObjects = InitializeSkippableWorldObjects();
|
_skippableCompTypes = new()
|
||||||
|
{
|
||||||
|
ValueInitializer = static type => ComputeIsSkippableCompType(type)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static HashSet<Type> InitializeSkippableComps()
|
private static void ResetSkippableWorldObjectTypes()
|
||||||
=> MakeSubclassHashSet(typeof(WorldObjectComp), nameof(WorldObjectComp.CompTick),
|
{
|
||||||
_whitelistedTickingCompTypes);
|
_skippableWorldObjectTypes = new()
|
||||||
|
{
|
||||||
|
ValueInitializer = static type => ComputeIsSkippableWorldObjectType(type)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static HashSet<Type> InitializeSkippableWorldObjects()
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
=> MakeSubclassHashSet(typeof(WorldObject), nameof(WorldObject.Tick), _whitelistedWorldObjectTypes);
|
private static bool IsSkippableCompType(Type type) => _skippableCompTypes.GetOrAdd(type);
|
||||||
|
|
||||||
private static HashSet<Type> MakeSubclassHashSet(Type type, string name, Type?[] allowedDeclaringTypes)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
=> type.SubclassesWithNoMethodOverrideAndSelf(allowedDeclaringTypes, name).ToHashSet();
|
private static bool IsSkippableWorldObjectType(Type type) => _skippableWorldObjectTypes.GetOrAdd(type);
|
||||||
|
|
||||||
|
private static bool ComputeIsSkippableCompType(Type type)
|
||||||
|
{
|
||||||
|
if (AccessTools.Method(type, nameof(WorldObjectComp.CompTick))?.DeclaringType is not { } declaringType
|
||||||
|
|| !_whitelistedTickingCompTypes.Contains(declaringType))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if V1_6
|
||||||
|
return AccessTools.Method(type, nameof(WorldObjectComp.CompTickInterval), [typeof(int)])?.DeclaringType
|
||||||
|
is { } intervalDeclaringType
|
||||||
|
&& _whitelistedTickingCompTypes.Contains(intervalDeclaringType);
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ComputeIsSkippableWorldObjectType(Type type)
|
||||||
|
{
|
||||||
|
if (AccessTools.Method(type, "Tick")?.DeclaringType is not { } declaringType
|
||||||
|
|| !_whitelistedWorldObjectTypes.Contains(declaringType))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if V1_6
|
||||||
|
return AccessTools.Method(type, "TickInterval", [typeof(int)])?.DeclaringType is { } intervalDeclaringType
|
||||||
|
&& _whitelistedWorldObjectTypes.Contains(intervalDeclaringType);
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
public static bool CacheDirty(WorldObjectsHolder instance)
|
public static bool CacheDirty(WorldObjectsHolder instance)
|
||||||
=> CachedWorldObjectsVersion != instance.worldObjects._version
|
=> CachedWorldObjectsVersion != instance.worldObjects._version
|
||||||
@@ -135,7 +192,12 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
public static void SetDirty() => CachedWorldObjectsVersion = CachedMapsVersion = -2;
|
public static void SetDirty() => CachedWorldObjectsVersion = CachedMapsVersion = -2;
|
||||||
|
|
||||||
static WorldObjectsHolderTickPatch() => Cache.Utility.Cleared += SetDirty;
|
static WorldObjectsHolderTickPatch()
|
||||||
|
{
|
||||||
|
ResetSkippableCompTypes();
|
||||||
|
ResetSkippableWorldObjectTypes();
|
||||||
|
Cache.Utility.Cleared += SetDirty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ExpandingIconCaching : FishPrepatch
|
public sealed class ExpandingIconCaching : FishPrepatch
|
||||||
@@ -245,4 +307,4 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using PerformanceFish.Hauling;
|
|||||||
using PerformanceFish.Planet;
|
using PerformanceFish.Planet;
|
||||||
using Prepatcher;
|
using Prepatcher;
|
||||||
using RimWorld.Planet;
|
using RimWorld.Planet;
|
||||||
|
using PFMapEvents = PerformanceFish.Events.MapEvents;
|
||||||
|
|
||||||
namespace PerformanceFish;
|
namespace PerformanceFish;
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ public static class PrepatcherFields
|
|||||||
[PrepatcherField]
|
[PrepatcherField]
|
||||||
[ValueInitializer(nameof(CreateMapEvents))]
|
[ValueInitializer(nameof(CreateMapEvents))]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static extern MapEvents.Instanced Events(this Map map);
|
public static extern PFMapEvents.Instanced Events(this Map map);
|
||||||
|
|
||||||
[PrepatcherField]
|
[PrepatcherField]
|
||||||
[ValueInitializer(nameof(CreateThingEvents))]
|
[ValueInitializer(nameof(CreateThingEvents))]
|
||||||
@@ -175,7 +176,7 @@ public static class PrepatcherFields
|
|||||||
public static FishTable<GroupThingPair, int> CreateIndexMapByGroup() => new();
|
public static FishTable<GroupThingPair, int> CreateIndexMapByGroup() => new();
|
||||||
public static Cache.CellGrid<int> CreateItemCountGrid(Map map) => new(map);
|
public static Cache.CellGrid<int> CreateItemCountGrid(Map map) => new(map);
|
||||||
public static Cache.BitCellGrid CreateBitCellGrid(Map map) => new(map);
|
public static Cache.BitCellGrid CreateBitCellGrid(Map map) => new(map);
|
||||||
public static MapEvents.Instanced CreateMapEvents() => new();
|
public static PFMapEvents.Instanced CreateMapEvents() => new();
|
||||||
public static ThingEvents.Instanced CreateThingEvents() => new();
|
public static ThingEvents.Instanced CreateThingEvents() => new();
|
||||||
public static StorageSettingsPatches.StorageSettingsCache CreateStorageSettingsCache() => new();
|
public static StorageSettingsPatches.StorageSettingsCache CreateStorageSettingsCache() => new();
|
||||||
public static StorageDistrict[] GetDefaultDistrictArray() => StorageDistrict.GetDefaultArray();
|
public static StorageDistrict[] GetDefaultDistrictArray() => StorageDistrict.GetDefaultArray();
|
||||||
@@ -290,4 +291,4 @@ public static class PrepatcherFields
|
|||||||
}
|
}
|
||||||
|
|
||||||
// public static List<HediffComp> CreateHediffCompList() => new();
|
// public static List<HediffComp> CreateHediffCompList() => new();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using Mono.Cecil.Rocks;
|
using Mono.Cecil.Rocks;
|
||||||
@@ -192,14 +193,26 @@ public abstract class FishPrepatch : FishPrepatchBase
|
|||||||
var stateParameter = postfixParameters.TryGetNamed("__state");
|
var stateParameter = postfixParameters.TryGetNamed("__state");
|
||||||
// Verify? It would make sense to match against prefix state
|
// Verify? It would make sense to match against prefix state
|
||||||
|
|
||||||
// var skipOriginalFlag = ilProcessor.DeclareLocal(typeof(bool));
|
var hasReturnValue = targetMethod.ReturnType.FullName != module.TypeSystem.Void.FullName;
|
||||||
var resultVariable = GetResultVariable(ilProcessor, ref patchVariables, resultParameter, targetMethod);
|
var resultVariable = hasReturnValue
|
||||||
|
? patchVariables.Result ??= ilProcessor.DeclareLocal(targetMethod.ReturnType)
|
||||||
|
: GetResultVariable(ilProcessor, ref patchVariables, resultParameter, targetMethod);
|
||||||
var stateVariable = GetStateVariable(ilProcessor, ref patchVariables, stateParameter);
|
var stateVariable = GetStateVariable(ilProcessor, ref patchVariables, stateParameter);
|
||||||
|
var retInstructions = instructions.Where(static instruction => instruction.OpCode == OpCodes.Ret).ToArray();
|
||||||
|
if (retInstructions.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
var postfixInstructions = new Collection<object>();
|
var postfixInstructions = new Collection<object>();
|
||||||
|
|
||||||
if (resultVariable != null)
|
if (hasReturnValue)
|
||||||
postfixInstructions.Add((OpCodes.Stloc, resultVariable));
|
{
|
||||||
|
postfixInstructions.Add((OpCodes.Stloc, resultVariable!));
|
||||||
|
postfixInstructions.Add(OpCodes.Nop);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
postfixInstructions.Add(OpCodes.Nop);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var parameter in postfixParameters)
|
foreach (var parameter in postfixParameters)
|
||||||
{
|
{
|
||||||
@@ -217,36 +230,87 @@ public abstract class FishPrepatch : FishPrepatchBase
|
|||||||
postfixInstructions.Add((OpCodes.Call, postfixMethodInfo));
|
postfixInstructions.Add((OpCodes.Call, postfixMethodInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postfixMethodInfo.ReturnType == typeof(void) && resultVariable != null)
|
if (postfixMethodInfo.ReturnType != typeof(void))
|
||||||
postfixInstructions.Add((OpCodes.Ldloc, resultVariable));
|
postfixInstructions.Add((OpCodes.Stloc, resultVariable!));
|
||||||
|
|
||||||
Span<int> retInstructions = stackalloc int[instructions.Count];
|
if (hasReturnValue)
|
||||||
var retInstructionCount = 0;
|
postfixInstructions.Add((OpCodes.Ldloc, resultVariable!));
|
||||||
|
|
||||||
for (var i = 0; i < instructions.Count; i++)
|
|
||||||
{
|
|
||||||
if (instructions[i].OpCode == OpCodes.Ret)
|
|
||||||
retInstructions[retInstructionCount++] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastRetInstructionIndex = retInstructions[retInstructionCount - 1];
|
var lastRetInstruction = retInstructions[^1];
|
||||||
|
var lastRetInstructionIndex = instructions.IndexOf(lastRetInstruction);
|
||||||
ilProcessor.InsertRange(lastRetInstructionIndex, postfixInstructions);
|
ilProcessor.InsertRange(lastRetInstructionIndex, postfixInstructions);
|
||||||
|
var captureStart = instructions[lastRetInstructionIndex];
|
||||||
|
var postfixStart = instructions[lastRetInstructionIndex + (hasReturnValue ? 1 : 0)];
|
||||||
|
|
||||||
for (var i = 0; i < instructions.Count; i++)
|
RetargetExceptionHandlerEnds(targetMethod.Body, lastRetInstruction, captureStart);
|
||||||
|
var retInstructionSet = retInstructions.ToHashSet();
|
||||||
|
foreach (var instruction in instructions)
|
||||||
{
|
{
|
||||||
var instruction = instructions[i];
|
if (instruction.Operand is Instruction branchTarget && retInstructionSet.Contains(branchTarget))
|
||||||
if (instruction.Operand is Instruction branchTarget && branchTarget.OpCode == OpCodes.Ret)
|
instruction.Operand = captureStart;
|
||||||
instruction.Operand = instructions[lastRetInstructionIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < retInstructionCount - 1; i++)
|
for (var i = 0; i < retInstructions.Length - 1; i++)
|
||||||
{
|
{
|
||||||
var instruction = instructions[retInstructions[i]];
|
var instruction = retInstructions[i];
|
||||||
instruction.OpCode = OpCodes.Br;
|
if (hasReturnValue)
|
||||||
instruction.Operand = instructions[lastRetInstructionIndex];
|
ilProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Stloc, resultVariable!));
|
||||||
|
|
||||||
|
instruction.OpCode = IsInProtectedRegion(targetMethod.Body, instruction) ? OpCodes.Leave : OpCodes.Br;
|
||||||
|
instruction.Operand = postfixStart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RetargetExceptionHandlerEnds(Mono.Cecil.Cil.MethodBody methodBody, Instruction oldBoundary,
|
||||||
|
Instruction newBoundary)
|
||||||
|
{
|
||||||
|
foreach (var exceptionHandler in methodBody.ExceptionHandlers)
|
||||||
|
{
|
||||||
|
if (exceptionHandler.TryEnd == oldBoundary)
|
||||||
|
exceptionHandler.TryEnd = newBoundary;
|
||||||
|
if (exceptionHandler.HandlerEnd == oldBoundary)
|
||||||
|
exceptionHandler.HandlerEnd = newBoundary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsInProtectedRegion(Mono.Cecil.Cil.MethodBody methodBody, Instruction instruction)
|
||||||
|
{
|
||||||
|
var instructions = methodBody.Instructions;
|
||||||
|
var instructionIndex = instructions.IndexOf(instruction);
|
||||||
|
|
||||||
|
foreach (var exceptionHandler in methodBody.ExceptionHandlers)
|
||||||
|
{
|
||||||
|
if (IsWithinRange(instructions, instructionIndex, exceptionHandler.TryStart, exceptionHandler.TryEnd)
|
||||||
|
|| IsWithinRange(instructions, instructionIndex, exceptionHandler.HandlerStart,
|
||||||
|
exceptionHandler.HandlerEnd)
|
||||||
|
|| exceptionHandler.FilterStart != null
|
||||||
|
&& IsWithinRange(instructions, instructionIndex, exceptionHandler.FilterStart,
|
||||||
|
exceptionHandler.HandlerStart))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsWithinRange(Mono.Collections.Generic.Collection<Instruction> instructions,
|
||||||
|
int instructionIndex, Instruction? rangeStart, Instruction? rangeEnd)
|
||||||
|
{
|
||||||
|
if (rangeStart == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var rangeStartIndex = instructions.IndexOf(rangeStart);
|
||||||
|
if (rangeStartIndex < 0 || instructionIndex < rangeStartIndex)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rangeEnd == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var rangeEndIndex = instructions.IndexOf(rangeEnd);
|
||||||
|
return rangeEndIndex < 0 || instructionIndex < rangeEndIndex;
|
||||||
|
}
|
||||||
|
|
||||||
private static VariableDefinition? GetStateVariable(ILProcessor ilProcessor, ref PatchVariables patchVariables,
|
private static VariableDefinition? GetStateVariable(ILProcessor ilProcessor, ref PatchVariables patchVariables,
|
||||||
ParameterInfo? stateParameter)
|
ParameterInfo? stateParameter)
|
||||||
=> stateParameter != null
|
=> stateParameter != null
|
||||||
@@ -352,4 +416,4 @@ public abstract class FishPrepatch : FishPrepatchBase
|
|||||||
{
|
{
|
||||||
public VariableDefinition? Result, State;
|
public VariableDefinition? Result, State;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,40 +68,79 @@ public sealed class DynamicDrawManagerPatches : ClassWithFishPrepatches
|
|||||||
public static void CullAndInitializeThings(DynamicDrawManager instance)
|
public static void CullAndInitializeThings(DynamicDrawManager instance)
|
||||||
{
|
{
|
||||||
var map = instance.map;
|
var map = instance.map;
|
||||||
var mapSizeX = map.SizeX();
|
var cellIndices = map.cellIndices;
|
||||||
|
var mapSizeX = GetMapSizeX(cellIndices);
|
||||||
var fogGrid = map.fogGrid.fogGrid;
|
var fogGrid = map.fogGrid.fogGrid;
|
||||||
var snowGrid = map.snowGrid.depthGrid;
|
var snowGrid = map.snowGrid.depthGrid;
|
||||||
var checkShadows = MatBases.SunShadow.shader.isSupported;
|
var checkShadows = MatBases.SunShadow.shader.isSupported;
|
||||||
var currentViewRect = Find.CameraDriver.CurrentViewRect.ClipInsideMap(map).ExpandedBy(1);
|
var currentViewRect = Find.CameraDriver.CurrentViewRect.ExpandedBy(1);
|
||||||
|
#if V1_6
|
||||||
|
if (WorldComponent_GravshipController.GravshipRenderInProgess)
|
||||||
|
currentViewRect = currentViewRect.Encapsulate(WorldComponent_GravshipController.GravshipRenderBounds);
|
||||||
|
#endif
|
||||||
|
currentViewRect = currentViewRect.ClipInsideMap(map);
|
||||||
var shadowViewRect = SectionLayer_SunShadows.GetSunShadowsViewRect(map, currentViewRect);
|
var shadowViewRect = SectionLayer_SunShadows.GetSunShadowsViewRect(map, currentViewRect);
|
||||||
instance.drawThings.UnwrapArray(out var drawThings, out var drawThingsCount);
|
instance.drawThings.UnwrapArray(out var drawThings, out var drawThingsCount);
|
||||||
|
|
||||||
for (var i = 0; i < drawThingsCount; i++)
|
for (var i = 0; i < drawThingsCount; i++)
|
||||||
{
|
{
|
||||||
var drawThing = drawThings.UnsafeLoad(i);
|
var drawThing = drawThings.UnsafeLoad(i);
|
||||||
var position = drawThing.Position;
|
var position = GetCullPosition(drawThing);
|
||||||
var drawThingDef = drawThing.def;
|
var drawThingDef = drawThing.def;
|
||||||
var cellIndex = position.CellToIndex(mapSizeX);
|
var cellIndex = position.CellToIndex(mapSizeX);
|
||||||
if ((!fogGrid[cellIndex] || drawThingDef.seeThroughFog)
|
if (!CellIndicesContain(cellIndices, cellIndex)
|
||||||
&& (drawThingDef.hideAtSnowDepth >= 1f
|
|| (!drawThingDef.seeThroughFog
|
||||||
|| drawThingDef.hideAtSnowDepth >= snowGrid[cellIndex]))
|
#if V1_6
|
||||||
|
&& fogGrid.IsSet(cellIndex))
|
||||||
|
#else
|
||||||
|
&& fogGrid[cellIndex])
|
||||||
|
#endif
|
||||||
|
#if V1_6
|
||||||
|
|| (drawThingDef.hideAtSnowOrSandDepth < 1f
|
||||||
|
&& snowGrid[cellIndex] > drawThingDef.hideAtSnowOrSandDepth))
|
||||||
|
#else
|
||||||
|
|| (drawThingDef.hideAtSnowDepth < 1f
|
||||||
|
&& snowGrid[cellIndex] > drawThingDef.hideAtSnowDepth))
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if (currentViewRect.Contains(position)
|
continue;
|
||||||
|| currentViewRect.Overlaps(drawThing.OccupiedDrawRect()))
|
}
|
||||||
{
|
|
||||||
ThingsToDraw.Add(drawThing);
|
if (currentViewRect.Contains(position)
|
||||||
|
|| currentViewRect.Overlaps(drawThing.OccupiedDrawRect()))
|
||||||
if (!DebugViewSettings.singleThreadedDrawing)
|
{
|
||||||
EnsureInitialized(drawThing);
|
ThingsToDraw.Add(drawThing);
|
||||||
}
|
|
||||||
else if (drawThing is Pawn pawn && checkShadows && shadowViewRect.Contains(drawThing.Position))
|
if (!DebugViewSettings.singleThreadedDrawing)
|
||||||
{
|
EnsureInitialized(drawThing);
|
||||||
PawnShadowsToDraw.Add(pawn);
|
}
|
||||||
}
|
else if (drawThing is Pawn pawn && checkShadows && shadowViewRect.Contains(position))
|
||||||
|
{
|
||||||
|
PawnShadowsToDraw.Add(pawn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static IntVec3 GetCullPosition(Thing drawThing)
|
||||||
|
=> drawThing is Pawn ? drawThing.DrawPos.ToIntVec3() : drawThing.Position;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool CellIndicesContain(CellIndices cellIndices, int index)
|
||||||
|
#if V1_6
|
||||||
|
=> cellIndices.Contains(index);
|
||||||
|
#else
|
||||||
|
=> (uint)index < (uint)(cellIndices.mapSizeX * cellIndices.mapSizeZ);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static int GetMapSizeX(CellIndices cellIndices)
|
||||||
|
#if V1_6
|
||||||
|
=> cellIndices.SizeX;
|
||||||
|
#else
|
||||||
|
=> cellIndices.mapSizeX;
|
||||||
|
#endif
|
||||||
|
|
||||||
public static void EnsureInitialized(Thing drawThing)
|
public static void EnsureInitialized(Thing drawThing)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -228,4 +267,4 @@ public sealed class DynamicDrawManagerPatches : ClassWithFishPrepatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -349,8 +349,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Room? RoomAt(IntVec3 c, Map map, RegionType allowedRegionTypes = RegionType.Set_All)
|
public static Room? RoomAt(IntVec3 c, Map map, RegionType allowedRegionTypes = RegionType.Set_All)
|
||||||
{
|
{
|
||||||
var cellIndices = map.cellIndices;
|
if (!c.InBounds(map))
|
||||||
if (((uint)c.x >= (uint)cellIndices.mapSizeX) | ((uint)c.z >= (uint)cellIndices.mapSizeZ))
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var regionAndRoomUpdater = map.regionAndRoomUpdater;
|
var regionAndRoomUpdater = map.regionAndRoomUpdater;
|
||||||
@@ -360,8 +359,8 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
else if (regionAndRoomUpdater.AnythingToRebuild)
|
else if (regionAndRoomUpdater.AnythingToRebuild)
|
||||||
LogIncorrectResultWarning(c);
|
LogIncorrectResultWarning(c);
|
||||||
|
|
||||||
var region = map.regionGrid.regionGrid[(c.z * cellIndices.mapSizeX) + c.x];
|
var region = map.regionGrid.GetValidRegionAt_NoRebuild(c);
|
||||||
return region is null || !region.valid || (region.type & allowedRegionTypes) == 0
|
return region is null || (region.type & allowedRegionTypes) == 0
|
||||||
? null
|
? null
|
||||||
: region.District?.Room;
|
: region.District?.Room;
|
||||||
}
|
}
|
||||||
@@ -400,4 +399,4 @@ public sealed class RoomPrepatches : ClassWithFishPrepatches
|
|||||||
return tmpRegions;
|
return tmpRegions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -510,44 +510,139 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
public static bool Prefix(ICustomAttributeProvider obj, Type attributeType, bool inherit,
|
public static bool Prefix(ICustomAttributeProvider obj, Type attributeType, bool inherit,
|
||||||
out object[]? __result, out CustomAttributeState __state)
|
out object[]? __result, out CustomAttributeState __state)
|
||||||
{
|
{
|
||||||
var key = new CustomAttributeCache(obj, (__state.AttributeType = attributeType).TypeHandle,
|
var key = new CustomAttributeCache(obj, attributeType.TypeHandle, inherit);
|
||||||
__state.Inherit = inherit);
|
__state = new() { Key = key };
|
||||||
|
|
||||||
ref var cache = ref CustomAttributeCache.GetOrAddReference(in key);
|
if (TryGetCachedAttributes(ref key, out __result))
|
||||||
if (cache.Attributes is null)
|
return false;
|
||||||
TryGetFromCentralCache(ref key, ref cache);
|
|
||||||
|
|
||||||
return __state.State = (__result = cache.Attributes) is null;
|
MarkInProgress(ref key);
|
||||||
|
__state.ShouldStore = true;
|
||||||
|
__result = null;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private static void TryGetFromCentralCache(ref CustomAttributeCache key,
|
private static bool TryGetCachedAttributes(ref CustomAttributeCache key, out object[]? result)
|
||||||
ref CustomAttributeCacheValue cache)
|
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
cache.Attributes = CustomAttributeCache.GetDirectly.GetOrAdd(ref key).Attributes;
|
{
|
||||||
|
if (TryUseCache(CustomAttributeCache.Get, ref key, out result))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (TryUseCache(CustomAttributeCache.GetDirectly, ref key, out var centralResult))
|
||||||
|
{
|
||||||
|
result = centralResult;
|
||||||
|
CustomAttributeCache.Get[key] = new()
|
||||||
|
{
|
||||||
|
Attributes = centralResult,
|
||||||
|
Status = CustomAttributeCacheStatus.Completed
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Postfix(ICustomAttributeProvider obj, object[]? __result,
|
public static void Postfix(ICustomAttributeProvider obj, object[]? __result,
|
||||||
in CustomAttributeState __state)
|
in CustomAttributeState __state)
|
||||||
{
|
{
|
||||||
if (!__state.State || __result is null)
|
if (!__state.ShouldStore || __result is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UpdateCache(obj, __state, __result);
|
StoreCompleted(ref Unsafe.AsRef(in __state.Key), __result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private static void UpdateCache(ICustomAttributeProvider obj, in CustomAttributeState __state,
|
public static Exception? Finalizer(Exception? __exception, in CustomAttributeState __state)
|
||||||
object[]? __result)
|
|
||||||
{
|
{
|
||||||
var key = new CustomAttributeCache(obj, __state.AttributeType.TypeHandle, __state.Inherit);
|
if (__state.ShouldStore && __exception != null)
|
||||||
|
ClearInProgress(ref Unsafe.AsRef(in __state.Key));
|
||||||
CustomAttributeCache.GetExistingReference(in key).Attributes = __result;
|
|
||||||
|
return __exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static bool TryUseCache(Dictionary<CustomAttributeCache, CustomAttributeCacheValue> cache,
|
||||||
|
ref CustomAttributeCache key, out object[]? result)
|
||||||
|
{
|
||||||
|
if (!cache.TryGetValue(key, out var value))
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value.Status)
|
||||||
|
{
|
||||||
|
case CustomAttributeCacheStatus.Completed:
|
||||||
|
result = value.Attributes;
|
||||||
|
return true;
|
||||||
|
case CustomAttributeCacheStatus.InProgress:
|
||||||
|
LogInProgress(ref key);
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void MarkInProgress(ref CustomAttributeCache key)
|
||||||
|
{
|
||||||
|
var value = new CustomAttributeCacheValue
|
||||||
|
{
|
||||||
|
Status = CustomAttributeCacheStatus.InProgress
|
||||||
|
};
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
CustomAttributeCache.GetDirectly.GetReference(ref key).Attributes = __result;
|
{
|
||||||
|
CustomAttributeCache.Get[key] = value;
|
||||||
|
CustomAttributeCache.GetDirectly[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void StoreCompleted(ref CustomAttributeCache key, object[] result)
|
||||||
|
{
|
||||||
|
var value = new CustomAttributeCacheValue
|
||||||
|
{
|
||||||
|
Attributes = result,
|
||||||
|
Status = CustomAttributeCacheStatus.Completed
|
||||||
|
};
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
CustomAttributeCache.Get[key] = value;
|
||||||
|
CustomAttributeCache.GetDirectly[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void ClearInProgress(ref CustomAttributeCache key)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (CustomAttributeCache.Get.TryGetValue(key, out var localValue)
|
||||||
|
&& localValue.Status == CustomAttributeCacheStatus.InProgress)
|
||||||
|
{
|
||||||
|
CustomAttributeCache.Get.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CustomAttributeCache.GetDirectly.TryGetValue(key, out var centralValue)
|
||||||
|
&& centralValue.Status == CustomAttributeCacheStatus.InProgress)
|
||||||
|
{
|
||||||
|
CustomAttributeCache.GetDirectly.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void LogInProgress(ref CustomAttributeCache _)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object _lock = new();
|
private static object _lock = new();
|
||||||
@@ -556,12 +651,20 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
public record struct CustomAttributeCacheValue
|
public record struct CustomAttributeCacheValue
|
||||||
{
|
{
|
||||||
public object[]? Attributes;
|
public object[]? Attributes;
|
||||||
|
public CustomAttributeCacheStatus Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record struct CustomAttributeState
|
public record struct CustomAttributeState
|
||||||
{
|
{
|
||||||
public bool State, Inherit;
|
public bool ShouldStore;
|
||||||
public Type AttributeType;
|
public CustomAttributeCache Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CustomAttributeCacheStatus : byte
|
||||||
|
{
|
||||||
|
Uninitialized,
|
||||||
|
InProgress,
|
||||||
|
Completed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,7 +855,8 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
public override MethodBase TargetMethodInfo { get; }
|
public override MethodBase TargetMethodInfo { get; }
|
||||||
= AccessTools.DeclaredMethod(typeof(MonoField), nameof(MonoField.GetValue))!;
|
= AccessTools.DeclaredMethod(AccessTools.TypeByName("System.Reflection.RuntimeFieldInfo"),
|
||||||
|
nameof(FieldInfo.GetValue), [typeof(object)])!;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool Prefix(FieldInfo __instance, object? obj, ref object? __result)
|
public static bool Prefix(FieldInfo __instance, object? obj, ref object? __result)
|
||||||
@@ -1181,4 +1285,4 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
// populated with dynamic methods by MethodBasePatches.MakeInvokeDelegate, MakeGetterDelegate and
|
// populated with dynamic methods by MethodBasePatches.MakeInvokeDelegate, MakeGetterDelegate and
|
||||||
// MakeSetterDelegate
|
// MakeSetterDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public static class CellIndexExtensions
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int CellToIndex(this in IntVec3 cell, CellIndices cellIndices)
|
public static int CellToIndex(this in IntVec3 cell, CellIndices cellIndices)
|
||||||
=> cell.CellToIndex(cellIndices.mapSizeX);
|
=> cell.CellToIndex(GetMapSizeX(cellIndices));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int CellToIndex(this in IntVec3 cell, int mapSizeX) => (cell.z * mapSizeX) + cell.x;
|
public static int CellToIndex(this in IntVec3 cell, int mapSizeX) => (cell.z * mapSizeX) + cell.x;
|
||||||
@@ -22,7 +22,7 @@ public static class CellIndexExtensions
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int CellToIndex(this IntVec2 cell, CellIndices cellIndices)
|
public static int CellToIndex(this IntVec2 cell, CellIndices cellIndices)
|
||||||
=> cell.CellToIndex(cellIndices.mapSizeX);
|
=> cell.CellToIndex(GetMapSizeX(cellIndices));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int CellToIndex(this IntVec2 cell, int mapSizeX) => (cell.z * mapSizeX) + cell.x;
|
public static int CellToIndex(this IntVec2 cell, int mapSizeX) => (cell.z * mapSizeX) + cell.x;
|
||||||
@@ -31,7 +31,7 @@ public static class CellIndexExtensions
|
|||||||
public static int CellToIndex(int x, int z, Map map) => CellToIndex(x, z, map.cellIndices);
|
public static int CellToIndex(int x, int z, Map map) => CellToIndex(x, z, map.cellIndices);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int CellToIndex(int x, int z, CellIndices cellIndices) => CellToIndex(x, z, cellIndices.mapSizeX);
|
public static int CellToIndex(int x, int z, CellIndices cellIndices) => CellToIndex(x, z, GetMapSizeX(cellIndices));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int CellToIndex(int x, int z, int mapSizeX) => (z * mapSizeX) + x;
|
public static int CellToIndex(int x, int z, int mapSizeX) => (z * mapSizeX) + x;
|
||||||
@@ -41,11 +41,24 @@ public static class CellIndexExtensions
|
|||||||
=> thingGrid.thingGrid[cellIndex.Value];
|
=> thingGrid.thingGrid[cellIndex.Value];
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool IsFogged(this FogGrid fogGrid, CellIndex cellIndex) => fogGrid.fogGrid[cellIndex.Value];
|
public static bool IsFogged(this FogGrid fogGrid, CellIndex cellIndex)
|
||||||
|
#if V1_6
|
||||||
|
=> fogGrid.fogGrid.IsSet(cellIndex.Value);
|
||||||
|
#else
|
||||||
|
=> fogGrid.fogGrid[cellIndex.Value];
|
||||||
|
#endif
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static float GetDepth(this SnowGrid snowGrid, CellIndex cellIndex) => snowGrid.depthGrid[cellIndex.Value];
|
public static float GetDepth(this SnowGrid snowGrid, CellIndex cellIndex) => snowGrid.depthGrid[cellIndex.Value];
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static float GetDepth(this SnowGrid snowGrid, int cellIndex) => snowGrid.depthGrid[cellIndex];
|
public static float GetDepth(this SnowGrid snowGrid, int cellIndex) => snowGrid.depthGrid[cellIndex];
|
||||||
}
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static int GetMapSizeX(CellIndices cellIndices)
|
||||||
|
#if V1_6
|
||||||
|
=> cellIndices.SizeX;
|
||||||
|
#else
|
||||||
|
=> cellIndices.mapSizeX;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ public static class MiscExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int SizeX(this Map map) => map.cellIndices.mapSizeX;
|
public static int SizeX(this Map map) => map.Size.x;
|
||||||
|
|
||||||
public static Map? TryGetMapHeld(this Thing thing)
|
public static Map? TryGetMapHeld(this Thing thing)
|
||||||
{
|
{
|
||||||
@@ -134,10 +134,18 @@ public static class MiscExtensions
|
|||||||
|
|
||||||
public static void InvokeWhenCellIndicesReady(this Map map, Action<Map> action)
|
public static void InvokeWhenCellIndicesReady(this Map map, Action<Map> action)
|
||||||
{
|
{
|
||||||
|
#if V1_6
|
||||||
|
if (map.cellIndices.NumGridCells > 0)
|
||||||
|
#else
|
||||||
if (map.cellIndices != null)
|
if (map.cellIndices != null)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
action(map);
|
action(map);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
map.Events().ComponentsConstructed += action;
|
map.Events().ComponentsConstructed += action;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddLetterSilently(this LetterStack letterStack, Letter letter, Color color, bool playSound = false,
|
public static void AddLetterSilently(this LetterStack letterStack, Letter letter, Color color, bool playSound = false,
|
||||||
@@ -164,4 +172,4 @@ public static class MiscExtensions
|
|||||||
letterStack.letters.Add(letter);
|
letterStack.letters.Add(letter);
|
||||||
letter.Received();
|
letter.Received();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,4 +93,4 @@ public sealed class UtilityPrepatches : ClassWithFishPrepatches
|
|||||||
instruction.Operand = null;
|
instruction.Operand = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user