1.5, added stuff, fixed stuff
- mod icon - fixes for new vanilla clipping issues - GenClosest optimization improving repairing, wardening, tending jobs - caching for new ListerBuildings methods - DynamicDrawManager single threading - Type.GetFields caching - StatParts analyzer category - removed duplicate steam warning and optionally window - added exception handling for debug menu, incidentworker, Graphic_Multi, InitializeComps, making all those now log what mod broke and not cause black screens or whatever
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>BlindSmoke</defName>
|
||||
<label>blind smoke</label>
|
||||
<dissipationRate>4</dissipationRate>
|
||||
<diffuses>false</diffuses>
|
||||
<color>(200,200,200)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>ToxGas</defName>
|
||||
<label>tox gas</label>
|
||||
<dissipationRate>3</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(180,214,24)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>RotStink</defName>
|
||||
<label>rot stink</label>
|
||||
<dissipationRate>4</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(214,90,24)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>FirstTestGas</defName>
|
||||
<label>first test gas</label>
|
||||
<dissipationRate>2</dissipationRate>
|
||||
<diffuses>false</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>SecondTestGas</defName>
|
||||
<label>second test gas</label>
|
||||
<dissipationRate>1</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
</Defs>
|
||||
Binary file not shown.
Binary file not shown.
@@ -4,8 +4,10 @@
|
||||
<packageId>bs.performance</packageId>
|
||||
<supportedVersions>
|
||||
<li>1.4</li>
|
||||
<li>1.5</li>
|
||||
</supportedVersions>
|
||||
<author>bradson</author>
|
||||
<modIconPath IgnoreIfNoMatchingField="True">PerformanceFish/ModIcon</modIconPath>
|
||||
<url>https://github.com/bbradson/performance-fish</url>
|
||||
<loadBefore>
|
||||
<li>Dubwise.DubsPerformanceAnalyzer</li>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,314 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>BlindSmoke</defName>
|
||||
<label>blind smoke</label>
|
||||
<dissipationRate>4</dissipationRate>
|
||||
<diffuses>false</diffuses>
|
||||
<color>(200,200,200)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>ToxGas</defName>
|
||||
<label>tox gas</label>
|
||||
<dissipationRate>3</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(180,214,24)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>RotStink</defName>
|
||||
<label>rot stink</label>
|
||||
<dissipationRate>4</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(214,90,24)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>DeadLifeDust</defName>
|
||||
<label>dead life dust</label>
|
||||
<dissipationRate>3</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(3,13,51)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>FirstTestGas</defName>
|
||||
<label>first test gas</label>
|
||||
<dissipationRate>2</dissipationRate>
|
||||
<diffuses>false</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>SecondTestGas</defName>
|
||||
<label>second test gas</label>
|
||||
<dissipationRate>1</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>ThirdTestGas</defName>
|
||||
<label>third test gas</label>
|
||||
<dissipationRate>1</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(255,113,0,0)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<!-- PerformanceFish.GasDef>
|
||||
<defName>Test1Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test2Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test3Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test4Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test5Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test6Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test7Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test8Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test9Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test10Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test11Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test12Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test13Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test14Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test15Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test16Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test17Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test18Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test19Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test20Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test21Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test22Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test23Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test24Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test25Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test26Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test27Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test28Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test29Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test30Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test31Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(229,52,235)</color>
|
||||
</PerformanceFish.GasDef>
|
||||
|
||||
<PerformanceFish.GasDef>
|
||||
<defName>Test32Gas</defName>
|
||||
<label>test gas</label>
|
||||
<dissipationRate>0</dissipationRate>
|
||||
<diffuses>true</diffuses>
|
||||
<color>(52,113,235)</color>
|
||||
</PerformanceFish.GasDef -->
|
||||
</Defs>
|
||||
@@ -184,6 +184,14 @@
|
||||
</DerivedMethods>
|
||||
</RitualObligationTriggers>
|
||||
|
||||
<StatParts>
|
||||
<Tooltip>TransformValue methods of StatParts. These run on every stat lookup, when not cached</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.StatPart:TransformValue</li>
|
||||
</DerivedMethods>
|
||||
</StatParts>
|
||||
|
||||
<ThingCompTick>
|
||||
<Tooltip>Tick methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutDir>..\..\1.4\Assemblies\</OutDir>
|
||||
<Copyright>Copyright (c) 2023 Bradson</Copyright>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DefineConstants>DEBUG;V1_4</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DefineConstants>V1_4</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.*" />
|
||||
<PackageReference Include="Lib.Harmony" Version="2.2.*" ExcludeAssets="runtime" />
|
||||
<Reference Include="1Fishery">
|
||||
<HintPath>..\..\..\Fishery\1.4\Assemblies\1Fishery.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutDir>..\..\1.5\Assemblies\</OutDir>
|
||||
<Copyright>Copyright (c) 2024 Bradson</Copyright>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DefineConstants>DEBUG;V1_5</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DefineConstants>V1_5</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.5.*" />
|
||||
<PackageReference Include="Lib.Harmony" Version="2.3.*" ExcludeAssets="runtime" />
|
||||
<Reference Include="1Fishery">
|
||||
<HintPath>..\..\..\Fishery\1.5\Assemblies\1Fishery.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -4,6 +4,7 @@
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Xml;
|
||||
@@ -305,9 +306,28 @@ public static class AnalyzerFixes
|
||||
var methods = Reflection.MethodsOfName(AccessTools.TypeByName(array[0]), array[1]);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
yield return method.IsGenericMethodDefinition
|
||||
? method.MakeGenericMethod(GetTypeArguments(method))
|
||||
: method;
|
||||
if (method.IsGenericMethodDefinition || method.ReturnType.IsByRef)
|
||||
continue;
|
||||
|
||||
MethodInfo? methodToReturn;
|
||||
try
|
||||
{
|
||||
methodToReturn = method.IsGenericMethodDefinition
|
||||
? method.MakeGenericMethod(GetTypeArguments(method))
|
||||
: method;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
methodToReturn = null;
|
||||
Log.Error($"Exception caught while trying to parse generic method '{
|
||||
method.FullDescription()}'{
|
||||
(XmlParser.CurrentModDir is { } currentModDir
|
||||
? $" in 'DubsAnalyzer.xml' within {currentModDir}"
|
||||
: "")}:\n{e}");
|
||||
}
|
||||
|
||||
if (methodToReturn != null)
|
||||
yield return methodToReturn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,9 +337,10 @@ public static class AnalyzerFixes
|
||||
var resultArray = new Type[genericArguments.Length];
|
||||
for (var i = 0; i < genericArguments.Length; i++)
|
||||
{
|
||||
resultArray[i] = genericArguments[i].GetGenericArguments() is var constraints && constraints.Length != 0
|
||||
? constraints[0]
|
||||
: typeof(object);
|
||||
var genericArgument = genericArguments[i];
|
||||
resultArray[i] = genericArgument.GetGenericArguments() is var constraints && constraints.Length != 0
|
||||
? genericArgument.MakeGenericType(constraints)
|
||||
: genericArgument;
|
||||
}
|
||||
|
||||
return resultArray;
|
||||
@@ -430,6 +451,8 @@ public static class AnalyzerFixes
|
||||
//Copied from Wiri's analyzer fork, with above fixes integrated. The steam version lacks this functionality
|
||||
public static class XmlParser
|
||||
{
|
||||
public static DirectoryInfo? CurrentModDir;
|
||||
|
||||
[SuppressMessage("Security", "CA3075")]
|
||||
public static void CollectXmlData()
|
||||
{
|
||||
@@ -439,6 +462,8 @@ public static class AnalyzerFixes
|
||||
if (xmlFiles.Length == 0)
|
||||
continue;
|
||||
|
||||
CurrentModDir = dir;
|
||||
|
||||
foreach (var file in xmlFiles)
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
@@ -446,6 +471,8 @@ public static class AnalyzerFixes
|
||||
|
||||
Parse(doc);
|
||||
}
|
||||
|
||||
CurrentModDir = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,7 +543,17 @@ public static class AnalyzerFixes
|
||||
continue;
|
||||
|
||||
if (childNode.NodeType != XmlNodeType.Comment)
|
||||
methods.AddRange(func(childNode.InnerText));
|
||||
{
|
||||
try
|
||||
{
|
||||
methods.AddRange(func(childNode.InnerText));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Exception caught while trying to parse DubsAnalyzer.xml at '{
|
||||
childNode.InnerText}' in '{CurrentModDir}':\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
using JetBrains.Annotations;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
// ReSharper disable WithExpressionModifiesAllMembers
|
||||
|
||||
namespace PerformanceFish.Cache;
|
||||
|
||||
@@ -25,12 +26,12 @@ public record struct ByInt<T, TResult> where T : notnull where TResult : new()
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => Key;
|
||||
|
||||
private static List<FishTable<ByInt<T, TResult>, TResult>> _allCaches = [];
|
||||
|
||||
private static object _valueInitializerLock = new();
|
||||
private static readonly object _valueInitializerLock = new();
|
||||
|
||||
private static FishTable<ByInt<T, TResult>, TResult> _get = InitializeNew();
|
||||
private static readonly List<IDictionary> _allCaches = [];
|
||||
|
||||
private static readonly FishTable<ByInt<T, TResult>, TResult> _get = InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static FishTable<ByInt<T, TResult>, TResult>? _getThreadStatic;
|
||||
@@ -74,18 +75,12 @@ public record struct ByInt<T, TResult> where T : notnull where TResult : new()
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref TResult GetOrAddReference(ByInt<T, TResult> key) => ref Get.GetOrAddReference(key);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref TResult GetOrAddReference(ref ByInt<T, TResult> key) => ref Get.GetOrAddReference(ref key);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TResult GetOrAdd(int key) => Get.GetOrAdd(Unsafe.As<int, ByInt<T, TResult>>(ref key));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TResult GetOrAdd(ByInt<T, TResult> key) => Get.GetOrAdd(key);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TResult GetOrAdd(ref ByInt<T, TResult> key) => Get.GetOrAdd(ref key);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(int key)
|
||||
=> ref Get.GetReference(Unsafe.As<int, ByInt<T, TResult>>(ref key));
|
||||
@@ -95,23 +90,9 @@ public record struct ByInt<T, TResult> where T : notnull where TResult : new()
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<ByInt<T, TResult>, TResult> InitializeNew()
|
||||
{
|
||||
var newCache = Utility.AddNew(ValueInitializer);
|
||||
|
||||
lock (_allCaches)
|
||||
_allCaches.Add(newCache);
|
||||
|
||||
return newCache;
|
||||
}
|
||||
=> Utility.AddNew(_allCaches, ValueInitializer);
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
lock (_allCaches)
|
||||
{
|
||||
foreach (var cache in _allCaches)
|
||||
cache.Clear();
|
||||
}
|
||||
}
|
||||
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
@@ -120,11 +101,11 @@ public record struct ByInt<T1, T2, TResult>
|
||||
{
|
||||
public int First, Second;
|
||||
|
||||
private static List<FishTable<ByInt<T1, T2, TResult>, TResult>> _allCaches = [];
|
||||
private static readonly object _valueInitializerLock = new();
|
||||
|
||||
private static object _valueInitializerLock = new();
|
||||
private static readonly List<IDictionary> _allCaches = [];
|
||||
|
||||
private static FishTable<ByInt<T1, T2, TResult>, TResult> _get = InitializeNew();
|
||||
private static readonly FishTable<ByInt<T1, T2, TResult>, TResult> _get = InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static FishTable<ByInt<T1, T2, TResult>, TResult>? _getThreadStatic;
|
||||
@@ -173,7 +154,7 @@ public record struct ByInt<T1, T2, TResult>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref TResult GetOrAddReference(int first, int second)
|
||||
=> ref Get.GetOrAddReference(new() { First = first, Second = second });
|
||||
=> ref Get.GetOrAddReference(default(ByInt<T1, T2, TResult>) with { First = first, Second = second });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref TResult GetOrAddReference(in ByInt<T1, T2, TResult> key)
|
||||
@@ -181,14 +162,14 @@ public record struct ByInt<T1, T2, TResult>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TResult GetOrAdd(int first, int second)
|
||||
=> Get.GetOrAdd(new() { First = first, Second = second });
|
||||
=> Get.GetOrAdd(default(ByInt<T1, T2, TResult>) with { First = first, Second = second });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TResult GetOrAdd(in ByInt<T1, T2, TResult> key) => Get.GetOrAdd(ref Unsafe.AsRef(in key));
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(int first, int second)
|
||||
=> ref Get.GetReference(new() { First = first, Second = second });
|
||||
=> ref Get.GetReference(default(ByInt<T1, T2, TResult>) with { First = first, Second = second });
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(T1 first, T2 second)
|
||||
@@ -217,23 +198,9 @@ public record struct ByInt<T1, T2, TResult>
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<ByInt<T1, T2, TResult>, TResult> InitializeNew()
|
||||
{
|
||||
var newCache = Utility.AddNew(ValueInitializer);
|
||||
|
||||
lock (_allCaches)
|
||||
_allCaches.Add(newCache);
|
||||
|
||||
return newCache;
|
||||
}
|
||||
=> Utility.AddNew(_allCaches, ValueInitializer);
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
lock (_allCaches)
|
||||
{
|
||||
foreach (var cache in _allCaches)
|
||||
cache.Clear();
|
||||
}
|
||||
}
|
||||
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
@@ -242,16 +209,39 @@ public record struct ByInt<T1, T2, T3, TResult>
|
||||
{
|
||||
public int First, Second, Third;
|
||||
|
||||
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult> _get
|
||||
= Utility.AddNew<ByInt<T1, T2, T3, TResult>, TResult>();
|
||||
private static readonly object _valueInitializerLock = new();
|
||||
|
||||
private static readonly List<IDictionary> _allCaches = [];
|
||||
|
||||
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult> _get = InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult>? _getThreadStatic;
|
||||
|
||||
private static Func<ByInt<T1, T2, T3, TResult>, TResult>? _valueInitializer;
|
||||
|
||||
public static Func<ByInt<T1, T2, T3, TResult>, TResult>? ValueInitializer
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
return _valueInitializer;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
{
|
||||
value ??= static _ => Reflection.New<TResult>();
|
||||
_valueInitializer = value;
|
||||
_get.ValueInitializer = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FishTable<ByInt<T1, T2, T3, TResult>, TResult> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _getThreadStatic ??= Utility.AddNew<ByInt<T1, T2, T3, TResult>, TResult>();
|
||||
get => _getThreadStatic ??= InitializeNew();
|
||||
}
|
||||
|
||||
public static FishTable<ByInt<T1, T2, T3, TResult>, TResult> GetDirectly
|
||||
@@ -263,7 +253,7 @@ public record struct ByInt<T1, T2, T3, TResult>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref TResult GetOrAddReference(int first, int second, int third)
|
||||
{
|
||||
var key = new ByInt<T1, T2, T3, TResult> { First = first, Second = second, Third = third };
|
||||
var key = default(ByInt<T1, T2, T3, TResult>) with { First = first, Second = second, Third = third };
|
||||
return ref Get.GetOrAddReference(ref key);
|
||||
}
|
||||
|
||||
@@ -273,7 +263,10 @@ public record struct ByInt<T1, T2, T3, TResult>
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(int first, int second, int third)
|
||||
=> ref Get.GetReference(new() { First = first, Second = second, Third = third });
|
||||
=> ref Get.GetReference(default(ByInt<T1, T2, T3, TResult>) with
|
||||
{
|
||||
First = first, Second = second, Third = third
|
||||
});
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(T1 first, T2 second, T3 third)
|
||||
@@ -295,6 +288,12 @@ public record struct ByInt<T1, T2, T3, TResult>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => HashCode.Combine(First, Second, Third);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult> InitializeNew()
|
||||
=> Utility.AddNew(_allCaches, ValueInitializer);
|
||||
|
||||
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
@@ -304,16 +303,39 @@ public record struct ByInt<T1, T2, T3, T4, TResult>
|
||||
{
|
||||
public int First, Second, Third, Fourth;
|
||||
|
||||
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> _get
|
||||
= Utility.AddNew<ByInt<T1, T2, T3, T4, TResult>, TResult>();
|
||||
private static readonly object _valueInitializerLock = new();
|
||||
|
||||
private static readonly List<IDictionary> _allCaches = [];
|
||||
|
||||
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> _get = InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult>? _getThreadStatic;
|
||||
|
||||
private static Func<ByInt<T1, T2, T3, T4, TResult>, TResult>? _valueInitializer;
|
||||
|
||||
public static Func<ByInt<T1, T2, T3, T4, TResult>, TResult>? ValueInitializer
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
return _valueInitializer;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
{
|
||||
value ??= static _ => Reflection.New<TResult>();
|
||||
_valueInitializer = value;
|
||||
_get.ValueInitializer = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _getThreadStatic ??= Utility.AddNew<ByInt<T1, T2, T3, T4, TResult>, TResult>();
|
||||
get => _getThreadStatic ??= InitializeNew();
|
||||
}
|
||||
|
||||
public static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> GetDirectly
|
||||
@@ -325,7 +347,7 @@ public record struct ByInt<T1, T2, T3, T4, TResult>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref TResult GetOrAddReference(int first, int second, int third, int fourth)
|
||||
{
|
||||
var key = new ByInt<T1, T2, T3, T4, TResult>
|
||||
var key = default(ByInt<T1, T2, T3, T4, TResult>) with
|
||||
{
|
||||
First = first, Second = second, Third = third, Fourth = fourth
|
||||
};
|
||||
@@ -338,7 +360,10 @@ public record struct ByInt<T1, T2, T3, T4, TResult>
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(int first, int second, int third, int fourth)
|
||||
=> ref Get.GetReference(new() { First = first, Second = second, Third = third, Fourth = fourth });
|
||||
=> ref Get.GetReference(default(ByInt<T1, T2, T3, T4, TResult>) with
|
||||
{
|
||||
First = first, Second = second, Third = third, Fourth = fourth
|
||||
});
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(T1 first, T2 second, T3 third, T4 fourth)
|
||||
@@ -361,5 +386,11 @@ public record struct ByInt<T1, T2, T3, T4, TResult>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => HashCode.Combine(First, Second, Third, Fourth);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> InitializeNew()
|
||||
=> Utility.AddNew(_allCaches, ValueInitializer);
|
||||
|
||||
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||
}
|
||||
#pragma warning restore CS9091
|
||||
@@ -8,6 +8,7 @@ using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
// ReSharper disable WithExpressionModifiesAllMembers
|
||||
|
||||
namespace PerformanceFish.Cache;
|
||||
|
||||
@@ -18,9 +19,9 @@ public static class ByReferenceUnclearable<T, TResult>
|
||||
{
|
||||
public const int MINIMUM_CALL_COUNT_FOR_STARTING_TASK = 8;
|
||||
|
||||
public static object SyncLock = new();
|
||||
public static readonly object SyncLock = new();
|
||||
|
||||
private static FishTable<T, TResult> _get = Utility.AddNewUnclearable<T, TResult>();
|
||||
private static readonly FishTable<T, TResult> _get = Utility.AddNewUnclearable<T, TResult>();
|
||||
|
||||
[ThreadStatic]
|
||||
private static FishTable<T, TResult>? _getThreadStatic;
|
||||
@@ -138,7 +139,8 @@ public static class ByReferenceUnclearable<T, TResult>
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public static Task<TCacheResult>? RequestFromCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TArgument2 second)
|
||||
public static Task<TCacheResult>? RequestFromCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key,
|
||||
TArgument2 second)
|
||||
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
||||
{
|
||||
TCacheValue cacheCopy;
|
||||
@@ -200,8 +202,8 @@ public static class ByReferenceUnclearable<T, TResult>
|
||||
return !cache.Result.Equals<TCacheResult>(default);
|
||||
}
|
||||
|
||||
private static async Task<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TCacheValue cacheCopy,
|
||||
TArgument2 second)
|
||||
private static async Task<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key,
|
||||
TCacheValue cacheCopy, TArgument2 second)
|
||||
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
||||
{
|
||||
TCacheResult? result = default;
|
||||
@@ -240,11 +242,13 @@ public static class ByReferenceUnclearable<T, TResult>
|
||||
public static class ByReference<T, TResult>
|
||||
where T : notnull where TResult : new()
|
||||
{
|
||||
private static object _valueInitializerLock = new();
|
||||
private static readonly object _valueInitializerLock = new();
|
||||
|
||||
public static object SyncLock = new();
|
||||
public static readonly object SyncLock = new();
|
||||
|
||||
private static readonly List<IDictionary> _allCaches = [];
|
||||
|
||||
private static FishTable<T, TResult> _get = InitializeNew();
|
||||
private static readonly FishTable<T, TResult> _get = InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static FishTable<T, TResult>? _getThreadStatic;
|
||||
@@ -293,15 +297,8 @@ public static class ByReference<T, TResult>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref TResult GetOrAddReference(ref T key) => ref Get.GetOrAddReference(ref key);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(T key) => ref Get.GetReference(key);
|
||||
|
||||
public static void Remove(T key)
|
||||
{
|
||||
Get.Remove(key);
|
||||
GetDirectly.Remove(key);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Update<VResult>(T key) where VResult : TResult, ICacheable<T>
|
||||
=> Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
|
||||
@@ -393,7 +390,8 @@ public static class ByReference<T, TResult>
|
||||
return result ?? ThrowHelper.ThrowInvalidOperationException<TCacheResult>();
|
||||
}
|
||||
|
||||
public static Task<TCacheResult> RequestFromCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TArgument2 second)
|
||||
public static Task<TCacheResult> RequestFromCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key,
|
||||
TArgument2 second)
|
||||
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
||||
{
|
||||
TCacheValue cacheCopy;
|
||||
@@ -424,8 +422,8 @@ public static class ByReference<T, TResult>
|
||||
return !cache.Result.Equals<TCacheResult>(default);
|
||||
}
|
||||
|
||||
private static async ValueTask<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TCacheValue cacheCopy,
|
||||
TArgument2 second)
|
||||
private static async ValueTask<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key,
|
||||
TCacheValue cacheCopy, TArgument2 second)
|
||||
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
||||
{
|
||||
TCacheResult? result = default;
|
||||
@@ -444,23 +442,14 @@ public static class ByReference<T, TResult>
|
||||
return result ?? ThrowHelper.ThrowInvalidOperationException<TCacheResult>();
|
||||
}
|
||||
|
||||
public static unsafe void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = FisheryLib.FunctionPointers.Equals<T>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T>.Default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(
|
||||
$"Performance Fish encountered an exception while trying to initialize {
|
||||
typeof(ByReference<T, TResult>).FullName}\n{ex}");
|
||||
}
|
||||
}
|
||||
public static void Initialize() => Utility.Internal.Initialize<T>();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<T, TResult> InitializeNew() => Utility.AddNew(ValueInitializer);
|
||||
private static FishTable<T, TResult> InitializeNew() => Utility.AddNew(_allCaches, ValueInitializer);
|
||||
|
||||
public static void Remove(T key) => Utility.Internal.RemoveIn(_allCaches, key);
|
||||
|
||||
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
@@ -468,16 +457,43 @@ public static class ByReference<T, TResult>
|
||||
public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
where T1 : notnull where T2 : notnull where TResult : new()
|
||||
{
|
||||
private static FishTable<ByReference<T1, T2, TResult>, TResult> _get
|
||||
= Utility.AddNew<ByReference<T1, T2, TResult>, TResult>();
|
||||
public T1 First;
|
||||
public T2 Second;
|
||||
|
||||
private static readonly object _valueInitializerLock = new();
|
||||
|
||||
private static readonly List<IDictionary> _allCaches = [];
|
||||
|
||||
private static readonly FishTable<ByReference<T1, T2, TResult>, TResult> _get
|
||||
= InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static FishTable<ByReference<T1, T2, TResult>, TResult>? _getThreadStatic;
|
||||
|
||||
private static Func<ByReference<T1, T2, TResult>, TResult>? _valueInitializer;
|
||||
|
||||
public static Func<ByReference<T1, T2, TResult>, TResult>? ValueInitializer
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
return _valueInitializer;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
{
|
||||
value ??= static _ => Reflection.New<TResult>();
|
||||
_valueInitializer = value;
|
||||
_get.ValueInitializer = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FishTable<ByReference<T1, T2, TResult>, TResult> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _getThreadStatic ??= Utility.AddNew<ByReference<T1, T2, TResult>, TResult>();
|
||||
get => _getThreadStatic ??= InitializeNew();
|
||||
}
|
||||
|
||||
public static FishTable<ByReference<T1, T2, TResult>, TResult> GetDirectly
|
||||
@@ -488,7 +504,7 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref TResult GetOrAddReference(T1 first, T2 second)
|
||||
=> ref Get.GetOrAddReference(new() { First = first, Second = second });
|
||||
=> ref Get.GetOrAddReference(default(ByReference<T1, T2, TResult>) with { First = first, Second = second });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref TResult GetOrAddReference(in ByReference<T1, T2, TResult> key)
|
||||
@@ -497,7 +513,7 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static unsafe ref TResult GetExistingReference(T1 first, T2 second)
|
||||
{
|
||||
var key = new ByReference<T1, T2, TResult>(first, second);
|
||||
var key = default(ByReference<T1, T2, TResult>) with { First = first, Second = second };
|
||||
return ref Get.GetReference(ref key);
|
||||
}
|
||||
|
||||
@@ -509,7 +525,7 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
public static void Update<VResult>(T1 first, T2 second)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>>
|
||||
{
|
||||
var key = new ByReference<T1, T2, TResult>(first, second);
|
||||
var key = default(ByReference<T1, T2, TResult>) with { First = first, Second = second };
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
|
||||
}
|
||||
|
||||
@@ -517,7 +533,7 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
public static void Update<VResult, V2>(T1 key1, T2 key2, V2 second)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2>
|
||||
{
|
||||
var key = new ByReference<T1, T2, TResult>(key1, key2);
|
||||
var key = default(ByReference<T1, T2, TResult>) with { First = key1, Second = key2 };
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
|
||||
}
|
||||
|
||||
@@ -525,7 +541,7 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
public static void Update<VResult, V2, V3>(T1 key1, T2 key2, V2 second, V3 third)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2, V3>
|
||||
{
|
||||
var key = new ByReference<T1, T2, TResult>(key1, key2);
|
||||
var key = default(ByReference<T1, T2, TResult>) with { First = key1, Second = key2 };
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
|
||||
}
|
||||
|
||||
@@ -533,7 +549,7 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
public static unsafe ref VResult GetAndCheck<VResult>(T1 first, T2 second)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>>
|
||||
{
|
||||
var key = new ByReference<T1, T2, TResult>(first, second);
|
||||
var key = default(ByReference<T1, T2, TResult>) with { First = first, Second = second };
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key);
|
||||
@@ -545,7 +561,7 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
public static unsafe ref VResult GetAndCheck<VResult, V2>(T1 key1, T2 key2, V2 second)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2>
|
||||
{
|
||||
var key = new ByReference<T1, T2, TResult>(key1, key2);
|
||||
var key = default(ByReference<T1, T2, TResult>) with { First = key1, Second = key2 };
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key, second);
|
||||
@@ -557,7 +573,7 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
public static unsafe ref VResult GetAndCheck<VResult, V2, V3>(T1 key1, T2 key2, V2 second, V3 third)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2, V3>
|
||||
{
|
||||
var key = new ByReference<T1, T2, TResult>(key1, key2);
|
||||
var key = default(ByReference<T1, T2, TResult>) with { First = key1, Second = key2 };
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key, second, third);
|
||||
@@ -579,29 +595,13 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
Second = second;
|
||||
}
|
||||
|
||||
public static unsafe void Initialize()
|
||||
public static void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = FisheryLib.FunctionPointers.Equals<T1>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T2>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<ByReference<T1, T2, TResult>>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T1>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T2>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T1, T2, TResult>>.Default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(
|
||||
$"Performance Fish encountered an exception while trying to initialize {
|
||||
typeof(ByReference<T1, T2, TResult>).FullName}\n{ex}");
|
||||
}
|
||||
Utility.Internal.Initialize<T1>();
|
||||
Utility.Internal.Initialize<T2>();
|
||||
Utility.Internal.Initialize<ByReference<T1, T2, TResult>>();
|
||||
}
|
||||
|
||||
public T1 First;
|
||||
|
||||
public T2 Second;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByReference<T1, T2, TResult> other)
|
||||
=> First.Equals<T1>(other.First)
|
||||
@@ -610,6 +610,14 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => HashCode.Combine(First, Second);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<ByReference<T1, T2, TResult>, TResult> InitializeNew()
|
||||
=> Utility.AddNew(_allCaches, ValueInitializer);
|
||||
|
||||
public static void Remove(ByReference<T1, T2, TResult> key) => Utility.Internal.RemoveIn(_allCaches, key);
|
||||
|
||||
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||
|
||||
T1 IMemberCount<T1>.First
|
||||
{
|
||||
get => First;
|
||||
@@ -628,22 +636,49 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||
public record struct ByReference<T1, T2, T3, TResult>
|
||||
where T1 : notnull where T2 : notnull where T3 : notnull where TResult : new()
|
||||
{
|
||||
// TODO: Switch to FishTable. This is used by some of the reflection patches and currently crashes when not used
|
||||
// with standard dictionaries
|
||||
public T1 First;
|
||||
public T2 Second;
|
||||
public T3 Third;
|
||||
public int HashCode;
|
||||
|
||||
private static Dictionary<ByReference<T1, T2, T3, TResult>, TResult> _get
|
||||
= Utility.AddNew<Dictionary<ByReference<T1, T2, T3, TResult>, TResult>>();
|
||||
// TODO: Fix ReflectionCaching:CustomAttributeCache causing crashes when not used with standard dictionaries
|
||||
|
||||
private static readonly object _valueInitializerLock = new();
|
||||
|
||||
private static readonly List<IDictionary> _allCaches = [];
|
||||
|
||||
private static readonly FishTable<ByReference<T1, T2, T3, TResult>, TResult> _get = InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByReference<T1, T2, T3, TResult>, TResult>? _getThreadStatic;
|
||||
private static FishTable<ByReference<T1, T2, T3, TResult>, TResult>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByReference<T1, T2, T3, TResult>, TResult> Get
|
||||
private static Func<ByReference<T1, T2, T3, TResult>, TResult>? _valueInitializer;
|
||||
|
||||
public static Func<ByReference<T1, T2, T3, TResult>, TResult>? ValueInitializer
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _getThreadStatic ??= Utility.AddNew<Dictionary<ByReference<T1, T2, T3, TResult>, TResult>>();
|
||||
get
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
return _valueInitializer;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
{
|
||||
value ??= static _ => Reflection.New<TResult>();
|
||||
_valueInitializer = value;
|
||||
_get.ValueInitializer = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<ByReference<T1, T2, T3, TResult>, TResult> GetDirectly
|
||||
public static FishTable<ByReference<T1, T2, T3, TResult>, TResult> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _getThreadStatic ??= InitializeNew();
|
||||
}
|
||||
|
||||
public static FishTable<ByReference<T1, T2, T3, TResult>, TResult> GetDirectly
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _get;
|
||||
@@ -652,7 +687,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third)
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, TResult> { First = first, Second = second, Third = third };
|
||||
var key = Create(first, second, third);
|
||||
return ref Get.GetOrAddReference(ref key);
|
||||
}
|
||||
|
||||
@@ -663,7 +698,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static unsafe ref TResult GetExistingReference(T1 first, T2 second, T3 third)
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, TResult>(first, second, third);
|
||||
var key = Create(first, second, third);
|
||||
return ref Get.GetReference(ref key);
|
||||
}
|
||||
|
||||
@@ -675,7 +710,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
public static void Update<VResult>(T1 first, T2 second, T3 third)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, TResult>(first, second, third);
|
||||
var key = Create(first, second, third);
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
|
||||
}
|
||||
|
||||
@@ -683,7 +718,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
public static void Update<VResult, V2>(T1 key1, T2 key2, T3 key3, V2 second)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>, V2>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, TResult>(key1, key2, key3);
|
||||
var key = Create(key1, key2, key3);
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
|
||||
}
|
||||
|
||||
@@ -691,7 +726,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
public static void Update<VResult, V2, V3>(T1 key1, T2 key2, T3 key3, V2 second, V3 third)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>, V2, V3>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, TResult>(key1, key2, key3);
|
||||
var key = Create(key1, key2, key3);
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
|
||||
}
|
||||
|
||||
@@ -699,7 +734,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
public static unsafe ref VResult GetAndCheck<VResult>(T1 first, T2 second, T3 third)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, TResult>(first, second, third);
|
||||
var key = Create(first, second, third);
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key);
|
||||
@@ -711,7 +746,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
public static unsafe ref VResult GetAndCheck<VResult, V2>(T1 key1, T2 key2, T3 key3, V2 second)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>, V2>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, TResult>(key1, key2, key3);
|
||||
var key = Create(key1, key2, key3);
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key, second);
|
||||
@@ -723,7 +758,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
public static unsafe ref VResult GetAndCheck<VResult, V2, V3>(T1 key1, T2 key2, T3 key3, V2 second, V3 third)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>, V2, V3>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, TResult>(key1, key2, key3);
|
||||
var key = Create(key1, key2, key3);
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key, second, third);
|
||||
@@ -732,42 +767,33 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// ReSharper disable once ConvertToPrimaryConstructor
|
||||
public ByReference(T1 first, T2 second, T3 third)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
Third = third;
|
||||
_hashCode = HashCode.Combine(first, second, third);
|
||||
HashCode = FisheryLib.HashCode.Combine(first, second, third);
|
||||
}
|
||||
|
||||
public static unsafe void Initialize()
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ByReference<T1, T2, T3, TResult> Create(T1 first, T2 second, T3 third)
|
||||
=> default(ByReference<T1, T2, T3, TResult>) with
|
||||
{
|
||||
First = first,
|
||||
Second = second,
|
||||
Third = third,
|
||||
HashCode = FisheryLib.HashCode.Combine(first, second, third)
|
||||
};
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = FisheryLib.FunctionPointers.Equals<T1>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T2>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T3>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<ByReference<T1, T2, T3, TResult>>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T1>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T2>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T3>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T1, T2, T3, TResult>>.Default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Performance Fish encountered an exception while trying to initialize {
|
||||
typeof(ByReference<T1, T2, T3, TResult>).FullName}\n{ex}");
|
||||
}
|
||||
Utility.Internal.Initialize<T1>();
|
||||
Utility.Internal.Initialize<T2>();
|
||||
Utility.Internal.Initialize<T3>();
|
||||
Utility.Internal.Initialize<ByReference<T1, T2, T3, TResult>>();
|
||||
}
|
||||
|
||||
public T1 First;
|
||||
|
||||
public T2 Second;
|
||||
|
||||
public T3 Third;
|
||||
|
||||
private int _hashCode;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByReference<T1, T2, T3, TResult> other)
|
||||
=> First.Equals<T1>(other.First)
|
||||
@@ -775,7 +801,15 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
&& Third.Equals<T3>(other.Third);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => _hashCode;
|
||||
public override int GetHashCode() => HashCode;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<ByReference<T1, T2, T3, TResult>, TResult> InitializeNew()
|
||||
=> Utility.AddNew(_allCaches, ValueInitializer);
|
||||
|
||||
public static void Remove(ByReference<T1, T2, T3, TResult> key) => Utility.Internal.RemoveIn(_allCaches, key);
|
||||
|
||||
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
@@ -783,16 +817,45 @@ public record struct ByReference<T1, T2, T3, TResult>
|
||||
public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
where T1 : notnull where T2 : notnull where T3 : notnull where T4 : notnull where TResult : new()
|
||||
{
|
||||
private static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult> _get
|
||||
= Utility.AddNew<ByReference<T1, T2, T3, T4, TResult>, TResult>();
|
||||
public T1 First;
|
||||
public T2 Second;
|
||||
public T3 Third;
|
||||
public T4 Fourth;
|
||||
public int HashCode;
|
||||
|
||||
private static readonly object _valueInitializerLock = new();
|
||||
|
||||
private static readonly List<IDictionary> _allCaches = [];
|
||||
|
||||
private static readonly FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult> _get = InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult>? _getThreadStatic;
|
||||
|
||||
private static Func<ByReference<T1, T2, T3, T4, TResult>, TResult>? _valueInitializer;
|
||||
|
||||
public static Func<ByReference<T1, T2, T3, T4, TResult>, TResult>? ValueInitializer
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
return _valueInitializer;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
{
|
||||
value ??= static _ => Reflection.New<TResult>();
|
||||
_valueInitializer = value;
|
||||
_get.ValueInitializer = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _getThreadStatic ??= Utility.AddNew<ByReference<T1, T2, T3, T4, TResult>, TResult>();
|
||||
get => _getThreadStatic ??= InitializeNew();
|
||||
}
|
||||
|
||||
public static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult> GetDirectly
|
||||
@@ -804,7 +867,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third, T4 fourth)
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, T4, TResult> { First = first, Second = second, Third = third, Fourth = fourth};
|
||||
var key = Create(first, second, third, fourth);
|
||||
return ref Get.GetOrAddReference(ref key);
|
||||
}
|
||||
|
||||
@@ -815,7 +878,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static unsafe ref TResult GetExistingReference(T1 first, T2 second, T3 third, T4 fourth)
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, T4, TResult>(first, second, third, fourth);
|
||||
var key = Create(first, second, third, fourth);
|
||||
return ref Get.GetReference(ref key);
|
||||
}
|
||||
|
||||
@@ -827,7 +890,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
public static void Update<VResult>(T1 first, T2 second, T3 third, T4 fourth)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, T4, TResult>(first, second, third, fourth);
|
||||
var key = Create(first, second, third, fourth);
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
|
||||
}
|
||||
|
||||
@@ -835,7 +898,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
public static void Update<VResult, V2>(T1 key1, T2 key2, T3 key3, T4 key4, V2 second)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>, V2>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, T4, TResult>(key1, key2, key3, key4);
|
||||
var key = Create(key1, key2, key3, key4);
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
|
||||
}
|
||||
|
||||
@@ -843,7 +906,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
public static void Update<VResult, V2, V3>(T1 key1, T2 key2, T3 key3, T4 key4, V2 second, V3 third)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>, V2, V3>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, T4, TResult>(key1, key2, key3, key4);
|
||||
var key = Create(key1, key2, key3, key4);
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
|
||||
}
|
||||
|
||||
@@ -851,7 +914,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
public static unsafe ref VResult GetAndCheck<VResult>(T1 first, T2 second, T3 third, T4 fourth)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, T4, TResult>(first, second, third, fourth);
|
||||
var key = Create(first, second, third, fourth);
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key);
|
||||
@@ -863,7 +926,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
public static unsafe ref VResult GetAndCheck<VResult, V2>(T1 key1, T2 key2, T3 key3, T4 key4, V2 second)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>, V2>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, T4, TResult>(key1, key2, key3, key4);
|
||||
var key = Create(key1, key2, key3, key4);
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key, second);
|
||||
@@ -876,7 +939,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
V3 third)
|
||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>, V2, V3>
|
||||
{
|
||||
var key = new ByReference<T1, T2, T3, T4, TResult>(key1, key2, key3, key4);
|
||||
var key = Create(key1, key2, key3, key4);
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key, second, third);
|
||||
@@ -885,48 +948,36 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// ReSharper disable once ConvertToPrimaryConstructor
|
||||
public ByReference(T1 first, T2 second, T3 third, T4 fourth)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
Third = third;
|
||||
Fourth = fourth;
|
||||
_hashCode = HashCode.Combine(first, second, third, fourth);
|
||||
HashCode = FisheryLib.HashCode.Combine(first, second, third, fourth);
|
||||
}
|
||||
|
||||
public static unsafe void Initialize()
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ByReference<T1, T2, T3, T4, TResult> Create(T1 first, T2 second, T3 third, T4 fourth)
|
||||
=> default(ByReference<T1, T2, T3, T4, TResult>) with
|
||||
{
|
||||
First = first,
|
||||
Second = second,
|
||||
Third = third,
|
||||
Fourth = fourth,
|
||||
HashCode = FisheryLib.HashCode.Combine(first, second, third, fourth)
|
||||
};
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = FisheryLib.FunctionPointers.Equals<T1>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T2>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T3>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T4>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<ByReference<T1, T2, T3, T4, TResult>>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T1>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T2>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T3>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T4>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T1, T2, T3, T4, TResult>>.Default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(
|
||||
$"Performance Fish encountered an exception while trying to initialize {
|
||||
typeof(ByReference<T1, T2, T3, T4, TResult>).FullName}\n{ex}");
|
||||
}
|
||||
Utility.Internal.Initialize<T1>();
|
||||
Utility.Internal.Initialize<T2>();
|
||||
Utility.Internal.Initialize<T3>();
|
||||
Utility.Internal.Initialize<T4>();
|
||||
Utility.Internal.Initialize<ByReference<T1, T2, T3, T4, TResult>>();
|
||||
}
|
||||
|
||||
public T1 First;
|
||||
|
||||
public T2 Second;
|
||||
|
||||
public T3 Third;
|
||||
|
||||
public T4 Fourth;
|
||||
|
||||
private int _hashCode;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByReference<T1, T2, T3, T4, TResult> other)
|
||||
=> First.Equals<T1>(other.First)
|
||||
@@ -935,6 +986,182 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
||||
&& Fourth.Equals<T4>(other.Fourth);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => _hashCode;
|
||||
public override int GetHashCode() => HashCode;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult> InitializeNew()
|
||||
=> Utility.AddNew(_allCaches, ValueInitializer);
|
||||
|
||||
public static void Remove(ByReference<T1, T2, T3, T4, TResult> key) => Utility.Internal.RemoveIn(_allCaches, key);
|
||||
|
||||
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public record struct ByReferenceClassic<T1, T2, T3, TResult>
|
||||
where T1 : notnull where T2 : notnull where T3 : notnull where TResult : new()
|
||||
{
|
||||
public T1 First;
|
||||
public T2 Second;
|
||||
public T3 Third;
|
||||
public int HashCode;
|
||||
|
||||
// TODO: Figure out why this behaves differently compared to FishTables on ReflectionCaching:CustomAttributeCache
|
||||
|
||||
private static readonly object _valueInitializerLock = new();
|
||||
|
||||
private static readonly List<IDictionary> _allCaches = [];
|
||||
|
||||
private static readonly Dictionary<ByReferenceClassic<T1, T2, T3, TResult>, TResult> _get = InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByReferenceClassic<T1, T2, T3, TResult>, TResult>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByReferenceClassic<T1, T2, T3, TResult>, TResult> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _getThreadStatic ??= InitializeNew();
|
||||
}
|
||||
|
||||
public static Dictionary<ByReferenceClassic<T1, T2, T3, TResult>, TResult> GetDirectly
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _get;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third)
|
||||
{
|
||||
var key = Create(first, second, third);
|
||||
return ref Get.GetOrAddReference(ref key);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref TResult GetOrAddReference(in ByReferenceClassic<T1, T2, T3, TResult> key)
|
||||
=> ref Get.GetOrAddReference(ref Unsafe.AsRef(in key));
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static unsafe ref TResult GetExistingReference(T1 first, T2 second, T3 third)
|
||||
{
|
||||
var key = Create(first, second, third);
|
||||
return ref Get.GetReference(ref key);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(in ByReferenceClassic<T1, T2, T3, TResult> key)
|
||||
=> ref Get.GetReference(ref Unsafe.AsRef(in key));
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void Update<VResult>(T1 first, T2 second, T3 third)
|
||||
where VResult : TResult, ICacheable<ByReferenceClassic<T1, T2, T3, TResult>>
|
||||
{
|
||||
var key = Create(first, second, third);
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void Update<VResult, V2>(T1 key1, T2 key2, T3 key3, V2 second)
|
||||
where VResult : TResult, ICacheable<ByReferenceClassic<T1, T2, T3, TResult>, V2>
|
||||
{
|
||||
var key = Create(key1, key2, key3);
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void Update<VResult, V2, V3>(T1 key1, T2 key2, T3 key3, V2 second, V3 third)
|
||||
where VResult : TResult, ICacheable<ByReferenceClassic<T1, T2, T3, TResult>, V2, V3>
|
||||
{
|
||||
var key = Create(key1, key2, key3);
|
||||
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref VResult GetAndCheck<VResult>(T1 first, T2 second, T3 third)
|
||||
where VResult : TResult, ICacheable<ByReferenceClassic<T1, T2, T3, TResult>>
|
||||
{
|
||||
var key = Create(first, second, third);
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key);
|
||||
|
||||
return ref cache!;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref VResult GetAndCheck<VResult, V2>(T1 key1, T2 key2, T3 key3, V2 second)
|
||||
where VResult : TResult, ICacheable<ByReferenceClassic<T1, T2, T3, TResult>, V2>
|
||||
{
|
||||
var key = Create(key1, key2, key3);
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key, second);
|
||||
|
||||
return ref cache!;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref VResult GetAndCheck<VResult, V2, V3>(T1 key1, T2 key2, T3 key3, V2 second, V3 third)
|
||||
where VResult : TResult, ICacheable<ByReferenceClassic<T1, T2, T3, TResult>, V2, V3>
|
||||
{
|
||||
var key = Create(key1, key2, key3);
|
||||
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||
if (cache.Dirty)
|
||||
cache.Update(ref key, second, third);
|
||||
|
||||
return ref cache!;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// ReSharper disable once ConvertToPrimaryConstructor
|
||||
public ByReferenceClassic(T1 first, T2 second, T3 third)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
Third = third;
|
||||
HashCode = FisheryLib.HashCode.Combine(first, second, third);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ByReferenceClassic<T1, T2, T3, TResult> Create(T1 first, T2 second, T3 third)
|
||||
=> default(ByReferenceClassic<T1, T2, T3, TResult>) with
|
||||
{
|
||||
First = first,
|
||||
Second = second,
|
||||
Third = third,
|
||||
HashCode = FisheryLib.HashCode.Combine(first, second, third)
|
||||
};
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
Utility.Internal.Initialize<T1>();
|
||||
Utility.Internal.Initialize<T2>();
|
||||
Utility.Internal.Initialize<T3>();
|
||||
Utility.Internal.Initialize<ByReferenceClassic<T1, T2, T3, TResult>>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByReferenceClassic<T1, T2, T3, TResult> other)
|
||||
=> First.Equals<T1>(other.First)
|
||||
&& Second.Equals<T2>(other.Second)
|
||||
&& Third.Equals<T3>(other.Third);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => HashCode;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static Dictionary<ByReferenceClassic<T1, T2, T3, TResult>, TResult> InitializeNew()
|
||||
{
|
||||
var newCache = Utility.AddNew<Dictionary<ByReferenceClassic<T1, T2, T3, TResult>, TResult>>();
|
||||
|
||||
lock (_allCaches)
|
||||
_allCaches.Add(newCache);
|
||||
|
||||
return newCache;
|
||||
}
|
||||
|
||||
public static void Remove(ByReferenceClassic<T1, T2, T3, TResult> key) => Utility.Internal.RemoveIn(_allCaches, key);
|
||||
|
||||
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||
}
|
||||
#pragma warning restore CS9091
|
||||
@@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using FisheryLib.FunctionPointers;
|
||||
using JetBrains.Annotations;
|
||||
using PerformanceFish.Events;
|
||||
|
||||
@@ -218,6 +219,20 @@ public static class Utility
|
||||
return newCollection;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||||
public static FishTable<TKey, TValue> AddNew<TKey, TValue>(List<IDictionary> cacheList,
|
||||
Func<TKey, TValue>? valueInitializer = null,
|
||||
Action<KeyValuePair<TKey, TValue>>? onEntryAdded = null,
|
||||
Action<KeyValuePair<TKey, TValue>>? onEntryRemoved = null)
|
||||
{
|
||||
var newCache = AddNew(valueInitializer, onEntryAdded, onEntryRemoved);
|
||||
|
||||
lock (cacheList)
|
||||
cacheList.Add(newCache);
|
||||
|
||||
return newCache;
|
||||
}
|
||||
|
||||
private sealed record OnDestroyActionData<TKey, TValue>(TKey Key, FishTable<TKey, TValue> Collection)
|
||||
{
|
||||
public Action<Thing>? Action;
|
||||
@@ -276,6 +291,69 @@ public static class Utility
|
||||
All.Add(newCollection); // require different constructors.
|
||||
return newCollection; // Reflection.New<T> can't use arguments
|
||||
} // for resolving method calls (yet)
|
||||
|
||||
public static class Internal
|
||||
{
|
||||
public static void RemoveIn<T>(List<IDictionary> caches, T key)
|
||||
{
|
||||
lock (caches)
|
||||
{
|
||||
for (var i = caches.Count; i-- > 0;)
|
||||
caches[i].Remove(key!);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClearIn(List<IDictionary> caches)
|
||||
{
|
||||
lock (caches)
|
||||
{
|
||||
for (var i = caches.Count; i-- > 0;)
|
||||
caches[i].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void Initialize<T>()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = Equals<T>.Default;
|
||||
_ = EqualsByRef<T>.Default;
|
||||
_ = GetHashCode<T>.Default;
|
||||
_ = GetHashCodeByRef<T>.Default;
|
||||
_ = FisheryLib.Collections.Internal.KeyUtility<T>.Default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string name;
|
||||
|
||||
try
|
||||
{
|
||||
name = typeof(T).FullDescription();
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
name = typeof(T).FullName ?? typeof(T).Name;
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
name = typeof(T).Name;
|
||||
}
|
||||
catch (Exception inner)
|
||||
{
|
||||
name = inner.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.Error($"Performance Fish encountered an exception while trying to initialize '{
|
||||
name}'\n==================================================\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public delegate int IndexGetter<in T>(T item);
|
||||
|
||||
@@ -8,14 +8,20 @@ namespace PerformanceFish;
|
||||
public record struct CellIndex(int Value)
|
||||
{
|
||||
public int Value = Value;
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public CellIndex(IntVec3 cell, Map map) : this(cell, map.cellIndices.mapSizeX) {}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public CellIndex(IntVec3 cell, int mapSizeX) : this(cell.CellToIndex(mapSizeX)) {}
|
||||
|
||||
public IntVec3 ToCell(Map map) => ToCell(map.cellIndices.mapSizeX);
|
||||
|
||||
public IntVec3 ToCell(int mapSizeX) => CellIndicesUtility.IndexToCell(Value, mapSizeX);
|
||||
|
||||
public List<Thing> GetThingList(Map map) => map.thingGrid.ThingsListAtFast(Value);
|
||||
public List<Thing> GetThingList(Map map) => map.thingGrid.thingGrid[Value];
|
||||
|
||||
public bool IsFogged(Map map) => map.fogGrid.fogGrid[Value];
|
||||
|
||||
public float GetSnowDepth(Map map) => map.snowGrid.depthGrid[Value];
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) 2023 bradson
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#if !V1_4
|
||||
using LudeonTK;
|
||||
#endif
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using PerformanceFish.Prepatching;
|
||||
|
||||
namespace PerformanceFish;
|
||||
|
||||
public sealed class DebugActionFixes : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class InitActionsPatch : FishPrepatch
|
||||
{
|
||||
// useless without the other, as they initialize simultaneously
|
||||
public override List<Type> LinkedPatches { get; } = [typeof(InitOutputsPatch)];
|
||||
|
||||
public override string? Description { get; }
|
||||
= "Adds exception handling to the debug actions menu, allowing it to still generate and open when having "
|
||||
+ "one mod throw errors. Also logs where that error gets thrown";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; } = AccessTools.DeclaredMethod(typeof(DebugTabMenu_Actions),
|
||||
nameof(DebugTabMenu_Actions.InitActions));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
public static DebugActionNode ReplacementBody(DebugTabMenu_Actions instance, DebugActionNode absRoot)
|
||||
{
|
||||
var root = instance.myRoot = new("Actions");
|
||||
absRoot.AddChild(root);
|
||||
root.AddChild(instance.moreActionsNode = new("Show more actions")
|
||||
{
|
||||
category = "More debug actions",
|
||||
displayPriority = -999999
|
||||
});
|
||||
|
||||
foreach (var type in GenTypes.AllTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var methodInfo in methods)
|
||||
{
|
||||
if (methodInfo.TryGetAttribute<DebugActionAttribute>(out var customAttribute))
|
||||
instance.GenerateCacheForMethod(methodInfo, customAttribute);
|
||||
|
||||
if (!methodInfo.TryGetAttribute<DebugActionYielderAttribute>(out _))
|
||||
continue;
|
||||
|
||||
foreach (var item in (IEnumerable<DebugActionNode>)methodInfo.Invoke(null, null))
|
||||
root.AddChild(item);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Exception caught while trying to initialize debug actions for type '{
|
||||
type.FullDescription()}': {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
root.TrySort();
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class InitOutputsPatch : FishPrepatch
|
||||
{
|
||||
public override List<Type> LinkedPatches { get; } = [typeof(InitActionsPatch)];
|
||||
|
||||
public override string? Description { get; }
|
||||
= "Adds exception handling to the debug outputs menu, allowing it to still generate and open when having "
|
||||
+ "one mod throw errors. Also logs where that error gets thrown";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; } = AccessTools.DeclaredMethod(typeof(DebugTabMenu_Output),
|
||||
nameof(DebugTabMenu_Output.InitActions));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
public static DebugActionNode ReplacementBody(DebugTabMenu_Output instance, DebugActionNode absRoot)
|
||||
{
|
||||
var root = instance.myRoot = new("Outputs");
|
||||
absRoot.AddChild(root);
|
||||
|
||||
foreach (var type in GenTypes.AllTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var methodInfo in methods)
|
||||
{
|
||||
if (methodInfo.TryGetAttribute<DebugOutputAttribute>(out var customAttribute))
|
||||
instance.GenerateCacheForMethod(methodInfo, customAttribute);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Exception caught while trying to initialize debug outputs for type '{
|
||||
type.FullDescription()}': {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
root.TrySort();
|
||||
return root;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace PerformanceFish;
|
||||
namespace PerformanceFish.Defs;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
|
||||
public record struct DefPair
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2023 bradson
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using PerformanceFish.Prepatching;
|
||||
|
||||
namespace PerformanceFish.Defs;
|
||||
|
||||
public sealed class IncidentDefFixes : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class WorkerPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "RimWorld has no checks to verify that IncidentDefs include valid classes with their actual "
|
||||
+ "implementation. This means whenever mods either don't provide one or it simply fails to load for any "
|
||||
+ "reason, entirely uninformative errors get spammed at the point the storyteller tries starting an "
|
||||
+ "incident at. This patch here adds a check, logging the erroneous def with mod source when possible and "
|
||||
+ "disabling the IncidentWorker from running to prevent the error spam.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(IncidentDef), nameof(IncidentDef.Worker));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(Replacement);
|
||||
|
||||
public static IncidentWorker Replacement(IncidentDef instance)
|
||||
{
|
||||
if (instance.workerInt == null)
|
||||
{
|
||||
if (instance.workerClass is null)
|
||||
TryFixAndLogError(instance);
|
||||
|
||||
instance.workerInt = (IncidentWorker)Activator.CreateInstance(instance.workerClass!);
|
||||
instance.workerInt.def = instance;
|
||||
}
|
||||
|
||||
return instance.workerInt;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void TryFixAndLogError(IncidentDef instance)
|
||||
{
|
||||
instance.workerClass = typeof(IncidentWorker_AlwaysSkip);
|
||||
Log.Error($"Detected missing workerClass in IncidentDef '{
|
||||
instance.defName ?? "null"}' from mod '{
|
||||
instance.modContentPack?.Name ?? "null"}' for IncidentCategoryDef '{
|
||||
instance.category?.defName ?? "null"}'. Disabling now to avoid further issues.");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class IncidentWorker_AlwaysSkip : IncidentWorker
|
||||
{
|
||||
#if !V1_4
|
||||
public override float ChanceFactorNow(IIncidentTarget target) => 0f;
|
||||
#endif
|
||||
|
||||
public override float BaseChanceThisGame => 0f;
|
||||
|
||||
protected override bool CanFireNowSub(IncidentParms parms) => true;
|
||||
|
||||
protected override bool TryExecuteWorker(IncidentParms parms) => true;
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -7,17 +7,17 @@ using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using PerformanceFish.Prepatching;
|
||||
|
||||
namespace PerformanceFish;
|
||||
namespace PerformanceFish.Defs;
|
||||
|
||||
public sealed class WorkGiverDefFixes : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class WorkerPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "RimWorld has no checks to verify that workGiverDefs include valid classes with their actual "
|
||||
= "RimWorld has no checks to verify that WorkGiverDefs include valid classes with their actual "
|
||||
+ "implementation. This means whenever mods either don't provide one or it simply fails to load for any "
|
||||
+ "reason, entirely uninformative errors get spammed at the point pawns try starting a job at. This patch "
|
||||
+ "here adds a check, logging the erroneous def with mod source when possible and disabling the workGiver "
|
||||
+ "here adds a check, logging the erroneous def with mod source when possible and disabling the WorkGiver "
|
||||
+ "from running to prevent the error spam.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
@@ -40,6 +40,7 @@ public sealed class WorkGiverDefFixes : ClassWithFishPrepatches
|
||||
return instance.workerInt;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void TryFixAndLogError(WorkGiverDef instance)
|
||||
{
|
||||
instance.giverClass = typeof(WorkGiver_AlwaysSkip);
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<RootNamespace>PerformanceFish</RootNamespace>
|
||||
<AssemblyName>PerformanceFish</AssemblyName>
|
||||
<Authors>bradson</Authors>
|
||||
<Version>0.6.0.0</Version>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<Nullable>enable</Nullable>
|
||||
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||
<AnalysisLevel>preview-recommended</AnalysisLevel>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>5</WarningLevel>
|
||||
<DebugType>portable</DebugType>
|
||||
<NoWarn>
|
||||
CA1000;CA1010;CA1051;CA1304;CA1305;CA1310;CA1311;CA1707;CA1708;CA1710;CA1711;CA1715;CA1716;CA1720;CA1725;
|
||||
CA1852;CA2201;CS8500;CS9082
|
||||
</NoWarn>
|
||||
<BaseIntermediateOutputPath>obj\$(MSBuildProjectName)\$(Configuration)\</BaseIntermediateOutputPath>
|
||||
<MSBuildProjectExtensionsPath>obj\$(MSBuildProjectName)\$(Configuration)\</MSBuildProjectExtensionsPath>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);obj/**/*.*</DefaultItemExcludes>
|
||||
<PublicizerClearCacheOnClean>true</PublicizerClearCacheOnClean>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Zetrith.Prepatcher" Version="1.*" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="PolySharp" Version="1.*" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.*" Aliases="nuget" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
|
||||
|
||||
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
|
||||
<Publicize Include="Assembly-CSharp:RimWorld.JobGiver_Haul.TryGiveJob" />
|
||||
<!-- DoNotPublicize Include="0Harmony:Mono.Collections.Generic.Collection`1" / -->
|
||||
<DoNotPublicize Include="0Harmony:System.Memory`1" />
|
||||
<DoNotPublicize Include="0Harmony:System.ReadOnlyMemory`1" />
|
||||
<DoNotPublicize Include="0Harmony:System.Span`1" />
|
||||
<DoNotPublicize Include="0Harmony:System.ReadOnlySpan`1" />
|
||||
<DoNotPublicize Include="0Harmony:System.ThrowHelper" />
|
||||
<DoNotPublicize Include="0Harmony:System.HashCode" />
|
||||
<DoNotPublicize Include="0Harmony:System.StringExtensions" />
|
||||
<DoNotPublicize Include="0Harmony:System.MemoryExtensions" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.CallerArgumentExpressionAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.DefaultInterpolatedStringHandler" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IsByRefLikeAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IsExternalInit" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IsReadOnlyAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.NativeIntegerAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.NullableAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.NullableContextAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.NullablePublicOnlyAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.RefSafetyRulesAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.ModuleInitializerAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.ITuple" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.Unsafe" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.AllowNullAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DisallowNullAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.ExtraDynamicallyAccessedMemberTypes" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.MaybeNullAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.MemberNotNullAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.NotNullAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:System.Diagnostics.CodeAnalysis.UnscopedRefAttribute" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.buckets" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.entries" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.version" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2+Entry" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1._items" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1._version" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1._size" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1.EnsureCapacity" />
|
||||
<Publicize Include="System.Core:System.Collections.Generic.HashSet`1._version" />
|
||||
<Publicize Include="mscorlib:System.MonoCustomAttrs.GetCustomAttributes" />
|
||||
<Publicize Include="mscorlib:System.RuntimeType" />
|
||||
<Publicize Include="mscorlib:System.Reflection.MonoField" />
|
||||
<Publicize Include="mscorlib:System.String.m_firstChar" />
|
||||
<None Include="..\..\LICENSE">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath />
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) 2023 bradson
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#if false
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PerformanceFish.Experimental;
|
||||
|
||||
#pragma warning disable CS8500
|
||||
/// <summary>
|
||||
/// References within fixed size buffers don't get tracked by the GC. This leads to memory corruption. Don't use
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct InlineArray256<T> where T : class
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private T _firstEntry;
|
||||
|
||||
[FieldOffset(0)]
|
||||
private fixed ulong _entries[256];
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => (&_firstEntry)[index];
|
||||
set => (&_firstEntry)[index] = value;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS8500
|
||||
#endif
|
||||
@@ -5,54 +5,254 @@
|
||||
|
||||
#if false
|
||||
using System.Runtime.InteropServices;
|
||||
// ReSharper disable PrivateFieldCanBeConvertedToLocalVariable
|
||||
|
||||
namespace PerformanceFish.Experimental;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public unsafe struct UnalignedPointer
|
||||
{
|
||||
private fixed byte _data[6]; // 48bit
|
||||
|
||||
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] // 48bit
|
||||
// private byte[] _data;
|
||||
// 48 bit
|
||||
private ushort
|
||||
_top,
|
||||
_mid,
|
||||
_bot;
|
||||
|
||||
public void* Value
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (void* value = _data)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
var result = (void*)(Unsafe.As<ushort, ulong>(ref _top) & 0x0000_FFFF_FFFF_FFFF);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public UnalignedPointer(void* pointer)
|
||||
{
|
||||
fixed (UnalignedPointer* instance = &this)
|
||||
{
|
||||
instance->_data = (byte*)pointer;
|
||||
}
|
||||
Unsafe.As<ushort, uint>(ref _top) = (uint)(ulong)pointer;
|
||||
_bot = (ushort)((ulong)pointer >> 32);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe struct TaggedPointer
|
||||
{
|
||||
private void* _data;
|
||||
private const ulong
|
||||
LowerBitMask = ~(ulong.MaxValue >> 3),
|
||||
UpperBitMask = 0x0000_0000_0000_FFFF,
|
||||
ValueBitMask = ~LowerBitMask & ~UpperBitMask;
|
||||
|
||||
private ulong _data;
|
||||
|
||||
public void* Value
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = _data;
|
||||
var value = (void*)(_data & ValueBitMask);
|
||||
return value;
|
||||
}
|
||||
set => _data = (_data & ~ValueBitMask) | ((ulong)value & ValueBitMask);
|
||||
}
|
||||
|
||||
public ushort UpperBits => (ushort)_data;
|
||||
public ushort UpperBits
|
||||
{
|
||||
get => (ushort)(_data & UpperBitMask);
|
||||
set => _data = (_data & ~UpperBitMask) | (value & UpperBitMask);
|
||||
}
|
||||
|
||||
public byte LowerBits => (byte)_data;
|
||||
public byte LowerBits
|
||||
{
|
||||
get => (byte)((_data & LowerBitMask) >> 61);
|
||||
set => _data = (_data & ~LowerBitMask) | (((ulong)value << 61) & LowerBitMask);
|
||||
}
|
||||
|
||||
public TaggedPointer(void* pointer) => _data = pointer;
|
||||
public TaggedPointer(void* pointer) => Value = pointer;
|
||||
|
||||
public TaggedPointer(void* pointer, ushort upperBits, byte lowerBits)
|
||||
=> _data = ((ulong)pointer & ValueBitMask)
|
||||
| (upperBits & UpperBitMask)
|
||||
| (((ulong)lowerBits << 61) & LowerBitMask);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct UInt48 : IComparable<UInt48>, IEquatable<UInt48>
|
||||
{
|
||||
public const ulong MaxValue = ulong.MaxValue >> 16;
|
||||
|
||||
private ushort
|
||||
_top,
|
||||
_mid,
|
||||
_bot;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UInt48(ulong value)
|
||||
{
|
||||
Unsafe.As<ushort, uint>(ref _top) = (uint)value;
|
||||
_bot = (ushort)(value >> 32);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(UInt48 other) => this == other;
|
||||
|
||||
public override bool Equals(object? obj) => obj is UInt48 value && this == value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int CompareTo(UInt48 other) => ((ulong)this).CompareTo(other);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => Unsafe.As<ushort, int>(ref _top) ^ _bot;
|
||||
|
||||
public override string ToString() => ((ulong)this).ToString();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(UInt48 left, UInt48 right)
|
||||
{
|
||||
var x = left;
|
||||
var y = right;
|
||||
return (Unsafe.As<ushort, uint>(ref x._top) == Unsafe.As<ushort, uint>(ref y._top)) & (x._bot == y._bot);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(UInt48 left, UInt48 right) => !(left == right);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator <(UInt48 left, UInt48 right) => (ulong)left < right;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator <=(UInt48 left, UInt48 right) => (ulong)left <= right;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator >(UInt48 left, UInt48 right) => (ulong)left > right;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator >=(UInt48 left, UInt48 right) => (ulong)left >= right;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ulong(UInt48 value)
|
||||
{
|
||||
var temp = value;
|
||||
return Unsafe.As<ushort, ulong>(ref temp._top) & MaxValue;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator long(UInt48 value)
|
||||
{
|
||||
var temp = value;
|
||||
return Unsafe.As<ushort, long>(ref temp._top) & (long)MaxValue;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator UInt48(ulong value)
|
||||
{
|
||||
Unsafe.SkipInit(out UInt48 result);
|
||||
Unsafe.As<ushort, uint>(ref result._top) = (uint)value;
|
||||
result._bot = (ushort)(value >> 32);
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator UInt48(long value) => (UInt48)(ulong)value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator UInt48(uint value)
|
||||
{
|
||||
Unsafe.SkipInit(out UInt48 result);
|
||||
Unsafe.As<ushort, uint>(ref result._top) = value;
|
||||
result._bot = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator UInt48(int value) => (UInt48)(uint)value;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct Int48 : IComparable<Int48>, IEquatable<Int48>
|
||||
{
|
||||
public const long MaxValue = long.MaxValue >> 16;
|
||||
|
||||
private ushort
|
||||
_top,
|
||||
_mid,
|
||||
_bot;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Int48(long value)
|
||||
{
|
||||
Unsafe.As<ushort, uint>(ref _top) = (uint)value;
|
||||
_bot = (ushort)((((ulong)value >> 32) & (ulong)short.MaxValue)
|
||||
| ((ulong)(value & long.MinValue) >> 48));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(Int48 other) => this == other;
|
||||
|
||||
public override bool Equals(object? obj) => obj is Int48 value && this == value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int CompareTo(Int48 other) => ((long)this).CompareTo(other);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => Unsafe.As<ushort, int>(ref _top) ^ _bot;
|
||||
|
||||
public override string ToString() => ((long)this).ToString();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Int48 left, Int48 right)
|
||||
{
|
||||
var x = left;
|
||||
var y = right;
|
||||
return (Unsafe.As<ushort, uint>(ref x._top) == Unsafe.As<ushort, uint>(ref y._top)) & (x._bot == y._bot);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Int48 left, Int48 right) => !(left == right);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator <(Int48 left, Int48 right) => (long)left < right;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator <=(Int48 left, Int48 right) => (long)left <= right;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator >(Int48 left, Int48 right) => (long)left > right;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator >=(Int48 left, Int48 right) => (long)left >= right;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ulong(Int48 value) => (ulong)(long)value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator long(Int48 value)
|
||||
{
|
||||
var temp = value;
|
||||
return (Unsafe.As<ushort, long>(ref temp._top) & MaxValue)
|
||||
| (long)(((ulong)temp._bot << 48) & unchecked((ulong)long.MinValue));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Int48(ulong value) => (Int48)(long)value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Int48(long value)
|
||||
{
|
||||
Unsafe.SkipInit(out Int48 result);
|
||||
Unsafe.As<ushort, uint>(ref result._top) = (uint)value;
|
||||
result._bot = (ushort)((((ulong)value >> 32) & (ulong)short.MaxValue)
|
||||
| ((ulong)(value & long.MinValue) >> 48));
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Int48(uint value) => (Int48)(int)value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator Int48(int value)
|
||||
{
|
||||
Unsafe.SkipInit(out Int48 result);
|
||||
Unsafe.As<ushort, uint>(ref result._top) = (uint)(value & int.MaxValue);
|
||||
result._bot = (ushort)((uint)(value & int.MinValue) >> 16);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -93,6 +93,9 @@ public sealed class FishSettings : ModSettings
|
||||
ToggleAllPatches(checkModEnabled);
|
||||
}
|
||||
|
||||
ls.Gap();
|
||||
ls.Label("Changes to prepatches require a restart. Harmony patches update immediately when toggled.");
|
||||
|
||||
ls.Gap();
|
||||
var curFontStyle = Text.CurFontStyle;
|
||||
var curFontSize = curFontStyle.fontSize;
|
||||
|
||||
@@ -19,21 +19,31 @@ public static class IndexGetter
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||
public static class Methods
|
||||
{
|
||||
public static int HediffSet(HediffSet set) => set.pawn.thingIDNumber;
|
||||
public static int HediffDef(HediffDef def) => def.shortHash;
|
||||
public static int Ability(Ability ability) => ability.Id;
|
||||
public static int Bill(Bill bill) => bill.loadID;
|
||||
public static int Pawn(Pawn pawn) => pawn.thingIDNumber;
|
||||
public static int Thing(Thing thing) => thing.thingIDNumber;
|
||||
public static int CompAssignableToPawn_Bed(CompAssignableToPawn_Bed comp) => comp.parent.thingIDNumber;
|
||||
public static int Map(Map map) => map.uniqueID;
|
||||
public static int Def(Def def) => def.shortHash;
|
||||
public static int DesignationManager(DesignationManager manager) => manager.map.uniqueID;
|
||||
public static int DesignationDef(DesignationDef def) => def.shortHash;
|
||||
public static int Room(Room room) => room.ID;
|
||||
public static int Ideo(Ideo ideo) => ideo.id;
|
||||
public static int RecipeDef(RecipeDef def) => def.shortHash;
|
||||
public static int RuntimeTypeHandle(RuntimeTypeHandle handle) => (int)handle.Value;
|
||||
public static int WorldObject(WorldObject worldObject) => worldObject.ID;
|
||||
public static int Hediff(Hediff hediff) => hediff.loadID;
|
||||
public static int HediffComp(HediffComp comp) => comp.parent.loadID;
|
||||
public static int HediffSet(HediffSet set) => set.pawn.thingIDNumber;
|
||||
public static int Ideo(Ideo ideo) => ideo.id;
|
||||
public static int Map(Map map) => map.uniqueID;
|
||||
public static int Room(Room room) => room.ID;
|
||||
public static int RuntimeTypeHandle(RuntimeTypeHandle handle) => (int)handle.Value;
|
||||
public static int Thing(Thing thing) => thing.thingIDNumber;
|
||||
public static int ThingComp(ThingComp comp) => comp.parent.thingIDNumber;
|
||||
public static int ThingRequestGroup(ThingRequestGroup group) => (byte)group;
|
||||
public static int WorldObject(WorldObject worldObject) => worldObject.ID;
|
||||
public static int WorldObjectComp(WorldObjectComp comp) => comp.parent.ID;
|
||||
|
||||
public static int CompAssignableToPawn_Bed(CompAssignableToPawn_Bed comp) => comp.parent.thingIDNumber;
|
||||
public static int DesignationDef(DesignationDef def) => def.shortHash;
|
||||
public static int HediffDef(HediffDef def) => def.shortHash;
|
||||
public static int HediffWithComps(HediffWithComps hediff) => hediff.loadID;
|
||||
public static int Pawn(Pawn pawn) => pawn.thingIDNumber;
|
||||
public static int RecipeDef(RecipeDef def) => def.shortHash;
|
||||
public static int ThingDef(ThingDef def) => def.shortHash;
|
||||
public static int ThingWithComps(ThingWithComps thing) => thing.thingIDNumber;
|
||||
}
|
||||
|
||||
internal static Cache.IndexGetter<T> GetDefaultIndexGetter<T>() where T : notnull
|
||||
|
||||
@@ -15,7 +15,12 @@ public static class GasDefOf
|
||||
public static GasDef
|
||||
BlindSmoke,
|
||||
ToxGas,
|
||||
RotStink;
|
||||
RotStink
|
||||
#if !V1_4
|
||||
,
|
||||
DeadLifeDust
|
||||
#endif
|
||||
;
|
||||
|
||||
static GasDefOf() => DefOfHelper.EnsureInitializedInCtor(typeof(GasDefOf));
|
||||
}
|
||||
|
||||
@@ -8,8 +8,15 @@
|
||||
// #define GAS_DEBUG_L3 // individual cell changes
|
||||
|
||||
using System.Diagnostics;
|
||||
#if V1_4
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
#endif
|
||||
using System.Linq;
|
||||
#if !V1_4
|
||||
using Gilzoide.ManagedJobs;
|
||||
using LudeonTK;
|
||||
using Unity.Jobs;
|
||||
#endif
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using MonoMod.Utils;
|
||||
@@ -32,16 +39,28 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
public static void ReplacementBody(GasGrid __instance, int index, byte smoke, byte toxic, byte rotStink)
|
||||
public static void ReplacementBody(GasGrid __instance, int index, byte smoke, byte toxic, byte rotStink
|
||||
#if !V1_4
|
||||
, byte deadlife
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (!ModsConfig.BiotechActive)
|
||||
toxic = 0;
|
||||
|
||||
#if !V1_4
|
||||
if (!ModsConfig.AnomalyActive)
|
||||
deadlife = 0;
|
||||
#endif
|
||||
|
||||
var gasGrids = __instance.ParallelGasGrids();
|
||||
|
||||
gasGrids[0].SetDirect(index, smoke);
|
||||
gasGrids[1].SetDirect(index, toxic);
|
||||
gasGrids[2].SetDirect(index, rotStink);
|
||||
#if !V1_4
|
||||
gasGrids[3].SetDirect(index, deadlife);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +68,11 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
{
|
||||
public override List<Type> LinkedPatches { get; } =
|
||||
[
|
||||
typeof(SetDirectPatch), typeof(ColorAtPatch), typeof(AddGasPatch), typeof(AnyGasAtPatch),
|
||||
typeof(SetDirectPatch),
|
||||
#if V1_4
|
||||
typeof(ColorAtPatch),
|
||||
#endif
|
||||
typeof(AddGasPatch), typeof(AnyGasAtPatch),
|
||||
typeof(DensityAtPatch), typeof(Debug_ClearAllPatch), typeof(Debug_FillAllPatch),
|
||||
typeof(Notify_ThingSpawnedPatch), typeof(ExposeDataPatch), typeof(MouseOverReadOutOnGUI),
|
||||
typeof(DebugToolsGeneralPushGas)
|
||||
@@ -76,31 +99,47 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
if (gasGrids.Length < 1)
|
||||
return;
|
||||
|
||||
#if !V1_4
|
||||
JobHandle handle;
|
||||
#endif
|
||||
if (FishSettings.ThreadingEnabled
|
||||
#if V1_4
|
||||
&& TryGetRegisteredGasGridWorkers(__instance, out var monitorObject))
|
||||
{
|
||||
TryPulseRegisteredGasGridWorkers(gasGrids, monitorObject);
|
||||
#else
|
||||
)
|
||||
{
|
||||
handle = new ManagedJobParallelFor(__instance.ParallelJob()).Schedule(gasGrids.Length - 1, 1);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !V1_4
|
||||
Unsafe.SkipInit(out handle);
|
||||
#endif
|
||||
for (var i = 1; i < gasGrids.Length; i++)
|
||||
gasGrids[i].Tick();
|
||||
}
|
||||
|
||||
|
||||
var firstGasGrid = gasGrids[0];
|
||||
firstGasGrid.Tick();
|
||||
|
||||
__instance.cycleIndexDiffusion = firstGasGrid.CycleIndexDiffusion;
|
||||
__instance.cycleIndexDissipation = firstGasGrid.CycleIndexDissipation;
|
||||
|
||||
if (FishSettings.ThreadingEnabled)
|
||||
{
|
||||
#if V1_4
|
||||
WaitForGasGridWorkers(gasGrids);
|
||||
#else
|
||||
handle.Complete();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if V1_4
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool TryGetRegisteredGasGridWorkers(GasGrid __instance,
|
||||
[NotNullWhen(true)] out object? monitorObject)
|
||||
=> (monitorObject = __instance.map.GasGridMonitorObject()) != null
|
||||
=> (monitorObject = __instance.MonitorObject()) != null
|
||||
&& ParallelNoAlloc.HasAnyRegisteredWorkers(monitorObject);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
@@ -124,18 +163,33 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !V1_4
|
||||
public class ParallelJob : IJobParallelFor
|
||||
{
|
||||
public ParallelGasGrid[] GasGrids = null!;
|
||||
|
||||
public void Execute(int index) => GasGrids[index + 1].Tick();
|
||||
}
|
||||
#endif
|
||||
|
||||
public sealed class ColorAtPatch : FishPrepatch
|
||||
{
|
||||
public override bool ShowSettings => false;
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
#if V1_4
|
||||
= AccessTools.DeclaredMethod(typeof(GasGrid), nameof(GasGrid.ColorAt));
|
||||
#else
|
||||
= AccessTools.DeclaredMethod(typeof(SectionLayer_Gas), nameof(SectionLayer_Gas.ColorAt));
|
||||
#endif
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
#if V1_4
|
||||
public static Color ReplacementBody(GasGrid __instance, IntVec3 cell)
|
||||
{
|
||||
var cellIndex = cell.CellToIndex(__instance.map);
|
||||
@@ -159,6 +213,41 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
|
||||
return resultColor;
|
||||
}
|
||||
#else
|
||||
public static Color ReplacementBody(SectionLayer_Gas __instance, IntVec3 cell)
|
||||
{
|
||||
if (__instance.GetType() != typeof(SectionLayer_Gas))
|
||||
return __instance.DensityAt(cell);
|
||||
|
||||
var map = __instance.Map;
|
||||
var cellIndex = cell.CellToIndex(map);
|
||||
|
||||
var gasGrids = map.gasGrid.ParallelGasGrids();
|
||||
var gasGridCount = gasGrids.Length;
|
||||
|
||||
Color resultColor;
|
||||
resultColor.r = gasGrids[0].DensityAt(cellIndex);
|
||||
resultColor.g = gasGrids[1].DensityAt(cellIndex);
|
||||
resultColor.b = gasGrids[2].DensityAt(cellIndex);
|
||||
resultColor.a = gasGrids[3].DensityAt(cellIndex);
|
||||
|
||||
if (gasGridCount > 4)
|
||||
{
|
||||
for (var i = gasGridCount; i-- > 4;)
|
||||
{
|
||||
var density = gasGrids[i].DensityAt(cellIndex);
|
||||
if (density > 0)
|
||||
resultColor += gasGrids[i].Color * (density * 0.25f);
|
||||
}
|
||||
// Not remotely accurate, but the shader now essentially applies the previous version's formula with
|
||||
// hardcoded values to each density in the result. High values, especially on a, lead to faded results.
|
||||
// The choices are basically blind smoke color, tox gas color, rot stink color, dead life dust color, or
|
||||
// something in between now
|
||||
}
|
||||
|
||||
return resultColor;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public sealed class AddGasPatch : FishPrepatch
|
||||
@@ -231,7 +320,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
for (var i = 0; i < gasGrids.Length; i++)
|
||||
gasGrids[i].Clear();
|
||||
|
||||
__instance.map.mapDrawer.WholeMapChanged(MapMeshFlag.Gas);
|
||||
__instance.map.mapDrawer.WholeMapChanged(
|
||||
#if V1_4
|
||||
MapMeshFlag.Gas
|
||||
#else
|
||||
MapMeshFlagDefOf.Gas
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +395,15 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
}
|
||||
|
||||
if (anyGridDirty)
|
||||
mapDrawer.MapMeshDirty(occupiedCell, MapMeshFlag.Gas);
|
||||
{
|
||||
mapDrawer.MapMeshDirty(occupiedCell,
|
||||
#if V1_4
|
||||
MapMeshFlag.Gas
|
||||
#else
|
||||
MapMeshFlagDefOf.Gas
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,12 +427,23 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
var smokeGrid = grids[0];
|
||||
var toxicGrid = grids[1];
|
||||
var rotStinkGrid = grids[2];
|
||||
#if !V1_4
|
||||
var deadLifeGrid = grids[3];
|
||||
#endif
|
||||
|
||||
for (var i = smokeGrid.CellCount; i-- > 0;)
|
||||
{
|
||||
__instance.gasDensity[i] = (rotStinkGrid.DensityAt(i) << 16)
|
||||
__instance.gasDensity[i]
|
||||
#if V1_4
|
||||
= (rotStinkGrid.DensityAt(i) << 16)
|
||||
| (toxicGrid.DensityAt(i) << 8)
|
||||
| smokeGrid.DensityAt(i);
|
||||
#else
|
||||
= ((uint)deadLifeGrid.DensityAt(i) << 24)
|
||||
| ((uint)rotStinkGrid.DensityAt(i) << 16)
|
||||
| ((uint)toxicGrid.DensityAt(i) << 8)
|
||||
| smokeGrid.DensityAt(i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,14 +456,29 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
var smokeGrid = grids[0];
|
||||
var toxicGrid = grids[1];
|
||||
var rotStinkGrid = grids[2];
|
||||
#if !V1_4
|
||||
var deadLifeGrid = grids[3];
|
||||
#endif
|
||||
|
||||
for (var i = smokeGrid.CellCount; i-- > 0;)
|
||||
{
|
||||
var gasDensityAtCell = __instance.gasDensity[i];
|
||||
|
||||
smokeGrid.SetDirect(i, (byte)gasDensityAtCell);
|
||||
// ReSharper disable once RedundantCast
|
||||
toxicGrid.SetDirect(i, (byte)((uint)gasDensityAtCell >> 8));
|
||||
// ReSharper disable once RedundantCast
|
||||
rotStinkGrid.SetDirect(i, (byte)((uint)gasDensityAtCell >> 16));
|
||||
#if !V1_4
|
||||
deadLifeGrid.SetDirect(i, (byte)(gasDensityAtCell >> 24));
|
||||
#endif
|
||||
}
|
||||
|
||||
for (var i = grids.Length; i-- > 0;)
|
||||
{
|
||||
var grid = grids[i];
|
||||
grid.CycleIndexDiffusion = __instance.cycleIndexDiffusion;
|
||||
grid.CycleIndexDissipation = __instance.cycleIndexDissipation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -444,6 +573,8 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
private byte[] _gasDensity;
|
||||
private volatile int _lastFinishedTick = -1;
|
||||
|
||||
private bool _allowedToRun;
|
||||
|
||||
public event Action<ParallelGasGrid>? Ticked;
|
||||
|
||||
public GasDef GasDef { get; private set; }
|
||||
@@ -487,6 +618,16 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
private void Initialize(Map map, GasDef gasDef)
|
||||
{
|
||||
GasDef = gasDef;
|
||||
|
||||
_allowedToRun = true switch
|
||||
{
|
||||
_ when gasDef == GasDefOf.ToxGas => ModLister.BiotechInstalled,
|
||||
#if !V1_4
|
||||
_ when gasDef == GasDefOf.DeadLifeDust => ModLister.AnomalyInstalled,
|
||||
#endif
|
||||
_ => true
|
||||
};
|
||||
|
||||
Map = map;
|
||||
CellCount = map.cellIndices.NumGridCells;
|
||||
_gasDensity = new byte[CellCount];
|
||||
@@ -495,7 +636,7 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
SourceIndicesOfRandomCells = new int[CellCount];
|
||||
CardinalDirections = new IntVec3[GenAdj.CardinalDirections.Length];
|
||||
Array.Copy(GenAdj.CardinalDirections, CardinalDirections, GenAdj.CardinalDirections.Length);
|
||||
CycleIndexDiffusion = Rand.Range(0, map.Area >> 1);
|
||||
// CycleIndexDiffusion = Rand.Range(0, map.Area >> 1);
|
||||
|
||||
_dissipationTicker = new() { GasGrid = this };
|
||||
_diffusionTicker = new() { GasGrid = this };
|
||||
@@ -514,8 +655,14 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
// created per map while loading it's stupid
|
||||
// Also cellsInRandomOrder itself initializes after gasGrid within CreateComponents, but that's easy to
|
||||
// solve
|
||||
grid.CellsInRandomOrder = grid.Map.cellsInRandomOrder.GetAll();
|
||||
var map = grid.Map;
|
||||
grid.CellsInRandomOrder = map.cellsInRandomOrder.GetAll();
|
||||
grid.UpdateCellIndicesInRandomOrder();
|
||||
|
||||
var mapGasGrid = map.gasGrid;
|
||||
grid.CycleIndexDiffusion = mapGasGrid.cycleIndexDiffusion;
|
||||
grid.CycleIndexDissipation = mapGasGrid.cycleIndexDissipation;
|
||||
|
||||
grid.Ticked -= _onFirstTickAction;
|
||||
}
|
||||
|
||||
@@ -585,7 +732,7 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
|
||||
public void AddGas(in IntVec3 cell, int amount, bool canOverflow = true)
|
||||
{
|
||||
if (amount <= 0 || !GasCanMoveTo(cell))
|
||||
if (amount <= 0 || !GasCanMoveTo(cell) || !_allowedToRun)
|
||||
return;
|
||||
|
||||
anyGasEverAdded = true;
|
||||
@@ -593,7 +740,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
|
||||
SetDirect(index, AdjustedDensity(_gasDensity[index] + amount, out var overflow));
|
||||
|
||||
Map.mapDrawer.MapMeshDirty(cell, MapMeshFlag.Gas);
|
||||
Map.mapDrawer.MapMeshDirty(cell,
|
||||
#if V1_4
|
||||
MapMeshFlag.Gas
|
||||
#else
|
||||
MapMeshFlagDefOf.Gas
|
||||
#endif
|
||||
);
|
||||
|
||||
if (canOverflow && overflow > 0)
|
||||
Overflow(cell, overflow);
|
||||
@@ -765,9 +918,15 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
#endif
|
||||
|
||||
_dissipationTicker.TickCells(area, (area + 63) >> 6); // Mathf.CeilToInt(area * (1f / 64f))
|
||||
|
||||
var mapGasGrid = Map.gasGrid;
|
||||
mapGasGrid.cycleIndexDissipation = CycleIndexDissipation;
|
||||
|
||||
if (Diffuses)
|
||||
{
|
||||
_diffusionTicker.TickCells(area, (area + 31) >> 5); // Mathf.CeilToInt(area * (1f / 32f))
|
||||
mapGasGrid.cycleIndexDiffusion = CycleIndexDiffusion;
|
||||
}
|
||||
|
||||
#if GAS_DEBUG_L2
|
||||
Debug.LogTotalGasChange(this, previousDensity);
|
||||
@@ -817,7 +976,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
if (densityAfterDissipation == 0)
|
||||
{
|
||||
Debug.LogMapMeshDirty(this, index);
|
||||
Map.mapDrawer.MapMeshDirty(IndexToCell(index), MapMeshFlag.Gas);
|
||||
Map.mapDrawer.MapMeshDirty(IndexToCell(index),
|
||||
#if V1_4
|
||||
MapMeshFlag.Gas
|
||||
#else
|
||||
MapMeshFlagDefOf.Gas
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -844,7 +1009,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
continue;
|
||||
|
||||
SetDirect(otherCellIndex, (byte)densityOtOtherCell);
|
||||
Map.mapDrawer.MapMeshDirty(otherCell, MapMeshFlag.Gas);
|
||||
Map.mapDrawer.MapMeshDirty(otherCell,
|
||||
#if V1_4
|
||||
MapMeshFlag.Gas
|
||||
#else
|
||||
MapMeshFlagDefOf.Gas
|
||||
#endif
|
||||
);
|
||||
diffusedToAnyCell = true;
|
||||
if (densityAtOriginCell < 17)
|
||||
break;
|
||||
@@ -854,7 +1025,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||
return;
|
||||
|
||||
SetDirect(originCellIndex, (byte)densityAtOriginCell);
|
||||
Map.mapDrawer.MapMeshDirty(cell, MapMeshFlag.Gas);
|
||||
Map.mapDrawer.MapMeshDirty(cell,
|
||||
#if V1_4
|
||||
MapMeshFlag.Gas
|
||||
#else
|
||||
MapMeshFlagDefOf.Gas
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
private static bool TryDiffuseIndividualGas(ref int gasA, ref int gasB)
|
||||
|
||||
@@ -16,7 +16,7 @@ public sealed class GenLocalDateCaching : ClassWithFishPatches
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Caches results of GenLocalDate.DayTick for the first map. This is similar to Rim73's mind state "
|
||||
+ "optimization, but yields accurate results instead of a placeholder value to avoid issues.";
|
||||
+ "optimization, but yields accurate results instead of a placeholder value to avoid issues";
|
||||
|
||||
public override Delegate TargetMethodGroup { get; } = (Func<Thing, int>)GenLocalDate.DayTick;
|
||||
public override int TranspilerMethodPriority => Priority.First;
|
||||
@@ -64,7 +64,7 @@ public sealed class GenLocalDateCaching : ClassWithFishPatches
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Caches results of GenLocalDate.DayTick for the first map. This is similar to Rim73's mind state "
|
||||
+ "optimization, but yields accurate results instead of a placeholder value to avoid issues.";
|
||||
+ "optimization, but yields accurate results instead of a placeholder value to avoid issues";
|
||||
|
||||
public override Delegate TargetMethodGroup { get; } = (Func<Map, int>)GenLocalDate.DayTick;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
||||
public override string? Description { get; }
|
||||
= "Fix for thread safety. The vanilla method was rarely breaking and preventing the game from loading";
|
||||
|
||||
public static object Lock = new();
|
||||
public static readonly object Lock = new();
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.PropertyGetter(typeof(GenTypes), nameof(GenTypes.AllTypes));
|
||||
@@ -34,7 +34,7 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
||||
public override string? Description { get; }
|
||||
= "Fix for thread safety. The vanilla method was rarely breaking and preventing the game from loading";
|
||||
|
||||
public static object Lock = new();
|
||||
public static readonly object Lock = new();
|
||||
|
||||
public override MethodBase TargetMethodBase { get; } = methodof(GenTypes.AllTypesWithAttribute<Attribute>);
|
||||
|
||||
@@ -67,7 +67,7 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
||||
public override string? Description { get; }
|
||||
= "Fix for thread safety. The vanilla method was rarely breaking and preventing the game from loading";
|
||||
|
||||
public static object Lock = new();
|
||||
public static readonly object Lock = new();
|
||||
|
||||
public override MethodBase TargetMethodBase { get; } = methodof(GenTypes.AllSubclasses);
|
||||
|
||||
@@ -107,7 +107,7 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
||||
= "Fix for thread safety, paired with a small optimization for faster loading. The vanilla method was "
|
||||
+ "rarely breaking and preventing the game from loading";
|
||||
|
||||
public static object Lock = new();
|
||||
public static readonly object Lock = new();
|
||||
|
||||
public override MethodBase TargetMethodBase { get; } = methodof(GenTypes.AllSubclassesNonAbstract);
|
||||
|
||||
|
||||
@@ -49,9 +49,6 @@ public sealed class GeneTrackerOptimization : ClassWithFishPrepatches
|
||||
}
|
||||
}
|
||||
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// public static bool Dirty(List<Gene> genes, List<Gene> genesToTick) => genes._version != genesToTick._version;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Dirty(Pawn_GeneTracker instance, List<Gene> genesToTick)
|
||||
=> GetListVersion(instance) != genesToTick._version;
|
||||
@@ -69,7 +66,6 @@ public sealed class GeneTrackerOptimization : ClassWithFishPrepatches
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetListVersion(Pawn_GeneTracker instance)
|
||||
// => HashCode.Combine(instance.xenogenes._version, instance.endogenes._version);
|
||||
=> (instance.xenogenes._version << 16) | (instance.endogenes._version & 0b1111_1111_1111_1111);
|
||||
|
||||
private static void AddTickableGenes(List<Gene> genesToTick, List<Gene> genes)
|
||||
@@ -87,27 +83,6 @@ public sealed class GeneTrackerOptimization : ClassWithFishPrepatches
|
||||
genesToTick.Add(gene);
|
||||
}
|
||||
}
|
||||
|
||||
// [MethodImpl(MethodImplOptions.NoInlining)]
|
||||
// public static void Update(List<Gene> genes, List<Gene> genesToTick)
|
||||
// {
|
||||
// genesToTick.Clear();
|
||||
//
|
||||
// var count = genes.Count;
|
||||
// for (var i = 0; i < count; i++)
|
||||
// {
|
||||
// var gene = genes[i];
|
||||
// if (SkippableTypes.Contains(gene.GetType())
|
||||
// && (gene.def.mentalBreakMtbDays <= 0f || gene.def.mentalBreakDef == null))
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// genesToTick.Add(gene);
|
||||
// }
|
||||
//
|
||||
// genesToTick._version = genes._version;
|
||||
// }
|
||||
|
||||
public static HashSet<Type> SkippableTypes
|
||||
= typeof(Gene).SubclassesWithNoMethodOverrideAndSelf(nameof(Gene.Tick)).ToHashSet();
|
||||
|
||||
@@ -138,7 +138,7 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
+ "faster than the vanilla method. Replaces Performance Optimizer's error prone and slower optimization.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.Method(typeof(ThingDef), nameof(ThingDef.HasComp));
|
||||
= AccessTools.DeclaredMethod(typeof(ThingDef), nameof(ThingDef.HasComp), [typeof(Type)]);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(HasComp);
|
||||
@@ -192,6 +192,69 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
}
|
||||
}
|
||||
|
||||
#if !V1_4
|
||||
public sealed class ThingDefGenericHasCompPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Optimizes HasComp lookups with a fast custom dictionary implementation. This is often more than 10x "
|
||||
+ "faster than the vanilla method. Replaces Performance Optimizer's error prone and slower optimization.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ThingDef), nameof(ThingDef.HasComp), []);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(HasComp<ThingComp>);
|
||||
|
||||
public static bool HasComp<T>(ThingDef thingDef) where T : ThingComp
|
||||
{
|
||||
if (thingDef.comps != null)
|
||||
{
|
||||
ref var cache = ref Cache.ByInt<ushort, CacheValue<T>>.GetOrAddReference(thingDef.shortHash);
|
||||
|
||||
return !cache.IsDirty(thingDef.comps) ? cache.HasComp : UpdateCache(thingDef, ref cache);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool UpdateCache<T>(ThingDef thingDef, ref CacheValue<T> cache) where T : ThingComp
|
||||
{
|
||||
var targetType = typeof(T);
|
||||
for (var i = 0; i < thingDef.comps.Count; i++)
|
||||
{
|
||||
var compClass = thingDef.comps[i].compClass;
|
||||
if (compClass != targetType && !targetType.IsAssignableFrom(compClass))
|
||||
continue;
|
||||
|
||||
cache.Update(thingDef, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
cache.Update(thingDef, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
public record struct CacheValue<T>() where T : ThingComp
|
||||
{
|
||||
private int _listVersion = -2;
|
||||
public bool HasComp;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsDirty(List<CompProperties> comps) => comps._version != _listVersion;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Update(ThingDef thingDef, bool hasComp)
|
||||
{
|
||||
if (thingDef.shortHash == default)
|
||||
return;
|
||||
|
||||
_listVersion = thingDef.comps._version;
|
||||
HasComp = hasComp;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public sealed class HediffCompPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
@@ -199,7 +262,7 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
+ "faster than the vanilla method. Replaces Performance Optimizer's error prone and slower optimization.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= methodof(HediffUtility.TryGetComp<HediffComp>);
|
||||
= methodof((Func<Hediff, HediffComp>)HediffUtility.TryGetComp<HediffComp>);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(TryGetComp<HediffComp>);
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace PerformanceFish;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
|
||||
public record struct GroupThingPair
|
||||
{
|
||||
public const int MaxValue = 0xFF_FF_FF;
|
||||
|
||||
private uint _data;
|
||||
|
||||
public ThingRequestGroup ThingRequestGroup
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace PerformanceFish.Hauling;
|
||||
|
||||
public class StorageDistrict
|
||||
@@ -93,11 +95,18 @@ public class StorageDistrict
|
||||
public void RemoveThing(Thing thing)
|
||||
{
|
||||
if (!StoredThingsOfDef(thing.def).Remove(thing))
|
||||
ThrowHelper.ThrowInvalidOperationException();
|
||||
{
|
||||
ThrowForInvalidRemovalAttempt(thing);
|
||||
}
|
||||
|
||||
FreeSlots = TotalSlots - --_storedThingCount;
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
private void ThrowForInvalidRemovalAttempt(Thing thing)
|
||||
=> ThrowHelper.ThrowInvalidOperationException($"Failed removing thing '{
|
||||
thing}' from district at cells '{Cells.ToStringSafeEnumerable()}'");
|
||||
|
||||
public List<Thing> StoredThingsOfDef(ThingDef def) => _storedThingsByDef.GetOrAdd(def.shortHash);
|
||||
|
||||
public bool CapacityAllows(Thing t) => FreeSlots > 0 || StoredThingsOfDef(t.def).ContainsThingStackableWith(t);
|
||||
|
||||
@@ -206,7 +206,7 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
public void Update(Thing thing, bool allowedToAccept)
|
||||
{
|
||||
_value = allowedToAccept.AsInt();
|
||||
_nextUpdateTick = TickHelper.Add(512, thing.thingIDNumber, 128);
|
||||
_nextUpdateTick = TickHelper.Add(2048, thing.thingIDNumber, 512);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,11 +141,16 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
public static void ReplacementBody(Thing t, Pawn carrier, Map map, Faction faction, SlotGroup? slotGroup,
|
||||
public static void ReplacementBody(Thing t, Pawn carrier, Map map, Faction faction,
|
||||
#if V1_4
|
||||
SlotGroup? slotGroup,
|
||||
#else
|
||||
ISlotGroup? slotGroup,
|
||||
#endif
|
||||
bool needAccurateResult, ref IntVec3 closestSlot, ref float closestDistSquared,
|
||||
ref StoragePriority foundPriority)
|
||||
{
|
||||
if (slotGroup == null || !slotGroup.parent.Accepts(t) || !CapacityAllows(slotGroup, t))
|
||||
if (slotGroup == null || !slotGroup.Settings.AllowedToAccept(t) || !CapacityAllows(slotGroup, t))
|
||||
return;
|
||||
|
||||
var thingPosition = t.SpawnedOrAnyParentSpawned ? t.PositionHeld : carrier.PositionHeld;
|
||||
@@ -194,9 +199,18 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
}
|
||||
}
|
||||
|
||||
#if V1_4
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static StorageDistrict[] GetDistricts(SlotGroup slotGroup) => slotGroup.Districts();
|
||||
// wrapper for analyzer compatibility, as it otherwise throws on ref returns
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static StorageDistrict[] GetDistricts(ISlotGroup iSlotGroup)
|
||||
=> iSlotGroup is SlotGroup slotGroup ? slotGroup.Districts() : StorageDistrict.GetDefaultArray();
|
||||
#endif
|
||||
|
||||
public static bool CapacityAllows(ISlotGroup iSlotGroup, Thing t)
|
||||
=> iSlotGroup is not SlotGroup slotGroup || CapacityAllows(slotGroup, t);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool CapacityAllows(SlotGroup slotGroup, Thing t)
|
||||
@@ -218,9 +232,9 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
public sealed class TryFindBestBetterStoreCellForPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Optimizes a loop in the method to make use of cached storage priorities for fewer comparisons against "
|
||||
+ "those. Also removes haulables from ListerHaulables if the found best store cell turns out to be the "
|
||||
+ "current cell";
|
||||
= "Optimizes a loop in the method to treat storage groups as proper groups without duplicate checks and to "
|
||||
+ "make use of cached storage priorities for fewer comparisons against those. Also removes haulables from "
|
||||
+ "ListerHaulables if the found best store cell turns out to be the current cell";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; } = methodof(StoreUtility.TryFindBestBetterStoreCellFor);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#if V1_4
|
||||
using static System.Reflection.Emit.OpCodes;
|
||||
|
||||
namespace PerformanceFish.Hediffs;
|
||||
@@ -48,4 +49,5 @@ public sealed class HediffComp_PsychicHarmonizer_Bugfix : ClassWithFishPatches
|
||||
return codes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -15,6 +15,7 @@ using VisibleHediffCache
|
||||
using NotMissingPartsCache
|
||||
= PerformanceFish.Cache.ByInt<Verse.HediffSet,
|
||||
PerformanceFish.Hediffs.HediffSetCaching.NotMissingPartsCacheValue>;
|
||||
// ReSharper disable WithExpressionModifiesAllMembers
|
||||
|
||||
namespace PerformanceFish.Hediffs;
|
||||
|
||||
@@ -24,26 +25,24 @@ public sealed class HediffSetPrecaching : ClassWithFishPrepatches
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Caches results of the HediffSet.GetFirstHediffOfDef method. Impact scales with hediff count.";
|
||||
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= SymbolExtensions.GetMethodInfo(static ()
|
||||
=> default(HediffSet)!.GetFirstHediffOfDef(null, default));
|
||||
= AccessTools.DeclaredMethod(typeof(HediffSet), nameof(HediffSet.GetFirstHediffOfDef));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(Replacement);
|
||||
|
||||
public static Hediff? Replacement(HediffSet instance, HediffDef? def, bool mustBeVisible)
|
||||
{
|
||||
var canCache = true;
|
||||
bool canCache;
|
||||
|
||||
if (instance.hediffs.Count >= 5
|
||||
&& def is not null
|
||||
// && def.GetType() == typeof(HediffDef) // different types have different indexing. Can't use those
|
||||
)
|
||||
if ((instance.hediffs.Count >= 5) & def is not null)
|
||||
{
|
||||
ref var cache = ref GetCacheValue(instance, def, mustBeVisible);
|
||||
ref var cache = ref GetCacheValue(instance, def!, mustBeVisible);
|
||||
if (!cache.Dirty)
|
||||
return cache.Hediff;
|
||||
|
||||
canCache = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -75,7 +74,7 @@ public sealed class HediffSetPrecaching : ClassWithFishPrepatches
|
||||
public static unsafe ref HediffSetCaching.HediffCacheValue GetCacheValue(HediffSet instance, HediffDef def,
|
||||
bool mustBeVisible)
|
||||
{
|
||||
var key = new HediffCache { First = instance.pawn.thingIDNumber, Second = def.shortHash };
|
||||
var key = default(HediffCache) with { First = instance.pawn.thingIDNumber, Second = def.shortHash };
|
||||
ref var cache = ref mustBeVisible
|
||||
? ref Unsafe.As<HediffSetCaching.VisibleHediffCacheValue, HediffSetCaching.HediffCacheValue>(
|
||||
ref VisibleHediffCache.GetOrAddReference(Unsafe.As<HediffCache, VisibleHediffCache>(ref key)))
|
||||
@@ -98,10 +97,10 @@ public sealed class HediffSetPrecaching : ClassWithFishPrepatches
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Caches results of the HediffSet.HasHediff method. Impact scales with hediff count.";
|
||||
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= SymbolExtensions.GetMethodInfo(static ()
|
||||
=> default(HediffSet)!.HasHediff(null, default));
|
||||
= AccessTools.DeclaredMethod(typeof(HediffSet), nameof(HediffSet.HasHediff),
|
||||
[typeof(HediffDef), typeof(bool)]);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(Replacement);
|
||||
@@ -117,7 +116,7 @@ public sealed class HediffSetPrecaching : ClassWithFishPrepatches
|
||||
= "Caches results of the HediffSet.GetNotMissingParts method. Impact scales with hediff count.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.Method(typeof(HediffSet), nameof(HediffSet.GetNotMissingParts));
|
||||
= AccessTools.DeclaredMethod(typeof(HediffSet), nameof(HediffSet.GetNotMissingParts));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(HediffSet __instance, BodyPartHeight height, BodyPartDepth depth, BodyPartTagDef? tag,
|
||||
@@ -160,11 +159,14 @@ public sealed class HediffSetCaching : ClassWithFishPatches
|
||||
{
|
||||
public sealed class DirtyCache : FishPatch
|
||||
{
|
||||
public override List<Type> LinkedPatches { get; }
|
||||
= [typeof(Pawn_PsychicEntropyTrackerOptimization.Psylink_Patch)];
|
||||
|
||||
public override string Description { get; }
|
||||
= "Patched to trigger psylink cache clearing for the psychic entropy optimization.";
|
||||
= "Patched to trigger psylink cache clearing for the psychic entropy optimization";
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.Method(typeof(HediffSet), nameof(HediffSet.DirtyCache));
|
||||
= AccessTools.DeclaredMethod(typeof(HediffSet), nameof(HediffSet.DirtyCache));
|
||||
|
||||
public static void Postfix(HediffSet __instance)
|
||||
{
|
||||
@@ -175,9 +177,10 @@ public sealed class HediffSetCaching : ClassWithFishPatches
|
||||
|
||||
public record struct NotMissingPartsCacheValue()
|
||||
{
|
||||
private int _version = -1;
|
||||
private int
|
||||
_version = -1,
|
||||
_nextRefreshTick;
|
||||
private List<Hediff> _hediffsInSet = [];
|
||||
private int _nextRefreshTick;
|
||||
public List<BodyPartRecord> Parts = [];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -204,9 +207,10 @@ public sealed class HediffSetCaching : ClassWithFishPatches
|
||||
|
||||
public record struct HediffCacheValue()
|
||||
{
|
||||
private int _version = -1;
|
||||
private int
|
||||
_version = -1,
|
||||
_nextRefreshTick;
|
||||
private List<Hediff> _hediffsInSet = [];
|
||||
private int _nextRefreshTick;
|
||||
public Hediff? Hediff;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -9,7 +9,11 @@ public sealed class Pawn_PsychicEntropyTrackerOptimization : ClassWithFishPatche
|
||||
{
|
||||
public sealed class Psylink_Patch : FirstPriorityFishPatch
|
||||
{
|
||||
public override string Description { get; } = "Caches/throttles the psylink method to only update every 32 ticks.";
|
||||
public override List<Type> LinkedPatches { get; }
|
||||
= [typeof(HediffSetCaching.DirtyCache)];
|
||||
|
||||
public override string Description { get; }
|
||||
= "Caches/throttles the psylink method to only update on hediff changes or after 128 ticks";
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.PropertyGetter(typeof(Pawn_PsychicEntropyTracker),
|
||||
@@ -22,7 +26,7 @@ public sealed class Pawn_PsychicEntropyTrackerOptimization : ClassWithFishPatche
|
||||
if (__instance.psylinkCachedForTick <= TickHelper.TicksGame)
|
||||
{
|
||||
__instance.psylinkCached = __instance.pawn.GetMainPsylinkSource();
|
||||
__instance.psylinkCachedForTick = TickHelper.TicksGame + 32;
|
||||
__instance.psylinkCachedForTick = TickHelper.TicksGame + 128;
|
||||
}
|
||||
|
||||
return __instance.psylinkCached;
|
||||
|
||||
@@ -26,14 +26,23 @@ public sealed class IdeoBuildingPresenceDemandOptimization : ClassWithFishPrepat
|
||||
|
||||
public static bool ReplacementBody(IdeoBuildingPresenceDemand __instance, Map map)
|
||||
{
|
||||
var buildings = map.listerBuildings.AllBuildingsColonistOfDef(__instance.parent.ThingDef);
|
||||
var parentBuilding = __instance.parent;
|
||||
var buildings = map.listerBuildings.AllBuildingsColonistOfDef(parentBuilding.ThingDef);
|
||||
#if !V1_4
|
||||
for (var i = buildings.Count; i-- > 0;)
|
||||
{
|
||||
if (buildings[i].StyleSourcePrecept == parentBuilding)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
if (buildings is IndexedFishSet<Building> fishSet)
|
||||
{
|
||||
var list = fishSet.ReadOnlyList;
|
||||
for (var i = list.Count; i-- > 0;)
|
||||
{
|
||||
if (list[i].StyleSourcePrecept == __instance.parent)
|
||||
if (list[i].StyleSourcePrecept == parentBuilding)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -43,17 +52,21 @@ public sealed class IdeoBuildingPresenceDemandOptimization : ClassWithFishPrepat
|
||||
{
|
||||
return FallbackCheck(__instance, buildings);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if V1_4
|
||||
public static bool FallbackCheck(IdeoBuildingPresenceDemand __instance, IEnumerable<Building> buildings)
|
||||
{
|
||||
var parentBuilding = __instance.parent;
|
||||
foreach (var building in buildings)
|
||||
{
|
||||
if (building.StyleSourcePrecept == __instance.parent)
|
||||
if (building.StyleSourcePrecept == parentBuilding)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#if V1_4
|
||||
using PerformanceFish.ModCompatibility;
|
||||
|
||||
namespace PerformanceFish.JobSystem;
|
||||
@@ -27,4 +28,5 @@ public sealed class DisabledWorkTypesOptimization : ClassWithFishPatches
|
||||
? __instance.cachedDisabledWorkTypesPermanent
|
||||
: __instance.cachedDisabledWorkTypes) is null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -3,227 +3,27 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#if disabledForNow
|
||||
using System.Threading.Tasks;
|
||||
using Verse.AI;
|
||||
using PerformanceFish.Prepatching;
|
||||
|
||||
namespace PerformanceFish.JobSystem;
|
||||
|
||||
public sealed class GenClosestPatches : ClassWithFishPatches
|
||||
public sealed class GenClosestPatches : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class ClosestThing_Global_Patch : FishPatch
|
||||
public sealed class ClosestThingReachablePatch : FishPrepatch
|
||||
{
|
||||
public override string Description { get; } = "Threading for job scanning. Disabled and unstable.";
|
||||
public override bool DefaultState => false;
|
||||
public static bool ThreadingEnabled { get; set; }
|
||||
public override string Description { get; }
|
||||
= "Job scanning optimization making repairing, wardening and tending use a cache of targets where "
|
||||
+ "appropriate";
|
||||
|
||||
public override MethodBase TargetMethodBase => methodof(GenClosest.ClosestThingReachable);
|
||||
|
||||
public override Expression<Action> TargetMethod { get; }
|
||||
= static () => GenClosest.ClosestThing_Global(default, null, default, null, null);
|
||||
|
||||
public static CodeInstructions Transpiler(CodeInstructions CodeInstructions)
|
||||
=> Reflection.MakeReplacementCall(ClosestThing_Global_Replacement);
|
||||
|
||||
private struct Variables
|
||||
public static void Prefix(Map map, ref ThingRequest thingReq, IEnumerable<Thing>? customGlobalSearchSet)
|
||||
{
|
||||
public IntVec3 center;
|
||||
public float maxDistanceSquared;
|
||||
public Func<Thing, float>? priorityGetter;
|
||||
public float closestDistSquared;
|
||||
public Predicate<Thing>? validator;
|
||||
}
|
||||
if (!thingReq.CanBeFoundInRegion || customGlobalSearchSet is not ICollection<Thing> collection)
|
||||
return;
|
||||
|
||||
public static Thing? ClosestThing_Global_Replacement(IntVec3 center, IEnumerable? searchSet,
|
||||
float maxDistance = 99999f, Predicate<Thing>? validator = null, Func<Thing, float>? priorityGetter = null)
|
||||
{
|
||||
if (searchSet == null)
|
||||
return null;
|
||||
|
||||
var closestDistSquared = 2.14748365E+09f;
|
||||
Thing? chosen = null;
|
||||
var bestPrio = float.MinValue;
|
||||
//float maxDistanceSquared = maxDistance * maxDistance;
|
||||
Variables variables = new()
|
||||
{
|
||||
center = center,
|
||||
maxDistanceSquared = maxDistance * maxDistance,
|
||||
validator = validator,
|
||||
priorityGetter = priorityGetter,
|
||||
closestDistSquared = closestDistSquared
|
||||
};
|
||||
|
||||
var predicate = GenClosest_Predicate.GetValidator(validator?.Target)?.Target ?? validator?.Target;
|
||||
if (ThreadingEnabled
|
||||
&& validator != null
|
||||
&& JobGiver_Work_Predicate.GetScanner(predicate) is WorkGiver_DoBill
|
||||
/*or WorkGiver_ConstructDeliverResources or WorkGiver_ConstructFinishFrames*/
|
||||
)
|
||||
{
|
||||
if (searchSet is IList<Thing> thingList)
|
||||
{
|
||||
Log.Message($"Running threading code for {validator.Target.GetType()}");
|
||||
Exception? exception = null;
|
||||
try
|
||||
{
|
||||
Parallel.For<(IList<Thing> list, Variables vars)>(0, thingList.Count,
|
||||
() => (thingList, variables),
|
||||
(i, loop, listAndVars) => (listAndVars.list,
|
||||
ProcessThreaded(listAndVars.list[i], listAndVars.vars)), static _ => { });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
Log.Error($"Caught error while threading:\n{ex}");
|
||||
}
|
||||
|
||||
if (exception is null)
|
||||
Log.Message("Finished threading code without errors");
|
||||
|
||||
return chosen;
|
||||
}
|
||||
}
|
||||
|
||||
switch (searchSet)
|
||||
{
|
||||
case IList<Thing> thingList:
|
||||
{
|
||||
for (var i = 0; i < thingList.Count; i++)
|
||||
Process(thingList[i], variables);
|
||||
break;
|
||||
}
|
||||
case IList<Pawn> pawnList:
|
||||
{
|
||||
for (var j = 0; j < pawnList.Count; j++)
|
||||
Process(pawnList[j], variables);
|
||||
break;
|
||||
}
|
||||
case IList<Building> buildingList:
|
||||
{
|
||||
for (var k = 0; k < buildingList.Count; k++)
|
||||
Process(buildingList[k], variables);
|
||||
break;
|
||||
}
|
||||
case IList<IAttackTarget> attackTargetList:
|
||||
{
|
||||
for (var l = 0; l < attackTargetList.Count; l++)
|
||||
Process((Thing)attackTargetList[l], variables);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
foreach (Thing item in searchSet)
|
||||
Process(item, variables);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return chosen;
|
||||
|
||||
void Process<T>(T t, Variables variables) where T : Thing
|
||||
{
|
||||
if (!t.Spawned)
|
||||
return;
|
||||
|
||||
float num = (variables.center - t.Position).LengthHorizontalSquared;
|
||||
if (!(num > variables.maxDistanceSquared)
|
||||
&& (variables.priorityGetter != null || num < closestDistSquared)
|
||||
&& (variables.validator == null || variables.validator(t)))
|
||||
{
|
||||
var num2 = 0f;
|
||||
if (variables.priorityGetter != null)
|
||||
{
|
||||
num2 = variables.priorityGetter(t);
|
||||
if (num2 < bestPrio || (Math.Abs(num2 - bestPrio) < 0.001f && num >= closestDistSquared))
|
||||
return;
|
||||
}
|
||||
|
||||
chosen = t;
|
||||
closestDistSquared = variables.closestDistSquared = num;
|
||||
bestPrio = num2;
|
||||
}
|
||||
}
|
||||
|
||||
Variables ProcessThreaded<T>(T t, Variables variables) where T : Thing
|
||||
{
|
||||
if (!t.Spawned)
|
||||
return variables;
|
||||
|
||||
float num = (variables.center - t.Position).LengthHorizontalSquared;
|
||||
if (num > variables.maxDistanceSquared
|
||||
|| (variables.priorityGetter == null
|
||||
&& (!(num < variables.closestDistSquared)
|
||||
|| !(num < (variables.closestDistSquared = closestDistSquared))))
|
||||
|| (variables.validator != null && !variables.validator(t)))
|
||||
{
|
||||
return variables;
|
||||
}
|
||||
|
||||
lock (Lock)
|
||||
{
|
||||
var num2 = 0f;
|
||||
if (variables.priorityGetter != null)
|
||||
{
|
||||
num2 = variables.priorityGetter(t);
|
||||
if (num2 < bestPrio || (Math.Abs(num2 - bestPrio) < 0.001f && num >= closestDistSquared))
|
||||
return variables;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (num >= closestDistSquared)
|
||||
return variables;
|
||||
}
|
||||
|
||||
chosen = t;
|
||||
closestDistSquared = variables.closestDistSquared = num;
|
||||
bestPrio = num2;
|
||||
}
|
||||
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
|
||||
private static object Lock { get; } = new();
|
||||
|
||||
private static Type JobGiverWorkPredicateType { get; } = AccessTools.FirstInner(typeof(JobGiver_Work),
|
||||
t => AccessTools.Field(t, "scanner") != null
|
||||
&& AccessTools.FirstMethod(t, m => m.Name.Contains("TryIssueJobPackage")) != null);
|
||||
|
||||
private static JobGiver_Work_PredicateAccess JobGiver_Work_Predicate { get; }
|
||||
= (JobGiver_Work_PredicateAccess)Activator.CreateInstance(
|
||||
typeof(JobGiver_Work_PredicateAccessImplementation<>).MakeGenericType(JobGiverWorkPredicateType));
|
||||
|
||||
private static Type GenClosestPredicateType { get; } = AccessTools.FirstInner(typeof(GenClosest),
|
||||
t => AccessTools.Field(t, "validator") != null
|
||||
&& AccessTools.FirstMethod(t, m => m.Name.Contains("ClosestThingReachable")) != null);
|
||||
|
||||
private static GenClosest_PredicateAccess GenClosest_Predicate { get; }
|
||||
= (GenClosest_PredicateAccess)Activator.CreateInstance(
|
||||
typeof(GenClosest_PredicateAccessImplementation<>).MakeGenericType(GenClosestPredicateType));
|
||||
|
||||
private abstract class JobGiver_Work_PredicateAccess
|
||||
{
|
||||
public abstract WorkGiver_Scanner? GetScanner(object? obj);
|
||||
}
|
||||
|
||||
private class JobGiver_Work_PredicateAccessImplementation<T> : JobGiver_Work_PredicateAccess
|
||||
{
|
||||
public override WorkGiver_Scanner? GetScanner(object? obj) => obj is T t ? Scanner(t) : null;
|
||||
|
||||
public static AccessTools.FieldRef<T, WorkGiver_Scanner> Scanner { get; }
|
||||
= AccessTools.FieldRefAccess<T, WorkGiver_Scanner>("scanner");
|
||||
}
|
||||
|
||||
private abstract class GenClosest_PredicateAccess
|
||||
{
|
||||
public abstract Predicate<Thing>? GetValidator(object? obj);
|
||||
}
|
||||
|
||||
private class GenClosest_PredicateAccessImplementation<T> : GenClosest_PredicateAccess
|
||||
{
|
||||
public override Predicate<Thing>? GetValidator(object? obj) => obj is T t ? Validator(t) : null;
|
||||
|
||||
public static AccessTools.FieldRef<T, Predicate<Thing>> Validator { get; }
|
||||
= AccessTools.FieldRefAccess<T, Predicate<Thing>>("validator");
|
||||
if (map.listerThings.ThingsMatching(thingReq).Count > collection.Count * 5)
|
||||
thingReq = ThingRequest.ForUndefined();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//}
|
||||
}
|
||||
@@ -3,13 +3,8 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
/*using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
/*using System.Linq;
|
||||
using static System.Reflection.Emit.OpCodes;
|
||||
using static PerformanceFish.TranspilerHelpers;
|
||||
|
||||
namespace PerformanceFish.JobSystem;
|
||||
|
||||
@@ -17,9 +12,17 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
{
|
||||
public sealed class TryIssueJobPackage_InnerDelegate_Patch : FishPatch
|
||||
{
|
||||
public override string Description => "Work scanning optimization. Splits the IsForbidden and HasJobOnThing checks into separate loops, then threads the IsForbidden loop, sorts it, caches the result and skips any following duplicate checks";
|
||||
public override MethodBase TargetMethodInfo => AccessTools.FindIncludingInnerTypes(typeof(JobGiver_Work),
|
||||
t => AccessTools.Field(t, "scanner") != null ? AccessTools.FirstMethod(t, m => m.Name.Contains("TryIssueJobPackage") && m.ReturnType == typeof(bool)) : null);
|
||||
public override string Description { get; }
|
||||
= "Work scanning optimization. Splits the IsForbidden and HasJobOnThing checks into separate loops, then "
|
||||
+ "sorts the IsForbidden loop, caches the result and skips any following duplicate checks";
|
||||
|
||||
public override MethodBase? TargetMethodInfo { get; }
|
||||
= AccessTools.FindIncludingInnerTypes(typeof(JobGiver_Work), static t
|
||||
=> AccessTools.Field(t, "scanner") != null
|
||||
? AccessTools.FirstMethod(t,
|
||||
static m => m.Name.Contains("TryIssueJobPackage") && m.ReturnType == typeof(bool))
|
||||
: null);
|
||||
|
||||
public static CodeInstructions Transpiler(CodeInstructions CodeInstructions)
|
||||
{
|
||||
var shouldRemove = true;
|
||||
@@ -46,29 +49,40 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
|
||||
public sealed class TryIssueJobPackage_Patch : FishPatch
|
||||
{
|
||||
public override string Description => "Work scanning optimization. Splits the IsForbidden and HasJobOnThing checks into separate loops, then threads the IsForbidden loop, sorts it, caches the result and skips any following duplicate checks";
|
||||
public override MethodBase TargetMethodInfo => AccessTools.Method(typeof(JobGiver_Work), nameof(JobGiver_Work.TryIssueJobPackage));
|
||||
public override string Description { get; }
|
||||
= "Work scanning optimization. Splits the IsForbidden and HasJobOnThing checks into separate loops, "
|
||||
+ "then sorts it, caches the result and skips any following duplicate checks";
|
||||
|
||||
public override MethodBase TargetMethodInfo
|
||||
=> AccessTools.Method(typeof(JobGiver_Work), nameof(JobGiver_Work.TryIssueJobPackage));
|
||||
|
||||
public static void Prefix(Pawn pawn)
|
||||
{
|
||||
PrefilteredPotentialWorkThingsGlobal.Clear();
|
||||
FishCache.ClearAll();
|
||||
CurrentPawn = pawn;
|
||||
}
|
||||
|
||||
public static void Postfix()
|
||||
{
|
||||
PrefilteredPotentialWorkThingsGlobal.Clear();
|
||||
FishCache.ClearAll();
|
||||
CurrentPawn = null;
|
||||
}
|
||||
|
||||
public static CodeInstructions Transpiler(CodeInstructions CodeInstructions, MethodBase original)
|
||||
{
|
||||
var pawn = GetLdarg(original, "pawn");
|
||||
var pawn = FishTranspiler.Argument(original, "pawn");
|
||||
|
||||
var scanner_pawn_PotentialWorkThingsGlobal = GetCallvirt(typeof(WorkGiver_Scanner), nameof(WorkGiver_Scanner.PotentialWorkThingsGlobal));
|
||||
var potentialWorkThingsGlobal_pawn_TryGetPrefilteredPotentialWorkThingsAnyType = GetCall(() => TryGetPrefilteredPotentialWorkThingsAnyType(null, null));
|
||||
var scanner_pawn_PotentialWorkThingsGlobal = FishTranspiler.Call(typeof(WorkGiver_Scanner),
|
||||
nameof(WorkGiver_Scanner.PotentialWorkThingsGlobal));
|
||||
var potentialWorkThingsGlobal_pawn_TryGetPrefilteredPotentialWorkThingsAnyType
|
||||
= FishTranspiler.Call(static () => TryGetPrefilteredPotentialWorkThingsAnyType(null!, null!));
|
||||
|
||||
var scanner_PotentialWorkThingRequest = GetGetPropertyCallvirt(typeof(WorkGiver_Scanner), nameof(WorkGiver_Scanner.PotentialWorkThingRequest));
|
||||
var scanner_pawn_GetCachedThingRequest = GetCall(() => GetCachedThingRequest(null, null));
|
||||
var scanner_PotentialWorkThingRequest = FishTranspiler.PropertyGetter(typeof(WorkGiver_Scanner),
|
||||
nameof(WorkGiver_Scanner.PotentialWorkThingRequest));
|
||||
var scanner_pawn_GetCachedThingRequest
|
||||
= FishTranspiler.Call(static () => GetCachedThingRequest(null!, null!));
|
||||
|
||||
foreach (var code in CodeInstructions)
|
||||
{
|
||||
@@ -97,31 +111,35 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
return originalRequest;
|
||||
|
||||
var listerThings = pawn.Map.listerThings.ThingsMatching(originalRequest);
|
||||
var collection = listerThings.Count > 100 ? (scanner.PotentialWorkThingsGlobal(pawn) as ICollection<Thing>) : null;
|
||||
var collection = listerThings.Count > 100
|
||||
? scanner.PotentialWorkThingsGlobal(pawn) as ICollection<Thing>
|
||||
: null;
|
||||
|
||||
var processedThings = TryGetPrefilteredPotentialWorkThingsAnyType(collection is null || listerThings.Count < collection.Count * 4 ? listerThings : collection, pawn);
|
||||
var fishCache = originalRequest.singleDef != null ? FishCache.ForDef(originalRequest.singleDef) : FishCache.ForGroup(originalRequest.group);
|
||||
fishCache.FilteredThings = processedThings as List<Thing> ?? new(processedThings);
|
||||
return new ThingRequest() { group = originalRequest.group, singleDef = fishCache };
|
||||
var processedThings = TryGetPrefilteredPotentialWorkThingsAnyType(
|
||||
collection is null || listerThings.Count < collection.Count * 4 ? listerThings : collection, pawn);
|
||||
var fishCache = originalRequest.singleDef != null
|
||||
? FishCache.ForDef(originalRequest.singleDef)
|
||||
: FishCache.ForGroup(originalRequest.group);
|
||||
fishCache.FilteredThings = processedThings as List<Thing> ?? [..processedThings];
|
||||
return new() { group = originalRequest.group, singleDef = fishCache };
|
||||
}
|
||||
public static IEnumerable<Thing> TryGetPrefilteredPotentialWorkThingsAnyType(IEnumerable<Thing> originalWorkThings, Pawn pawn)
|
||||
|
||||
public static IEnumerable<Thing> TryGetPrefilteredPotentialWorkThingsAnyType(
|
||||
IEnumerable<Thing> originalWorkThings, Pawn pawn)
|
||||
=> originalWorkThings switch
|
||||
{
|
||||
List<Thing> => Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(originalWorkThings, pawn),
|
||||
List<Pawn> => Generic<Pawn>.TryGetPrefilteredPotentialWorkThingsForList(originalWorkThings, pawn),
|
||||
_ => GetPrefilteredPotentialWorkThingsUncached(originalWorkThings, pawn)
|
||||
};
|
||||
public static IEnumerable<Thing> GetPrefilteredPotentialWorkThingsUncached(IEnumerable<Thing> originalWorkThings, Pawn pawn)
|
||||
=> (originalWorkThings is not ICollection<Thing> collection || collection.Count < 2048)
|
||||
? CheckForbidden(originalWorkThings, pawn)
|
||||
: Generic<Thing>.Threaded_CheckForbiddenAndAddToAnyCollection(collection, pawn);
|
||||
|
||||
public static IEnumerable<Thing> CheckForbidden(IEnumerable<Thing> things, Pawn pawn)
|
||||
public static IEnumerable<Thing>
|
||||
GetPrefilteredPotentialWorkThingsUncached(IEnumerable<Thing>? originalWorkThings, Pawn pawn)
|
||||
{
|
||||
if (things is null)
|
||||
if (originalWorkThings is null)
|
||||
yield break;
|
||||
|
||||
foreach (var thing in things)
|
||||
foreach (var thing in originalWorkThings)
|
||||
{
|
||||
if (!thing.IsForbidden(pawn))
|
||||
yield return thing;
|
||||
@@ -130,7 +148,8 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
|
||||
public static class Generic<T> where T : Thing
|
||||
{
|
||||
public static IEnumerable<Thing> TryGetPrefilteredPotentialWorkThingsForList(IEnumerable<Thing> originalWorkThings, Pawn pawn)
|
||||
public static IEnumerable<Thing> TryGetPrefilteredPotentialWorkThingsForList(
|
||||
IEnumerable<Thing> originalWorkThings, Pawn pawn)
|
||||
{
|
||||
if (!originalWorkThings.Any())
|
||||
return originalWorkThings;
|
||||
@@ -140,19 +159,20 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
|
||||
return cache;
|
||||
}
|
||||
public static T[] RecalculatePrefilteredPotentialWorkThings(IEnumerable<Thing> originalWorkThings, Pawn pawn)
|
||||
|
||||
public static T[] RecalculatePrefilteredPotentialWorkThings(IEnumerable<Thing> originalWorkThings,
|
||||
Pawn pawn)
|
||||
{
|
||||
var inputList = originalWorkThings as List<T>;
|
||||
if (inputList.Count < 512)
|
||||
Sequential_CheckForbiddenAndAddToList(inputList, pawn);
|
||||
else
|
||||
Threaded_CheckForbiddenAndAddToList(inputList, pawn);
|
||||
var inputList = (List<T>)originalWorkThings;
|
||||
Sequential_CheckForbiddenAndAddToList(inputList, pawn);
|
||||
|
||||
var cache = _tempThingList.ToArray();
|
||||
if (cache.Length < 1024)
|
||||
{
|
||||
var pawnPosition = pawn.Position;
|
||||
Array.Sort(cache, (a, b) => (pawnPosition - a.Position).LengthHorizontalSquared.CompareTo((pawnPosition - b.Position).LengthHorizontalSquared));
|
||||
Array.Sort(cache,
|
||||
(a, b) => (pawnPosition - a.Position).LengthHorizontalSquared.CompareTo(
|
||||
(pawnPosition - b.Position).LengthHorizontalSquared));
|
||||
}
|
||||
|
||||
PrefilteredPotentialWorkThingsGlobal[originalWorkThings] = cache;
|
||||
@@ -160,6 +180,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
private static void Sequential_CheckForbiddenAndAddToList(List<T> inputList, Pawn pawn)
|
||||
{
|
||||
_tempThingList.Clear();
|
||||
@@ -171,82 +192,20 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
tempThingList.Add(inputList[i]);
|
||||
}
|
||||
}
|
||||
private static void Threaded_CheckForbiddenAndAddToList(List<T> inputList, Pawn pawn)
|
||||
{
|
||||
_tempThingList.Clear();
|
||||
#if DEBUG
|
||||
DebugLog.Message("Running threading code");
|
||||
var stopwatch = new System.Diagnostics.Stopwatch();
|
||||
stopwatch.Start();
|
||||
#endif
|
||||
var partitioner = Partitioner.Create(0, inputList.Count);
|
||||
|
||||
Parallel.ForEach<Tuple<int, int>, (List<T> localList, List<T> sourceList, Pawn pawn)>(partitioner,
|
||||
() => new(new(), inputList, pawn),
|
||||
(range, state, vars) =>
|
||||
{
|
||||
for (int i = range.Item1; i < range.Item2; i++)
|
||||
{
|
||||
var item = vars.sourceList[i];
|
||||
if (!item.IsForbidden(vars.pawn))
|
||||
vars.localList.Add(item);
|
||||
}
|
||||
return vars;
|
||||
},
|
||||
vars =>
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_tempThingList.AddRange(vars.localList);
|
||||
}
|
||||
});
|
||||
#if DEBUG
|
||||
stopwatch.Stop();
|
||||
DebugLog.Message($"Finished threading code without errors in {stopwatch.Elapsed}");
|
||||
#endif
|
||||
}
|
||||
public static List<T> Threaded_CheckForbiddenAndAddToAnyCollection(ICollection<T> inputCollection, Pawn pawn)
|
||||
{
|
||||
_tempThingList.Clear();
|
||||
#if DEBUG
|
||||
DebugLog.Message("Running threading code");
|
||||
var stopwatch = new System.Diagnostics.Stopwatch();
|
||||
stopwatch.Start();
|
||||
#endif
|
||||
|
||||
Parallel.ForEach<T, (List<T> localList, Pawn pawn)>(inputCollection,
|
||||
() => new(new(), pawn),
|
||||
(thing, state, vars) =>
|
||||
{
|
||||
if (!thing.IsForbidden(vars.pawn))
|
||||
vars.localList.Add(thing);
|
||||
return vars;
|
||||
},
|
||||
vars =>
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_tempThingList.AddRange(vars.localList);
|
||||
}
|
||||
});
|
||||
#if DEBUG
|
||||
stopwatch.Stop();
|
||||
DebugLog.Message($"Finished threading code without errors in {stopwatch.Elapsed}");
|
||||
#endif
|
||||
return _tempThingList;
|
||||
}
|
||||
|
||||
private static object _lock { get; } = new();
|
||||
private static List<T> _tempThingList { get; } = new();
|
||||
private static readonly List<T> _tempThingList = [];
|
||||
}
|
||||
|
||||
public static Dictionary<IEnumerable<Thing>, IEnumerable<Thing>> PrefilteredPotentialWorkThingsGlobal { get; } = new();
|
||||
public static Dictionary<IEnumerable<Thing>, IEnumerable<Thing>> PrefilteredPotentialWorkThingsGlobal { get; }
|
||||
= [];
|
||||
}
|
||||
|
||||
public sealed class ThingRequest_IsUndefined_Patch : FishPatch
|
||||
{
|
||||
public override string Description => "Part of the work scanning optimization";
|
||||
public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.IsUndefined));
|
||||
|
||||
public override MethodBase TargetMethodInfo
|
||||
=> AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.IsUndefined));
|
||||
|
||||
[HarmonyPriority(Priority.Last)]
|
||||
public static void Postfix(ThingRequest __instance, ref bool __result)
|
||||
{
|
||||
@@ -255,6 +214,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
|
||||
__result = NewResult(__instance);
|
||||
}
|
||||
|
||||
public static bool NewResult(ThingRequest __instance)
|
||||
=> __instance.group == ThingRequestGroup.Undefined && !((FishCache)__instance.singleDef).IsSingleDef;
|
||||
}
|
||||
@@ -262,7 +222,10 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
public sealed class ThingRequest_CanBeFoundInRegion_Patch : FirstPriorityFishPatch
|
||||
{
|
||||
public override string Description => "Part of the work scanning optimization";
|
||||
public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.CanBeFoundInRegion));
|
||||
|
||||
public override MethodBase TargetMethodInfo
|
||||
=> AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.CanBeFoundInRegion));
|
||||
|
||||
public static bool Prefix(ThingRequest __instance, ref bool __result)
|
||||
{
|
||||
if (__instance.singleDef is not FishCache)
|
||||
@@ -271,15 +234,20 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
__result = NewResult(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool NewResult(ThingRequest __instance)
|
||||
=> ((FishCache)__instance.singleDef).IsSingleDef
|
||||
|| (__instance.group != ThingRequestGroup.Undefined && (__instance.group == ThingRequestGroup.Nothing || __instance.group.StoreInRegion()));
|
||||
|| (__instance.group != ThingRequestGroup.Undefined
|
||||
&& (__instance.group == ThingRequestGroup.Nothing || __instance.group.StoreInRegion()));
|
||||
}
|
||||
|
||||
public sealed class ListerThings_ThingsMatching_Patch : FirstPriorityFishPatch
|
||||
{
|
||||
public override string Description => "Part of the work scanning optimization";
|
||||
public override Expression<Action> TargetMethod => () => new ListerThings(default).ThingsMatching(default);
|
||||
|
||||
public override MethodBase? TargetMethodInfo { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ListerThings), nameof(ListerThings.ThingsMatching));
|
||||
|
||||
public static bool Prefix(ListerThings __instance, ThingRequest req, ref List<Thing> __result)
|
||||
{
|
||||
if (req.singleDef is not FishCache)
|
||||
@@ -288,6 +256,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
__result = GetCache(__instance, req);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<Thing> GetCache(ListerThings __instance, ThingRequest req)
|
||||
{
|
||||
var cachedThings = ((FishCache)req.singleDef).FilteredThings ?? ListerThings.EmptyList;
|
||||
@@ -301,26 +270,35 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
return cachedThings;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Thing> GetForRegion(ListerThings __instance, ThingRequest req)
|
||||
=> new(TryIssueJobPackage_Patch.Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(
|
||||
(((FishCache)req.singleDef) is var cache && cache.IsSingleDef
|
||||
? __instance.listsByDef.TryGetValue(cache.Def, out var value) ? value : null
|
||||
: __instance.listsByGroup.TryGetItem((int)req.group, out var item) ? item : null) ?? ListerThings.EmptyList, CurrentPawn));
|
||||
=>
|
||||
[
|
||||
..TryIssueJobPackage_Patch.Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(
|
||||
((FishCache)req.singleDef is { IsSingleDef: true } cache
|
||||
? __instance.listsByDef.TryGetValue(cache.Def, out var value) ? value : null
|
||||
: __instance.listsByGroup.TryGetItem((int)req.group, out var item)
|
||||
? item
|
||||
: null)
|
||||
?? ListerThings.EmptyList, CurrentPawn)
|
||||
];
|
||||
}
|
||||
|
||||
public class FishCache : ThingDef
|
||||
{
|
||||
public bool IsSingleDef => Def != null;
|
||||
public ThingDef Def { get; private set; }
|
||||
public List<Thing> FilteredThings { get; set; }
|
||||
public ThingDef? Def { get; private init; }
|
||||
public List<Thing>? FilteredThings { get; set; }
|
||||
|
||||
public static FishCache ForDef(ThingDef def)
|
||||
{
|
||||
if (_dictOfDefs.TryGetValue(def, out var value))
|
||||
return value;
|
||||
|
||||
|
||||
_dictOfDefs[def] = value = new(def.defName) { Def = def };
|
||||
return value;
|
||||
}
|
||||
|
||||
public static FishCache ForGroup(ThingRequestGroup group)
|
||||
{
|
||||
if (_dictOfGroups.TryGetValue(group, out var value))
|
||||
@@ -329,15 +307,17 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
||||
_dictOfGroups[group] = value = new(group.ToString());
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void ClearAll()
|
||||
{
|
||||
_dictOfGroups.Clear();
|
||||
_dictOfDefs.Clear();
|
||||
}
|
||||
|
||||
private FishCache(string name) => defName = name;
|
||||
private static Dictionary<ThingRequestGroup, FishCache> _dictOfGroups { get; } = new();
|
||||
private static Dictionary<ThingDef, FishCache> _dictOfDefs { get; } = new();
|
||||
private static readonly Dictionary<ThingRequestGroup, FishCache> _dictOfGroups = new();
|
||||
private static readonly Dictionary<ThingDef, FishCache> _dictOfDefs = new();
|
||||
}
|
||||
|
||||
private static Pawn CurrentPawn { get; set; }
|
||||
private static Pawn? CurrentPawn { get; set; }
|
||||
}*/
|
||||
@@ -66,11 +66,6 @@ public sealed class WorkGiver_DoBillPrepatches : ClassWithFishPrepatches
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
// public static CodeInstructions Transpiler(CodeInstructions codeInstructions, MethodBase method)
|
||||
// => Reflection.MakeReplacementCall(AccessTools.Method(
|
||||
// typeof(TryFindBestIngredientsHelper_InnerDelegate_Patch), nameof(ReplacementBody),
|
||||
// generics: new[] { DelegateInstanceType }));
|
||||
|
||||
public static bool ReplacementBody(object __instance, Region r)
|
||||
{
|
||||
var instance = _delegateInstance.Get(__instance);
|
||||
@@ -86,8 +81,11 @@ public sealed class WorkGiver_DoBillPrepatches : ClassWithFishPrepatches
|
||||
var bill = thingValidator.Bill;
|
||||
|
||||
if (HasNoFilters(bill) // apparently applies to a rim reborn benches?
|
||||
|| bill.billStack?.billGiver
|
||||
#if V1_4
|
||||
|| bill.billStack?.billGiver is Building_MechGestator
|
||||
is Building_MechGestator
|
||||
#else
|
||||
is Building_WorkTableAutonomous
|
||||
#endif
|
||||
|| (BlackListedModExtensions.Count > 0
|
||||
&& IsBlackListed(bill))) // <-- Fuck these too
|
||||
@@ -532,9 +530,12 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
|
||||
public static void Prefix(ref bool alreadySorted, Bill? bill)
|
||||
=> alreadySorted = alreadySorted
|
||||
|| (bill?.billStack is { } stack
|
||||
#if V1_4
|
||||
// ReSharper disable once MergeIntoPattern
|
||||
&& stack.billGiver is not Building_MechGestator
|
||||
&& stack.billGiver is not
|
||||
#if V1_4
|
||||
Building_MechGestator
|
||||
#else
|
||||
Building_WorkTableAutonomous
|
||||
#endif
|
||||
);
|
||||
|
||||
@@ -575,8 +576,11 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
|
||||
public static void MarkIngredientCountAsFound(IngredientCount ingredientCount, Bill? bill)
|
||||
{
|
||||
if (bill?.billStack is not { } billStack
|
||||
|| billStack.billGiver is
|
||||
#if V1_4
|
||||
|| billStack.billGiver is Building_MechGestator
|
||||
Building_MechGestator
|
||||
#else
|
||||
Building_WorkTableAutonomous
|
||||
#endif
|
||||
)
|
||||
{
|
||||
|
||||
@@ -27,6 +27,23 @@ public sealed class Buildings : ClassWithFishPrepatches
|
||||
=> (IEnumerable<T>)__instance.Cache().ColonistBuildingsByType.GetOrAdd(typeof(T));
|
||||
}
|
||||
|
||||
#if !V1_4
|
||||
public sealed class AllColonistBuildingsOfTypePatch : FishPrepatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Same as AllBuildingsColonistOfClass, just there as a new copy in 1.5";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ListerBuildings), nameof(ListerBuildings.AllColonistBuildingsOfType));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody<Building>);
|
||||
|
||||
public static IEnumerable<T> ReplacementBody<T>(ListerBuildings __instance)
|
||||
=> (IEnumerable<T>)__instance.Cache().ColonistBuildingsByType.GetOrAdd(typeof(T));
|
||||
}
|
||||
#endif
|
||||
|
||||
public sealed class ColonistsHaveResearchBenchPatch : FishPrepatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
@@ -60,6 +77,72 @@ public sealed class Buildings : ClassWithFishPrepatches
|
||||
=> __instance.Cache().ColonistBuildingsByDef.GetOrAdd(def).Count > 0;
|
||||
}
|
||||
|
||||
#if !V1_4
|
||||
public sealed class AllBuildingsColonistOfGroup_Patch : FishPrepatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Optimizes building lookups by group on ListerBuildings to directly return the result of cached "
|
||||
+ "data instead of iterating over all buildings on the map trying to find all matches";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ListerBuildings), nameof(ListerBuildings.AllBuildingsColonistOfGroup));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
public static List<Building> ReplacementBody(ListerBuildings __instance, ThingRequestGroup group)
|
||||
{
|
||||
var result = ListerBuildings.allBuildingsColonistOfGroupResult;
|
||||
result.Clear();
|
||||
|
||||
if (group == ThingRequestGroup.Undefined)
|
||||
return result;
|
||||
|
||||
var listerThings = TryGetListerThings(__instance);
|
||||
if (listerThings != null)
|
||||
{
|
||||
var thingsOfGroup = listerThings.ThingsInGroup(group);
|
||||
var playerFaction = Faction.OfPlayerSilentFail;
|
||||
for (var i = 0; i < thingsOfGroup.Count; i++)
|
||||
{
|
||||
if (thingsOfGroup[i] is Building building && building.Faction == playerFaction)
|
||||
result.Add(building);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FallbackLoop(__instance, group);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void FallbackLoop(ListerBuildings listerBuildings, ThingRequestGroup group)
|
||||
{
|
||||
var allBuildingsColonist = listerBuildings.allBuildingsColonist;
|
||||
|
||||
for (var i = 0; i < allBuildingsColonist.Count; i++)
|
||||
{
|
||||
if (group.Includes(allBuildingsColonist[i].def))
|
||||
ListerBuildings.allBuildingsColonistOfGroupResult.Add(allBuildingsColonist[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static ListerThings? TryGetListerThings(ListerBuildings listerBuildings)
|
||||
{
|
||||
var maps = Current.Game.Maps;
|
||||
for (var i = 0; i < maps.Count; i++)
|
||||
{
|
||||
if (maps[i].listerBuildings == listerBuildings)
|
||||
return maps[i].listerThings;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public sealed class AllBuildingsColonistOfDef_Patch : FishPrepatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
@@ -73,8 +156,18 @@ public sealed class Buildings : ClassWithFishPrepatches
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
#if !V1_5
|
||||
public static IEnumerable<Building> ReplacementBody(ListerBuildings __instance, ThingDef def)
|
||||
=> __instance.Cache().ColonistBuildingsByDef.GetOrAdd(def);
|
||||
#else
|
||||
public static List<Building> ReplacementBody(ListerBuildings __instance, ThingDef def)
|
||||
{
|
||||
var result = ListerBuildings.allBuildingsColonistOfDefResult;
|
||||
result.Clear();
|
||||
result.AddRange(__instance.Cache().ColonistBuildingsByDef.GetOrAdd(def));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public sealed class AllBuildingsNonColonistOfDef_Patch : FishPrepatch
|
||||
@@ -228,8 +321,8 @@ public sealed class Buildings : ClassWithFishPrepatches
|
||||
public readonly record struct Cache()
|
||||
{
|
||||
public readonly FishTable<ThingDef, IndexedFishSet<Building>>
|
||||
ColonistBuildingsByDef = new(),
|
||||
NonColonistBuildingsByDef = new();
|
||||
ColonistBuildingsByDef = [],
|
||||
NonColonistBuildingsByDef = [];
|
||||
|
||||
public readonly IndexedFishSet<Building_ResearchBench> ColonistResearchBenches = [];
|
||||
|
||||
|
||||
@@ -97,13 +97,18 @@ public sealed class Haulables : ClassWithFishPrepatches
|
||||
public sealed class TickPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
#if V1_4
|
||||
= "Essentially fixes a bug that was causing the method to tick cells multiple times for storages with less "
|
||||
+ "than 4 cells. Not ticking multiple times leads to a performance improvement. Also handles things queued "
|
||||
+ "for removal by the StoreUtilityPrepatches:TryFindBestBetterStoreCellFor patch";
|
||||
#else
|
||||
= "Handles things queued for removal by the StoreUtilityPrepatches:TryFindBestBetterStoreCellFor patch";
|
||||
#endif
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ListerHaulables), nameof(ListerHaulables.ListerHaulablesTick));
|
||||
|
||||
#if V1_4
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
{
|
||||
var instructions = ilProcessor.instructions;
|
||||
@@ -120,6 +125,7 @@ public sealed class Haulables : ClassWithFishPrepatches
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetCellCountToTick(int original, SlotGroup slotGroup)
|
||||
=> Math.Min(original, slotGroup.CellsList.Count);
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Postfix(ListerHaulables __instance)
|
||||
@@ -141,6 +147,35 @@ public sealed class Haulables : ClassWithFishPrepatches
|
||||
}
|
||||
}
|
||||
|
||||
#if !V1_4
|
||||
public sealed class CellsCheckTickPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Essentially fixes a bug that was causing the method to tick cells multiple times for storages with less "
|
||||
+ "than 4 cells. Not ticking multiple times leads to a performance improvement";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ListerHaulables), nameof(ListerHaulables.CellsCheckTick));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
{
|
||||
var instructions = ilProcessor.instructions;
|
||||
|
||||
var fourInstructionIndex = instructions.FirstIndexOf(static code
|
||||
=> code.OpCode == OpCodes.Ldc_I4_4 || (code.OpCode == OpCodes.Ldc_I4 && code.Operand is 4));
|
||||
|
||||
ilProcessor.InsertRange(fourInstructionIndex + 1,
|
||||
(OpCodes.Ldloc,
|
||||
ilProcessor.Body.Variables.First(static local => local.VariableType.Is(typeof(SlotGroup)))),
|
||||
(OpCodes.Call, methodof(GetCellCountToTick)));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetCellCountToTick(int original, SlotGroup slotGroup)
|
||||
=> Math.Min(original, slotGroup.CellsList.Count);
|
||||
}
|
||||
#endif
|
||||
|
||||
public record struct Cache()
|
||||
{
|
||||
public readonly FishSet<int> ContainedThings = [];
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using PerformanceFish.Prepatching;
|
||||
@@ -26,20 +27,29 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
||||
{
|
||||
if (!ListerThings.EverListable(t.def, __instance.use))
|
||||
return;
|
||||
|
||||
AddToDefList(__instance, t);
|
||||
|
||||
var allGroups = ThingListGroupHelper.AllGroups;
|
||||
foreach (var thingRequestGroup in allGroups)
|
||||
lock (__instance.LockObject())
|
||||
{
|
||||
if ((__instance.use == ListerThingsUse.Region && !thingRequestGroup.StoreInRegion())
|
||||
|| !thingRequestGroup.Includes(t.def))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AddToDefList(__instance, t);
|
||||
|
||||
AddToGroupList(__instance, t, thingRequestGroup);
|
||||
var allGroups = ThingListGroupHelper.AllGroups;
|
||||
foreach (var thingRequestGroup in allGroups)
|
||||
{
|
||||
if ((__instance.use == ListerThingsUse.Region && !thingRequestGroup.StoreInRegion())
|
||||
|| !thingRequestGroup.Includes(t.def))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddToGroupList(__instance, t, thingRequestGroup);
|
||||
}
|
||||
|
||||
AddToTypeList(__instance, t);
|
||||
}
|
||||
|
||||
#if !V1_4
|
||||
__instance.thingListChangedCallbacks?.onThingAdded?.Invoke(t);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,57 +71,86 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
||||
if (!ListerThings.EverListable(t.def, __instance.use))
|
||||
return;
|
||||
|
||||
RemoveFromDefList(__instance, t);
|
||||
|
||||
var allGroups = ThingListGroupHelper.AllGroups;
|
||||
for (var i = 0; i < allGroups.Length; i++)
|
||||
lock (__instance.LockObject())
|
||||
{
|
||||
var thingRequestGroup = allGroups[i];
|
||||
if ((__instance.use == ListerThingsUse.Region && !thingRequestGroup.StoreInRegion())
|
||||
|| !thingRequestGroup.Includes(t.def))
|
||||
RemoveFromDefList(__instance, t);
|
||||
|
||||
var allGroups = ThingListGroupHelper.AllGroups;
|
||||
for (var i = 0; i < allGroups.Length; i++)
|
||||
{
|
||||
continue;
|
||||
var thingRequestGroup = allGroups[i];
|
||||
if ((__instance.use == ListerThingsUse.Region && !thingRequestGroup.StoreInRegion())
|
||||
|| !thingRequestGroup.Includes(t.def))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RemoveFromGroupList(__instance, t, thingRequestGroup);
|
||||
}
|
||||
|
||||
RemoveFromGroupList(__instance, t, thingRequestGroup);
|
||||
RemoveFromTypeList(__instance, t);
|
||||
}
|
||||
|
||||
#if !V1_4
|
||||
__instance.thingListChangedCallbacks?.onThingRemoved?.Invoke(t);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddToDefList(ListerThings lister, Thing thing)
|
||||
{
|
||||
if (!lister.listsByDef.TryGetValue(thing.def, out var value))
|
||||
{
|
||||
value = [];
|
||||
lister.listsByDef.Add(thing.def, value);
|
||||
}
|
||||
lister.listsByDef.Add(thing.def, value = []);
|
||||
|
||||
value.Add(thing);
|
||||
lister.IndexMapByDef()[thing.GetKey()] = lister.listsByDef[thing.def].Count - 1;
|
||||
lister.IndexMapByDef()[thing.GetKey()] = value.Count - 1;
|
||||
}
|
||||
|
||||
public static void AddToGroupList(ListerThings lister, Thing thing, ThingRequestGroup thingRequestGroup)
|
||||
{
|
||||
var list = lister.listsByGroup[(uint)thingRequestGroup];
|
||||
ref var list = ref lister.listsByGroup[(uint)thingRequestGroup];
|
||||
ref var hash = ref lister.stateHashByGroup[(uint)thingRequestGroup];
|
||||
|
||||
if (list == null)
|
||||
{
|
||||
list = [];
|
||||
lister.listsByGroup[(uint)thingRequestGroup] = list;
|
||||
lister.stateHashByGroup[(uint)thingRequestGroup] = 0;
|
||||
hash = 0;
|
||||
}
|
||||
|
||||
list.Add(thing);
|
||||
lister.stateHashByGroup[(uint)thingRequestGroup]++;
|
||||
hash++;
|
||||
|
||||
lister.IndexMapByGroup()[new(thingRequestGroup, thing.GetKey())]
|
||||
= lister.listsByGroup[(uint)thingRequestGroup].Count - 1;
|
||||
lister.IndexMapByGroup()[new(thingRequestGroup, thing.GetKey())] = list.Count - 1;
|
||||
}
|
||||
|
||||
public static void AddToTypeList(ListerThings lister, Thing thing)
|
||||
{
|
||||
var thingType = thing.GetType();
|
||||
if (thingType == typeof(Thing))
|
||||
return;
|
||||
|
||||
var cache = lister.Cache();
|
||||
|
||||
do
|
||||
{
|
||||
if (thingType == null)
|
||||
break;
|
||||
|
||||
cache.ThingsByType.GetOrAdd(thingType).Add(thing);
|
||||
}
|
||||
while ((thingType = thingType.BaseType) != typeof(Thing));
|
||||
}
|
||||
|
||||
public static void RemoveFromDefList(ListerThings lister, Thing thing)
|
||||
{
|
||||
var thingKey = thing.GetKey();
|
||||
var indexMapByDef = lister.IndexMapByDef();
|
||||
var listByDef = lister.listsByDef[thing.def];
|
||||
|
||||
if (lister.listsByDef.TryGetValue(thing.def) is not { } listByDef)
|
||||
{
|
||||
LogFailureToRemoveThingFromLister(lister, thing);
|
||||
return;
|
||||
}
|
||||
|
||||
var indexByDef = indexMapByDef.TryGetValue(thingKey, out var knownIndexByDef)
|
||||
&& knownIndexByDef < listByDef.Count
|
||||
@@ -127,10 +166,19 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
||||
indexMapByDef[listByDef[indexByDef].GetKey()] = indexByDef;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void LogFailureToRemoveThingFromLister(ListerThings lister, Thing thing)
|
||||
=> Log.Error($"Tried and failed removing Thing '{thing}' of map '{
|
||||
thing.TryGetMap().ToStringSafe()}' from ListerThings of map '{
|
||||
lister.AllThings?.FirstOrDefault()?.TryGetMap().ToStringSafe()}'.\n{
|
||||
new StackTrace(2, true)}");
|
||||
|
||||
public static void RemoveFromGroupList(ListerThings lister, Thing thing, ThingRequestGroup thingRequestGroup)
|
||||
{
|
||||
var indexMapByGroup = lister.IndexMapByGroup();
|
||||
var listByGroup = lister.listsByGroup[(int)thingRequestGroup];
|
||||
|
||||
if (lister.listsByGroup[(int)thingRequestGroup] is not { } listByGroup)
|
||||
return;
|
||||
|
||||
var groupMapKey = new GroupThingPair(thingRequestGroup, thing.GetKey());
|
||||
|
||||
@@ -151,6 +199,24 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
||||
lister.stateHashByGroup[(uint)thingRequestGroup]++;
|
||||
}
|
||||
|
||||
public static void RemoveFromTypeList(ListerThings lister, Thing thing)
|
||||
{
|
||||
var thingType = thing.GetType();
|
||||
if (thingType == typeof(Thing))
|
||||
return;
|
||||
|
||||
var cache = lister.Cache();
|
||||
|
||||
do
|
||||
{
|
||||
if (thingType == null)
|
||||
break;
|
||||
|
||||
cache.ThingsByType.GetOrAdd(thingType).Remove(thing);
|
||||
}
|
||||
while ((thingType = thingType.BaseType) != typeof(Thing));
|
||||
}
|
||||
|
||||
public sealed class ContainsPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
@@ -163,24 +229,99 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
public static bool ReplacementBody(ListerThings __instance, Thing t)
|
||||
=> __instance.IndexMapByDef().ContainsKey(t.GetKey())
|
||||
|| (__instance.listsByDef.TryGetValue(t.def)?.Contains(t) ?? false);
|
||||
{
|
||||
lock (__instance.LockObject())
|
||||
{
|
||||
return __instance.IndexMapByDef().ContainsKey(t.GetKey())
|
||||
|| (__instance.listsByDef.TryGetValue(t.def)?.Contains(t) ?? false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ClearPatch : FishPrepatch
|
||||
{
|
||||
public override List<Type> LinkedPatches { get; }
|
||||
= [typeof(AddPatch), typeof(RemovePatch), typeof(ContainsPatch)];
|
||||
|
||||
public override string? Description { get; }
|
||||
= "Required to keep the ListerThings cache synced";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ListerThings), nameof(ListerThings.Clear));
|
||||
|
||||
public static void Postfix(ListerThings __instance)
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
public static void ReplacementBody(ListerThings __instance)
|
||||
{
|
||||
__instance.IndexMapByDef().Clear();
|
||||
__instance.IndexMapByGroup().Clear();
|
||||
lock (__instance.LockObject())
|
||||
{
|
||||
__instance.listsByDef.Clear();
|
||||
|
||||
var listsByGroup = __instance.listsByGroup;
|
||||
|
||||
for (var i = 0; i < listsByGroup.Length; i++)
|
||||
{
|
||||
listsByGroup[i]?.Clear();
|
||||
__instance.stateHashByGroup[i] = 0;
|
||||
}
|
||||
|
||||
__instance.IndexMapByDef().Clear();
|
||||
__instance.IndexMapByGroup().Clear();
|
||||
ClearTypeLists(__instance);
|
||||
}
|
||||
|
||||
// no thing __instance.thingListChangedCallbacks?.onThingRemoved?.Invoke(t); calls in original
|
||||
}
|
||||
|
||||
public static void ClearTypeLists(ListerThings lister)
|
||||
{
|
||||
var lists = lister.Cache().ThingsByType;
|
||||
foreach (var (_, list) in lists)
|
||||
list.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
#if !V1_4
|
||||
public sealed class GetThingsOfTypePatch : FishPrepatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Optimizes thing lookups for generic types on ListerThings to directly return the result of cached "
|
||||
+ "data instead of iterating over all things on the map trying to find all matches";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ListerThings), nameof(ListerThings.GetThingsOfType), Type.EmptyTypes);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody<Thing>);
|
||||
|
||||
public static IEnumerable<T> ReplacementBody<T>(ListerThings __instance) where T : Thing
|
||||
=> typeof(T) != typeof(Thing) // normally logs an error instead of simply returning all things
|
||||
? (IEnumerable<T>)__instance.Cache().ThingsByType.GetOrAdd(typeof(T))
|
||||
: (IEnumerable<T>)__instance.AllThings;
|
||||
}
|
||||
|
||||
public sealed class GetThingsOfTypeIntoListPatch : FishPrepatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Optimizes thing lookups for generic types on ListerThings to directly return the result of cached "
|
||||
+ "data instead of iterating over all things on the map trying to find all matches";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= typeof(ListerThings).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
||||
.First(static method => method.Name == nameof(ListerThings.GetThingsOfType)
|
||||
&& method.ReturnType == typeof(void)
|
||||
&& method.GetParameters().Length == 1);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody<Thing>);
|
||||
|
||||
public static void ReplacementBody<T>(ListerThings __instance, List<T> list) where T : Thing
|
||||
=> list.AddRange(typeof(T) != typeof(Thing) // normally logs an error instead of simply returning all things
|
||||
? (IEnumerable<T>)__instance.Cache().ThingsByType.GetOrAdd(typeof(T))
|
||||
: (IEnumerable<T>)__instance.AllThings);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public sealed class Things : ClassWithFishPatches
|
||||
@@ -193,8 +334,8 @@ public sealed class Things : ClassWithFishPatches
|
||||
= "Part of the ThingFilter.BestThingRequest optimization. Required by that particular patch and gets "
|
||||
+ "toggled alongside it";
|
||||
|
||||
public override Expression<Action> TargetMethod { get; }
|
||||
= static () => new ListerThings(default).ThingsMatching(default);
|
||||
public override MethodBase? TargetMethodInfo { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ListerThings), nameof(ListerThings.ThingsMatching));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(ListerThings __instance, ThingRequest req, ref List<Thing> __result)
|
||||
@@ -223,11 +364,12 @@ public sealed class Things : ClassWithFishPatches
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static List<Thing> GetThingsOfDefsListFor(IEnumerable<ThingDef> thingDefs)
|
||||
=> (_thingsOfDefsDictionary ??= Cache.Utility.AddNew<Dictionary<IEnumerable<ThingDef>, List<Thing>>>())
|
||||
=> (_thingsOfDefsDictionary
|
||||
??= PerformanceFish.Cache.Utility.AddNew<FishTable<IEnumerable<ThingDef>, List<Thing>>>())
|
||||
.GetOrAdd(thingDefs);
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<IEnumerable<ThingDef>, List<Thing>>? _thingsOfDefsDictionary;
|
||||
private static FishTable<IEnumerable<ThingDef>, List<Thing>>? _thingsOfDefsDictionary;
|
||||
|
||||
#if disabled
|
||||
public sealed class ThingRequest_IsUndefined_Patch : FishPatch
|
||||
@@ -412,4 +554,13 @@ public sealed class Things : ClassWithFishPatches
|
||||
private static Dictionary<ThingDef, FishCache> _dictOfDefs { get; } = new();
|
||||
}
|
||||
#endif
|
||||
|
||||
public readonly record struct Cache()
|
||||
{
|
||||
public readonly FishTable<Type, IList> ThingsByType = new()
|
||||
{
|
||||
ValueInitializer = static type
|
||||
=> (IList)Activator.CreateInstance(typeof(IndexedFishSet<>).MakeGenericType(type))
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public sealed class MassUtilityCaching : ClassWithFishPatches
|
||||
public void Update(float mass, Pawn p)
|
||||
{
|
||||
Mass = mass;
|
||||
//shorter refresh interval because of stack sizes not being considered by the listVersion check
|
||||
// shorter refresh interval because of stack sizes not being considered by the listVersion check
|
||||
_nextRefreshTick = TickHelper.Add(GenTicks.TickRareInterval, p.thingIDNumber);
|
||||
_inventoryListsState = GetInventoryListsState(p);
|
||||
}
|
||||
|
||||
@@ -18,15 +18,12 @@ public sealed class MiscPrepatchedOptimizations : ClassWithFishPrepatches
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Optimization for a basic and frequently used list copying method. Replaces a for loop with an "
|
||||
+ "optimized IL instruction.";/*{(ActiveMods.GradientHair
|
||||
? " Disabled due to incompatibility with Gradient Hair" : "")}";*/
|
||||
|
||||
// public override bool Enabled => base.Enabled && !ActiveMods.GradientHair;
|
||||
+ "optimized IL instruction.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; } = methodof(GenList.ListFullCopy<object>);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ListFullCopy<object>/*Utility.CollectionExtensions.Copy<object>*/);
|
||||
=> ilProcessor.ReplaceBodyWith(ListFullCopy<object>);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static List<T> ListFullCopy<T>(List<T> source) => source.Copy();
|
||||
@@ -51,7 +48,7 @@ public sealed class MiscPrepatchedOptimizations : ClassWithFishPrepatches
|
||||
{
|
||||
public override string? Description { get; } = "Tiny optimization by fixing an incorrect cast";
|
||||
|
||||
public override MethodBase TargetMethodBase => methodof((Func<IntVec3, Map, bool>)GenGrid.InBounds);
|
||||
public override MethodBase TargetMethodBase { get; } = methodof((Func<IntVec3, Map, bool>)GenGrid.InBounds);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
@@ -60,7 +57,7 @@ public sealed class MiscPrepatchedOptimizations : ClassWithFishPrepatches
|
||||
public static bool ReplacementBody(IntVec3 c, Map map)
|
||||
{
|
||||
var size = map.Size;
|
||||
return (uint)c.x < (uint)size.x && (uint)c.z < (uint)size.z;
|
||||
return ((uint)c.x < (uint)size.x) & ((uint)c.z < (uint)size.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,4 +135,23 @@ public static class ActiveMods
|
||||
private static void WarnForMissingModDependency(string modName)
|
||||
=> Log.Warning(
|
||||
$"{modName} mod is missing. Performance Fish may not work correctly without this dependency.");
|
||||
}
|
||||
|
||||
public static class Installed
|
||||
{
|
||||
public static bool[] DLC =
|
||||
[
|
||||
ModLister.RoyaltyInstalled, ModLister.IdeologyInstalled, ModLister.BiotechInstalled
|
||||
#if !V1_4
|
||||
, ModLister.AnomalyInstalled
|
||||
#endif
|
||||
];
|
||||
}
|
||||
|
||||
public enum DLCs
|
||||
{
|
||||
Royalty,
|
||||
Ideology,
|
||||
Biotech,
|
||||
Anomaly
|
||||
}
|
||||
@@ -20,6 +20,15 @@ public static class OnAssembliesLoaded
|
||||
public static void Start()
|
||||
{
|
||||
Loaded = true;
|
||||
|
||||
try
|
||||
{
|
||||
RuntimeHelpers.RunClassConstructor(typeof(ReflectionCaching).TypeHandle);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex.ToString());
|
||||
}
|
||||
|
||||
TryPatchGraphicsSetter();
|
||||
TryPatchPerformanceOptimizer();
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<Optimize>true</Optimize>
|
||||
<Authors>bradson</Authors>
|
||||
<Copyright>Copyright (c) 2023 bradson</Copyright>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||
<AnalysisLevel>preview-all</AnalysisLevel>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>5</WarningLevel>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<Configurations>v1.4</Configurations>
|
||||
<Version>0.5.0.0</Version>
|
||||
<NoWarn>
|
||||
1701;1702;IL2121;CS8500;CS9082;CA1000;CA1002;CA1003;CA1707;CA1010;CA1715;CA1716;CA1724;CA1031;CA1033;
|
||||
CA1034;CA1043;CA1051;CA1305;CA1310;CA1710;CA1711;CA1810;CA1812;CA1819;CA1851;CA2002;CA5394;CA1725;CA1062
|
||||
</NoWarn>
|
||||
</PropertyGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(Configuration)' == 'v1.4'">
|
||||
<PropertyGroup>
|
||||
<OutDir>..\..\1.4\Assemblies\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.*" />
|
||||
<Reference Include="1Fishery">
|
||||
<HintPath>..\..\..\Fishery\1.4\Assemblies\1Fishery.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</When>
|
||||
<!-- When Condition="'$(Configuration)' == 'v1.3'">
|
||||
<PropertyGroup>
|
||||
<OutDir>..\..\1.3\Assemblies\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.3.*" />
|
||||
<Reference Include="1Fishery">
|
||||
<HintPath>..\..\..\Fishery\1.3\Assemblies\1Fishery.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</When -->
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Lib.Harmony" Version="2.2.*" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Zetrith.Prepatcher" Version="1.*" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" ExcludeAssets="runtime" /><!-- Version included in Unity is missing IsNullRef -->
|
||||
<PackageReference Include="PolySharp" Version="1.*" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2022.*" Aliases="nuget" ExcludeAssets="runtime" />
|
||||
<!-- PackageReference Include="Mono.Cecil" Version="0.*" /-->
|
||||
<!-- PackageReference Include="Mono.Reflection" Version="2.*" /-->
|
||||
|
||||
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
|
||||
<Publicize Include="Assembly-CSharp:RimWorld.JobGiver_Haul.TryGiveJob" />
|
||||
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute" />
|
||||
<DoNotPublicize Include="0Harmony:Mono.Collections.Generic.Collection`1" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.buckets" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.entries" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.version" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2+Entry" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1._items" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1._version" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1._size" />
|
||||
<Publicize Include="mscorlib:System.Collections.Generic.List`1.EnsureCapacity" />
|
||||
<Publicize Include="System.Core:System.Collections.Generic.HashSet`1._version" />
|
||||
<Publicize Include="mscorlib:System.MonoCustomAttrs.GetCustomAttributes" />
|
||||
<Publicize Include="mscorlib:System.RuntimeType" />
|
||||
<Publicize Include="mscorlib:System.Reflection.MonoField" />
|
||||
<Publicize Include="mscorlib:System.String.m_firstChar" />
|
||||
|
||||
<None Include="..\..\LICENSE">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath />
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Target Name="ChangeAliasesOfStrongNameAssemblies" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
|
||||
<ItemGroup>
|
||||
<ReferencePath Condition="'%(FileName)' == 'JetBrains.Annotations'">
|
||||
<Aliases>nuget</Aliases><!-- Rider bug, the earlier Aliases="nuget" attribute should cover this -->
|
||||
</ReferencePath>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -3,25 +3,31 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.31911.260
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerformanceFish", "PerformanceFish.csproj", "{E8A3D63E-5850-4866-AB06-DBD44B11F67B}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.5", "1.5.csproj", "{E8A3D63E-5850-4866-AB06-DBD44B11F67B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.4", "1.4.csproj", "{0257ABCD-7009-4C7D-9BD8-599FC47595DB}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6948F7E6-7ED7-4716-87EF-CFF1F465F561}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
..\..\1.4\Defs\GasDefs.xml = ..\..\1.4\Defs\GasDefs.xml
|
||||
..\..\1.4\Defs\MainButtonDefs.xml = ..\..\1.4\Defs\MainButtonDefs.xml
|
||||
..\..\Defs\GasDefs.xml = ..\..\Defs\GasDefs.xml
|
||||
..\..\Defs\MainButtonDefs.xml = ..\..\Defs\MainButtonDefs.xml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
v1.3|Any CPU = v1.3|Any CPU
|
||||
v1.4|Any CPU = v1.4|Any CPU
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.v1.3|Any CPU.ActiveCfg = v1.3|Any CPU
|
||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.v1.3|Any CPU.Build.0 = v1.3|Any CPU
|
||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.v1.4|Any CPU.ActiveCfg = v1.4|Any CPU
|
||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.v1.4|Any CPU.Build.0 = v1.4|Any CPU
|
||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Debug|Any CPU.ActiveCfg = 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.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.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.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -48,19 +48,22 @@ public sealed class PerformanceFishMod : Mod
|
||||
PACKAGE_ID = PackageIDs.PERFORMANCE_FISH;
|
||||
|
||||
[Obsolete("I am not incrementing this version number")]
|
||||
public const decimal VERSION = 0.5M;
|
||||
public const decimal VERSION = 0.6M;
|
||||
|
||||
public static Version LoadedVersion => typeof(PerformanceFishMod).Assembly.GetLoadedVersion();
|
||||
|
||||
public static FishSettings? Settings { get; internal set; }
|
||||
public static PerformanceFishMod? Mod { get; private set; }
|
||||
public static Harmony Harmony { get; private set; } = new(PACKAGE_ID);
|
||||
|
||||
public static Harmony Harmony => _harmony.Value;
|
||||
|
||||
public static IHasFishPatch[]? AllPatchClasses { get; internal set; }
|
||||
public static ClassWithFishPrepatches[]? AllPrepatchClasses { get; internal set; }
|
||||
|
||||
private static FishPatch[]? _allFishPatches;
|
||||
|
||||
private static FishPrepatchBase[]? _allFishPrepatches;
|
||||
private static readonly Lazy<Harmony> _harmony = new(static () => new(PACKAGE_ID), true);
|
||||
|
||||
public static IEnumerable<FishPatch> AllHarmonyPatches
|
||||
=> _allFishPatches ??= AllPatchClasses?.SelectMany(static patchClass => patchClass.Patches.All.Values).ToArray()
|
||||
|
||||
@@ -14,16 +14,21 @@ namespace PerformanceFish;
|
||||
|
||||
public static class PrepatcherFields
|
||||
{
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateObject))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern object LockObject(this ListerThings listerThings);
|
||||
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateMapEvents))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern MapEvents.Instanced Events(this Map map);
|
||||
|
||||
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateThingEvents))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ThingEvents.Instanced Events(this Thing thing);
|
||||
|
||||
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateGeneList))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -69,16 +74,24 @@ public static class PrepatcherFields
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref StorageSettingsPatches.StorageSettingsCache Cache(this StorageSettings storageSettings);
|
||||
|
||||
#if V1_4
|
||||
[PrepatcherField]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref object? MonitorObject(this GasGrid gasGrid);
|
||||
#endif
|
||||
|
||||
#if !V1_4
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateParallelGasJob))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern GasGridOptimization.ParallelJob ParallelJob(this GasGrid gasGrid);
|
||||
#endif
|
||||
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateParallelGasGridArray))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern GasGridOptimization.ParallelGasGrid[] ParallelGasGrids(this GasGrid gasGrid);
|
||||
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateGroupMonitorObject))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern object GasGridMonitorObject(this Map map);
|
||||
|
||||
[PrepatcherField]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref Texture2D? ExpandingIconCache(this WorldObject worldObject);
|
||||
@@ -102,6 +115,11 @@ public static class PrepatcherFields
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref Listers.Buildings.Cache Cache(this ListerBuildings listerBuildings);
|
||||
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateListerThingsCache))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref Listers.Things.Cache Cache(this ListerThings listerThings);
|
||||
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateListerHaulablesCache))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -122,10 +140,6 @@ public static class PrepatcherFields
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref float[] StatsCache(this BuildableDef buildableDef);
|
||||
|
||||
[PrepatcherField]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref object MonitorObject(this GasGrid gasGrid);
|
||||
|
||||
// [PrepatcherField]
|
||||
// [ValueInitializer(nameof(CreateHediffCompList))]
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -149,7 +163,7 @@ public static class PrepatcherFields
|
||||
|
||||
public static List<Hediff> CreateHediffList() => new();
|
||||
#endif
|
||||
|
||||
|
||||
public static List<Gene> CreateGeneList() => [];
|
||||
public static WorldPawnsOptimization.Cache CreateWorldPawnsCache() => new();
|
||||
public static HashSet<Building_Bed> CreateBedHashSet() => [];
|
||||
@@ -163,9 +177,14 @@ public static class PrepatcherFields
|
||||
public static StorageDistrict[] GetDefaultDistrictArray() => StorageDistrict.GetDefaultArray();
|
||||
public static Cache.CellGrid<StorageDistrict> CreateStorageDistrictGrid(Map map) => new(map);
|
||||
public static Listers.Buildings.Cache CreateListerBuildingsCache() => new();
|
||||
public static Listers.Things.Cache CreateListerThingsCache() => new();
|
||||
public static Listers.Haulables.Cache CreateListerHaulablesCache() => new();
|
||||
public static HaulDestinationManagerCache CreateHaulDestinationManagerCache() => new();
|
||||
public static float[] GetEmptyArray() => Array.Empty<float>();
|
||||
public static object CreateObject() => new();
|
||||
#if !V1_4
|
||||
public static GasGridOptimization.ParallelJob CreateParallelGasJob() => new();
|
||||
#endif
|
||||
|
||||
public static float[] CreateBaseStatsCache()
|
||||
{
|
||||
@@ -180,18 +199,36 @@ public static class PrepatcherFields
|
||||
public static GasGridOptimization.ParallelGasGrid[] CreateParallelGasGridArray(GasGrid gasGrid)
|
||||
{
|
||||
DefDatabase<GasDef>.SetIndices();
|
||||
|
||||
|
||||
var map = gasGrid.map;
|
||||
var gasDefs = DefDatabase<GasDef>.AllDefsListForReading;
|
||||
|
||||
if (gasDefs.Count < 3)
|
||||
if (gasDefs.Count
|
||||
#if V1_4
|
||||
< 3
|
||||
#else
|
||||
< 4
|
||||
#endif
|
||||
)
|
||||
{
|
||||
Log.Error($"GasDefs are missing! Expected are at least 3, DefDatabase contains {
|
||||
gasDefs.Count} instead. ParallelGasGrid won't work correctly in this state.");
|
||||
Log.Error($"GasDefs are missing! Expected are at least "
|
||||
#if V1_4
|
||||
+ $"3"
|
||||
#else
|
||||
+ $"4"
|
||||
#endif
|
||||
+ $", DefDatabase contains {
|
||||
gasDefs.Count} instead. ParallelGasGrid won't work correctly in this state.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gasDefs[0] != GasDefOf.BlindSmoke || gasDefs[1] != GasDefOf.ToxGas || gasDefs[2] != GasDefOf.RotStink)
|
||||
if (gasDefs[0] != GasDefOf.BlindSmoke
|
||||
|| gasDefs[1] != GasDefOf.ToxGas
|
||||
|| gasDefs[2] != GasDefOf.RotStink
|
||||
#if !V1_4
|
||||
|| gasDefs[3] != GasDefOf.DeadLifeDust
|
||||
#endif
|
||||
)
|
||||
{
|
||||
Log.Warning("GasDefs out of order. Sorting now to fix.");
|
||||
var allGasDefs = gasDefs.ToList();
|
||||
@@ -199,29 +236,52 @@ public static class PrepatcherFields
|
||||
allGasDefs.Remove(gasDefs[0] = GasDefOf.BlindSmoke);
|
||||
allGasDefs.Remove(gasDefs[1] = GasDefOf.ToxGas);
|
||||
allGasDefs.Remove(gasDefs[2] = GasDefOf.RotStink);
|
||||
#if !V1_4
|
||||
allGasDefs.Remove(gasDefs[3] = GasDefOf.DeadLifeDust);
|
||||
#endif
|
||||
|
||||
for (var i = 0; i < allGasDefs.Count; i++)
|
||||
gasDefs[i + 3] = allGasDefs[i];
|
||||
|
||||
{
|
||||
gasDefs[i
|
||||
#if V1_4
|
||||
+ 3
|
||||
#else
|
||||
+ 4
|
||||
#endif
|
||||
] = allGasDefs[i];
|
||||
}
|
||||
|
||||
DefDatabase<GasDef>.SetIndices();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var grids = new GasGridOptimization.ParallelGasGrid[gasDefs.Count];
|
||||
for (var i = 0; i < grids.Length; i++)
|
||||
grids[i] = new(map, gasDefs[i]);
|
||||
|
||||
#if V1_4
|
||||
if (FishSettings.ThreadingEnabled)
|
||||
{
|
||||
var monitorObject = map.GasGridMonitorObject();
|
||||
|
||||
var monitorObject = gasGrid.MonitorObject() ??= CreateGroupMonitorObject();
|
||||
|
||||
for (var i = 1; i < grids.Length; i++)
|
||||
{
|
||||
var grid = grids[i];
|
||||
ParallelNoAlloc.RegisterBackgroundWaitingWorker(monitorObject, () => grid.Tick());
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
var parallelJob = gasGrid.ParallelJob();
|
||||
if (parallelJob != null!)
|
||||
{
|
||||
parallelJob.GasGrids = grids;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error(
|
||||
"Failed to initialize GasGrid parallel job. Map components may have loaded in an incorrect order.");
|
||||
}
|
||||
#endif
|
||||
return grids;
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
using PerformanceFish.Prepatching;
|
||||
|
||||
namespace PerformanceFish;
|
||||
namespace PerformanceFish.Rendering;
|
||||
|
||||
public sealed class ContentFinderCaching : ClassWithFishPrepatches
|
||||
{
|
||||
@@ -22,7 +22,7 @@ public sealed class ContentFinderCaching : ClassWithFishPrepatches
|
||||
{
|
||||
ref var cache = ref Cache.ByReference<string, CacheValue<T>>.GetOrAddReference(itemPath);
|
||||
|
||||
if (!cache.Cached || !OnStartup.State.Initialized)
|
||||
if (!cache.Cached)
|
||||
return __state = true;
|
||||
|
||||
__result = cache.Value;
|
||||
@@ -42,6 +42,9 @@ public sealed class ContentFinderCaching : ClassWithFishPrepatches
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void UpdateCache<T>(string itemPath, T? __result)
|
||||
{
|
||||
if (!OnStartup.State.Initialized)
|
||||
return;
|
||||
|
||||
ref var cache = ref Cache.ByReference<string, CacheValue<T>>.GetExistingReference(itemPath);
|
||||
|
||||
cache.Cached = true;
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace PerformanceFish;
|
||||
namespace PerformanceFish.Rendering;
|
||||
|
||||
//DDS importing from // https://github.com/sarbian/DDSLoader/blob/master/DatabaseLoaderTexture_DDS.cs
|
||||
public static class DDSLoader
|
||||
@@ -0,0 +1,188 @@
|
||||
// Copyright (c) 2023 bradson
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#if !V1_4
|
||||
using Gilzoide.ManagedJobs;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using PerformanceFish.Prepatching;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace PerformanceFish.Rendering;
|
||||
|
||||
public sealed class DynamicDrawManagerPatches : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class DrawDynamicThingsPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Optimizes rendering by reverting most of the threading added in 1.5, making 5 of the 6 parallel loops "
|
||||
+ "in DrawDynamicThings run on the main thread again. This generally improves rendering performance by "
|
||||
+ "about 10% or so";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(DynamicDrawManager), nameof(DynamicDrawManager.DrawDynamicThings));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
public static void ReplacementBody(DynamicDrawManager instance)
|
||||
{
|
||||
if (!DebugViewSettings.drawThingsDynamic || instance.map.Disposed)
|
||||
return;
|
||||
|
||||
instance.drawingNow = true;
|
||||
|
||||
try
|
||||
{
|
||||
CullAndInitializeThings(instance);
|
||||
|
||||
var hasAnyThingsToDraw = ThingsToDraw.Count > 0;
|
||||
if (hasAnyThingsToDraw)
|
||||
{
|
||||
if (!DebugViewSettings.singleThreadedDrawing)
|
||||
DoPreDrawThings();
|
||||
|
||||
DrawThingsNow();
|
||||
}
|
||||
|
||||
if (PawnShadowsToDraw.Count > 0)
|
||||
DrawShadowsNow();
|
||||
|
||||
if (SilhouetteUtility.CanHighlightAny() && hasAnyThingsToDraw)
|
||||
DrawSilhouettes(instance);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("Exception drawing dynamic things: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ThingsToDraw.Clear();
|
||||
PawnShadowsToDraw.Clear();
|
||||
instance.drawingNow = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CullAndInitializeThings(DynamicDrawManager instance)
|
||||
{
|
||||
var map = instance.map;
|
||||
var mapSizeX = map.SizeX();
|
||||
var fogGrid = map.fogGrid.fogGrid;
|
||||
var snowGrid = map.snowGrid.depthGrid;
|
||||
var checkShadows = MatBases.SunShadow.shader.isSupported;
|
||||
var currentViewRect = Find.CameraDriver.CurrentViewRect.ClipInsideMap(map).ExpandedBy(1);
|
||||
var shadowViewRect = SectionLayer_SunShadows.GetSunShadowsViewRect(map, currentViewRect);
|
||||
instance.drawThings.UnwrapArray(out var drawThings, out var drawThingsCount);
|
||||
|
||||
for (var i = 0; i < drawThingsCount; i++)
|
||||
{
|
||||
var drawThing = drawThings.UnsafeLoad(i);
|
||||
var position = drawThing.Position;
|
||||
var drawThingDef = drawThing.def;
|
||||
var cellIndex = position.CellToIndex(mapSizeX);
|
||||
if ((!fogGrid[cellIndex] || drawThingDef.seeThroughFog)
|
||||
&& (drawThingDef.hideAtSnowDepth >= 1f
|
||||
|| drawThingDef.hideAtSnowDepth >= snowGrid[cellIndex]))
|
||||
{
|
||||
if (currentViewRect.Contains(position)
|
||||
|| currentViewRect.Overlaps(drawThing.OccupiedDrawRect()))
|
||||
{
|
||||
ThingsToDraw.Add(drawThing);
|
||||
|
||||
if (!DebugViewSettings.singleThreadedDrawing)
|
||||
drawThing.DynamicDrawPhase(DrawPhase.EnsureInitialized);
|
||||
}
|
||||
else if (drawThing is Pawn pawn && checkShadows && shadowViewRect.Contains(drawThing.Position))
|
||||
{
|
||||
PawnShadowsToDraw.Add(pawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawShadowsNow()
|
||||
{
|
||||
PawnShadowsToDraw.UnwrapArray(out var pawnShadowsToDraw, out var pawnShadowsToDrawCount);
|
||||
|
||||
for (var i = 0; i < pawnShadowsToDrawCount; i++)
|
||||
{
|
||||
var pawn = pawnShadowsToDraw.UnsafeLoad(i);
|
||||
try
|
||||
{
|
||||
pawn.DrawShadowAt(pawn.DrawPos);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Exception drawing shadow for '{pawn}': {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawThingsNow()
|
||||
{
|
||||
ThingsToDraw.UnwrapArray(out var thingsToDraw, out var thingsToDrawCount);
|
||||
|
||||
for (var i = 0; i < thingsToDrawCount; i++)
|
||||
{
|
||||
var drawThing = thingsToDraw.UnsafeLoad(i);
|
||||
try
|
||||
{
|
||||
drawThing.DynamicDrawPhase(DrawPhase.Draw);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Exception drawing '{drawThing}': {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DoPreDrawThings()
|
||||
{
|
||||
ThingsToDraw.UnwrapArray(out PreDrawThingsJob.thingsToDraw, out var thingsToDrawCount);
|
||||
new ManagedJobParallelFor(PreDrawThingsJob)
|
||||
.Schedule(thingsToDrawCount, UnityData.GetIdealBatchCount(thingsToDrawCount)).Complete();
|
||||
}
|
||||
|
||||
public static readonly List<Thing> ThingsToDraw = [];
|
||||
public static readonly List<Pawn> PawnShadowsToDraw = [];
|
||||
public static readonly PreDrawThings PreDrawThingsJob = new();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void DrawSilhouettes(DynamicDrawManager instance)
|
||||
{
|
||||
ThingsToDraw.UnwrapArray(out var thingsToDraw, out var thingsToDrawCount);
|
||||
|
||||
var inverseFovScale = Find.CameraDriver.InverseFovScale;
|
||||
var altitude = AltitudeLayer.Silhouettes.AltitudeFor();
|
||||
var angleAxis = Quaternion.AngleAxis(0f, Vector3.up);
|
||||
|
||||
for (var i = 0; i < thingsToDrawCount; i++)
|
||||
{
|
||||
if (thingsToDraw.UnsafeLoad(i) is not Pawn pawn || !SilhouetteUtility.ShouldDrawSilhouette(pawn))
|
||||
continue;
|
||||
|
||||
var pawnRenderer = pawn.Drawer.renderer;
|
||||
|
||||
var drawSize = pawnRenderer.SilhouetteGraphic.drawSize;
|
||||
drawSize.x = drawSize.x < 2.5f ? drawSize.x + SilhouetteUtility.AdjustScale(drawSize.x) : drawSize.x;
|
||||
drawSize.y = drawSize.y < 2.5f ? drawSize.y + SilhouetteUtility.AdjustScale(drawSize.y) : drawSize.y;
|
||||
|
||||
var pos = pawnRenderer.SilhouettePos;
|
||||
pos.y = altitude;
|
||||
|
||||
SilhouetteUtility.DrawSilhouetteJob(pawn, Matrix4x4.TRS(pos, angleAxis,
|
||||
inverseFovScale with { x = inverseFovScale.x * drawSize.x, z = inverseFovScale.z * drawSize.y }));
|
||||
}
|
||||
}
|
||||
|
||||
public class PreDrawThings : IJobParallelFor
|
||||
{
|
||||
public Thing[] thingsToDraw = Array.Empty<Thing>();
|
||||
|
||||
public void Execute(int index) => thingsToDraw[index].DynamicDrawPhase(DrawPhase.ParallelPreDraw);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) 2023 bradson
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
/*namespace PerformanceFish.Rendering;
|
||||
public sealed class GenDrawPatches : ClassWithFishPatches
|
||||
{
|
||||
public sealed class DrawMeshNowOrLater_Patch : FishPatch
|
||||
{
|
||||
public override Delegate TargetMethodGroup { get; }
|
||||
= (Action<Mesh, Vector3, Quaternion, Material, bool>)GenDraw.DrawMeshNowOrLater;
|
||||
|
||||
public static void Replacement(Mesh mesh, Vector3 loc, Quaternion quat, Material mat, bool drawNow)
|
||||
{
|
||||
if (drawNow)
|
||||
{
|
||||
if (!mat.SetPass(0))
|
||||
Log.Error("SetPass(0) call failed on material " + mat.name + " with shader " + mat.shader.name);
|
||||
Graphics.DrawMeshNow(mesh, loc, quat);
|
||||
}
|
||||
else
|
||||
{
|
||||
Graphics.DrawMesh(mesh, loc, quat, mat, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DrawMeshNowOrLaterWithMatrix_Patch : FishPatch
|
||||
{
|
||||
public override Delegate TargetMethodGroup { get; }
|
||||
= (Action<Mesh, Matrix4x4, Material, bool>)GenDraw.DrawMeshNowOrLater;
|
||||
|
||||
public static void Replacement(Mesh mesh, Matrix4x4 matrix, Material mat, bool drawNow)
|
||||
{
|
||||
if (drawNow)
|
||||
{
|
||||
mat.SetPass(0);
|
||||
Graphics.DrawMeshNow(mesh, matrix);
|
||||
}
|
||||
else
|
||||
{
|
||||
Graphics.DrawMesh(mesh, matrix, mat, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
@@ -3,40 +3,39 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
/*namespace PerformanceFish.Rendering;
|
||||
public sealed class GraphicPatches : ClassWithFishPatches
|
||||
using PerformanceFish.Prepatching;
|
||||
|
||||
namespace PerformanceFish.Rendering;
|
||||
|
||||
public sealed class GraphicPatches : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class DrawMeshInt_Patch : FishPatch
|
||||
public sealed class MultiInitFix : FishPrepatch
|
||||
{
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(Graphic), nameof(Graphic.DrawMeshInt));
|
||||
public override string? Description { get; }
|
||||
= "Fixes a bug in this type of Graphic that causes it to return with null material when failing to find a "
|
||||
+ "texture, instead of the usual pink square seen in all other Graphics";
|
||||
|
||||
public static void Replacement(Graphic __instance, Mesh mesh, Vector3 loc, Quaternion quat, Material mat)
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(Graphic_Multi), nameof(Graphic_Multi.Init));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Postfix(Graphic_Multi __instance)
|
||||
{
|
||||
Graphics.DrawMesh(mesh, loc, quat, mat, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Shadow_DrawWorker_Patch : FishPatch
|
||||
{
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(Graphic_Shadow), nameof(Graphic_Shadow.DrawWorker));
|
||||
|
||||
public static void Replacement(Graphic_Shadow __instance, Vector3 loc, Rot4 rot, ThingDef? thingDef, Thing thing,
|
||||
float extraRotation)
|
||||
{
|
||||
if (__instance.shadowMesh is null
|
||||
|| thingDef is null
|
||||
|| __instance.shadowInfo is null
|
||||
|| (Find.CurrentMap is { } map && loc.ToIntVec3().InBounds(map) && map.roofGrid.Roofed(loc.ToIntVec3()))
|
||||
|| !DebugViewSettings.drawShadows)
|
||||
{
|
||||
if (__instance.MatSingle != null)
|
||||
return;
|
||||
}
|
||||
|
||||
var position = loc + __instance.shadowInfo.offset;
|
||||
position.y = AltitudeLayer.Shadows.AltitudeFor();
|
||||
Graphics.DrawMesh(__instance.shadowMesh, position, rot.AsQuat, MatBases.SunShadowFade, 0);
|
||||
AssignBadMats(__instance);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void AssignBadMats(Graphic_Multi instance)
|
||||
{
|
||||
var mats = instance.mats;
|
||||
for (var i = mats.Length; i-- > 0;)
|
||||
{
|
||||
if (mats[i] == null)
|
||||
mats[i] = BaseContent.BadMat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
+22
-2
@@ -10,7 +10,7 @@ using FisheryLib.Pools;
|
||||
using PerformanceFish.Prepatching;
|
||||
using RimWorld.IO;
|
||||
|
||||
namespace PerformanceFish;
|
||||
namespace PerformanceFish.Rendering;
|
||||
|
||||
public sealed class TextureLoadingPatches : ClassWithFishPrepatches
|
||||
{
|
||||
@@ -93,6 +93,7 @@ public sealed class TextureLoadingPatches : ClassWithFishPrepatches
|
||||
var data = file.ReadAllBytes();
|
||||
texture2D = new(2, 2, TextureFormat.Alpha8, true);
|
||||
texture2D.LoadImage(data);
|
||||
FixMipMapsIfNeeded(texture2D, data, file);
|
||||
}
|
||||
|
||||
if (!isDds && Prefs.TextureCompression)
|
||||
@@ -101,7 +102,10 @@ public sealed class TextureLoadingPatches : ClassWithFishPrepatches
|
||||
texture2D.name = Path.GetFileNameWithoutExtension(file.Name);
|
||||
texture2D.filterMode = FilterMode.Trilinear;
|
||||
texture2D.anisoLevel = 0;
|
||||
texture2D.mipMapBias = -0.7f;
|
||||
|
||||
if (texture2D.mipmapCount > 1)
|
||||
texture2D.mipMapBias = -0.7f;
|
||||
|
||||
texture2D.Apply(!hasStoredMipMaps, true);
|
||||
return texture2D;
|
||||
}
|
||||
@@ -116,9 +120,25 @@ public sealed class TextureLoadingPatches : ClassWithFishPrepatches
|
||||
|
||||
var texture2D = new Texture2D(2, 2, TextureFormat.Alpha8, true);
|
||||
texture2D.LoadImage(data);
|
||||
FixMipMapsIfNeeded(texture2D, data, file);
|
||||
return texture2D;
|
||||
}
|
||||
|
||||
public static void FixMipMapsIfNeeded(Texture2D texture2D, byte[] data, VirtualFile file)
|
||||
{
|
||||
if (((texture2D.width & 3) == 0) & ((texture2D.height & 3) == 0))
|
||||
return;
|
||||
|
||||
if (Prefs.LogVerbose)
|
||||
{
|
||||
Log.Warning($"Texture does not support mipmapping, needs to be divisible by 4 ({
|
||||
texture2D.width}x{texture2D.height}) for '{file.Name}'");
|
||||
}
|
||||
|
||||
texture2D = new(2, 2, TextureFormat.Alpha8, mipChain: false);
|
||||
texture2D.LoadImage(data);
|
||||
}
|
||||
|
||||
private static byte[]? ReadBytesForFallbackFormat(VirtualFile file)
|
||||
{
|
||||
byte[]? data = null;
|
||||
@@ -3,8 +3,12 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using PerformanceFish.Prepatching;
|
||||
using RoomOwnersCache
|
||||
= PerformanceFish.Cache.ByInt<Verse.Room, PerformanceFish.RoomOptimizations.Owners_Patch.CacheValue>;
|
||||
using RoomRoleCache
|
||||
@@ -27,7 +31,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
+ "instead of every time anything at all changes or moves";
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.PropertyGetter(typeof(Room), nameof(Room.Role));
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(Room), nameof(Room.Role));
|
||||
|
||||
public static CodeInstructions Transpiler(CodeInstructions instructions)
|
||||
{
|
||||
@@ -55,13 +59,11 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ShouldUpdate(Room __instance)
|
||||
{
|
||||
if (__instance.role is not null
|
||||
|| !RoomRoleCache.GetOrAddReference(__instance.ID).Dirty)
|
||||
{
|
||||
if (__instance.role is not null || !RoomRoleCache.GetOrAddReference(__instance.ID).Dirty)
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateCache(__instance);
|
||||
return true;
|
||||
@@ -92,7 +94,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
+ "quite expensive";
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.PropertyGetter(typeof(Room), nameof(Room.Owners));
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(Room), nameof(Room.Owners));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(Room __instance, ref IEnumerable<Pawn> __result, out bool __state)
|
||||
@@ -111,7 +113,8 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
return __state = false;
|
||||
}
|
||||
|
||||
private static bool CanCache(Room __instance)
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool CanCache(Room __instance)
|
||||
=> __instance is { TouchesMapEdge: false, IsHuge: false }
|
||||
&& (!__instance.statsAndRoleDirty || __instance.ContainedBeds.Any());
|
||||
|
||||
@@ -157,29 +160,6 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Regions_Patch : FirstPriorityFishPatch
|
||||
{
|
||||
public override string Description { get; } = "Minor optimization";
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.PropertyGetter(typeof(Room), nameof(Room.Regions));
|
||||
|
||||
public static CodeInstructions Transpiler(CodeInstructions codeInstructions)
|
||||
=> Reflection.MakeReplacementCall(Regions_Replacement);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static List<Region> Regions_Replacement(Room instance)
|
||||
{
|
||||
var tmpRegions = instance.tmpRegions;
|
||||
var districts = instance.districts;
|
||||
|
||||
tmpRegions.Clear();
|
||||
for (var i = 0; i < districts.Count; i++)
|
||||
tmpRegions.AddRangeFast(districts[i].Regions);
|
||||
|
||||
return tmpRegions;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ContainedBeds_Patch : FirstPriorityFishPatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
@@ -188,7 +168,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
+ "against spikes";
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.PropertyGetter(typeof(Room), nameof(Room.ContainedBeds));
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(Room), nameof(Room.ContainedBeds));
|
||||
|
||||
public static CodeInstructions Transpiler(CodeInstructions codeInstructions, ILGenerator generator)
|
||||
=> Reflection.GetCodeInstructions(ContainedBeds_Replacement, generator);
|
||||
@@ -219,7 +199,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
public override string Description { get; } = "Caching of room content info. Variable impact";
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.PropertyGetter(typeof(Room), nameof(Room.ContainedAndAdjacentThings));
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(Room), nameof(Room.ContainedAndAdjacentThings));
|
||||
|
||||
public static CodeInstructions Transpiler(CodeInstructions codeInstructions)
|
||||
=> Reflection.MakeReplacementCall(ContainedAndAdjacentThings_Replacement);
|
||||
@@ -236,18 +216,11 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
if (cache.ListVersions.Count == regions.Count && !cache.Dirty)
|
||||
{
|
||||
var cacheIsAccurate = true;
|
||||
for (var i = 0; i < regions.Count; i++)
|
||||
|
||||
for (var i = regions.Count; i-- > 0;)
|
||||
{
|
||||
var allThings = regions[i].listerThings.AllThings;
|
||||
if (allThings == null)
|
||||
{
|
||||
if (cache.ListVersions[i] != -1)
|
||||
cacheIsAccurate = false;
|
||||
}
|
||||
else if (cache.ListVersions[i] != allThings._version)
|
||||
{
|
||||
if (cache.ListVersions[i] != (regions[i]?.listerThings.AllThings?._version ?? -1))
|
||||
cacheIsAccurate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheIsAccurate)
|
||||
@@ -260,6 +233,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
return RefreshContainedAndAdjacentThings(instance, regions, ref cache);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static List<Thing> RefreshContainedAndAdjacentThings(Room instance, List<Region> regions,
|
||||
ref CacheValue cache)
|
||||
{
|
||||
@@ -270,8 +244,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
|
||||
for (var i = 0; i < regions.Count; i++)
|
||||
{
|
||||
var allThings = regions[i].ListerThings.AllThings;
|
||||
if (allThings == null)
|
||||
if (regions[i]?.ListerThings.AllThings is not { } allThings)
|
||||
{
|
||||
cache.ListVersions.Add(-1);
|
||||
continue;
|
||||
@@ -281,67 +254,66 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
for (var j = 0; j < count; j++)
|
||||
uniqueContainedThingsSet.Add(allThings[j]);
|
||||
|
||||
cache.ListVersions.Add(allThings.Version());
|
||||
cache.ListVersions.Add(allThings._version);
|
||||
}
|
||||
|
||||
uniqueContainedThings.AddRange(uniqueContainedThingsSet);
|
||||
if (cache.Things is { } list)
|
||||
list.ReplaceContentsWith(uniqueContainedThings);
|
||||
else
|
||||
cache.Things = [..uniqueContainedThings];
|
||||
|
||||
cache.Things.ReplaceContentsWith(uniqueContainedThings);
|
||||
cache.SetDirty(false, instance);
|
||||
|
||||
uniqueContainedThingsSet.Clear();
|
||||
return uniqueContainedThings;
|
||||
}
|
||||
|
||||
/*private static List<Thing> RefreshContainedAndAdjacentThingsAlternative(Room instance, List<Region> regions,
|
||||
FishThingsInfo cache)
|
||||
/*[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static List<Thing> RefreshContainedAndAdjacentThingsAlternative(Room instance, List<Region> regions,
|
||||
ref CacheValue cache)
|
||||
{
|
||||
var uniqueContainedThings = instance.uniqueContainedThings;
|
||||
(cache.listVersions ??= new()).Clear();
|
||||
cache.ListVersions.Clear();
|
||||
var map = instance.Map;
|
||||
var regionGrid = map.regionGrid.regionGrid;
|
||||
var mapSizeX = map.cellIndices.mapSizeX;
|
||||
|
||||
var adjacentThings = new List<Thing>();
|
||||
using var pooledAdjacentThings = new Pools.PooledIList<List<Thing>>();
|
||||
var adjacentThings = pooledAdjacentThings.List;
|
||||
var regionCount = regions.Count;
|
||||
using var pooledRegionIDs = new PooledArray<int>(regionCount);
|
||||
var regionIDs = pooledRegionIDs.BackingArray;
|
||||
|
||||
for (var i = regionCount; i-- > 0;)
|
||||
regionIDs[i] = regions[i].id;
|
||||
|
||||
for (var i = 0; i < regions.Count; i++)
|
||||
for (var i = 0; i < regionCount; i++)
|
||||
{
|
||||
var region = regions[i];
|
||||
var allThings = region.ListerThings.AllThings;
|
||||
if (allThings == null)
|
||||
if (region.ListerThings.AllThings is not { } allThings)
|
||||
{
|
||||
cache.listVersions.Add(-1);
|
||||
cache.ListVersions.Add(-1);
|
||||
continue;
|
||||
}
|
||||
|
||||
var count = allThings.Count;
|
||||
for (var j = 0; j < count; j++)
|
||||
{
|
||||
if (regionGrid[allThings[j].Position.CellToIndex(mapSizeX)] == region)
|
||||
uniqueContainedThings.Add(allThings[j]);
|
||||
else
|
||||
adjacentThings.Add(allThings[j]);
|
||||
var thing = allThings[j];
|
||||
var thingRegionId = regionGrid[thing.Position.CellToIndex(mapSizeX)].id;
|
||||
|
||||
if (thingRegionId == region.id)
|
||||
uniqueContainedThings.Add(thing);
|
||||
else if (!regionIDs.Contains(thingRegionId) && !adjacentThings.Contains(thing))
|
||||
adjacentThings.Add(thing);
|
||||
}
|
||||
|
||||
cache.listVersions.Add(allThings._version);
|
||||
cache.ListVersions.Add(allThings._version);
|
||||
}
|
||||
|
||||
for (var i = 0; i < adjacentThings.Count; i++)
|
||||
{
|
||||
if (!uniqueContainedThings.Contains(adjacentThings[i]))
|
||||
uniqueContainedThings.Add(adjacentThings[i]);
|
||||
}
|
||||
for (var i = adjacentThings.Count; i-- > 0;)
|
||||
uniqueContainedThings.Add(adjacentThings[i]);
|
||||
|
||||
if (cache.things is List<Thing> list)
|
||||
list.ReplaceContentsWith(uniqueContainedThings);
|
||||
else
|
||||
cache.things = new List<Thing>(uniqueContainedThings);
|
||||
cache.ShouldRefreshNow = false;
|
||||
|
||||
CachedThingLists[instance] = cache;
|
||||
cache.Things.ReplaceContentsWith(uniqueContainedThings);
|
||||
cache.SetDirty(false, instance);
|
||||
|
||||
return uniqueContainedThings;
|
||||
}*/
|
||||
@@ -349,8 +321,8 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
public record struct CacheValue()
|
||||
{
|
||||
private int _nextRefreshTick = -2;
|
||||
public List<int> ListVersions = [];
|
||||
public List<Thing> Things = [];
|
||||
public readonly List<int> ListVersions = [];
|
||||
public readonly List<Thing> Things = [];
|
||||
|
||||
public void SetDirty(bool value, Room room)
|
||||
=> _nextRefreshTick = value ? 0 : TickHelper.Add(3072, room.ID, 2048);
|
||||
@@ -382,20 +354,50 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
return null;
|
||||
|
||||
var regionAndRoomUpdater = map.regionAndRoomUpdater;
|
||||
|
||||
if (regionAndRoomUpdater.Enabled)
|
||||
{
|
||||
regionAndRoomUpdater.TryRebuildDirtyRegionsAndRooms();
|
||||
}
|
||||
else if (regionAndRoomUpdater.AnythingToRebuild)
|
||||
{
|
||||
Verse.Log.Warning($"Trying to get valid region at {
|
||||
c.ToString()} but RegionAndRoomUpdater is disabled. The result may be incorrect.");
|
||||
}
|
||||
LogIncorrectResultWarning(c);
|
||||
|
||||
var region = map.regionGrid.regionGrid[(c.z * cellIndices.mapSizeX) + c.x];
|
||||
return region is null || !region.valid || (region.type & allowedRegionTypes) == 0
|
||||
? null
|
||||
: region.District?.Room;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogIncorrectResultWarning(IntVec3 c)
|
||||
=> Log.Warning($"Trying to get valid region at {
|
||||
c.ToString()} but RegionAndRoomUpdater is disabled. The result may be incorrect.\nThis commonly "
|
||||
+ $"happens when incorrectly accessing a region within a method that itself gets accessed by the "
|
||||
+ $"RegionAndRoomUpdater when computing the region grid. The updater gets disabled to avoid a stack "
|
||||
+ $"overflow.\n{new StackTrace(1, true)}");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RoomPrepatches : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class Regions_Patch : FishPrepatch
|
||||
{
|
||||
public override string Description { get; } = "Minor optimization by simplifying instructions";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(Room), nameof(Room.Regions));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(Regions_Replacement);
|
||||
|
||||
public static List<Region> Regions_Replacement(Room instance)
|
||||
{
|
||||
var tmpRegions = instance.tmpRegions;
|
||||
var districts = instance.districts;
|
||||
|
||||
tmpRegions.Clear();
|
||||
for (var i = 0; i < districts.Count; i++)
|
||||
tmpRegions.AddRangeFast(districts[i].Regions);
|
||||
|
||||
return tmpRegions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using PerformanceFish.Defs;
|
||||
using PerformanceFish.Prepatching;
|
||||
using StatsCache = PerformanceFish.Cache.ByInt<uint, ushort, PerformanceFish.StatCaching.CachedStatValue>;
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ public sealed class EquatableValueTypeEqualityComparer<T> : EqualityComparer<T>
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj) => obj is EquatableValueTypeEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj?.GetType() == typeof(EquatableValueTypeEqualityComparer<T>);
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
@@ -106,7 +106,7 @@ public sealed class EquatableNullableEqualityComparer<T> : EqualityComparer<T?>
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj) => obj is EquatableNullableEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj?.GetType() == typeof(EquatableNullableEqualityComparer<T>);
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
@@ -126,7 +126,7 @@ public sealed class NullableEqualityComparer<T> : EqualityComparer<T?> where T :
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj) => obj is NullableEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj?.GetType() == typeof(NullableEqualityComparer<T>);
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
@@ -135,7 +135,6 @@ public sealed class NullableEqualityComparer<T> : EqualityComparer<T?> where T :
|
||||
public sealed class StringEqualityComparer : EqualityComparer<string?>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SuppressMessage("Globalization", "CA1307")]
|
||||
[SuppressMessage("Globalization", "CA1309")]
|
||||
public override bool Equals(string? x, string? y)
|
||||
=> string.Equals(x, y); // already does a null check, so this skips the extra check that'd otherwise happen
|
||||
@@ -146,7 +145,7 @@ public sealed class StringEqualityComparer : EqualityComparer<string?>
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj) => obj is StringEqualityComparer;
|
||||
public override bool Equals(object? obj) => obj?.GetType() == typeof(StringEqualityComparer);
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
@@ -163,7 +162,7 @@ public sealed class IntEqualityComparer : EqualityComparer<int>
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj) => obj is IntEqualityComparer;
|
||||
public override bool Equals(object? obj) => obj?.GetType() == typeof(IntEqualityComparer);
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
@@ -180,7 +179,7 @@ public sealed class UshortEqualityComparer : EqualityComparer<ushort>
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj) => obj is UshortEqualityComparer;
|
||||
public override bool Equals(object? obj) => obj?.GetType() == typeof(UshortEqualityComparer);
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
@@ -199,7 +198,7 @@ public sealed class ReferenceEqualityComparer<T> : EqualityComparer<T>
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj) => obj is ReferenceEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj?.GetType() == typeof(ReferenceEqualityComparer<T>);
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
@@ -218,7 +217,7 @@ public sealed class ValueTypeEqualityComparer<T> : EqualityComparer<T> where T :
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object? obj) => obj is ReferenceEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj?.GetType() == typeof(ReferenceEqualityComparer<T>);
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
@@ -6,14 +6,13 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
using PerformanceFish.Cache;
|
||||
using PerformanceFish.ModCompatibility;
|
||||
using ConstructorInfoCache
|
||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags,
|
||||
PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
||||
PerformanceFish.System.ReflectionCaching.RecordArray<System.Type>,
|
||||
PerformanceFish.System.ReflectionCaching.TypePatches.ConstructorInfoCacheValue>;
|
||||
using FieldGetters
|
||||
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeFieldHandle,
|
||||
@@ -21,6 +20,9 @@ using FieldGetters
|
||||
using FieldInfoCache
|
||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags, string,
|
||||
PerformanceFish.System.ReflectionCaching.TypePatches.FieldInfoCacheValue>;
|
||||
using FieldInfosCache
|
||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags,
|
||||
PerformanceFish.System.ReflectionCaching.TypePatches.FieldInfosCacheValue>;
|
||||
using FieldSetters
|
||||
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeFieldHandle,
|
||||
PerformanceFish.System.ReflectionCaching.FieldInfoPatches.FieldInfoSetterCache>;
|
||||
@@ -29,11 +31,11 @@ using MethodInfoCache
|
||||
PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>;
|
||||
using MethodInfoWithTypesAndFlagsCache
|
||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags, string,
|
||||
PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
||||
PerformanceFish.System.ReflectionCaching.RecordArray<System.Type>,
|
||||
PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>;
|
||||
using MethodInfoWithTypesCache
|
||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, string,
|
||||
PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
||||
PerformanceFish.System.ReflectionCaching.RecordArray<System.Type>,
|
||||
PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>;
|
||||
using MethodInvokers
|
||||
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeMethodHandle,
|
||||
@@ -41,9 +43,6 @@ using MethodInvokers
|
||||
using PropertyInfoCache
|
||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags, string,
|
||||
PerformanceFish.System.ReflectionCaching.TypePatches.PropertyInfoCacheValue>;
|
||||
using CustomAttributeCache
|
||||
= PerformanceFish.Cache.ByReference<System.Reflection.ICustomAttributeProvider, System.RuntimeTypeHandle, bool,
|
||||
PerformanceFish.System.ReflectionCaching.MonoCustomAttrs.CustomAttributeCacheValue>;
|
||||
using TypeFullNameCache
|
||||
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeTypeHandle,
|
||||
PerformanceFish.System.ReflectionCaching.TypePatches.TypeFullNameCacheValue>;
|
||||
@@ -54,6 +53,9 @@ using ActivatorCache
|
||||
// = PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle,
|
||||
// PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
||||
// PerformanceFish.System.ReflectionCaching.ActivatorPatches.ActivatorCacheValue>;
|
||||
using CustomAttributeCache
|
||||
= PerformanceFish.Cache.ByReferenceClassic<System.Reflection.ICustomAttributeProvider, System.RuntimeTypeHandle, bool,
|
||||
PerformanceFish.System.ReflectionCaching.MonoCustomAttrs.CustomAttributeCacheValue>;
|
||||
|
||||
namespace PerformanceFish.System;
|
||||
|
||||
@@ -66,12 +68,17 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
||||
ConstructorInfoCache.Initialize();
|
||||
FieldGetters.Initialize();
|
||||
FieldInfoCache.Initialize();
|
||||
FieldInfosCache.Initialize();
|
||||
FieldSetters.Initialize();
|
||||
MethodInfoCache.Initialize();
|
||||
MethodInfoWithTypesAndFlagsCache.Initialize();
|
||||
MethodInfoWithTypesCache.Initialize();
|
||||
MethodInvokers.Initialize();
|
||||
PropertyInfoCache.Initialize();
|
||||
TypeFullNameCache.Initialize();
|
||||
ActivatorCache.Initialize();
|
||||
// ActivatorWithArgumentsCache.Initialize();
|
||||
CustomAttributeCache.Initialize();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -104,16 +111,17 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
||||
ToLowerIfNeededForBindingFlags(name, bindingAttr));
|
||||
|
||||
__result = FieldInfoCache.Get.GetOrAdd(ref key).Info;
|
||||
__state.Flags = bindingAttr;
|
||||
|
||||
if (__result is null)
|
||||
if (__result != null)
|
||||
{
|
||||
__state = new() { State = true, Flags = bindingAttr };
|
||||
return true;
|
||||
__state.State = false;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
__state = new() { State = false, Flags = bindingAttr };
|
||||
return false;
|
||||
__state.State = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +140,47 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
||||
ToLowerIfNeededForBindingFlags(name, __state.Flags)).Info
|
||||
= __result;
|
||||
}
|
||||
|
||||
public sealed class GetFieldsPatch : FishPatch
|
||||
{
|
||||
public override string Description { get; } = "Caches GetFields lookups";
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.Method(typeof(RuntimeType), nameof(RuntimeType.GetFields), [typeof(BindingFlags)])!;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(Type __instance, BindingFlags bindingAttr, out FieldInfo[]? __result,
|
||||
out bool __state)
|
||||
{
|
||||
var key = new FieldInfosCache(__instance.TypeHandle, bindingAttr);
|
||||
|
||||
__result = FieldInfosCache.Get.GetOrAdd(ref key).Info;
|
||||
|
||||
if (__result != null)
|
||||
{
|
||||
__state = false;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
__state = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Postfix(Type __instance, FieldInfo[] __result, BindingFlags bindingAttr, bool __state)
|
||||
{
|
||||
if (!__state)
|
||||
return;
|
||||
|
||||
UpdateCache(__instance, __result, bindingAttr);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void UpdateCache(Type __instance, FieldInfo[] __result, BindingFlags bindingAttr)
|
||||
=> FieldInfosCache.GetExistingReference(__instance.TypeHandle, bindingAttr).Info = __result;
|
||||
}
|
||||
|
||||
public sealed class GetMethodWithFlags_Patch : FishPatch
|
||||
{
|
||||
@@ -400,6 +449,11 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
||||
public FieldInfo? Info;
|
||||
}
|
||||
|
||||
public record struct FieldInfosCacheValue
|
||||
{
|
||||
public FieldInfo[]? Info;
|
||||
}
|
||||
|
||||
public record struct MethodInfoCacheValue
|
||||
{
|
||||
public MethodInfo? Info;
|
||||
@@ -506,33 +560,35 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public record struct ByValueComparableArray<T>
|
||||
public record struct RecordArray<T>
|
||||
where T : class
|
||||
{
|
||||
public byte Length;
|
||||
private int _hashCode;
|
||||
public T[] Array;
|
||||
public T[]? Array;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ByValueComparableArray(T[] array)
|
||||
public RecordArray(T[] array)
|
||||
{
|
||||
Guard.IsNotNull(array);
|
||||
Guard.IsLessThanOrEqualTo(array.Length, byte.MaxValue);
|
||||
Array = array;
|
||||
Length = (byte)array.Length;
|
||||
_hashCode = HashCode.Combine((int)Length, Length > 0 && Array[0] is { } item ? item.GetHashCode() : 0);
|
||||
var length = array.Length;
|
||||
_hashCode = HashCode.Combine(length, length > 0 && Array[0] is { } item ? item.GetHashCode() : 0);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByValueComparableArray<T> other)
|
||||
public bool Equals(RecordArray<T> other)
|
||||
{
|
||||
if (Length != other.Length)
|
||||
if (Array is null)
|
||||
return other.Array is null;
|
||||
|
||||
if (other.Array is null || Array.Length != other.Array.Length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < Length; i++)
|
||||
var length = Array.Length;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
if (!Array[i].Equals(other.Array[i]))
|
||||
if (!Array[i].Equals(other.Array![i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -540,8 +596,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode()
|
||||
=> _hashCode;
|
||||
public override int GetHashCode() => _hashCode;
|
||||
}
|
||||
|
||||
public sealed class ActivatorPatches
|
||||
@@ -744,7 +799,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
||||
cache.Task = _fieldSetterFailureTask;
|
||||
Log.Error($"FieldInfo.SetValue has been called on '{
|
||||
info.FullDescription()}'. This is a const that gets copied at compile time. An attempt at "
|
||||
+ $"setting a value will always fail.\n{new StackTrace(true)}");
|
||||
+ $"setting a value will always fail.\n{new StackTrace(2, true)}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ public sealed class ThingFilterPatches : ClassWithFishPatches
|
||||
|
||||
public record struct AllowedDefsListCacheValue() : ICacheable<ThingFilter>, ICacheable<ThingFilter, int>
|
||||
{
|
||||
public List<ThingDef> Defs = [];
|
||||
public readonly List<ThingDef> Defs = [];
|
||||
private int _nextRefreshTick = -2;
|
||||
|
||||
public bool Dirty
|
||||
|
||||
@@ -48,7 +48,7 @@ public sealed class ThingPatches : ClassWithFishPrepatches
|
||||
__instance.SetStuffDirect(GenStuff.DefaultStuffFor(def));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public sealed class SpawnedPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
@@ -60,7 +60,7 @@ public sealed class ThingPatches : ClassWithFishPrepatches
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(MiscExtensions.IsSpawned);
|
||||
}
|
||||
|
||||
|
||||
public sealed class MapHeldPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
@@ -72,4 +72,40 @@ public sealed class ThingPatches : ClassWithFishPrepatches
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(MiscExtensions.TryGetMapHeld);
|
||||
}
|
||||
|
||||
public sealed class InitializeCompsFix : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Improves the error message in ThingWithComps.InitializeComps to include thing, props and mod";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ThingWithComps), nameof(ThingWithComps.InitializeComps));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
{
|
||||
var codes = ilProcessor.instructions;
|
||||
|
||||
var errorMessageIndex = codes.FirstIndexOf(static code
|
||||
=> code.Operand is "Could not instantiate or initialize a ThingComp: ");
|
||||
|
||||
codes[errorMessageIndex].OpCode = OpCodes.Ldarg_0;
|
||||
codes[errorMessageIndex].Operand = null;
|
||||
|
||||
ilProcessor.InsertRange(errorMessageIndex + 1,
|
||||
(OpCodes.Ldloca,
|
||||
ilProcessor.Body.Variables.First(local => local.VariableType == module.TypeSystem.Int32)),
|
||||
(OpCodes.Call, methodof(MakeErrorMessage)));
|
||||
}
|
||||
|
||||
public static string MakeErrorMessage(ThingWithComps thing, ref int index)
|
||||
{
|
||||
var compProps = thing.def.comps;
|
||||
if (compProps.TryGetItem(index, out var props))
|
||||
compProps.RemoveAt(index--);
|
||||
|
||||
return $"Could not instantiate or initialize a ThingComp with props '{
|
||||
(props != null! ? props.GetType().FullName : "null")}' on thing '{thing}' from mod '{
|
||||
thing.def.modContentPack?.Name ?? "null"}': ";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,4 +35,17 @@ public static class CellIndexExtensions
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int CellToIndex(int x, int z, int mapSizeX) => (z * mapSizeX) + x;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static List<Thing> ThingsListAtFast(this ThingGrid thingGrid, CellIndex cellIndex)
|
||||
=> thingGrid.thingGrid[cellIndex.Value];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsFogged(this FogGrid fogGrid, CellIndex cellIndex) => fogGrid.fogGrid[cellIndex.Value];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float GetDepth(this SnowGrid snowGrid, CellIndex cellIndex) => snowGrid.depthGrid[cellIndex.Value];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float GetDepth(this SnowGrid snowGrid, int cellIndex) => snowGrid.depthGrid[cellIndex];
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace PerformanceFish.Utility;
|
||||
|
||||
@@ -308,6 +310,28 @@ public static class CollectionExtensions
|
||||
}
|
||||
|
||||
public static List<T> AsOrToList<T>(this IEnumerable<T> enumerable) => enumerable as List<T> ?? enumerable.ToList();
|
||||
|
||||
public static unsafe ref T GetReference<T>(this NativeArray<T> nativeArray, int index) where T : struct
|
||||
=> ref Unsafe.AsRef<T>(Unsafe.Add<T>(NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(nativeArray),
|
||||
index));
|
||||
|
||||
public static void UnwrapArray<T>(this List<T> list, out T[] array, out int count)
|
||||
{
|
||||
array = list._items;
|
||||
count = list._size;
|
||||
Guard.IsLessThanOrEqualTo(count, array.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SetSize<T>(this List<T> list, int size) => list._size = size;
|
||||
|
||||
public static void AddRange<T>(this List<T> list, IndexedFishSet<T> set)
|
||||
{
|
||||
list.EnsureCapacity(list._size + set.Count);
|
||||
set.CopyTo(list._items, list._size);
|
||||
list._size += set.Count;
|
||||
list._version++;
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
private static T ThrowForNoElements<T>()
|
||||
|
||||
+12
-2
@@ -5,16 +5,19 @@
|
||||
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
#if V1_5
|
||||
using LudeonTK;
|
||||
#endif
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace PerformanceFish.Utility;
|
||||
|
||||
public static class PipetteTool
|
||||
public static class DebugActions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DebugAction("Misc", "Pipette tool", actionType = DebugActionType.ToolMap,
|
||||
allowedGameStates = AllowedGameStates.PlayingOnMap)]
|
||||
public static void Action()
|
||||
public static void PipetteTool()
|
||||
{
|
||||
var things = UI.MouseCell().GetThingList(Find.CurrentMap).ToList();
|
||||
things.Sort(static (x, y) => ((int)x.def.altitudeLayer).CompareTo((int)y.def.altitudeLayer));
|
||||
@@ -77,4 +80,11 @@ public static class PipetteTool
|
||||
|
||||
private static byte GetAverage(IEnumerable<Color32> colors, Func<Color32, int> selector)
|
||||
=> (byte)Mathf.RoundToInt((float)colors.Average(selector));
|
||||
|
||||
#if testing
|
||||
[UsedImplicitly]
|
||||
[DebugAction("Misc", "Test ListerThings", actionType = DebugActionType.ToolMap,
|
||||
allowedGameStates = AllowedGameStates.PlayingOnMap)]
|
||||
public static void TestListerThings() => Find.CurrentMap.listerThings.Remove(ThingMaker.MakeThing(ThingDefOf.Beer));
|
||||
#endif
|
||||
}
|
||||
@@ -95,6 +95,9 @@ public static class MiscExtensions
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int SizeX(this Map map) => map.cellIndices.mapSizeX;
|
||||
|
||||
public static Map? TryGetMapHeld(this Thing thing)
|
||||
{
|
||||
var maps = Current.Game.Maps;
|
||||
|
||||
@@ -24,4 +24,12 @@ public static class StringExtensions
|
||||
|
||||
public static string AppendWhenNotNull(this string instance, object? value)
|
||||
=> value != null ? instance + value : instance;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Contains(this string self, string value, StringComparison comparison)
|
||||
{
|
||||
Guard.IsNotNull(self);
|
||||
Guard.IsNotNull(value);
|
||||
return self.IndexOf(value, comparison) >= 0;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,11 @@
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using MonoMod.Utils;
|
||||
using PerformanceFish.Prepatching;
|
||||
using Verse.Steam;
|
||||
|
||||
namespace PerformanceFish;
|
||||
|
||||
@@ -20,8 +24,7 @@ public sealed class UtilityPrepatches : ClassWithFishPrepatches
|
||||
|
||||
public override bool ShowSettings => false;
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= methodof(LoadedModManager.CreateModClasses);
|
||||
public override MethodBase TargetMethodBase { get; } = methodof(LoadedModManager.CreateModClasses);
|
||||
|
||||
public static void Prefix()
|
||||
{
|
||||
@@ -35,4 +38,118 @@ public sealed class UtilityPrepatches : ClassWithFishPrepatches
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BetterPrinting : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Attempt at fixing the clipping between textures by slightly adjusting their angle to have the top left "
|
||||
+ "placed closer to the camera than the bottom right";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; } = methodof(Printer_Plane.PrintPlane);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
{
|
||||
var codes = ilProcessor.instructions;
|
||||
|
||||
var altitudeBiasParameter = ilProcessor.GetParameter("topVerticesAltitudeBias");
|
||||
var sizeParameter = ilProcessor.GetParameter("size");
|
||||
|
||||
var altitudeBiasIndex = codes.FirstIndexOf(code => code.Operand == altitudeBiasParameter);
|
||||
|
||||
LoadSizeAtAndCall(altitudeBiasIndex, TopLeftAdjustment);
|
||||
|
||||
while (codes[++altitudeBiasIndex].Operand != altitudeBiasParameter)
|
||||
;
|
||||
|
||||
LoadSizeAtAndCall(altitudeBiasIndex, TopRightAdjustment);
|
||||
|
||||
while (codes[--altitudeBiasIndex].Operand is not 0f)
|
||||
;
|
||||
|
||||
LoadSizeAtAndCall(altitudeBiasIndex, BottomLeftAdjustment);
|
||||
|
||||
while (codes[++altitudeBiasIndex].Operand is not 0f)
|
||||
;
|
||||
|
||||
LoadSizeAtAndCall(altitudeBiasIndex, BottomRightAdjustment);
|
||||
|
||||
void LoadSizeAtAndCall(int instructionIndex, Delegate method)
|
||||
{
|
||||
codes[instructionIndex].OpCode = OpCodes.Ldarga;
|
||||
codes[instructionIndex].Operand = sizeParameter;
|
||||
ilProcessor.InsertAt(instructionIndex + 1, (OpCodes.Call, method.Method));
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float TopLeftAdjustment(in Vector2 size)
|
||||
=> (size.y * 0.0045f) + (size.x * 0.0005f);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float TopRightAdjustment(in Vector2 size)
|
||||
=> (size.y * 0.0045f) - (size.x * 0.0005f);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float BottomLeftAdjustment(in Vector2 size)
|
||||
=> (size.y * -0.0045f) + (size.x * 0.0005f);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float BottomRightAdjustment(in Vector2 size)
|
||||
=> (size.y * -0.0045f) - (size.x * 0.0005f);
|
||||
}
|
||||
|
||||
public sealed class NoSteamLogWarning : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Disables the \"[Steamworks.NET] SteamAPI.Init() failed\" warning. It's duplicated with prepatcher "
|
||||
+ "anyway";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(SteamManager), nameof(SteamManager.InitIfNeeded));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
{
|
||||
var codes = ilProcessor.instructions;
|
||||
|
||||
var warningIndex = codes.FirstIndexOf(static code
|
||||
=> code.Operand is string text && text.Contains("SteamAPI.Init() failed"));
|
||||
|
||||
if (warningIndex < 0)
|
||||
return;
|
||||
|
||||
codes[warningIndex].OpCode = OpCodes.Nop;
|
||||
codes[warningIndex].Operand = null;
|
||||
|
||||
codes[++warningIndex].OpCode = OpCodes.Nop;
|
||||
codes[warningIndex].Operand = null;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NoSteamFailWindow : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Disables the window that opens when running the steam version of rimworld without steam. Off by default";
|
||||
|
||||
public override bool DefaultState => false;
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(UIRoot_Entry), nameof(UIRoot_Entry.Init));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
{
|
||||
var codes = ilProcessor.instructions;
|
||||
|
||||
var steamInitializedProperty
|
||||
= AccessTools.PropertyGetter(typeof(SteamManager), nameof(SteamManager.Initialized));
|
||||
|
||||
var instruction = codes.FirstOrDefault(code
|
||||
=> code.Operand is MethodReference method && method.Is(steamInitializedProperty));
|
||||
|
||||
if (instruction is null)
|
||||
return;
|
||||
|
||||
instruction.OpCode = OpCodes.Ldc_I4_1;
|
||||
instruction.Operand = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Reference in New Issue
Block a user