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>
|
<packageId>bs.performance</packageId>
|
||||||
<supportedVersions>
|
<supportedVersions>
|
||||||
<li>1.4</li>
|
<li>1.4</li>
|
||||||
|
<li>1.5</li>
|
||||||
</supportedVersions>
|
</supportedVersions>
|
||||||
<author>bradson</author>
|
<author>bradson</author>
|
||||||
|
<modIconPath IgnoreIfNoMatchingField="True">PerformanceFish/ModIcon</modIconPath>
|
||||||
<url>https://github.com/bbradson/performance-fish</url>
|
<url>https://github.com/bbradson/performance-fish</url>
|
||||||
<loadBefore>
|
<loadBefore>
|
||||||
<li>Dubwise.DubsPerformanceAnalyzer</li>
|
<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>
|
</DerivedMethods>
|
||||||
</RitualObligationTriggers>
|
</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>
|
<ThingCompTick>
|
||||||
<Tooltip>Tick methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones</Tooltip>
|
<Tooltip>Tick methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones</Tooltip>
|
||||||
<Category>Tick</Category>
|
<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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
@@ -305,10 +306,29 @@ public static class AnalyzerFixes
|
|||||||
var methods = Reflection.MethodsOfName(AccessTools.TypeByName(array[0]), array[1]);
|
var methods = Reflection.MethodsOfName(AccessTools.TypeByName(array[0]), array[1]);
|
||||||
foreach (var method in methods)
|
foreach (var method in methods)
|
||||||
{
|
{
|
||||||
yield return method.IsGenericMethodDefinition
|
if (method.IsGenericMethodDefinition || method.ReturnType.IsByRef)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MethodInfo? methodToReturn;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
methodToReturn = method.IsGenericMethodDefinition
|
||||||
? method.MakeGenericMethod(GetTypeArguments(method))
|
? method.MakeGenericMethod(GetTypeArguments(method))
|
||||||
: 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Type[] GetTypeArguments(MethodBase method)
|
private static Type[] GetTypeArguments(MethodBase method)
|
||||||
@@ -317,9 +337,10 @@ public static class AnalyzerFixes
|
|||||||
var resultArray = new Type[genericArguments.Length];
|
var resultArray = new Type[genericArguments.Length];
|
||||||
for (var i = 0; i < genericArguments.Length; i++)
|
for (var i = 0; i < genericArguments.Length; i++)
|
||||||
{
|
{
|
||||||
resultArray[i] = genericArguments[i].GetGenericArguments() is var constraints && constraints.Length != 0
|
var genericArgument = genericArguments[i];
|
||||||
? constraints[0]
|
resultArray[i] = genericArgument.GetGenericArguments() is var constraints && constraints.Length != 0
|
||||||
: typeof(object);
|
? genericArgument.MakeGenericType(constraints)
|
||||||
|
: genericArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultArray;
|
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
|
//Copied from Wiri's analyzer fork, with above fixes integrated. The steam version lacks this functionality
|
||||||
public static class XmlParser
|
public static class XmlParser
|
||||||
{
|
{
|
||||||
|
public static DirectoryInfo? CurrentModDir;
|
||||||
|
|
||||||
[SuppressMessage("Security", "CA3075")]
|
[SuppressMessage("Security", "CA3075")]
|
||||||
public static void CollectXmlData()
|
public static void CollectXmlData()
|
||||||
{
|
{
|
||||||
@@ -439,6 +462,8 @@ public static class AnalyzerFixes
|
|||||||
if (xmlFiles.Length == 0)
|
if (xmlFiles.Length == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
CurrentModDir = dir;
|
||||||
|
|
||||||
foreach (var file in xmlFiles)
|
foreach (var file in xmlFiles)
|
||||||
{
|
{
|
||||||
var doc = new XmlDocument();
|
var doc = new XmlDocument();
|
||||||
@@ -446,6 +471,8 @@ public static class AnalyzerFixes
|
|||||||
|
|
||||||
Parse(doc);
|
Parse(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CurrentModDir = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,8 +543,18 @@ public static class AnalyzerFixes
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (childNode.NodeType != XmlNodeType.Comment)
|
if (childNode.NodeType != XmlNodeType.Comment)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
methods.AddRange(func(childNode.InnerText));
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool MissingRequiredMod(XmlAttributeCollection? attributes)
|
private static bool MissingRequiredMod(XmlAttributeCollection? attributes)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
// ReSharper disable WithExpressionModifiesAllMembers
|
||||||
|
|
||||||
namespace PerformanceFish.Cache;
|
namespace PerformanceFish.Cache;
|
||||||
|
|
||||||
@@ -26,11 +27,11 @@ public record struct ByInt<T, TResult> where T : notnull where TResult : new()
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override int GetHashCode() => Key;
|
public override int GetHashCode() => Key;
|
||||||
|
|
||||||
private static List<FishTable<ByInt<T, TResult>, TResult>> _allCaches = [];
|
private static readonly object _valueInitializerLock = new();
|
||||||
|
|
||||||
private static object _valueInitializerLock = new();
|
private static readonly List<IDictionary> _allCaches = [];
|
||||||
|
|
||||||
private static FishTable<ByInt<T, TResult>, TResult> _get = InitializeNew();
|
private static readonly FishTable<ByInt<T, TResult>, TResult> _get = InitializeNew();
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static FishTable<ByInt<T, TResult>, TResult>? _getThreadStatic;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ref TResult GetOrAddReference(ByInt<T, TResult> key) => ref Get.GetOrAddReference(key);
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static TResult GetOrAdd(int key) => Get.GetOrAdd(Unsafe.As<int, ByInt<T, TResult>>(ref key));
|
public static TResult GetOrAdd(int key) => Get.GetOrAdd(Unsafe.As<int, ByInt<T, TResult>>(ref key));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static TResult GetOrAdd(ByInt<T, TResult> key) => Get.GetOrAdd(key);
|
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)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static ref TResult GetExistingReference(int key)
|
public static ref TResult GetExistingReference(int key)
|
||||||
=> ref Get.GetReference(Unsafe.As<int, ByInt<T, TResult>>(ref 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)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private static FishTable<ByInt<T, TResult>, TResult> InitializeNew()
|
private static FishTable<ByInt<T, TResult>, TResult> InitializeNew()
|
||||||
{
|
=> Utility.AddNew(_allCaches, ValueInitializer);
|
||||||
var newCache = Utility.AddNew(ValueInitializer);
|
|
||||||
|
|
||||||
lock (_allCaches)
|
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||||
_allCaches.Add(newCache);
|
|
||||||
|
|
||||||
return newCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Clear()
|
|
||||||
{
|
|
||||||
lock (_allCaches)
|
|
||||||
{
|
|
||||||
foreach (var cache in _allCaches)
|
|
||||||
cache.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
@@ -120,11 +101,11 @@ public record struct ByInt<T1, T2, TResult>
|
|||||||
{
|
{
|
||||||
public int First, Second;
|
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]
|
[ThreadStatic]
|
||||||
private static FishTable<ByInt<T1, T2, TResult>, TResult>? _getThreadStatic;
|
private static FishTable<ByInt<T1, T2, TResult>, TResult>? _getThreadStatic;
|
||||||
@@ -173,7 +154,7 @@ public record struct ByInt<T1, T2, TResult>
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ref TResult GetOrAddReference(int first, int second)
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ref TResult GetOrAddReference(in ByInt<T1, T2, TResult> key)
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static TResult GetOrAdd(int first, int second)
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static TResult GetOrAdd(in ByInt<T1, T2, TResult> key) => Get.GetOrAdd(ref Unsafe.AsRef(in key));
|
public static TResult GetOrAdd(in ByInt<T1, T2, TResult> key) => Get.GetOrAdd(ref Unsafe.AsRef(in key));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static ref TResult GetExistingReference(int first, int second)
|
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)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static ref TResult GetExistingReference(T1 first, T2 second)
|
public static ref TResult GetExistingReference(T1 first, T2 second)
|
||||||
@@ -217,23 +198,9 @@ public record struct ByInt<T1, T2, TResult>
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private static FishTable<ByInt<T1, T2, TResult>, TResult> InitializeNew()
|
private static FishTable<ByInt<T1, T2, TResult>, TResult> InitializeNew()
|
||||||
{
|
=> Utility.AddNew(_allCaches, ValueInitializer);
|
||||||
var newCache = Utility.AddNew(ValueInitializer);
|
|
||||||
|
|
||||||
lock (_allCaches)
|
public static void Clear() => Utility.Internal.ClearIn(_allCaches);
|
||||||
_allCaches.Add(newCache);
|
|
||||||
|
|
||||||
return newCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Clear()
|
|
||||||
{
|
|
||||||
lock (_allCaches)
|
|
||||||
{
|
|
||||||
foreach (var cache in _allCaches)
|
|
||||||
cache.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
@@ -242,16 +209,39 @@ public record struct ByInt<T1, T2, T3, TResult>
|
|||||||
{
|
{
|
||||||
public int First, Second, Third;
|
public int First, Second, Third;
|
||||||
|
|
||||||
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult> _get
|
private static readonly object _valueInitializerLock = new();
|
||||||
= Utility.AddNew<ByInt<T1, T2, T3, TResult>, TResult>();
|
|
||||||
|
private static readonly List<IDictionary> _allCaches = [];
|
||||||
|
|
||||||
|
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult> _get = InitializeNew();
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult>? _getThreadStatic;
|
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
|
public static FishTable<ByInt<T1, T2, T3, TResult>, TResult> Get
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static unsafe ref TResult GetOrAddReference(int first, int second, int third)
|
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);
|
return ref Get.GetOrAddReference(ref key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +263,10 @@ public record struct ByInt<T1, T2, T3, TResult>
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static ref TResult GetExistingReference(int first, int second, int third)
|
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)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static ref TResult GetExistingReference(T1 first, T2 second, T3 third)
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override int GetHashCode() => HashCode.Combine(First, Second, Third);
|
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]
|
[PublicAPI]
|
||||||
@@ -304,16 +303,39 @@ public record struct ByInt<T1, T2, T3, T4, TResult>
|
|||||||
{
|
{
|
||||||
public int First, Second, Third, Fourth;
|
public int First, Second, Third, Fourth;
|
||||||
|
|
||||||
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> _get
|
private static readonly object _valueInitializerLock = new();
|
||||||
= Utility.AddNew<ByInt<T1, T2, T3, T4, TResult>, TResult>();
|
|
||||||
|
private static readonly List<IDictionary> _allCaches = [];
|
||||||
|
|
||||||
|
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> _get = InitializeNew();
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult>? _getThreadStatic;
|
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
|
public static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> Get
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static unsafe ref TResult GetOrAddReference(int first, int second, int third, int fourth)
|
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
|
First = first, Second = second, Third = third, Fourth = fourth
|
||||||
};
|
};
|
||||||
@@ -338,7 +360,10 @@ public record struct ByInt<T1, T2, T3, T4, TResult>
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static ref TResult GetExistingReference(int first, int second, int third, int fourth)
|
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)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static ref TResult GetExistingReference(T1 first, T2 second, T3 third, T4 fourth)
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override int GetHashCode() => HashCode.Combine(First, Second, Third, Fourth);
|
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
|
#pragma warning restore CS9091
|
||||||
@@ -8,6 +8,7 @@ using System.Security;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
// ReSharper disable WithExpressionModifiesAllMembers
|
||||||
|
|
||||||
namespace PerformanceFish.Cache;
|
namespace PerformanceFish.Cache;
|
||||||
|
|
||||||
@@ -18,9 +19,9 @@ public static class ByReferenceUnclearable<T, TResult>
|
|||||||
{
|
{
|
||||||
public const int MINIMUM_CALL_COUNT_FOR_STARTING_TASK = 8;
|
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]
|
[ThreadStatic]
|
||||||
private static FishTable<T, TResult>? _getThreadStatic;
|
private static FishTable<T, TResult>? _getThreadStatic;
|
||||||
@@ -138,7 +139,8 @@ public static class ByReferenceUnclearable<T, TResult>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SecuritySafeCritical]
|
[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>
|
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
||||||
{
|
{
|
||||||
TCacheValue cacheCopy;
|
TCacheValue cacheCopy;
|
||||||
@@ -200,8 +202,8 @@ public static class ByReferenceUnclearable<T, TResult>
|
|||||||
return !cache.Result.Equals<TCacheResult>(default);
|
return !cache.Result.Equals<TCacheResult>(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TCacheValue cacheCopy,
|
private static async Task<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key,
|
||||||
TArgument2 second)
|
TCacheValue cacheCopy, TArgument2 second)
|
||||||
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
||||||
{
|
{
|
||||||
TCacheResult? result = default;
|
TCacheResult? result = default;
|
||||||
@@ -240,11 +242,13 @@ public static class ByReferenceUnclearable<T, TResult>
|
|||||||
public static class ByReference<T, TResult>
|
public static class ByReference<T, TResult>
|
||||||
where T : notnull where TResult : new()
|
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 FishTable<T, TResult> _get = InitializeNew();
|
private static readonly List<IDictionary> _allCaches = [];
|
||||||
|
|
||||||
|
private static readonly FishTable<T, TResult> _get = InitializeNew();
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static FishTable<T, TResult>? _getThreadStatic;
|
private static FishTable<T, TResult>? _getThreadStatic;
|
||||||
@@ -293,15 +297,8 @@ public static class ByReference<T, TResult>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ref TResult GetOrAddReference(ref T key) => ref Get.GetOrAddReference(ref key);
|
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 ref TResult GetExistingReference(T key) => ref Get.GetReference(key);
|
||||||
|
|
||||||
public static void Remove(T key)
|
|
||||||
{
|
|
||||||
Get.Remove(key);
|
|
||||||
GetDirectly.Remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Update<VResult>(T key) where VResult : TResult, ICacheable<T>
|
public static void Update<VResult>(T key) where VResult : TResult, ICacheable<T>
|
||||||
=> Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
|
=> 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>();
|
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>
|
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
||||||
{
|
{
|
||||||
TCacheValue cacheCopy;
|
TCacheValue cacheCopy;
|
||||||
@@ -424,8 +422,8 @@ public static class ByReference<T, TResult>
|
|||||||
return !cache.Result.Equals<TCacheResult>(default);
|
return !cache.Result.Equals<TCacheResult>(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async ValueTask<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TCacheValue cacheCopy,
|
private static async ValueTask<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key,
|
||||||
TArgument2 second)
|
TCacheValue cacheCopy, TArgument2 second)
|
||||||
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
|
||||||
{
|
{
|
||||||
TCacheResult? result = default;
|
TCacheResult? result = default;
|
||||||
@@ -444,23 +442,14 @@ public static class ByReference<T, TResult>
|
|||||||
return result ?? ThrowHelper.ThrowInvalidOperationException<TCacheResult>();
|
return result ?? ThrowHelper.ThrowInvalidOperationException<TCacheResult>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe void Initialize()
|
public static void Initialize() => Utility.Internal.Initialize<T>();
|
||||||
{
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[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]
|
[PublicAPI]
|
||||||
@@ -468,16 +457,43 @@ public static class ByReference<T, TResult>
|
|||||||
public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
||||||
where T1 : notnull where T2 : notnull where TResult : new()
|
where T1 : notnull where T2 : notnull where TResult : new()
|
||||||
{
|
{
|
||||||
private static FishTable<ByReference<T1, T2, TResult>, TResult> _get
|
public T1 First;
|
||||||
= Utility.AddNew<ByReference<T1, T2, TResult>, TResult>();
|
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]
|
[ThreadStatic]
|
||||||
private static FishTable<ByReference<T1, T2, TResult>, TResult>? _getThreadStatic;
|
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
|
public static FishTable<ByReference<T1, T2, TResult>, TResult> Get
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => _getThreadStatic ??= Utility.AddNew<ByReference<T1, T2, TResult>, TResult>();
|
get => _getThreadStatic ??= InitializeNew();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FishTable<ByReference<T1, T2, TResult>, TResult> GetDirectly
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ref TResult GetOrAddReference(T1 first, T2 second)
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ref TResult GetOrAddReference(in ByReference<T1, T2, TResult> key)
|
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)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static unsafe ref TResult GetExistingReference(T1 first, T2 second)
|
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);
|
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)
|
public static void Update<VResult>(T1 first, T2 second)
|
||||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>>
|
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);
|
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)
|
public static void Update<VResult, V2>(T1 key1, T2 key2, V2 second)
|
||||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2>
|
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);
|
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)
|
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>
|
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);
|
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)
|
public static unsafe ref VResult GetAndCheck<VResult>(T1 first, T2 second)
|
||||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>>
|
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));
|
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||||
if (cache.Dirty)
|
if (cache.Dirty)
|
||||||
cache.Update(ref key);
|
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)
|
public static unsafe ref VResult GetAndCheck<VResult, V2>(T1 key1, T2 key2, V2 second)
|
||||||
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2>
|
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));
|
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||||
if (cache.Dirty)
|
if (cache.Dirty)
|
||||||
cache.Update(ref key, second);
|
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)
|
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>
|
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));
|
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||||
if (cache.Dirty)
|
if (cache.Dirty)
|
||||||
cache.Update(ref key, second, third);
|
cache.Update(ref key, second, third);
|
||||||
@@ -579,28 +595,12 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
|||||||
Second = second;
|
Second = second;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe void Initialize()
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
try
|
Utility.Internal.Initialize<T1>();
|
||||||
{
|
Utility.Internal.Initialize<T2>();
|
||||||
_ = FisheryLib.FunctionPointers.Equals<T1>.Default;
|
Utility.Internal.Initialize<ByReference<T1, T2, TResult>>();
|
||||||
_ = 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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T1 First;
|
|
||||||
|
|
||||||
public T2 Second;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool Equals(ByReference<T1, T2, TResult> other)
|
public bool Equals(ByReference<T1, T2, TResult> other)
|
||||||
@@ -610,6 +610,14 @@ public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override int GetHashCode() => HashCode.Combine(First, Second);
|
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
|
T1 IMemberCount<T1>.First
|
||||||
{
|
{
|
||||||
get => 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>
|
public record struct ByReference<T1, T2, T3, TResult>
|
||||||
where T1 : notnull where T2 : notnull where T3 : notnull where TResult : new()
|
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
|
public T1 First;
|
||||||
// with standard dictionaries
|
public T2 Second;
|
||||||
|
public T3 Third;
|
||||||
|
public int HashCode;
|
||||||
|
|
||||||
private static Dictionary<ByReference<T1, T2, T3, TResult>, TResult> _get
|
// TODO: Fix ReflectionCaching:CustomAttributeCache causing crashes when not used with standard dictionaries
|
||||||
= Utility.AddNew<Dictionary<ByReference<T1, T2, T3, TResult>, TResult>>();
|
|
||||||
|
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]
|
[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
|
||||||
get => _getThreadStatic ??= Utility.AddNew<Dictionary<ByReference<T1, T2, T3, TResult>, TResult>>();
|
{
|
||||||
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => _get;
|
get => _get;
|
||||||
@@ -652,7 +687,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third)
|
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);
|
return ref Get.GetOrAddReference(ref key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,7 +698,7 @@ public record struct ByReference<T1, T2, T3, TResult>
|
|||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static unsafe ref TResult GetExistingReference(T1 first, T2 second, T3 third)
|
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);
|
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)
|
public static void Update<VResult>(T1 first, T2 second, T3 third)
|
||||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>>
|
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);
|
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)
|
public static void Update<VResult, V2>(T1 key1, T2 key2, T3 key3, V2 second)
|
||||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>, V2>
|
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);
|
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)
|
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>
|
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);
|
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)
|
public static unsafe ref VResult GetAndCheck<VResult>(T1 first, T2 second, T3 third)
|
||||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>>
|
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));
|
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||||
if (cache.Dirty)
|
if (cache.Dirty)
|
||||||
cache.Update(ref key);
|
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)
|
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>
|
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));
|
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||||
if (cache.Dirty)
|
if (cache.Dirty)
|
||||||
cache.Update(ref key, second);
|
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)
|
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>
|
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));
|
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||||
if (cache.Dirty)
|
if (cache.Dirty)
|
||||||
cache.Update(ref key, second, third);
|
cache.Update(ref key, second, third);
|
||||||
@@ -732,41 +767,32 @@ public record struct ByReference<T1, T2, T3, TResult>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
// ReSharper disable once ConvertToPrimaryConstructor
|
||||||
public ByReference(T1 first, T2 second, T3 third)
|
public ByReference(T1 first, T2 second, T3 third)
|
||||||
{
|
{
|
||||||
First = first;
|
First = first;
|
||||||
Second = second;
|
Second = second;
|
||||||
Third = third;
|
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
|
||||||
{
|
{
|
||||||
try
|
First = first,
|
||||||
|
Second = second,
|
||||||
|
Third = third,
|
||||||
|
HashCode = FisheryLib.HashCode.Combine(first, second, third)
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
_ = FisheryLib.FunctionPointers.Equals<T1>.Default;
|
Utility.Internal.Initialize<T1>();
|
||||||
_ = FisheryLib.FunctionPointers.Equals<T2>.Default;
|
Utility.Internal.Initialize<T2>();
|
||||||
_ = FisheryLib.FunctionPointers.Equals<T3>.Default;
|
Utility.Internal.Initialize<T3>();
|
||||||
_ = FisheryLib.FunctionPointers.Equals<ByReference<T1, T2, T3, TResult>>.Default;
|
Utility.Internal.Initialize<ByReference<T1, T2, T3, TResult>>();
|
||||||
_ = 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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T1 First;
|
|
||||||
|
|
||||||
public T2 Second;
|
|
||||||
|
|
||||||
public T3 Third;
|
|
||||||
|
|
||||||
private int _hashCode;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool Equals(ByReference<T1, T2, T3, TResult> other)
|
public bool Equals(ByReference<T1, T2, T3, TResult> other)
|
||||||
@@ -775,7 +801,15 @@ public record struct ByReference<T1, T2, T3, TResult>
|
|||||||
&& Third.Equals<T3>(other.Third);
|
&& Third.Equals<T3>(other.Third);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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]
|
[PublicAPI]
|
||||||
@@ -783,16 +817,45 @@ public record struct ByReference<T1, T2, T3, TResult>
|
|||||||
public record struct ByReference<T1, T2, T3, T4, 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()
|
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
|
public T1 First;
|
||||||
= Utility.AddNew<ByReference<T1, T2, T3, T4, TResult>, TResult>();
|
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]
|
[ThreadStatic]
|
||||||
private static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult>? _getThreadStatic;
|
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
|
public static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult> Get
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third, T4 fourth)
|
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);
|
return ref Get.GetOrAddReference(ref key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -815,7 +878,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
|||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static unsafe ref TResult GetExistingReference(T1 first, T2 second, T3 third, T4 fourth)
|
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);
|
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)
|
public static void Update<VResult>(T1 first, T2 second, T3 third, T4 fourth)
|
||||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>>
|
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);
|
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)
|
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>
|
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);
|
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)
|
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>
|
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);
|
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)
|
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>>
|
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));
|
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||||
if (cache.Dirty)
|
if (cache.Dirty)
|
||||||
cache.Update(ref key);
|
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)
|
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>
|
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));
|
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||||
if (cache.Dirty)
|
if (cache.Dirty)
|
||||||
cache.Update(ref key, second);
|
cache.Update(ref key, second);
|
||||||
@@ -876,7 +939,7 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
|||||||
V3 third)
|
V3 third)
|
||||||
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>, V2, V3>
|
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));
|
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
|
||||||
if (cache.Dirty)
|
if (cache.Dirty)
|
||||||
cache.Update(ref key, second, third);
|
cache.Update(ref key, second, third);
|
||||||
@@ -885,47 +948,35 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
// ReSharper disable once ConvertToPrimaryConstructor
|
||||||
public ByReference(T1 first, T2 second, T3 third, T4 fourth)
|
public ByReference(T1 first, T2 second, T3 third, T4 fourth)
|
||||||
{
|
{
|
||||||
First = first;
|
First = first;
|
||||||
Second = second;
|
Second = second;
|
||||||
Third = third;
|
Third = third;
|
||||||
Fourth = fourth;
|
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
|
||||||
{
|
{
|
||||||
try
|
First = first,
|
||||||
|
Second = second,
|
||||||
|
Third = third,
|
||||||
|
Fourth = fourth,
|
||||||
|
HashCode = FisheryLib.HashCode.Combine(first, second, third, fourth)
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
_ = FisheryLib.FunctionPointers.Equals<T1>.Default;
|
Utility.Internal.Initialize<T1>();
|
||||||
_ = FisheryLib.FunctionPointers.Equals<T2>.Default;
|
Utility.Internal.Initialize<T2>();
|
||||||
_ = FisheryLib.FunctionPointers.Equals<T3>.Default;
|
Utility.Internal.Initialize<T3>();
|
||||||
_ = FisheryLib.FunctionPointers.Equals<T4>.Default;
|
Utility.Internal.Initialize<T4>();
|
||||||
_ = FisheryLib.FunctionPointers.Equals<ByReference<T1, T2, T3, T4, TResult>>.Default;
|
Utility.Internal.Initialize<ByReference<T1, T2, T3, T4, TResult>>();
|
||||||
_ = 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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T1 First;
|
|
||||||
|
|
||||||
public T2 Second;
|
|
||||||
|
|
||||||
public T3 Third;
|
|
||||||
|
|
||||||
public T4 Fourth;
|
|
||||||
|
|
||||||
private int _hashCode;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool Equals(ByReference<T1, T2, T3, T4, TResult> other)
|
public bool Equals(ByReference<T1, T2, T3, T4, TResult> other)
|
||||||
@@ -935,6 +986,182 @@ public record struct ByReference<T1, T2, T3, T4, TResult>
|
|||||||
&& Fourth.Equals<T4>(other.Fourth);
|
&& Fourth.Equals<T4>(other.Fourth);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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
|
#pragma warning restore CS9091
|
||||||
@@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
using FisheryLib.FunctionPointers;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using PerformanceFish.Events;
|
using PerformanceFish.Events;
|
||||||
|
|
||||||
@@ -218,6 +219,20 @@ public static class Utility
|
|||||||
return newCollection;
|
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)
|
private sealed record OnDestroyActionData<TKey, TValue>(TKey Key, FishTable<TKey, TValue> Collection)
|
||||||
{
|
{
|
||||||
public Action<Thing>? Action;
|
public Action<Thing>? Action;
|
||||||
@@ -276,6 +291,69 @@ public static class Utility
|
|||||||
All.Add(newCollection); // require different constructors.
|
All.Add(newCollection); // require different constructors.
|
||||||
return newCollection; // Reflection.New<T> can't use arguments
|
return newCollection; // Reflection.New<T> can't use arguments
|
||||||
} // for resolving method calls (yet)
|
} // 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);
|
public delegate int IndexGetter<in T>(T item);
|
||||||
|
|||||||
@@ -9,13 +9,19 @@ public record struct CellIndex(int Value)
|
|||||||
{
|
{
|
||||||
public int Value = Value;
|
public int Value = Value;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public CellIndex(IntVec3 cell, Map map) : this(cell, map.cellIndices.mapSizeX) {}
|
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 CellIndex(IntVec3 cell, int mapSizeX) : this(cell.CellToIndex(mapSizeX)) {}
|
||||||
|
|
||||||
public IntVec3 ToCell(Map map) => ToCell(map.cellIndices.mapSizeX);
|
public IntVec3 ToCell(Map map) => ToCell(map.cellIndices.mapSizeX);
|
||||||
|
|
||||||
public IntVec3 ToCell(int mapSizeX) => CellIndicesUtility.IndexToCell(Value, mapSizeX);
|
public IntVec3 ToCell(int mapSizeX) => CellIndicesUtility.IndexToCell(Value, mapSizeX);
|
||||||
|
|
||||||
public List<Thing> GetThingList(Map map) => map.thingGrid.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;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace PerformanceFish;
|
namespace PerformanceFish.Defs;
|
||||||
|
|
||||||
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
|
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
|
||||||
public record struct DefPair
|
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 Mono.Cecil.Cil;
|
||||||
using PerformanceFish.Prepatching;
|
using PerformanceFish.Prepatching;
|
||||||
|
|
||||||
namespace PerformanceFish;
|
namespace PerformanceFish.Defs;
|
||||||
|
|
||||||
public sealed class WorkGiverDefFixes : ClassWithFishPrepatches
|
public sealed class WorkGiverDefFixes : ClassWithFishPrepatches
|
||||||
{
|
{
|
||||||
public sealed class WorkerPatch : FishPrepatch
|
public sealed class WorkerPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override string? Description { get; }
|
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 "
|
+ "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 "
|
+ "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.";
|
+ "from running to prevent the error spam.";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
@@ -40,6 +40,7 @@ public sealed class WorkGiverDefFixes : ClassWithFishPrepatches
|
|||||||
return instance.workerInt;
|
return instance.workerInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static void TryFixAndLogError(WorkGiverDef instance)
|
public static void TryFixAndLogError(WorkGiverDef instance)
|
||||||
{
|
{
|
||||||
instance.giverClass = typeof(WorkGiver_AlwaysSkip);
|
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
|
#if false
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
// ReSharper disable PrivateFieldCanBeConvertedToLocalVariable
|
||||||
|
|
||||||
namespace PerformanceFish.Experimental;
|
namespace PerformanceFish.Experimental;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public unsafe struct UnalignedPointer
|
public unsafe struct UnalignedPointer
|
||||||
{
|
{
|
||||||
private fixed byte _data[6]; // 48bit
|
// 48 bit
|
||||||
|
private ushort
|
||||||
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] // 48bit
|
_top,
|
||||||
// private byte[] _data;
|
_mid,
|
||||||
|
_bot;
|
||||||
|
|
||||||
public void* Value
|
public void* Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
fixed (void* value = _data)
|
var result = (void*)(Unsafe.As<ushort, ulong>(ref _top) & 0x0000_FFFF_FFFF_FFFF);
|
||||||
{
|
return result;
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnalignedPointer(void* pointer)
|
public UnalignedPointer(void* pointer)
|
||||||
{
|
{
|
||||||
fixed (UnalignedPointer* instance = &this)
|
Unsafe.As<ushort, uint>(ref _top) = (uint)(ulong)pointer;
|
||||||
{
|
_bot = (ushort)((ulong)pointer >> 32);
|
||||||
instance->_data = (byte*)pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe struct TaggedPointer
|
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
|
public void* Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var value = _data;
|
var value = (void*)(_data & ValueBitMask);
|
||||||
return value;
|
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
|
#endif
|
||||||
@@ -93,6 +93,9 @@ public sealed class FishSettings : ModSettings
|
|||||||
ToggleAllPatches(checkModEnabled);
|
ToggleAllPatches(checkModEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ls.Gap();
|
||||||
|
ls.Label("Changes to prepatches require a restart. Harmony patches update immediately when toggled.");
|
||||||
|
|
||||||
ls.Gap();
|
ls.Gap();
|
||||||
var curFontStyle = Text.CurFontStyle;
|
var curFontStyle = Text.CurFontStyle;
|
||||||
var curFontSize = curFontStyle.fontSize;
|
var curFontSize = curFontStyle.fontSize;
|
||||||
|
|||||||
@@ -19,21 +19,31 @@ public static class IndexGetter
|
|||||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||||
public static class Methods
|
public static class Methods
|
||||||
{
|
{
|
||||||
public static int HediffSet(HediffSet set) => set.pawn.thingIDNumber;
|
public static int Ability(Ability ability) => ability.Id;
|
||||||
public static int HediffDef(HediffDef def) => def.shortHash;
|
|
||||||
public static int Bill(Bill bill) => bill.loadID;
|
public static int Bill(Bill bill) => bill.loadID;
|
||||||
public static int Pawn(Pawn pawn) => pawn.thingIDNumber;
|
public static int Def(Def def) => def.shortHash;
|
||||||
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 DesignationManager(DesignationManager manager) => manager.map.uniqueID;
|
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 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
|
internal static Cache.IndexGetter<T> GetDefaultIndexGetter<T>() where T : notnull
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ public static class GasDefOf
|
|||||||
public static GasDef
|
public static GasDef
|
||||||
BlindSmoke,
|
BlindSmoke,
|
||||||
ToxGas,
|
ToxGas,
|
||||||
RotStink;
|
RotStink
|
||||||
|
#if !V1_4
|
||||||
|
,
|
||||||
|
DeadLifeDust
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
static GasDefOf() => DefOfHelper.EnsureInitializedInCtor(typeof(GasDefOf));
|
static GasDefOf() => DefOfHelper.EnsureInitializedInCtor(typeof(GasDefOf));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,15 @@
|
|||||||
// #define GAS_DEBUG_L3 // individual cell changes
|
// #define GAS_DEBUG_L3 // individual cell changes
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
#if V1_4
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
#endif
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
#if !V1_4
|
||||||
|
using Gilzoide.ManagedJobs;
|
||||||
|
using LudeonTK;
|
||||||
|
using Unity.Jobs;
|
||||||
|
#endif
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using MonoMod.Utils;
|
using MonoMod.Utils;
|
||||||
@@ -32,16 +39,28 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
=> 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)
|
if (!ModsConfig.BiotechActive)
|
||||||
toxic = 0;
|
toxic = 0;
|
||||||
|
|
||||||
|
#if !V1_4
|
||||||
|
if (!ModsConfig.AnomalyActive)
|
||||||
|
deadlife = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
var gasGrids = __instance.ParallelGasGrids();
|
var gasGrids = __instance.ParallelGasGrids();
|
||||||
|
|
||||||
gasGrids[0].SetDirect(index, smoke);
|
gasGrids[0].SetDirect(index, smoke);
|
||||||
gasGrids[1].SetDirect(index, toxic);
|
gasGrids[1].SetDirect(index, toxic);
|
||||||
gasGrids[2].SetDirect(index, rotStink);
|
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; } =
|
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(DensityAtPatch), typeof(Debug_ClearAllPatch), typeof(Debug_FillAllPatch),
|
||||||
typeof(Notify_ThingSpawnedPatch), typeof(ExposeDataPatch), typeof(MouseOverReadOutOnGUI),
|
typeof(Notify_ThingSpawnedPatch), typeof(ExposeDataPatch), typeof(MouseOverReadOutOnGUI),
|
||||||
typeof(DebugToolsGeneralPushGas)
|
typeof(DebugToolsGeneralPushGas)
|
||||||
@@ -76,13 +99,25 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
if (gasGrids.Length < 1)
|
if (gasGrids.Length < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#if !V1_4
|
||||||
|
JobHandle handle;
|
||||||
|
#endif
|
||||||
if (FishSettings.ThreadingEnabled
|
if (FishSettings.ThreadingEnabled
|
||||||
|
#if V1_4
|
||||||
&& TryGetRegisteredGasGridWorkers(__instance, out var monitorObject))
|
&& TryGetRegisteredGasGridWorkers(__instance, out var monitorObject))
|
||||||
{
|
{
|
||||||
TryPulseRegisteredGasGridWorkers(gasGrids, monitorObject);
|
TryPulseRegisteredGasGridWorkers(gasGrids, monitorObject);
|
||||||
|
#else
|
||||||
|
)
|
||||||
|
{
|
||||||
|
handle = new ManagedJobParallelFor(__instance.ParallelJob()).Schedule(gasGrids.Length - 1, 1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if !V1_4
|
||||||
|
Unsafe.SkipInit(out handle);
|
||||||
|
#endif
|
||||||
for (var i = 1; i < gasGrids.Length; i++)
|
for (var i = 1; i < gasGrids.Length; i++)
|
||||||
gasGrids[i].Tick();
|
gasGrids[i].Tick();
|
||||||
}
|
}
|
||||||
@@ -90,17 +125,21 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
var firstGasGrid = gasGrids[0];
|
var firstGasGrid = gasGrids[0];
|
||||||
firstGasGrid.Tick();
|
firstGasGrid.Tick();
|
||||||
|
|
||||||
__instance.cycleIndexDiffusion = firstGasGrid.CycleIndexDiffusion;
|
|
||||||
__instance.cycleIndexDissipation = firstGasGrid.CycleIndexDissipation;
|
|
||||||
|
|
||||||
if (FishSettings.ThreadingEnabled)
|
if (FishSettings.ThreadingEnabled)
|
||||||
|
{
|
||||||
|
#if V1_4
|
||||||
WaitForGasGridWorkers(gasGrids);
|
WaitForGasGridWorkers(gasGrids);
|
||||||
|
#else
|
||||||
|
handle.Complete();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if V1_4
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static bool TryGetRegisteredGasGridWorkers(GasGrid __instance,
|
public static bool TryGetRegisteredGasGridWorkers(GasGrid __instance,
|
||||||
[NotNullWhen(true)] out object? monitorObject)
|
[NotNullWhen(true)] out object? monitorObject)
|
||||||
=> (monitorObject = __instance.map.GasGridMonitorObject()) != null
|
=> (monitorObject = __instance.MonitorObject()) != null
|
||||||
&& ParallelNoAlloc.HasAnyRegisteredWorkers(monitorObject);
|
&& ParallelNoAlloc.HasAnyRegisteredWorkers(monitorObject);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
@@ -124,18 +163,33 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
i++;
|
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 sealed class ColorAtPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override bool ShowSettings => false;
|
public override bool ShowSettings => false;
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
|
#if V1_4
|
||||||
= AccessTools.DeclaredMethod(typeof(GasGrid), nameof(GasGrid.ColorAt));
|
= 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)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||||
|
|
||||||
|
#if V1_4
|
||||||
public static Color ReplacementBody(GasGrid __instance, IntVec3 cell)
|
public static Color ReplacementBody(GasGrid __instance, IntVec3 cell)
|
||||||
{
|
{
|
||||||
var cellIndex = cell.CellToIndex(__instance.map);
|
var cellIndex = cell.CellToIndex(__instance.map);
|
||||||
@@ -159,6 +213,41 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
return resultColor;
|
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
|
public sealed class AddGasPatch : FishPrepatch
|
||||||
@@ -231,7 +320,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
for (var i = 0; i < gasGrids.Length; i++)
|
for (var i = 0; i < gasGrids.Length; i++)
|
||||||
gasGrids[i].Clear();
|
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)
|
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 smokeGrid = grids[0];
|
||||||
var toxicGrid = grids[1];
|
var toxicGrid = grids[1];
|
||||||
var rotStinkGrid = grids[2];
|
var rotStinkGrid = grids[2];
|
||||||
|
#if !V1_4
|
||||||
|
var deadLifeGrid = grids[3];
|
||||||
|
#endif
|
||||||
|
|
||||||
for (var i = smokeGrid.CellCount; i-- > 0;)
|
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)
|
| (toxicGrid.DensityAt(i) << 8)
|
||||||
| smokeGrid.DensityAt(i);
|
| 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 smokeGrid = grids[0];
|
||||||
var toxicGrid = grids[1];
|
var toxicGrid = grids[1];
|
||||||
var rotStinkGrid = grids[2];
|
var rotStinkGrid = grids[2];
|
||||||
|
#if !V1_4
|
||||||
|
var deadLifeGrid = grids[3];
|
||||||
|
#endif
|
||||||
|
|
||||||
for (var i = smokeGrid.CellCount; i-- > 0;)
|
for (var i = smokeGrid.CellCount; i-- > 0;)
|
||||||
{
|
{
|
||||||
var gasDensityAtCell = __instance.gasDensity[i];
|
var gasDensityAtCell = __instance.gasDensity[i];
|
||||||
|
|
||||||
smokeGrid.SetDirect(i, (byte)gasDensityAtCell);
|
smokeGrid.SetDirect(i, (byte)gasDensityAtCell);
|
||||||
|
// ReSharper disable once RedundantCast
|
||||||
toxicGrid.SetDirect(i, (byte)((uint)gasDensityAtCell >> 8));
|
toxicGrid.SetDirect(i, (byte)((uint)gasDensityAtCell >> 8));
|
||||||
|
// ReSharper disable once RedundantCast
|
||||||
rotStinkGrid.SetDirect(i, (byte)((uint)gasDensityAtCell >> 16));
|
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 byte[] _gasDensity;
|
||||||
private volatile int _lastFinishedTick = -1;
|
private volatile int _lastFinishedTick = -1;
|
||||||
|
|
||||||
|
private bool _allowedToRun;
|
||||||
|
|
||||||
public event Action<ParallelGasGrid>? Ticked;
|
public event Action<ParallelGasGrid>? Ticked;
|
||||||
|
|
||||||
public GasDef GasDef { get; private set; }
|
public GasDef GasDef { get; private set; }
|
||||||
@@ -487,6 +618,16 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
private void Initialize(Map map, GasDef gasDef)
|
private void Initialize(Map map, GasDef gasDef)
|
||||||
{
|
{
|
||||||
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;
|
Map = map;
|
||||||
CellCount = map.cellIndices.NumGridCells;
|
CellCount = map.cellIndices.NumGridCells;
|
||||||
_gasDensity = new byte[CellCount];
|
_gasDensity = new byte[CellCount];
|
||||||
@@ -495,7 +636,7 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
SourceIndicesOfRandomCells = new int[CellCount];
|
SourceIndicesOfRandomCells = new int[CellCount];
|
||||||
CardinalDirections = new IntVec3[GenAdj.CardinalDirections.Length];
|
CardinalDirections = new IntVec3[GenAdj.CardinalDirections.Length];
|
||||||
Array.Copy(GenAdj.CardinalDirections, CardinalDirections, 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 };
|
_dissipationTicker = new() { GasGrid = this };
|
||||||
_diffusionTicker = new() { GasGrid = this };
|
_diffusionTicker = new() { GasGrid = this };
|
||||||
@@ -514,8 +655,14 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
// created per map while loading it's stupid
|
// created per map while loading it's stupid
|
||||||
// Also cellsInRandomOrder itself initializes after gasGrid within CreateComponents, but that's easy to
|
// Also cellsInRandomOrder itself initializes after gasGrid within CreateComponents, but that's easy to
|
||||||
// solve
|
// solve
|
||||||
grid.CellsInRandomOrder = grid.Map.cellsInRandomOrder.GetAll();
|
var map = grid.Map;
|
||||||
|
grid.CellsInRandomOrder = map.cellsInRandomOrder.GetAll();
|
||||||
grid.UpdateCellIndicesInRandomOrder();
|
grid.UpdateCellIndicesInRandomOrder();
|
||||||
|
|
||||||
|
var mapGasGrid = map.gasGrid;
|
||||||
|
grid.CycleIndexDiffusion = mapGasGrid.cycleIndexDiffusion;
|
||||||
|
grid.CycleIndexDissipation = mapGasGrid.cycleIndexDissipation;
|
||||||
|
|
||||||
grid.Ticked -= _onFirstTickAction;
|
grid.Ticked -= _onFirstTickAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,7 +732,7 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
public void AddGas(in IntVec3 cell, int amount, bool canOverflow = true)
|
public void AddGas(in IntVec3 cell, int amount, bool canOverflow = true)
|
||||||
{
|
{
|
||||||
if (amount <= 0 || !GasCanMoveTo(cell))
|
if (amount <= 0 || !GasCanMoveTo(cell) || !_allowedToRun)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
anyGasEverAdded = true;
|
anyGasEverAdded = true;
|
||||||
@@ -593,7 +740,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
SetDirect(index, AdjustedDensity(_gasDensity[index] + amount, out var overflow));
|
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)
|
if (canOverflow && overflow > 0)
|
||||||
Overflow(cell, overflow);
|
Overflow(cell, overflow);
|
||||||
@@ -766,8 +919,14 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
_dissipationTicker.TickCells(area, (area + 63) >> 6); // Mathf.CeilToInt(area * (1f / 64f))
|
_dissipationTicker.TickCells(area, (area + 63) >> 6); // Mathf.CeilToInt(area * (1f / 64f))
|
||||||
|
|
||||||
|
var mapGasGrid = Map.gasGrid;
|
||||||
|
mapGasGrid.cycleIndexDissipation = CycleIndexDissipation;
|
||||||
|
|
||||||
if (Diffuses)
|
if (Diffuses)
|
||||||
|
{
|
||||||
_diffusionTicker.TickCells(area, (area + 31) >> 5); // Mathf.CeilToInt(area * (1f / 32f))
|
_diffusionTicker.TickCells(area, (area + 31) >> 5); // Mathf.CeilToInt(area * (1f / 32f))
|
||||||
|
mapGasGrid.cycleIndexDiffusion = CycleIndexDiffusion;
|
||||||
|
}
|
||||||
|
|
||||||
#if GAS_DEBUG_L2
|
#if GAS_DEBUG_L2
|
||||||
Debug.LogTotalGasChange(this, previousDensity);
|
Debug.LogTotalGasChange(this, previousDensity);
|
||||||
@@ -817,7 +976,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
if (densityAfterDissipation == 0)
|
if (densityAfterDissipation == 0)
|
||||||
{
|
{
|
||||||
Debug.LogMapMeshDirty(this, index);
|
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;
|
continue;
|
||||||
|
|
||||||
SetDirect(otherCellIndex, (byte)densityOtOtherCell);
|
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;
|
diffusedToAnyCell = true;
|
||||||
if (densityAtOriginCell < 17)
|
if (densityAtOriginCell < 17)
|
||||||
break;
|
break;
|
||||||
@@ -854,7 +1025,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
SetDirect(originCellIndex, (byte)densityAtOriginCell);
|
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)
|
private static bool TryDiffuseIndividualGas(ref int gasA, ref int gasB)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public sealed class GenLocalDateCaching : ClassWithFishPatches
|
|||||||
{
|
{
|
||||||
public override string Description { get; }
|
public override string Description { get; }
|
||||||
= "Caches results of GenLocalDate.DayTick for the first map. This is similar to Rim73's mind state "
|
= "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 Delegate TargetMethodGroup { get; } = (Func<Thing, int>)GenLocalDate.DayTick;
|
||||||
public override int TranspilerMethodPriority => Priority.First;
|
public override int TranspilerMethodPriority => Priority.First;
|
||||||
@@ -64,7 +64,7 @@ public sealed class GenLocalDateCaching : ClassWithFishPatches
|
|||||||
{
|
{
|
||||||
public override string Description { get; }
|
public override string Description { get; }
|
||||||
= "Caches results of GenLocalDate.DayTick for the first map. This is similar to Rim73's mind state "
|
= "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;
|
public override Delegate TargetMethodGroup { get; } = (Func<Map, int>)GenLocalDate.DayTick;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
|||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
= "Fix for thread safety. The vanilla method was rarely breaking and preventing the game from loading";
|
= "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; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= AccessTools.PropertyGetter(typeof(GenTypes), nameof(GenTypes.AllTypes));
|
= AccessTools.PropertyGetter(typeof(GenTypes), nameof(GenTypes.AllTypes));
|
||||||
@@ -34,7 +34,7 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
|||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
= "Fix for thread safety. The vanilla method was rarely breaking and preventing the game from loading";
|
= "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>);
|
public override MethodBase TargetMethodBase { get; } = methodof(GenTypes.AllTypesWithAttribute<Attribute>);
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ public sealed class GenTypesPatches : ClassWithFishPrepatches
|
|||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
= "Fix for thread safety. The vanilla method was rarely breaking and preventing the game from loading";
|
= "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);
|
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 "
|
= "Fix for thread safety, paired with a small optimization for faster loading. The vanilla method was "
|
||||||
+ "rarely breaking and preventing the game from loading";
|
+ "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);
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool Dirty(Pawn_GeneTracker instance, List<Gene> genesToTick)
|
public static bool Dirty(Pawn_GeneTracker instance, List<Gene> genesToTick)
|
||||||
=> GetListVersion(instance) != genesToTick._version;
|
=> GetListVersion(instance) != genesToTick._version;
|
||||||
@@ -69,7 +66,6 @@ public sealed class GeneTrackerOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static int GetListVersion(Pawn_GeneTracker instance)
|
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);
|
=> (instance.xenogenes._version << 16) | (instance.endogenes._version & 0b1111_1111_1111_1111);
|
||||||
|
|
||||||
private static void AddTickableGenes(List<Gene> genesToTick, List<Gene> genes)
|
private static void AddTickableGenes(List<Gene> genesToTick, List<Gene> genes)
|
||||||
@@ -88,27 +84,6 @@ public sealed class GeneTrackerOptimization : ClassWithFishPrepatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [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
|
public static HashSet<Type> SkippableTypes
|
||||||
= typeof(Gene).SubclassesWithNoMethodOverrideAndSelf(nameof(Gene.Tick)).ToHashSet();
|
= 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.";
|
+ "faster than the vanilla method. Replaces Performance Optimizer's error prone and slower optimization.";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
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)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(HasComp);
|
=> 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 sealed class HediffCompPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override string? Description { get; }
|
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.";
|
+ "faster than the vanilla method. Replaces Performance Optimizer's error prone and slower optimization.";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= methodof(HediffUtility.TryGetComp<HediffComp>);
|
= methodof((Func<Hediff, HediffComp>)HediffUtility.TryGetComp<HediffComp>);
|
||||||
|
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(TryGetComp<HediffComp>);
|
=> ilProcessor.ReplaceBodyWith(TryGetComp<HediffComp>);
|
||||||
|
|||||||
@@ -4,12 +4,15 @@
|
|||||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
namespace PerformanceFish;
|
namespace PerformanceFish;
|
||||||
|
|
||||||
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
|
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
|
||||||
public record struct GroupThingPair
|
public record struct GroupThingPair
|
||||||
{
|
{
|
||||||
|
public const int MaxValue = 0xFF_FF_FF;
|
||||||
|
|
||||||
private uint _data;
|
private uint _data;
|
||||||
|
|
||||||
public ThingRequestGroup ThingRequestGroup
|
public ThingRequestGroup ThingRequestGroup
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace PerformanceFish.Hauling;
|
namespace PerformanceFish.Hauling;
|
||||||
|
|
||||||
public class StorageDistrict
|
public class StorageDistrict
|
||||||
@@ -93,11 +95,18 @@ public class StorageDistrict
|
|||||||
public void RemoveThing(Thing thing)
|
public void RemoveThing(Thing thing)
|
||||||
{
|
{
|
||||||
if (!StoredThingsOfDef(thing.def).Remove(thing))
|
if (!StoredThingsOfDef(thing.def).Remove(thing))
|
||||||
ThrowHelper.ThrowInvalidOperationException();
|
{
|
||||||
|
ThrowForInvalidRemovalAttempt(thing);
|
||||||
|
}
|
||||||
|
|
||||||
FreeSlots = TotalSlots - --_storedThingCount;
|
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 List<Thing> StoredThingsOfDef(ThingDef def) => _storedThingsByDef.GetOrAdd(def.shortHash);
|
||||||
|
|
||||||
public bool CapacityAllows(Thing t) => FreeSlots > 0 || StoredThingsOfDef(t.def).ContainsThingStackableWith(t);
|
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)
|
public void Update(Thing thing, bool allowedToAccept)
|
||||||
{
|
{
|
||||||
_value = allowedToAccept.AsInt();
|
_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)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
=> 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,
|
bool needAccurateResult, ref IntVec3 closestSlot, ref float closestDistSquared,
|
||||||
ref StoragePriority foundPriority)
|
ref StoragePriority foundPriority)
|
||||||
{
|
{
|
||||||
if (slotGroup == null || !slotGroup.parent.Accepts(t) || !CapacityAllows(slotGroup, t))
|
if (slotGroup == null || !slotGroup.Settings.AllowedToAccept(t) || !CapacityAllows(slotGroup, t))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var thingPosition = t.SpawnedOrAnyParentSpawned ? t.PositionHeld : carrier.PositionHeld;
|
var thingPosition = t.SpawnedOrAnyParentSpawned ? t.PositionHeld : carrier.PositionHeld;
|
||||||
@@ -194,9 +199,18 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if V1_4
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static StorageDistrict[] GetDistricts(SlotGroup slotGroup) => slotGroup.Districts();
|
public static StorageDistrict[] GetDistricts(SlotGroup slotGroup) => slotGroup.Districts();
|
||||||
// wrapper for analyzer compatibility, as it otherwise throws on ref returns
|
// 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)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public static bool CapacityAllows(SlotGroup slotGroup, Thing t)
|
public static bool CapacityAllows(SlotGroup slotGroup, Thing t)
|
||||||
@@ -218,9 +232,9 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
|||||||
public sealed class TryFindBestBetterStoreCellForPatch : FishPrepatch
|
public sealed class TryFindBestBetterStoreCellForPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
= "Optimizes a loop in the method to make use of cached storage priorities for fewer comparisons against "
|
= "Optimizes a loop in the method to treat storage groups as proper groups without duplicate checks and to "
|
||||||
+ "those. Also removes haulables from ListerHaulables if the found best store cell turns out to be the "
|
+ "make use of cached storage priorities for fewer comparisons against those. Also removes haulables from "
|
||||||
+ "current cell";
|
+ "ListerHaulables if the found best store cell turns out to be the current cell";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; } = methodof(StoreUtility.TryFindBestBetterStoreCellFor);
|
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
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#if V1_4
|
||||||
using static System.Reflection.Emit.OpCodes;
|
using static System.Reflection.Emit.OpCodes;
|
||||||
|
|
||||||
namespace PerformanceFish.Hediffs;
|
namespace PerformanceFish.Hediffs;
|
||||||
@@ -49,3 +50,4 @@ public sealed class HediffComp_PsychicHarmonizer_Bugfix : ClassWithFishPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
@@ -15,6 +15,7 @@ using VisibleHediffCache
|
|||||||
using NotMissingPartsCache
|
using NotMissingPartsCache
|
||||||
= PerformanceFish.Cache.ByInt<Verse.HediffSet,
|
= PerformanceFish.Cache.ByInt<Verse.HediffSet,
|
||||||
PerformanceFish.Hediffs.HediffSetCaching.NotMissingPartsCacheValue>;
|
PerformanceFish.Hediffs.HediffSetCaching.NotMissingPartsCacheValue>;
|
||||||
|
// ReSharper disable WithExpressionModifiesAllMembers
|
||||||
|
|
||||||
namespace PerformanceFish.Hediffs;
|
namespace PerformanceFish.Hediffs;
|
||||||
|
|
||||||
@@ -26,24 +27,22 @@ public sealed class HediffSetPrecaching : ClassWithFishPrepatches
|
|||||||
= "Caches results of the HediffSet.GetFirstHediffOfDef method. Impact scales with hediff count.";
|
= "Caches results of the HediffSet.GetFirstHediffOfDef method. Impact scales with hediff count.";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= SymbolExtensions.GetMethodInfo(static ()
|
= AccessTools.DeclaredMethod(typeof(HediffSet), nameof(HediffSet.GetFirstHediffOfDef));
|
||||||
=> default(HediffSet)!.GetFirstHediffOfDef(null, default));
|
|
||||||
|
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(Replacement);
|
=> ilProcessor.ReplaceBodyWith(Replacement);
|
||||||
|
|
||||||
public static Hediff? Replacement(HediffSet instance, HediffDef? def, bool mustBeVisible)
|
public static Hediff? Replacement(HediffSet instance, HediffDef? def, bool mustBeVisible)
|
||||||
{
|
{
|
||||||
var canCache = true;
|
bool canCache;
|
||||||
|
|
||||||
if (instance.hediffs.Count >= 5
|
if ((instance.hediffs.Count >= 5) & def is not null)
|
||||||
&& def is not null
|
|
||||||
// && def.GetType() == typeof(HediffDef) // different types have different indexing. Can't use those
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
ref var cache = ref GetCacheValue(instance, def, mustBeVisible);
|
ref var cache = ref GetCacheValue(instance, def!, mustBeVisible);
|
||||||
if (!cache.Dirty)
|
if (!cache.Dirty)
|
||||||
return cache.Hediff;
|
return cache.Hediff;
|
||||||
|
|
||||||
|
canCache = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -75,7 +74,7 @@ public sealed class HediffSetPrecaching : ClassWithFishPrepatches
|
|||||||
public static unsafe ref HediffSetCaching.HediffCacheValue GetCacheValue(HediffSet instance, HediffDef def,
|
public static unsafe ref HediffSetCaching.HediffCacheValue GetCacheValue(HediffSet instance, HediffDef def,
|
||||||
bool mustBeVisible)
|
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 var cache = ref mustBeVisible
|
||||||
? ref Unsafe.As<HediffSetCaching.VisibleHediffCacheValue, HediffSetCaching.HediffCacheValue>(
|
? ref Unsafe.As<HediffSetCaching.VisibleHediffCacheValue, HediffSetCaching.HediffCacheValue>(
|
||||||
ref VisibleHediffCache.GetOrAddReference(Unsafe.As<HediffCache, VisibleHediffCache>(ref key)))
|
ref VisibleHediffCache.GetOrAddReference(Unsafe.As<HediffCache, VisibleHediffCache>(ref key)))
|
||||||
@@ -100,8 +99,8 @@ public sealed class HediffSetPrecaching : ClassWithFishPrepatches
|
|||||||
= "Caches results of the HediffSet.HasHediff method. Impact scales with hediff count.";
|
= "Caches results of the HediffSet.HasHediff method. Impact scales with hediff count.";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= SymbolExtensions.GetMethodInfo(static ()
|
= AccessTools.DeclaredMethod(typeof(HediffSet), nameof(HediffSet.HasHediff),
|
||||||
=> default(HediffSet)!.HasHediff(null, default));
|
[typeof(HediffDef), typeof(bool)]);
|
||||||
|
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(Replacement);
|
=> ilProcessor.ReplaceBodyWith(Replacement);
|
||||||
@@ -117,7 +116,7 @@ public sealed class HediffSetPrecaching : ClassWithFishPrepatches
|
|||||||
= "Caches results of the HediffSet.GetNotMissingParts method. Impact scales with hediff count.";
|
= "Caches results of the HediffSet.GetNotMissingParts method. Impact scales with hediff count.";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= AccessTools.Method(typeof(HediffSet), nameof(HediffSet.GetNotMissingParts));
|
= AccessTools.DeclaredMethod(typeof(HediffSet), nameof(HediffSet.GetNotMissingParts));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool Prefix(HediffSet __instance, BodyPartHeight height, BodyPartDepth depth, BodyPartTagDef? tag,
|
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 sealed class DirtyCache : FishPatch
|
||||||
{
|
{
|
||||||
|
public override List<Type> LinkedPatches { get; }
|
||||||
|
= [typeof(Pawn_PsychicEntropyTrackerOptimization.Psylink_Patch)];
|
||||||
|
|
||||||
public override string Description { get; }
|
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; }
|
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)
|
public static void Postfix(HediffSet __instance)
|
||||||
{
|
{
|
||||||
@@ -175,9 +177,10 @@ public sealed class HediffSetCaching : ClassWithFishPatches
|
|||||||
|
|
||||||
public record struct NotMissingPartsCacheValue()
|
public record struct NotMissingPartsCacheValue()
|
||||||
{
|
{
|
||||||
private int _version = -1;
|
private int
|
||||||
|
_version = -1,
|
||||||
|
_nextRefreshTick;
|
||||||
private List<Hediff> _hediffsInSet = [];
|
private List<Hediff> _hediffsInSet = [];
|
||||||
private int _nextRefreshTick;
|
|
||||||
public List<BodyPartRecord> Parts = [];
|
public List<BodyPartRecord> Parts = [];
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -204,9 +207,10 @@ public sealed class HediffSetCaching : ClassWithFishPatches
|
|||||||
|
|
||||||
public record struct HediffCacheValue()
|
public record struct HediffCacheValue()
|
||||||
{
|
{
|
||||||
private int _version = -1;
|
private int
|
||||||
|
_version = -1,
|
||||||
|
_nextRefreshTick;
|
||||||
private List<Hediff> _hediffsInSet = [];
|
private List<Hediff> _hediffsInSet = [];
|
||||||
private int _nextRefreshTick;
|
|
||||||
public Hediff? Hediff;
|
public Hediff? Hediff;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ public sealed class Pawn_PsychicEntropyTrackerOptimization : ClassWithFishPatche
|
|||||||
{
|
{
|
||||||
public sealed class Psylink_Patch : FirstPriorityFishPatch
|
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; }
|
public override MethodBase TargetMethodInfo { get; }
|
||||||
= AccessTools.PropertyGetter(typeof(Pawn_PsychicEntropyTracker),
|
= AccessTools.PropertyGetter(typeof(Pawn_PsychicEntropyTracker),
|
||||||
@@ -22,7 +26,7 @@ public sealed class Pawn_PsychicEntropyTrackerOptimization : ClassWithFishPatche
|
|||||||
if (__instance.psylinkCachedForTick <= TickHelper.TicksGame)
|
if (__instance.psylinkCachedForTick <= TickHelper.TicksGame)
|
||||||
{
|
{
|
||||||
__instance.psylinkCached = __instance.pawn.GetMainPsylinkSource();
|
__instance.psylinkCached = __instance.pawn.GetMainPsylinkSource();
|
||||||
__instance.psylinkCachedForTick = TickHelper.TicksGame + 32;
|
__instance.psylinkCachedForTick = TickHelper.TicksGame + 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
return __instance.psylinkCached;
|
return __instance.psylinkCached;
|
||||||
|
|||||||
@@ -26,14 +26,23 @@ public sealed class IdeoBuildingPresenceDemandOptimization : ClassWithFishPrepat
|
|||||||
|
|
||||||
public static bool ReplacementBody(IdeoBuildingPresenceDemand __instance, Map map)
|
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)
|
if (buildings is IndexedFishSet<Building> fishSet)
|
||||||
{
|
{
|
||||||
var list = fishSet.ReadOnlyList;
|
var list = fishSet.ReadOnlyList;
|
||||||
for (var i = list.Count; i-- > 0;)
|
for (var i = list.Count; i-- > 0;)
|
||||||
{
|
{
|
||||||
if (list[i].StyleSourcePrecept == __instance.parent)
|
if (list[i].StyleSourcePrecept == parentBuilding)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,17 +52,21 @@ public sealed class IdeoBuildingPresenceDemandOptimization : ClassWithFishPrepat
|
|||||||
{
|
{
|
||||||
return FallbackCheck(__instance, buildings);
|
return FallbackCheck(__instance, buildings);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if V1_4
|
||||||
public static bool FallbackCheck(IdeoBuildingPresenceDemand __instance, IEnumerable<Building> buildings)
|
public static bool FallbackCheck(IdeoBuildingPresenceDemand __instance, IEnumerable<Building> buildings)
|
||||||
{
|
{
|
||||||
|
var parentBuilding = __instance.parent;
|
||||||
foreach (var building in buildings)
|
foreach (var building in buildings)
|
||||||
{
|
{
|
||||||
if (building.StyleSourcePrecept == __instance.parent)
|
if (building.StyleSourcePrecept == parentBuilding)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#if V1_4
|
||||||
using PerformanceFish.ModCompatibility;
|
using PerformanceFish.ModCompatibility;
|
||||||
|
|
||||||
namespace PerformanceFish.JobSystem;
|
namespace PerformanceFish.JobSystem;
|
||||||
@@ -28,3 +29,4 @@ public sealed class DisabledWorkTypesOptimization : ClassWithFishPatches
|
|||||||
: __instance.cachedDisabledWorkTypes) is null;
|
: __instance.cachedDisabledWorkTypes) is null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
@@ -3,227 +3,27 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
#if disabledForNow
|
using PerformanceFish.Prepatching;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Verse.AI;
|
|
||||||
|
|
||||||
namespace PerformanceFish.JobSystem;
|
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 string Description { get; }
|
||||||
public override bool DefaultState => false;
|
= "Job scanning optimization making repairing, wardening and tending use a cache of targets where "
|
||||||
public static bool ThreadingEnabled { get; set; }
|
+ "appropriate";
|
||||||
|
|
||||||
public override Expression<Action> TargetMethod { get; }
|
public override MethodBase TargetMethodBase => methodof(GenClosest.ClosestThingReachable);
|
||||||
= static () => GenClosest.ClosestThing_Global(default, null, default, null, null);
|
|
||||||
|
|
||||||
public static CodeInstructions Transpiler(CodeInstructions CodeInstructions)
|
public static void Prefix(Map map, ref ThingRequest thingReq, IEnumerable<Thing>? customGlobalSearchSet)
|
||||||
=> Reflection.MakeReplacementCall(ClosestThing_Global_Replacement);
|
|
||||||
|
|
||||||
private struct Variables
|
|
||||||
{
|
{
|
||||||
public IntVec3 center;
|
if (!thingReq.CanBeFoundInRegion || customGlobalSearchSet is not ICollection<Thing> collection)
|
||||||
public float maxDistanceSquared;
|
|
||||||
public Func<Thing, float>? priorityGetter;
|
|
||||||
public float closestDistSquared;
|
|
||||||
public Predicate<Thing>? validator;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return;
|
||||||
|
|
||||||
float num = (variables.center - t.Position).LengthHorizontalSquared;
|
if (map.listerThings.ThingsMatching(thingReq).Count > collection.Count * 5)
|
||||||
if (!(num > variables.maxDistanceSquared)
|
thingReq = ThingRequest.ForUndefined();
|
||||||
&& (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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
//}
|
|
||||||
@@ -3,13 +3,8 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
/*using System.Collections.Concurrent;
|
/*using System.Linq;
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using static System.Reflection.Emit.OpCodes;
|
using static System.Reflection.Emit.OpCodes;
|
||||||
using static PerformanceFish.TranspilerHelpers;
|
|
||||||
|
|
||||||
namespace PerformanceFish.JobSystem;
|
namespace PerformanceFish.JobSystem;
|
||||||
|
|
||||||
@@ -17,9 +12,17 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
{
|
{
|
||||||
public sealed class TryIssueJobPackage_InnerDelegate_Patch : FishPatch
|
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 string Description { get; }
|
||||||
public override MethodBase TargetMethodInfo => AccessTools.FindIncludingInnerTypes(typeof(JobGiver_Work),
|
= "Work scanning optimization. Splits the IsForbidden and HasJobOnThing checks into separate loops, then "
|
||||||
t => AccessTools.Field(t, "scanner") != null ? AccessTools.FirstMethod(t, m => m.Name.Contains("TryIssueJobPackage") && m.ReturnType == typeof(bool)) : null);
|
+ "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)
|
public static CodeInstructions Transpiler(CodeInstructions CodeInstructions)
|
||||||
{
|
{
|
||||||
var shouldRemove = true;
|
var shouldRemove = true;
|
||||||
@@ -46,29 +49,40 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
|
|
||||||
public sealed class TryIssueJobPackage_Patch : FishPatch
|
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 string Description { get; }
|
||||||
public override MethodBase TargetMethodInfo => AccessTools.Method(typeof(JobGiver_Work), nameof(JobGiver_Work.TryIssueJobPackage));
|
= "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)
|
public static void Prefix(Pawn pawn)
|
||||||
{
|
{
|
||||||
PrefilteredPotentialWorkThingsGlobal.Clear();
|
PrefilteredPotentialWorkThingsGlobal.Clear();
|
||||||
FishCache.ClearAll();
|
FishCache.ClearAll();
|
||||||
CurrentPawn = pawn;
|
CurrentPawn = pawn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Postfix()
|
public static void Postfix()
|
||||||
{
|
{
|
||||||
PrefilteredPotentialWorkThingsGlobal.Clear();
|
PrefilteredPotentialWorkThingsGlobal.Clear();
|
||||||
FishCache.ClearAll();
|
FishCache.ClearAll();
|
||||||
CurrentPawn = null;
|
CurrentPawn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CodeInstructions Transpiler(CodeInstructions CodeInstructions, MethodBase original)
|
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 scanner_pawn_PotentialWorkThingsGlobal = FishTranspiler.Call(typeof(WorkGiver_Scanner),
|
||||||
var potentialWorkThingsGlobal_pawn_TryGetPrefilteredPotentialWorkThingsAnyType = GetCall(() => TryGetPrefilteredPotentialWorkThingsAnyType(null, null));
|
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_PotentialWorkThingRequest = FishTranspiler.PropertyGetter(typeof(WorkGiver_Scanner),
|
||||||
var scanner_pawn_GetCachedThingRequest = GetCall(() => GetCachedThingRequest(null, null));
|
nameof(WorkGiver_Scanner.PotentialWorkThingRequest));
|
||||||
|
var scanner_pawn_GetCachedThingRequest
|
||||||
|
= FishTranspiler.Call(static () => GetCachedThingRequest(null!, null!));
|
||||||
|
|
||||||
foreach (var code in CodeInstructions)
|
foreach (var code in CodeInstructions)
|
||||||
{
|
{
|
||||||
@@ -97,31 +111,35 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
return originalRequest;
|
return originalRequest;
|
||||||
|
|
||||||
var listerThings = pawn.Map.listerThings.ThingsMatching(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 processedThings = TryGetPrefilteredPotentialWorkThingsAnyType(
|
||||||
var fishCache = originalRequest.singleDef != null ? FishCache.ForDef(originalRequest.singleDef) : FishCache.ForGroup(originalRequest.group);
|
collection is null || listerThings.Count < collection.Count * 4 ? listerThings : collection, pawn);
|
||||||
fishCache.FilteredThings = processedThings as List<Thing> ?? new(processedThings);
|
var fishCache = originalRequest.singleDef != null
|
||||||
return new ThingRequest() { group = originalRequest.group, singleDef = fishCache };
|
? 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
|
=> originalWorkThings switch
|
||||||
{
|
{
|
||||||
List<Thing> => Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(originalWorkThings, pawn),
|
List<Thing> => Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(originalWorkThings, pawn),
|
||||||
List<Pawn> => Generic<Pawn>.TryGetPrefilteredPotentialWorkThingsForList(originalWorkThings, pawn),
|
List<Pawn> => Generic<Pawn>.TryGetPrefilteredPotentialWorkThingsForList(originalWorkThings, pawn),
|
||||||
_ => GetPrefilteredPotentialWorkThingsUncached(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;
|
yield break;
|
||||||
|
|
||||||
foreach (var thing in things)
|
foreach (var thing in originalWorkThings)
|
||||||
{
|
{
|
||||||
if (!thing.IsForbidden(pawn))
|
if (!thing.IsForbidden(pawn))
|
||||||
yield return thing;
|
yield return thing;
|
||||||
@@ -130,7 +148,8 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
|
|
||||||
public static class Generic<T> where T : Thing
|
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())
|
if (!originalWorkThings.Any())
|
||||||
return originalWorkThings;
|
return originalWorkThings;
|
||||||
@@ -140,19 +159,20 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
|
|
||||||
return cache;
|
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>;
|
var inputList = (List<T>)originalWorkThings;
|
||||||
if (inputList.Count < 512)
|
|
||||||
Sequential_CheckForbiddenAndAddToList(inputList, pawn);
|
Sequential_CheckForbiddenAndAddToList(inputList, pawn);
|
||||||
else
|
|
||||||
Threaded_CheckForbiddenAndAddToList(inputList, pawn);
|
|
||||||
|
|
||||||
var cache = _tempThingList.ToArray();
|
var cache = _tempThingList.ToArray();
|
||||||
if (cache.Length < 1024)
|
if (cache.Length < 1024)
|
||||||
{
|
{
|
||||||
var pawnPosition = pawn.Position;
|
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;
|
PrefilteredPotentialWorkThingsGlobal[originalWorkThings] = cache;
|
||||||
@@ -160,6 +180,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
|
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Sequential_CheckForbiddenAndAddToList(List<T> inputList, Pawn pawn)
|
private static void Sequential_CheckForbiddenAndAddToList(List<T> inputList, Pawn pawn)
|
||||||
{
|
{
|
||||||
_tempThingList.Clear();
|
_tempThingList.Clear();
|
||||||
@@ -171,82 +192,20 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
tempThingList.Add(inputList[i]);
|
tempThingList.Add(inputList[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static void Threaded_CheckForbiddenAndAddToList(List<T> inputList, Pawn pawn)
|
private static readonly List<T> _tempThingList = [];
|
||||||
{
|
|
||||||
_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();
|
public static Dictionary<IEnumerable<Thing>, IEnumerable<Thing>> PrefilteredPotentialWorkThingsGlobal { get; }
|
||||||
private static List<T> _tempThingList { get; } = new();
|
= [];
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<IEnumerable<Thing>, IEnumerable<Thing>> PrefilteredPotentialWorkThingsGlobal { get; } = new();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ThingRequest_IsUndefined_Patch : FishPatch
|
public sealed class ThingRequest_IsUndefined_Patch : FishPatch
|
||||||
{
|
{
|
||||||
public override string Description => "Part of the work scanning optimization";
|
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)]
|
[HarmonyPriority(Priority.Last)]
|
||||||
public static void Postfix(ThingRequest __instance, ref bool __result)
|
public static void Postfix(ThingRequest __instance, ref bool __result)
|
||||||
{
|
{
|
||||||
@@ -255,6 +214,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
|
|
||||||
__result = NewResult(__instance);
|
__result = NewResult(__instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool NewResult(ThingRequest __instance)
|
public static bool NewResult(ThingRequest __instance)
|
||||||
=> __instance.group == ThingRequestGroup.Undefined && !((FishCache)__instance.singleDef).IsSingleDef;
|
=> __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 sealed class ThingRequest_CanBeFoundInRegion_Patch : FirstPriorityFishPatch
|
||||||
{
|
{
|
||||||
public override string Description => "Part of the work scanning optimization";
|
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)
|
public static bool Prefix(ThingRequest __instance, ref bool __result)
|
||||||
{
|
{
|
||||||
if (__instance.singleDef is not FishCache)
|
if (__instance.singleDef is not FishCache)
|
||||||
@@ -271,15 +234,20 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
__result = NewResult(__instance);
|
__result = NewResult(__instance);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool NewResult(ThingRequest __instance)
|
public static bool NewResult(ThingRequest __instance)
|
||||||
=> ((FishCache)__instance.singleDef).IsSingleDef
|
=> ((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 sealed class ListerThings_ThingsMatching_Patch : FirstPriorityFishPatch
|
||||||
{
|
{
|
||||||
public override string Description => "Part of the work scanning optimization";
|
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)
|
public static bool Prefix(ListerThings __instance, ThingRequest req, ref List<Thing> __result)
|
||||||
{
|
{
|
||||||
if (req.singleDef is not FishCache)
|
if (req.singleDef is not FishCache)
|
||||||
@@ -288,6 +256,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
__result = GetCache(__instance, req);
|
__result = GetCache(__instance, req);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Thing> GetCache(ListerThings __instance, ThingRequest req)
|
public static List<Thing> GetCache(ListerThings __instance, ThingRequest req)
|
||||||
{
|
{
|
||||||
var cachedThings = ((FishCache)req.singleDef).FilteredThings ?? ListerThings.EmptyList;
|
var cachedThings = ((FishCache)req.singleDef).FilteredThings ?? ListerThings.EmptyList;
|
||||||
@@ -301,18 +270,26 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
return cachedThings;
|
return cachedThings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Thing> GetForRegion(ListerThings __instance, ThingRequest req)
|
public static List<Thing> GetForRegion(ListerThings __instance, ThingRequest req)
|
||||||
=> new(TryIssueJobPackage_Patch.Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(
|
=>
|
||||||
(((FishCache)req.singleDef) is var cache && cache.IsSingleDef
|
[
|
||||||
|
..TryIssueJobPackage_Patch.Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(
|
||||||
|
((FishCache)req.singleDef is { IsSingleDef: true } cache
|
||||||
? __instance.listsByDef.TryGetValue(cache.Def, out var value) ? value : null
|
? __instance.listsByDef.TryGetValue(cache.Def, out var value) ? value : null
|
||||||
: __instance.listsByGroup.TryGetItem((int)req.group, out var item) ? item : null) ?? ListerThings.EmptyList, CurrentPawn));
|
: __instance.listsByGroup.TryGetItem((int)req.group, out var item)
|
||||||
|
? item
|
||||||
|
: null)
|
||||||
|
?? ListerThings.EmptyList, CurrentPawn)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FishCache : ThingDef
|
public class FishCache : ThingDef
|
||||||
{
|
{
|
||||||
public bool IsSingleDef => Def != null;
|
public bool IsSingleDef => Def != null;
|
||||||
public ThingDef Def { get; private set; }
|
public ThingDef? Def { get; private init; }
|
||||||
public List<Thing> FilteredThings { get; set; }
|
public List<Thing>? FilteredThings { get; set; }
|
||||||
|
|
||||||
public static FishCache ForDef(ThingDef def)
|
public static FishCache ForDef(ThingDef def)
|
||||||
{
|
{
|
||||||
if (_dictOfDefs.TryGetValue(def, out var value))
|
if (_dictOfDefs.TryGetValue(def, out var value))
|
||||||
@@ -321,6 +298,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
_dictOfDefs[def] = value = new(def.defName) { Def = def };
|
_dictOfDefs[def] = value = new(def.defName) { Def = def };
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FishCache ForGroup(ThingRequestGroup group)
|
public static FishCache ForGroup(ThingRequestGroup group)
|
||||||
{
|
{
|
||||||
if (_dictOfGroups.TryGetValue(group, out var value))
|
if (_dictOfGroups.TryGetValue(group, out var value))
|
||||||
@@ -329,15 +307,17 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
|
|||||||
_dictOfGroups[group] = value = new(group.ToString());
|
_dictOfGroups[group] = value = new(group.ToString());
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ClearAll()
|
public static void ClearAll()
|
||||||
{
|
{
|
||||||
_dictOfGroups.Clear();
|
_dictOfGroups.Clear();
|
||||||
_dictOfDefs.Clear();
|
_dictOfDefs.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private FishCache(string name) => defName = name;
|
private FishCache(string name) => defName = name;
|
||||||
private static Dictionary<ThingRequestGroup, FishCache> _dictOfGroups { get; } = new();
|
private static readonly Dictionary<ThingRequestGroup, FishCache> _dictOfGroups = new();
|
||||||
private static Dictionary<ThingDef, FishCache> _dictOfDefs { get; } = 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)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
=> 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)
|
public static bool ReplacementBody(object __instance, Region r)
|
||||||
{
|
{
|
||||||
var instance = _delegateInstance.Get(__instance);
|
var instance = _delegateInstance.Get(__instance);
|
||||||
@@ -86,8 +81,11 @@ public sealed class WorkGiver_DoBillPrepatches : ClassWithFishPrepatches
|
|||||||
var bill = thingValidator.Bill;
|
var bill = thingValidator.Bill;
|
||||||
|
|
||||||
if (HasNoFilters(bill) // apparently applies to a rim reborn benches?
|
if (HasNoFilters(bill) // apparently applies to a rim reborn benches?
|
||||||
|
|| bill.billStack?.billGiver
|
||||||
#if V1_4
|
#if V1_4
|
||||||
|| bill.billStack?.billGiver is Building_MechGestator
|
is Building_MechGestator
|
||||||
|
#else
|
||||||
|
is Building_WorkTableAutonomous
|
||||||
#endif
|
#endif
|
||||||
|| (BlackListedModExtensions.Count > 0
|
|| (BlackListedModExtensions.Count > 0
|
||||||
&& IsBlackListed(bill))) // <-- Fuck these too
|
&& IsBlackListed(bill))) // <-- Fuck these too
|
||||||
@@ -532,9 +530,12 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
|
|||||||
public static void Prefix(ref bool alreadySorted, Bill? bill)
|
public static void Prefix(ref bool alreadySorted, Bill? bill)
|
||||||
=> alreadySorted = alreadySorted
|
=> alreadySorted = alreadySorted
|
||||||
|| (bill?.billStack is { } stack
|
|| (bill?.billStack is { } stack
|
||||||
#if V1_4
|
|
||||||
// ReSharper disable once MergeIntoPattern
|
// ReSharper disable once MergeIntoPattern
|
||||||
&& stack.billGiver is not Building_MechGestator
|
&& stack.billGiver is not
|
||||||
|
#if V1_4
|
||||||
|
Building_MechGestator
|
||||||
|
#else
|
||||||
|
Building_WorkTableAutonomous
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -575,8 +576,11 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
|
|||||||
public static void MarkIngredientCountAsFound(IngredientCount ingredientCount, Bill? bill)
|
public static void MarkIngredientCountAsFound(IngredientCount ingredientCount, Bill? bill)
|
||||||
{
|
{
|
||||||
if (bill?.billStack is not { } billStack
|
if (bill?.billStack is not { } billStack
|
||||||
|
|| billStack.billGiver is
|
||||||
#if V1_4
|
#if V1_4
|
||||||
|| billStack.billGiver is Building_MechGestator
|
Building_MechGestator
|
||||||
|
#else
|
||||||
|
Building_WorkTableAutonomous
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,23 @@ public sealed class Buildings : ClassWithFishPrepatches
|
|||||||
=> (IEnumerable<T>)__instance.Cache().ColonistBuildingsByType.GetOrAdd(typeof(T));
|
=> (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 sealed class ColonistsHaveResearchBenchPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override string Description { get; }
|
public override string Description { get; }
|
||||||
@@ -60,6 +77,72 @@ public sealed class Buildings : ClassWithFishPrepatches
|
|||||||
=> __instance.Cache().ColonistBuildingsByDef.GetOrAdd(def).Count > 0;
|
=> __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 sealed class AllBuildingsColonistOfDef_Patch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override string Description { get; }
|
public override string Description { get; }
|
||||||
@@ -73,8 +156,18 @@ public sealed class Buildings : ClassWithFishPrepatches
|
|||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||||
|
|
||||||
|
#if !V1_5
|
||||||
public static IEnumerable<Building> ReplacementBody(ListerBuildings __instance, ThingDef def)
|
public static IEnumerable<Building> ReplacementBody(ListerBuildings __instance, ThingDef def)
|
||||||
=> __instance.Cache().ColonistBuildingsByDef.GetOrAdd(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
|
public sealed class AllBuildingsNonColonistOfDef_Patch : FishPrepatch
|
||||||
@@ -228,8 +321,8 @@ public sealed class Buildings : ClassWithFishPrepatches
|
|||||||
public readonly record struct Cache()
|
public readonly record struct Cache()
|
||||||
{
|
{
|
||||||
public readonly FishTable<ThingDef, IndexedFishSet<Building>>
|
public readonly FishTable<ThingDef, IndexedFishSet<Building>>
|
||||||
ColonistBuildingsByDef = new(),
|
ColonistBuildingsByDef = [],
|
||||||
NonColonistBuildingsByDef = new();
|
NonColonistBuildingsByDef = [];
|
||||||
|
|
||||||
public readonly IndexedFishSet<Building_ResearchBench> ColonistResearchBenches = [];
|
public readonly IndexedFishSet<Building_ResearchBench> ColonistResearchBenches = [];
|
||||||
|
|
||||||
|
|||||||
@@ -97,13 +97,18 @@ public sealed class Haulables : ClassWithFishPrepatches
|
|||||||
public sealed class TickPatch : FishPrepatch
|
public sealed class TickPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override string? Description { get; }
|
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 "
|
= "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 "
|
+ "than 4 cells. Not ticking multiple times leads to a performance improvement. Also handles things queued "
|
||||||
+ "for removal by the StoreUtilityPrepatches:TryFindBestBetterStoreCellFor patch";
|
+ "for removal by the StoreUtilityPrepatches:TryFindBestBetterStoreCellFor patch";
|
||||||
|
#else
|
||||||
|
= "Handles things queued for removal by the StoreUtilityPrepatches:TryFindBestBetterStoreCellFor patch";
|
||||||
|
#endif
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= AccessTools.DeclaredMethod(typeof(ListerHaulables), nameof(ListerHaulables.ListerHaulablesTick));
|
= AccessTools.DeclaredMethod(typeof(ListerHaulables), nameof(ListerHaulables.ListerHaulablesTick));
|
||||||
|
|
||||||
|
#if V1_4
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
{
|
{
|
||||||
var instructions = ilProcessor.instructions;
|
var instructions = ilProcessor.instructions;
|
||||||
@@ -120,6 +125,7 @@ public sealed class Haulables : ClassWithFishPrepatches
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int GetCellCountToTick(int original, SlotGroup slotGroup)
|
public static int GetCellCountToTick(int original, SlotGroup slotGroup)
|
||||||
=> Math.Min(original, slotGroup.CellsList.Count);
|
=> Math.Min(original, slotGroup.CellsList.Count);
|
||||||
|
#endif
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Postfix(ListerHaulables __instance)
|
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 record struct Cache()
|
||||||
{
|
{
|
||||||
public readonly FishSet<int> ContainedThings = [];
|
public readonly FishSet<int> ContainedThings = [];
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using PerformanceFish.Prepatching;
|
using PerformanceFish.Prepatching;
|
||||||
@@ -27,6 +28,8 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
|||||||
if (!ListerThings.EverListable(t.def, __instance.use))
|
if (!ListerThings.EverListable(t.def, __instance.use))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
lock (__instance.LockObject())
|
||||||
|
{
|
||||||
AddToDefList(__instance, t);
|
AddToDefList(__instance, t);
|
||||||
|
|
||||||
var allGroups = ThingListGroupHelper.AllGroups;
|
var allGroups = ThingListGroupHelper.AllGroups;
|
||||||
@@ -40,6 +43,13 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
|||||||
|
|
||||||
AddToGroupList(__instance, t, thingRequestGroup);
|
AddToGroupList(__instance, t, thingRequestGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddToTypeList(__instance, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !V1_4
|
||||||
|
__instance.thingListChangedCallbacks?.onThingAdded?.Invoke(t);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +71,8 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
|||||||
if (!ListerThings.EverListable(t.def, __instance.use))
|
if (!ListerThings.EverListable(t.def, __instance.use))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
lock (__instance.LockObject())
|
||||||
|
{
|
||||||
RemoveFromDefList(__instance, t);
|
RemoveFromDefList(__instance, t);
|
||||||
|
|
||||||
var allGroups = ThingListGroupHelper.AllGroups;
|
var allGroups = ThingListGroupHelper.AllGroups;
|
||||||
@@ -75,43 +87,70 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
|||||||
|
|
||||||
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)
|
public static void AddToDefList(ListerThings lister, Thing thing)
|
||||||
{
|
{
|
||||||
if (!lister.listsByDef.TryGetValue(thing.def, out var value))
|
if (!lister.listsByDef.TryGetValue(thing.def, out var value))
|
||||||
{
|
lister.listsByDef.Add(thing.def, value = []);
|
||||||
value = [];
|
|
||||||
lister.listsByDef.Add(thing.def, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
value.Add(thing);
|
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)
|
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)
|
if (list == null)
|
||||||
{
|
{
|
||||||
list = [];
|
list = [];
|
||||||
lister.listsByGroup[(uint)thingRequestGroup] = list;
|
hash = 0;
|
||||||
lister.stateHashByGroup[(uint)thingRequestGroup] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list.Add(thing);
|
list.Add(thing);
|
||||||
lister.stateHashByGroup[(uint)thingRequestGroup]++;
|
hash++;
|
||||||
|
|
||||||
lister.IndexMapByGroup()[new(thingRequestGroup, thing.GetKey())]
|
lister.IndexMapByGroup()[new(thingRequestGroup, thing.GetKey())] = list.Count - 1;
|
||||||
= lister.listsByGroup[(uint)thingRequestGroup].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)
|
public static void RemoveFromDefList(ListerThings lister, Thing thing)
|
||||||
{
|
{
|
||||||
var thingKey = thing.GetKey();
|
var thingKey = thing.GetKey();
|
||||||
var indexMapByDef = lister.IndexMapByDef();
|
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)
|
var indexByDef = indexMapByDef.TryGetValue(thingKey, out var knownIndexByDef)
|
||||||
&& knownIndexByDef < listByDef.Count
|
&& knownIndexByDef < listByDef.Count
|
||||||
@@ -127,10 +166,19 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
|||||||
indexMapByDef[listByDef[indexByDef].GetKey()] = indexByDef;
|
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)
|
public static void RemoveFromGroupList(ListerThings lister, Thing thing, ThingRequestGroup thingRequestGroup)
|
||||||
{
|
{
|
||||||
var indexMapByGroup = lister.IndexMapByGroup();
|
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());
|
var groupMapKey = new GroupThingPair(thingRequestGroup, thing.GetKey());
|
||||||
|
|
||||||
@@ -151,6 +199,24 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
|||||||
lister.stateHashByGroup[(uint)thingRequestGroup]++;
|
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 sealed class ContainsPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
@@ -163,24 +229,99 @@ public sealed class ThingsPrepatches : ClassWithFishPrepatches
|
|||||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||||
|
|
||||||
public static bool ReplacementBody(ListerThings __instance, Thing t)
|
public static bool ReplacementBody(ListerThings __instance, Thing t)
|
||||||
=> __instance.IndexMapByDef().ContainsKey(t.GetKey())
|
{
|
||||||
|
lock (__instance.LockObject())
|
||||||
|
{
|
||||||
|
return __instance.IndexMapByDef().ContainsKey(t.GetKey())
|
||||||
|| (__instance.listsByDef.TryGetValue(t.def)?.Contains(t) ?? false);
|
|| (__instance.listsByDef.TryGetValue(t.def)?.Contains(t) ?? false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class ClearPatch : FishPrepatch
|
public sealed class ClearPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
|
public override List<Type> LinkedPatches { get; }
|
||||||
|
= [typeof(AddPatch), typeof(RemovePatch), typeof(ContainsPatch)];
|
||||||
|
|
||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
= "Required to keep the ListerThings cache synced";
|
= "Required to keep the ListerThings cache synced";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= AccessTools.DeclaredMethod(typeof(ListerThings), nameof(ListerThings.Clear));
|
= 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)
|
||||||
{
|
{
|
||||||
|
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.IndexMapByDef().Clear();
|
||||||
__instance.IndexMapByGroup().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
|
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 "
|
= "Part of the ThingFilter.BestThingRequest optimization. Required by that particular patch and gets "
|
||||||
+ "toggled alongside it";
|
+ "toggled alongside it";
|
||||||
|
|
||||||
public override Expression<Action> TargetMethod { get; }
|
public override MethodBase? TargetMethodInfo { get; }
|
||||||
= static () => new ListerThings(default).ThingsMatching(default);
|
= AccessTools.DeclaredMethod(typeof(ListerThings), nameof(ListerThings.ThingsMatching));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool Prefix(ListerThings __instance, ThingRequest req, ref List<Thing> __result)
|
public static bool Prefix(ListerThings __instance, ThingRequest req, ref List<Thing> __result)
|
||||||
@@ -223,11 +364,12 @@ public sealed class Things : ClassWithFishPatches
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static List<Thing> GetThingsOfDefsListFor(IEnumerable<ThingDef> thingDefs)
|
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);
|
.GetOrAdd(thingDefs);
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static Dictionary<IEnumerable<ThingDef>, List<Thing>>? _thingsOfDefsDictionary;
|
private static FishTable<IEnumerable<ThingDef>, List<Thing>>? _thingsOfDefsDictionary;
|
||||||
|
|
||||||
#if disabled
|
#if disabled
|
||||||
public sealed class ThingRequest_IsUndefined_Patch : FishPatch
|
public sealed class ThingRequest_IsUndefined_Patch : FishPatch
|
||||||
@@ -412,4 +554,13 @@ public sealed class Things : ClassWithFishPatches
|
|||||||
private static Dictionary<ThingDef, FishCache> _dictOfDefs { get; } = new();
|
private static Dictionary<ThingDef, FishCache> _dictOfDefs { get; } = new();
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
public void Update(float mass, Pawn p)
|
||||||
{
|
{
|
||||||
Mass = mass;
|
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);
|
_nextRefreshTick = TickHelper.Add(GenTicks.TickRareInterval, p.thingIDNumber);
|
||||||
_inventoryListsState = GetInventoryListsState(p);
|
_inventoryListsState = GetInventoryListsState(p);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,15 +18,12 @@ public sealed class MiscPrepatchedOptimizations : ClassWithFishPrepatches
|
|||||||
{
|
{
|
||||||
public override string Description { get; }
|
public override string Description { get; }
|
||||||
= "Optimization for a basic and frequently used list copying method. Replaces a for loop with an "
|
= "Optimization for a basic and frequently used list copying method. Replaces a for loop with an "
|
||||||
+ "optimized IL instruction.";/*{(ActiveMods.GradientHair
|
+ "optimized IL instruction.";
|
||||||
? " Disabled due to incompatibility with Gradient Hair" : "")}";*/
|
|
||||||
|
|
||||||
// public override bool Enabled => base.Enabled && !ActiveMods.GradientHair;
|
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; } = methodof(GenList.ListFullCopy<object>);
|
public override MethodBase TargetMethodBase { get; } = methodof(GenList.ListFullCopy<object>);
|
||||||
|
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(ListFullCopy<object>/*Utility.CollectionExtensions.Copy<object>*/);
|
=> ilProcessor.ReplaceBodyWith(ListFullCopy<object>);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static List<T> ListFullCopy<T>(List<T> source) => source.Copy();
|
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 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)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||||
@@ -60,7 +57,7 @@ public sealed class MiscPrepatchedOptimizations : ClassWithFishPrepatches
|
|||||||
public static bool ReplacementBody(IntVec3 c, Map map)
|
public static bool ReplacementBody(IntVec3 c, Map map)
|
||||||
{
|
{
|
||||||
var size = map.Size;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,3 +136,22 @@ public static class ActiveMods
|
|||||||
=> Log.Warning(
|
=> Log.Warning(
|
||||||
$"{modName} mod is missing. Performance Fish may not work correctly without this dependency.");
|
$"{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
|
||||||
|
}
|
||||||
@@ -21,6 +21,15 @@ public static class OnAssembliesLoaded
|
|||||||
{
|
{
|
||||||
Loaded = true;
|
Loaded = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RuntimeHelpers.RunClassConstructor(typeof(ReflectionCaching).TypeHandle);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
TryPatchGraphicsSetter();
|
TryPatchGraphicsSetter();
|
||||||
TryPatchPerformanceOptimizer();
|
TryPatchPerformanceOptimizer();
|
||||||
TryPatchRIMMSqol();
|
TryPatchRIMMSqol();
|
||||||
|
|||||||
@@ -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
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.1.31911.260
|
VisualStudioVersion = 17.1.31911.260
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
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
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6948F7E6-7ED7-4716-87EF-CFF1F465F561}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6948F7E6-7ED7-4716-87EF-CFF1F465F561}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
..\..\1.4\Defs\GasDefs.xml = ..\..\1.4\Defs\GasDefs.xml
|
..\..\Defs\GasDefs.xml = ..\..\Defs\GasDefs.xml
|
||||||
..\..\1.4\Defs\MainButtonDefs.xml = ..\..\1.4\Defs\MainButtonDefs.xml
|
..\..\Defs\MainButtonDefs.xml = ..\..\Defs\MainButtonDefs.xml
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
v1.3|Any CPU = v1.3|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
v1.4|Any CPU = v1.4|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.v1.3|Any CPU.ActiveCfg = v1.3|Any CPU
|
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.v1.3|Any CPU.Build.0 = v1.3|Any CPU
|
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.v1.4|Any CPU.ActiveCfg = v1.4|Any CPU
|
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E8A3D63E-5850-4866-AB06-DBD44B11F67B}.v1.4|Any CPU.Build.0 = v1.4|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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -48,19 +48,22 @@ public sealed class PerformanceFishMod : Mod
|
|||||||
PACKAGE_ID = PackageIDs.PERFORMANCE_FISH;
|
PACKAGE_ID = PackageIDs.PERFORMANCE_FISH;
|
||||||
|
|
||||||
[Obsolete("I am not incrementing this version number")]
|
[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 Version LoadedVersion => typeof(PerformanceFishMod).Assembly.GetLoadedVersion();
|
||||||
|
|
||||||
public static FishSettings? Settings { get; internal set; }
|
public static FishSettings? Settings { get; internal set; }
|
||||||
public static PerformanceFishMod? Mod { get; private 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 IHasFishPatch[]? AllPatchClasses { get; internal set; }
|
||||||
public static ClassWithFishPrepatches[]? AllPrepatchClasses { get; internal set; }
|
public static ClassWithFishPrepatches[]? AllPrepatchClasses { get; internal set; }
|
||||||
|
|
||||||
private static FishPatch[]? _allFishPatches;
|
private static FishPatch[]? _allFishPatches;
|
||||||
|
|
||||||
private static FishPrepatchBase[]? _allFishPrepatches;
|
private static FishPrepatchBase[]? _allFishPrepatches;
|
||||||
|
private static readonly Lazy<Harmony> _harmony = new(static () => new(PACKAGE_ID), true);
|
||||||
|
|
||||||
public static IEnumerable<FishPatch> AllHarmonyPatches
|
public static IEnumerable<FishPatch> AllHarmonyPatches
|
||||||
=> _allFishPatches ??= AllPatchClasses?.SelectMany(static patchClass => patchClass.Patches.All.Values).ToArray()
|
=> _allFishPatches ??= AllPatchClasses?.SelectMany(static patchClass => patchClass.Patches.All.Values).ToArray()
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ namespace PerformanceFish;
|
|||||||
|
|
||||||
public static class PrepatcherFields
|
public static class PrepatcherFields
|
||||||
{
|
{
|
||||||
|
[PrepatcherField]
|
||||||
|
[ValueInitializer(nameof(CreateObject))]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static extern object LockObject(this ListerThings listerThings);
|
||||||
|
|
||||||
[PrepatcherField]
|
[PrepatcherField]
|
||||||
[ValueInitializer(nameof(CreateMapEvents))]
|
[ValueInitializer(nameof(CreateMapEvents))]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -69,16 +74,24 @@ public static class PrepatcherFields
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static extern ref StorageSettingsPatches.StorageSettingsCache Cache(this StorageSettings storageSettings);
|
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]
|
[PrepatcherField]
|
||||||
[ValueInitializer(nameof(CreateParallelGasGridArray))]
|
[ValueInitializer(nameof(CreateParallelGasGridArray))]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static extern GasGridOptimization.ParallelGasGrid[] ParallelGasGrids(this GasGrid gasGrid);
|
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]
|
[PrepatcherField]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static extern ref Texture2D? ExpandingIconCache(this WorldObject worldObject);
|
public static extern ref Texture2D? ExpandingIconCache(this WorldObject worldObject);
|
||||||
@@ -102,6 +115,11 @@ public static class PrepatcherFields
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static extern ref Listers.Buildings.Cache Cache(this ListerBuildings listerBuildings);
|
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]
|
[PrepatcherField]
|
||||||
[ValueInitializer(nameof(CreateListerHaulablesCache))]
|
[ValueInitializer(nameof(CreateListerHaulablesCache))]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -122,10 +140,6 @@ public static class PrepatcherFields
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static extern ref float[] StatsCache(this BuildableDef buildableDef);
|
public static extern ref float[] StatsCache(this BuildableDef buildableDef);
|
||||||
|
|
||||||
[PrepatcherField]
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static extern ref object MonitorObject(this GasGrid gasGrid);
|
|
||||||
|
|
||||||
// [PrepatcherField]
|
// [PrepatcherField]
|
||||||
// [ValueInitializer(nameof(CreateHediffCompList))]
|
// [ValueInitializer(nameof(CreateHediffCompList))]
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -163,9 +177,14 @@ public static class PrepatcherFields
|
|||||||
public static StorageDistrict[] GetDefaultDistrictArray() => StorageDistrict.GetDefaultArray();
|
public static StorageDistrict[] GetDefaultDistrictArray() => StorageDistrict.GetDefaultArray();
|
||||||
public static Cache.CellGrid<StorageDistrict> CreateStorageDistrictGrid(Map map) => new(map);
|
public static Cache.CellGrid<StorageDistrict> CreateStorageDistrictGrid(Map map) => new(map);
|
||||||
public static Listers.Buildings.Cache CreateListerBuildingsCache() => new();
|
public static Listers.Buildings.Cache CreateListerBuildingsCache() => new();
|
||||||
|
public static Listers.Things.Cache CreateListerThingsCache() => new();
|
||||||
public static Listers.Haulables.Cache CreateListerHaulablesCache() => new();
|
public static Listers.Haulables.Cache CreateListerHaulablesCache() => new();
|
||||||
public static HaulDestinationManagerCache CreateHaulDestinationManagerCache() => new();
|
public static HaulDestinationManagerCache CreateHaulDestinationManagerCache() => new();
|
||||||
public static float[] GetEmptyArray() => Array.Empty<float>();
|
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()
|
public static float[] CreateBaseStatsCache()
|
||||||
{
|
{
|
||||||
@@ -184,14 +203,32 @@ public static class PrepatcherFields
|
|||||||
var map = gasGrid.map;
|
var map = gasGrid.map;
|
||||||
var gasDefs = DefDatabase<GasDef>.AllDefsListForReading;
|
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 {
|
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.");
|
gasDefs.Count} instead. ParallelGasGrid won't work correctly in this state.");
|
||||||
}
|
}
|
||||||
else
|
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.");
|
Log.Warning("GasDefs out of order. Sorting now to fix.");
|
||||||
var allGasDefs = gasDefs.ToList();
|
var allGasDefs = gasDefs.ToList();
|
||||||
@@ -199,9 +236,20 @@ public static class PrepatcherFields
|
|||||||
allGasDefs.Remove(gasDefs[0] = GasDefOf.BlindSmoke);
|
allGasDefs.Remove(gasDefs[0] = GasDefOf.BlindSmoke);
|
||||||
allGasDefs.Remove(gasDefs[1] = GasDefOf.ToxGas);
|
allGasDefs.Remove(gasDefs[1] = GasDefOf.ToxGas);
|
||||||
allGasDefs.Remove(gasDefs[2] = GasDefOf.RotStink);
|
allGasDefs.Remove(gasDefs[2] = GasDefOf.RotStink);
|
||||||
|
#if !V1_4
|
||||||
|
allGasDefs.Remove(gasDefs[3] = GasDefOf.DeadLifeDust);
|
||||||
|
#endif
|
||||||
|
|
||||||
for (var i = 0; i < allGasDefs.Count; i++)
|
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();
|
DefDatabase<GasDef>.SetIndices();
|
||||||
}
|
}
|
||||||
@@ -211,9 +259,10 @@ public static class PrepatcherFields
|
|||||||
for (var i = 0; i < grids.Length; i++)
|
for (var i = 0; i < grids.Length; i++)
|
||||||
grids[i] = new(map, gasDefs[i]);
|
grids[i] = new(map, gasDefs[i]);
|
||||||
|
|
||||||
|
#if V1_4
|
||||||
if (FishSettings.ThreadingEnabled)
|
if (FishSettings.ThreadingEnabled)
|
||||||
{
|
{
|
||||||
var monitorObject = map.GasGridMonitorObject();
|
var monitorObject = gasGrid.MonitorObject() ??= CreateGroupMonitorObject();
|
||||||
|
|
||||||
for (var i = 1; i < grids.Length; i++)
|
for (var i = 1; i < grids.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -221,7 +270,18 @@ public static class PrepatcherFields
|
|||||||
ParallelNoAlloc.RegisterBackgroundWaitingWorker(monitorObject, () => grid.Tick());
|
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;
|
return grids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using PerformanceFish.Prepatching;
|
using PerformanceFish.Prepatching;
|
||||||
|
|
||||||
namespace PerformanceFish;
|
namespace PerformanceFish.Rendering;
|
||||||
|
|
||||||
public sealed class ContentFinderCaching : ClassWithFishPrepatches
|
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);
|
ref var cache = ref Cache.ByReference<string, CacheValue<T>>.GetOrAddReference(itemPath);
|
||||||
|
|
||||||
if (!cache.Cached || !OnStartup.State.Initialized)
|
if (!cache.Cached)
|
||||||
return __state = true;
|
return __state = true;
|
||||||
|
|
||||||
__result = cache.Value;
|
__result = cache.Value;
|
||||||
@@ -42,6 +42,9 @@ public sealed class ContentFinderCaching : ClassWithFishPrepatches
|
|||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private static void UpdateCache<T>(string itemPath, T? __result)
|
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);
|
ref var cache = ref Cache.ByReference<string, CacheValue<T>>.GetExistingReference(itemPath);
|
||||||
|
|
||||||
cache.Cached = true;
|
cache.Cached = true;
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace PerformanceFish;
|
namespace PerformanceFish.Rendering;
|
||||||
|
|
||||||
//DDS importing from // https://github.com/sarbian/DDSLoader/blob/master/DatabaseLoaderTexture_DDS.cs
|
//DDS importing from // https://github.com/sarbian/DDSLoader/blob/master/DatabaseLoaderTexture_DDS.cs
|
||||||
public static class DDSLoader
|
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
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
/*namespace PerformanceFish.Rendering;
|
using PerformanceFish.Prepatching;
|
||||||
public sealed class GraphicPatches : ClassWithFishPatches
|
|
||||||
|
namespace PerformanceFish.Rendering;
|
||||||
|
|
||||||
|
public sealed class GraphicPatches : ClassWithFishPrepatches
|
||||||
{
|
{
|
||||||
public sealed class DrawMeshInt_Patch : FishPatch
|
public sealed class MultiInitFix : FishPrepatch
|
||||||
{
|
{
|
||||||
public override MethodBase TargetMethodInfo { get; }
|
public override string? Description { get; }
|
||||||
= AccessTools.DeclaredMethod(typeof(Graphic), nameof(Graphic.DrawMeshInt));
|
= "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));
|
||||||
Graphics.DrawMesh(mesh, loc, quat, mat, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class Shadow_DrawWorker_Patch : FishPatch
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
{
|
public static void Postfix(Graphic_Multi __instance)
|
||||||
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;
|
return;
|
||||||
|
|
||||||
|
AssignBadMats(__instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
var position = loc + __instance.shadowInfo.offset;
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
position.y = AltitudeLayer.Shadows.AltitudeFor();
|
private static void AssignBadMats(Graphic_Multi instance)
|
||||||
Graphics.DrawMesh(__instance.shadowMesh, position, rot.AsQuat, MatBases.SunShadowFade, 0);
|
{
|
||||||
|
var mats = instance.mats;
|
||||||
|
for (var i = mats.Length; i-- > 0;)
|
||||||
|
{
|
||||||
|
if (mats[i] == null)
|
||||||
|
mats[i] = BaseContent.BadMat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
}
|
||||||
+21
-1
@@ -10,7 +10,7 @@ using FisheryLib.Pools;
|
|||||||
using PerformanceFish.Prepatching;
|
using PerformanceFish.Prepatching;
|
||||||
using RimWorld.IO;
|
using RimWorld.IO;
|
||||||
|
|
||||||
namespace PerformanceFish;
|
namespace PerformanceFish.Rendering;
|
||||||
|
|
||||||
public sealed class TextureLoadingPatches : ClassWithFishPrepatches
|
public sealed class TextureLoadingPatches : ClassWithFishPrepatches
|
||||||
{
|
{
|
||||||
@@ -93,6 +93,7 @@ public sealed class TextureLoadingPatches : ClassWithFishPrepatches
|
|||||||
var data = file.ReadAllBytes();
|
var data = file.ReadAllBytes();
|
||||||
texture2D = new(2, 2, TextureFormat.Alpha8, true);
|
texture2D = new(2, 2, TextureFormat.Alpha8, true);
|
||||||
texture2D.LoadImage(data);
|
texture2D.LoadImage(data);
|
||||||
|
FixMipMapsIfNeeded(texture2D, data, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDds && Prefs.TextureCompression)
|
if (!isDds && Prefs.TextureCompression)
|
||||||
@@ -101,7 +102,10 @@ public sealed class TextureLoadingPatches : ClassWithFishPrepatches
|
|||||||
texture2D.name = Path.GetFileNameWithoutExtension(file.Name);
|
texture2D.name = Path.GetFileNameWithoutExtension(file.Name);
|
||||||
texture2D.filterMode = FilterMode.Trilinear;
|
texture2D.filterMode = FilterMode.Trilinear;
|
||||||
texture2D.anisoLevel = 0;
|
texture2D.anisoLevel = 0;
|
||||||
|
|
||||||
|
if (texture2D.mipmapCount > 1)
|
||||||
texture2D.mipMapBias = -0.7f;
|
texture2D.mipMapBias = -0.7f;
|
||||||
|
|
||||||
texture2D.Apply(!hasStoredMipMaps, true);
|
texture2D.Apply(!hasStoredMipMaps, true);
|
||||||
return texture2D;
|
return texture2D;
|
||||||
}
|
}
|
||||||
@@ -116,9 +120,25 @@ public sealed class TextureLoadingPatches : ClassWithFishPrepatches
|
|||||||
|
|
||||||
var texture2D = new Texture2D(2, 2, TextureFormat.Alpha8, true);
|
var texture2D = new Texture2D(2, 2, TextureFormat.Alpha8, true);
|
||||||
texture2D.LoadImage(data);
|
texture2D.LoadImage(data);
|
||||||
|
FixMipMapsIfNeeded(texture2D, data, file);
|
||||||
return texture2D;
|
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)
|
private static byte[]? ReadBytesForFallbackFormat(VirtualFile file)
|
||||||
{
|
{
|
||||||
byte[]? data = null;
|
byte[]? data = null;
|
||||||
@@ -3,8 +3,12 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using PerformanceFish.Prepatching;
|
||||||
using RoomOwnersCache
|
using RoomOwnersCache
|
||||||
= PerformanceFish.Cache.ByInt<Verse.Room, PerformanceFish.RoomOptimizations.Owners_Patch.CacheValue>;
|
= PerformanceFish.Cache.ByInt<Verse.Room, PerformanceFish.RoomOptimizations.Owners_Patch.CacheValue>;
|
||||||
using RoomRoleCache
|
using RoomRoleCache
|
||||||
@@ -27,7 +31,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
+ "instead of every time anything at all changes or moves";
|
+ "instead of every time anything at all changes or moves";
|
||||||
|
|
||||||
public override MethodBase TargetMethodInfo { get; }
|
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)
|
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)
|
public static bool ShouldUpdate(Room __instance)
|
||||||
{
|
{
|
||||||
if (__instance.role is not null
|
if (__instance.role is not null || !RoomRoleCache.GetOrAddReference(__instance.ID).Dirty)
|
||||||
|| !RoomRoleCache.GetOrAddReference(__instance.ID).Dirty)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
UpdateCache(__instance);
|
UpdateCache(__instance);
|
||||||
return true;
|
return true;
|
||||||
@@ -92,7 +94,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
+ "quite expensive";
|
+ "quite expensive";
|
||||||
|
|
||||||
public override MethodBase TargetMethodInfo { get; }
|
public override MethodBase TargetMethodInfo { get; }
|
||||||
= AccessTools.PropertyGetter(typeof(Room), nameof(Room.Owners));
|
= AccessTools.DeclaredPropertyGetter(typeof(Room), nameof(Room.Owners));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool Prefix(Room __instance, ref IEnumerable<Pawn> __result, out bool __state)
|
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;
|
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 is { TouchesMapEdge: false, IsHuge: false }
|
||||||
&& (!__instance.statsAndRoleDirty || __instance.ContainedBeds.Any());
|
&& (!__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 sealed class ContainedBeds_Patch : FirstPriorityFishPatch
|
||||||
{
|
{
|
||||||
public override string Description { get; }
|
public override string Description { get; }
|
||||||
@@ -188,7 +168,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
+ "against spikes";
|
+ "against spikes";
|
||||||
|
|
||||||
public override MethodBase TargetMethodInfo { get; }
|
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)
|
public static CodeInstructions Transpiler(CodeInstructions codeInstructions, ILGenerator generator)
|
||||||
=> Reflection.GetCodeInstructions(ContainedBeds_Replacement, 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 string Description { get; } = "Caching of room content info. Variable impact";
|
||||||
|
|
||||||
public override MethodBase TargetMethodInfo { get; }
|
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)
|
public static CodeInstructions Transpiler(CodeInstructions codeInstructions)
|
||||||
=> Reflection.MakeReplacementCall(ContainedAndAdjacentThings_Replacement);
|
=> Reflection.MakeReplacementCall(ContainedAndAdjacentThings_Replacement);
|
||||||
@@ -236,19 +216,12 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
if (cache.ListVersions.Count == regions.Count && !cache.Dirty)
|
if (cache.ListVersions.Count == regions.Count && !cache.Dirty)
|
||||||
{
|
{
|
||||||
var cacheIsAccurate = true;
|
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 (cache.ListVersions[i] != (regions[i]?.listerThings.AllThings?._version ?? -1))
|
||||||
if (allThings == null)
|
|
||||||
{
|
|
||||||
if (cache.ListVersions[i] != -1)
|
|
||||||
cacheIsAccurate = false;
|
cacheIsAccurate = false;
|
||||||
}
|
}
|
||||||
else if (cache.ListVersions[i] != allThings._version)
|
|
||||||
{
|
|
||||||
cacheIsAccurate = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cacheIsAccurate)
|
if (cacheIsAccurate)
|
||||||
{
|
{
|
||||||
@@ -260,6 +233,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
return RefreshContainedAndAdjacentThings(instance, regions, ref cache);
|
return RefreshContainedAndAdjacentThings(instance, regions, ref cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private static List<Thing> RefreshContainedAndAdjacentThings(Room instance, List<Region> regions,
|
private static List<Thing> RefreshContainedAndAdjacentThings(Room instance, List<Region> regions,
|
||||||
ref CacheValue cache)
|
ref CacheValue cache)
|
||||||
{
|
{
|
||||||
@@ -270,8 +244,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
|
|
||||||
for (var i = 0; i < regions.Count; i++)
|
for (var i = 0; i < regions.Count; i++)
|
||||||
{
|
{
|
||||||
var allThings = regions[i].ListerThings.AllThings;
|
if (regions[i]?.ListerThings.AllThings is not { } allThings)
|
||||||
if (allThings == null)
|
|
||||||
{
|
{
|
||||||
cache.ListVersions.Add(-1);
|
cache.ListVersions.Add(-1);
|
||||||
continue;
|
continue;
|
||||||
@@ -281,67 +254,66 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
for (var j = 0; j < count; j++)
|
for (var j = 0; j < count; j++)
|
||||||
uniqueContainedThingsSet.Add(allThings[j]);
|
uniqueContainedThingsSet.Add(allThings[j]);
|
||||||
|
|
||||||
cache.ListVersions.Add(allThings.Version());
|
cache.ListVersions.Add(allThings._version);
|
||||||
}
|
}
|
||||||
|
|
||||||
uniqueContainedThings.AddRange(uniqueContainedThingsSet);
|
uniqueContainedThings.AddRange(uniqueContainedThingsSet);
|
||||||
if (cache.Things is { } list)
|
|
||||||
list.ReplaceContentsWith(uniqueContainedThings);
|
|
||||||
else
|
|
||||||
cache.Things = [..uniqueContainedThings];
|
|
||||||
|
|
||||||
|
cache.Things.ReplaceContentsWith(uniqueContainedThings);
|
||||||
cache.SetDirty(false, instance);
|
cache.SetDirty(false, instance);
|
||||||
|
|
||||||
uniqueContainedThingsSet.Clear();
|
uniqueContainedThingsSet.Clear();
|
||||||
return uniqueContainedThings;
|
return uniqueContainedThings;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private static List<Thing> RefreshContainedAndAdjacentThingsAlternative(Room instance, List<Region> regions,
|
/*[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
FishThingsInfo cache)
|
private static List<Thing> RefreshContainedAndAdjacentThingsAlternative(Room instance, List<Region> regions,
|
||||||
|
ref CacheValue cache)
|
||||||
{
|
{
|
||||||
var uniqueContainedThings = instance.uniqueContainedThings;
|
var uniqueContainedThings = instance.uniqueContainedThings;
|
||||||
(cache.listVersions ??= new()).Clear();
|
cache.ListVersions.Clear();
|
||||||
var map = instance.Map;
|
var map = instance.Map;
|
||||||
var regionGrid = map.regionGrid.regionGrid;
|
var regionGrid = map.regionGrid.regionGrid;
|
||||||
var mapSizeX = map.cellIndices.mapSizeX;
|
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 = 0; i < regions.Count; i++)
|
for (var i = regionCount; i-- > 0;)
|
||||||
|
regionIDs[i] = regions[i].id;
|
||||||
|
|
||||||
|
for (var i = 0; i < regionCount; i++)
|
||||||
{
|
{
|
||||||
var region = regions[i];
|
var region = regions[i];
|
||||||
var allThings = region.ListerThings.AllThings;
|
if (region.ListerThings.AllThings is not { } allThings)
|
||||||
if (allThings == null)
|
|
||||||
{
|
{
|
||||||
cache.listVersions.Add(-1);
|
cache.ListVersions.Add(-1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var count = allThings.Count;
|
var count = allThings.Count;
|
||||||
for (var j = 0; j < count; j++)
|
for (var j = 0; j < count; j++)
|
||||||
{
|
{
|
||||||
if (regionGrid[allThings[j].Position.CellToIndex(mapSizeX)] == region)
|
var thing = allThings[j];
|
||||||
uniqueContainedThings.Add(allThings[j]);
|
var thingRegionId = regionGrid[thing.Position.CellToIndex(mapSizeX)].id;
|
||||||
else
|
|
||||||
adjacentThings.Add(allThings[j]);
|
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++)
|
for (var i = adjacentThings.Count; i-- > 0;)
|
||||||
{
|
|
||||||
if (!uniqueContainedThings.Contains(adjacentThings[i]))
|
|
||||||
uniqueContainedThings.Add(adjacentThings[i]);
|
uniqueContainedThings.Add(adjacentThings[i]);
|
||||||
}
|
|
||||||
|
|
||||||
if (cache.things is List<Thing> list)
|
cache.Things.ReplaceContentsWith(uniqueContainedThings);
|
||||||
list.ReplaceContentsWith(uniqueContainedThings);
|
cache.SetDirty(false, instance);
|
||||||
else
|
|
||||||
cache.things = new List<Thing>(uniqueContainedThings);
|
|
||||||
cache.ShouldRefreshNow = false;
|
|
||||||
|
|
||||||
CachedThingLists[instance] = cache;
|
|
||||||
|
|
||||||
return uniqueContainedThings;
|
return uniqueContainedThings;
|
||||||
}*/
|
}*/
|
||||||
@@ -349,8 +321,8 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
public record struct CacheValue()
|
public record struct CacheValue()
|
||||||
{
|
{
|
||||||
private int _nextRefreshTick = -2;
|
private int _nextRefreshTick = -2;
|
||||||
public List<int> ListVersions = [];
|
public readonly List<int> ListVersions = [];
|
||||||
public List<Thing> Things = [];
|
public readonly List<Thing> Things = [];
|
||||||
|
|
||||||
public void SetDirty(bool value, Room room)
|
public void SetDirty(bool value, Room room)
|
||||||
=> _nextRefreshTick = value ? 0 : TickHelper.Add(3072, room.ID, 2048);
|
=> _nextRefreshTick = value ? 0 : TickHelper.Add(3072, room.ID, 2048);
|
||||||
@@ -382,20 +354,50 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
var regionAndRoomUpdater = map.regionAndRoomUpdater;
|
var regionAndRoomUpdater = map.regionAndRoomUpdater;
|
||||||
|
|
||||||
if (regionAndRoomUpdater.Enabled)
|
if (regionAndRoomUpdater.Enabled)
|
||||||
{
|
|
||||||
regionAndRoomUpdater.TryRebuildDirtyRegionsAndRooms();
|
regionAndRoomUpdater.TryRebuildDirtyRegionsAndRooms();
|
||||||
}
|
|
||||||
else if (regionAndRoomUpdater.AnythingToRebuild)
|
else if (regionAndRoomUpdater.AnythingToRebuild)
|
||||||
{
|
LogIncorrectResultWarning(c);
|
||||||
Verse.Log.Warning($"Trying to get valid region at {
|
|
||||||
c.ToString()} but RegionAndRoomUpdater is disabled. The result may be incorrect.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var region = map.regionGrid.regionGrid[(c.z * cellIndices.mapSizeX) + c.x];
|
var region = map.regionGrid.regionGrid[(c.z * cellIndices.mapSizeX) + c.x];
|
||||||
return region is null || !region.valid || (region.type & allowedRegionTypes) == 0
|
return region is null || !region.valid || (region.type & allowedRegionTypes) == 0
|
||||||
? null
|
? null
|
||||||
: region.District?.Room;
|
: 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;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
using PerformanceFish.Defs;
|
||||||
using PerformanceFish.Prepatching;
|
using PerformanceFish.Prepatching;
|
||||||
using StatsCache = PerformanceFish.Cache.ByInt<uint, ushort, PerformanceFish.StatCaching.CachedStatValue>;
|
using StatsCache = PerformanceFish.Cache.ByInt<uint, ushort, PerformanceFish.StatCaching.CachedStatValue>;
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public sealed class EquatableValueTypeEqualityComparer<T> : EqualityComparer<T>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equals method for the comparer itself.
|
/// Equals method for the comparer itself.
|
||||||
/// </summary>
|
/// </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();
|
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ public sealed class EquatableNullableEqualityComparer<T> : EqualityComparer<T?>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equals method for the comparer itself.
|
/// Equals method for the comparer itself.
|
||||||
/// </summary>
|
/// </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();
|
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ public sealed class NullableEqualityComparer<T> : EqualityComparer<T?> where T :
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equals method for the comparer itself.
|
/// Equals method for the comparer itself.
|
||||||
/// </summary>
|
/// </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();
|
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?>
|
public sealed class StringEqualityComparer : EqualityComparer<string?>
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[SuppressMessage("Globalization", "CA1307")]
|
|
||||||
[SuppressMessage("Globalization", "CA1309")]
|
[SuppressMessage("Globalization", "CA1309")]
|
||||||
public override bool Equals(string? x, string? y)
|
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
|
=> 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>
|
/// <summary>
|
||||||
/// Equals method for the comparer itself.
|
/// Equals method for the comparer itself.
|
||||||
/// </summary>
|
/// </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();
|
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||||
}
|
}
|
||||||
@@ -163,7 +162,7 @@ public sealed class IntEqualityComparer : EqualityComparer<int>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equals method for the comparer itself.
|
/// Equals method for the comparer itself.
|
||||||
/// </summary>
|
/// </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();
|
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||||
}
|
}
|
||||||
@@ -180,7 +179,7 @@ public sealed class UshortEqualityComparer : EqualityComparer<ushort>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equals method for the comparer itself.
|
/// Equals method for the comparer itself.
|
||||||
/// </summary>
|
/// </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();
|
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||||
}
|
}
|
||||||
@@ -199,7 +198,7 @@ public sealed class ReferenceEqualityComparer<T> : EqualityComparer<T>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equals method for the comparer itself.
|
/// Equals method for the comparer itself.
|
||||||
/// </summary>
|
/// </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();
|
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||||
}
|
}
|
||||||
@@ -218,7 +217,7 @@ public sealed class ValueTypeEqualityComparer<T> : EqualityComparer<T> where T :
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equals method for the comparer itself.
|
/// Equals method for the comparer itself.
|
||||||
/// </summary>
|
/// </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();
|
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||||
}
|
}
|
||||||
@@ -6,14 +6,13 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using PerformanceFish.Cache;
|
using PerformanceFish.Cache;
|
||||||
using PerformanceFish.ModCompatibility;
|
using PerformanceFish.ModCompatibility;
|
||||||
using ConstructorInfoCache
|
using ConstructorInfoCache
|
||||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags,
|
= 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>;
|
PerformanceFish.System.ReflectionCaching.TypePatches.ConstructorInfoCacheValue>;
|
||||||
using FieldGetters
|
using FieldGetters
|
||||||
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeFieldHandle,
|
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeFieldHandle,
|
||||||
@@ -21,6 +20,9 @@ using FieldGetters
|
|||||||
using FieldInfoCache
|
using FieldInfoCache
|
||||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags, string,
|
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags, string,
|
||||||
PerformanceFish.System.ReflectionCaching.TypePatches.FieldInfoCacheValue>;
|
PerformanceFish.System.ReflectionCaching.TypePatches.FieldInfoCacheValue>;
|
||||||
|
using FieldInfosCache
|
||||||
|
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags,
|
||||||
|
PerformanceFish.System.ReflectionCaching.TypePatches.FieldInfosCacheValue>;
|
||||||
using FieldSetters
|
using FieldSetters
|
||||||
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeFieldHandle,
|
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeFieldHandle,
|
||||||
PerformanceFish.System.ReflectionCaching.FieldInfoPatches.FieldInfoSetterCache>;
|
PerformanceFish.System.ReflectionCaching.FieldInfoPatches.FieldInfoSetterCache>;
|
||||||
@@ -29,11 +31,11 @@ using MethodInfoCache
|
|||||||
PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>;
|
PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>;
|
||||||
using MethodInfoWithTypesAndFlagsCache
|
using MethodInfoWithTypesAndFlagsCache
|
||||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags, string,
|
= 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>;
|
PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>;
|
||||||
using MethodInfoWithTypesCache
|
using MethodInfoWithTypesCache
|
||||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, string,
|
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, string,
|
||||||
PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
PerformanceFish.System.ReflectionCaching.RecordArray<System.Type>,
|
||||||
PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>;
|
PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>;
|
||||||
using MethodInvokers
|
using MethodInvokers
|
||||||
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeMethodHandle,
|
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeMethodHandle,
|
||||||
@@ -41,9 +43,6 @@ using MethodInvokers
|
|||||||
using PropertyInfoCache
|
using PropertyInfoCache
|
||||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags, string,
|
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags, string,
|
||||||
PerformanceFish.System.ReflectionCaching.TypePatches.PropertyInfoCacheValue>;
|
PerformanceFish.System.ReflectionCaching.TypePatches.PropertyInfoCacheValue>;
|
||||||
using CustomAttributeCache
|
|
||||||
= PerformanceFish.Cache.ByReference<System.Reflection.ICustomAttributeProvider, System.RuntimeTypeHandle, bool,
|
|
||||||
PerformanceFish.System.ReflectionCaching.MonoCustomAttrs.CustomAttributeCacheValue>;
|
|
||||||
using TypeFullNameCache
|
using TypeFullNameCache
|
||||||
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeTypeHandle,
|
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeTypeHandle,
|
||||||
PerformanceFish.System.ReflectionCaching.TypePatches.TypeFullNameCacheValue>;
|
PerformanceFish.System.ReflectionCaching.TypePatches.TypeFullNameCacheValue>;
|
||||||
@@ -54,6 +53,9 @@ using ActivatorCache
|
|||||||
// = PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle,
|
// = PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle,
|
||||||
// PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
// PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
||||||
// PerformanceFish.System.ReflectionCaching.ActivatorPatches.ActivatorCacheValue>;
|
// PerformanceFish.System.ReflectionCaching.ActivatorPatches.ActivatorCacheValue>;
|
||||||
|
using CustomAttributeCache
|
||||||
|
= PerformanceFish.Cache.ByReferenceClassic<System.Reflection.ICustomAttributeProvider, System.RuntimeTypeHandle, bool,
|
||||||
|
PerformanceFish.System.ReflectionCaching.MonoCustomAttrs.CustomAttributeCacheValue>;
|
||||||
|
|
||||||
namespace PerformanceFish.System;
|
namespace PerformanceFish.System;
|
||||||
|
|
||||||
@@ -66,12 +68,17 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
ConstructorInfoCache.Initialize();
|
ConstructorInfoCache.Initialize();
|
||||||
FieldGetters.Initialize();
|
FieldGetters.Initialize();
|
||||||
FieldInfoCache.Initialize();
|
FieldInfoCache.Initialize();
|
||||||
|
FieldInfosCache.Initialize();
|
||||||
FieldSetters.Initialize();
|
FieldSetters.Initialize();
|
||||||
MethodInfoCache.Initialize();
|
MethodInfoCache.Initialize();
|
||||||
MethodInfoWithTypesAndFlagsCache.Initialize();
|
MethodInfoWithTypesAndFlagsCache.Initialize();
|
||||||
MethodInfoWithTypesCache.Initialize();
|
MethodInfoWithTypesCache.Initialize();
|
||||||
MethodInvokers.Initialize();
|
MethodInvokers.Initialize();
|
||||||
PropertyInfoCache.Initialize();
|
PropertyInfoCache.Initialize();
|
||||||
|
TypeFullNameCache.Initialize();
|
||||||
|
ActivatorCache.Initialize();
|
||||||
|
// ActivatorWithArgumentsCache.Initialize();
|
||||||
|
CustomAttributeCache.Initialize();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -104,16 +111,17 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
ToLowerIfNeededForBindingFlags(name, bindingAttr));
|
ToLowerIfNeededForBindingFlags(name, bindingAttr));
|
||||||
|
|
||||||
__result = FieldInfoCache.Get.GetOrAdd(ref key).Info;
|
__result = FieldInfoCache.Get.GetOrAdd(ref key).Info;
|
||||||
|
__state.Flags = bindingAttr;
|
||||||
|
|
||||||
if (__result is null)
|
if (__result != null)
|
||||||
{
|
{
|
||||||
__state = new() { State = true, Flags = bindingAttr };
|
__state.State = false;
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
__state = new() { State = false, Flags = bindingAttr };
|
__state.State = true;
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +141,47 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
= __result;
|
= __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
|
public sealed class GetMethodWithFlags_Patch : FishPatch
|
||||||
{
|
{
|
||||||
public override string Description { get; } = "Caches GetMethod lookups";
|
public override string Description { get; } = "Caches GetMethod lookups";
|
||||||
@@ -400,6 +449,11 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
public FieldInfo? Info;
|
public FieldInfo? Info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record struct FieldInfosCacheValue
|
||||||
|
{
|
||||||
|
public FieldInfo[]? Info;
|
||||||
|
}
|
||||||
|
|
||||||
public record struct MethodInfoCacheValue
|
public record struct MethodInfoCacheValue
|
||||||
{
|
{
|
||||||
public MethodInfo? Info;
|
public MethodInfo? Info;
|
||||||
@@ -506,33 +560,35 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
public record struct RecordArray<T>
|
||||||
public record struct ByValueComparableArray<T>
|
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
public byte Length;
|
|
||||||
private int _hashCode;
|
private int _hashCode;
|
||||||
public T[] Array;
|
public T[]? Array;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ByValueComparableArray(T[] array)
|
public RecordArray(T[] array)
|
||||||
{
|
{
|
||||||
Guard.IsNotNull(array);
|
Guard.IsNotNull(array);
|
||||||
Guard.IsLessThanOrEqualTo(array.Length, byte.MaxValue);
|
Guard.IsLessThanOrEqualTo(array.Length, byte.MaxValue);
|
||||||
Array = array;
|
Array = array;
|
||||||
Length = (byte)array.Length;
|
var length = array.Length;
|
||||||
_hashCode = HashCode.Combine((int)Length, Length > 0 && Array[0] is { } item ? item.GetHashCode() : 0);
|
_hashCode = HashCode.Combine(length, length > 0 && Array[0] is { } item ? item.GetHashCode() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[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;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,8 +596,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override int GetHashCode()
|
public override int GetHashCode() => _hashCode;
|
||||||
=> _hashCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ActivatorPatches
|
public sealed class ActivatorPatches
|
||||||
@@ -744,7 +799,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
cache.Task = _fieldSetterFailureTask;
|
cache.Task = _fieldSetterFailureTask;
|
||||||
Log.Error($"FieldInfo.SetValue has been called on '{
|
Log.Error($"FieldInfo.SetValue has been called on '{
|
||||||
info.FullDescription()}'. This is a const that gets copied at compile time. An attempt at "
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ public sealed class ThingFilterPatches : ClassWithFishPatches
|
|||||||
|
|
||||||
public record struct AllowedDefsListCacheValue() : ICacheable<ThingFilter>, ICacheable<ThingFilter, int>
|
public record struct AllowedDefsListCacheValue() : ICacheable<ThingFilter>, ICacheable<ThingFilter, int>
|
||||||
{
|
{
|
||||||
public List<ThingDef> Defs = [];
|
public readonly List<ThingDef> Defs = [];
|
||||||
private int _nextRefreshTick = -2;
|
private int _nextRefreshTick = -2;
|
||||||
|
|
||||||
public bool Dirty
|
public bool Dirty
|
||||||
|
|||||||
@@ -72,4 +72,40 @@ public sealed class ThingPatches : ClassWithFishPrepatches
|
|||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(MiscExtensions.TryGetMapHeld);
|
=> 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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int CellToIndex(int x, int z, int mapSizeX) => (z * mapSizeX) + x;
|
public static int CellToIndex(int x, int z, int mapSizeX) => (z * mapSizeX) + x;
|
||||||
|
|
||||||
|
[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.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
|
||||||
namespace PerformanceFish.Utility;
|
namespace PerformanceFish.Utility;
|
||||||
|
|
||||||
@@ -309,6 +311,28 @@ public static class CollectionExtensions
|
|||||||
|
|
||||||
public static List<T> AsOrToList<T>(this IEnumerable<T> enumerable) => enumerable as List<T> ?? enumerable.ToList();
|
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]
|
[DoesNotReturn]
|
||||||
private static T ThrowForNoElements<T>()
|
private static T ThrowForNoElements<T>()
|
||||||
=> throw new InvalidOperationException("Sequence contains no elements");
|
=> throw new InvalidOperationException("Sequence contains no elements");
|
||||||
|
|||||||
+12
-2
@@ -5,16 +5,19 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
#if V1_5
|
||||||
|
using LudeonTK;
|
||||||
|
#endif
|
||||||
using UnityEngine.Rendering;
|
using UnityEngine.Rendering;
|
||||||
|
|
||||||
namespace PerformanceFish.Utility;
|
namespace PerformanceFish.Utility;
|
||||||
|
|
||||||
public static class PipetteTool
|
public static class DebugActions
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
[DebugAction("Misc", "Pipette tool", actionType = DebugActionType.ToolMap,
|
[DebugAction("Misc", "Pipette tool", actionType = DebugActionType.ToolMap,
|
||||||
allowedGameStates = AllowedGameStates.PlayingOnMap)]
|
allowedGameStates = AllowedGameStates.PlayingOnMap)]
|
||||||
public static void Action()
|
public static void PipetteTool()
|
||||||
{
|
{
|
||||||
var things = UI.MouseCell().GetThingList(Find.CurrentMap).ToList();
|
var things = UI.MouseCell().GetThingList(Find.CurrentMap).ToList();
|
||||||
things.Sort(static (x, y) => ((int)x.def.altitudeLayer).CompareTo((int)y.def.altitudeLayer));
|
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)
|
private static byte GetAverage(IEnumerable<Color32> colors, Func<Color32, int> selector)
|
||||||
=> (byte)Mathf.RoundToInt((float)colors.Average(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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static int SizeX(this Map map) => map.cellIndices.mapSizeX;
|
||||||
|
|
||||||
public static Map? TryGetMapHeld(this Thing thing)
|
public static Map? TryGetMapHeld(this Thing thing)
|
||||||
{
|
{
|
||||||
var maps = Current.Game.Maps;
|
var maps = Current.Game.Maps;
|
||||||
|
|||||||
@@ -24,4 +24,12 @@ public static class StringExtensions
|
|||||||
|
|
||||||
public static string AppendWhenNotNull(this string instance, object? value)
|
public static string AppendWhenNotNull(this string instance, object? value)
|
||||||
=> value != null ? instance + value : instance;
|
=> 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using MonoMod.Utils;
|
||||||
using PerformanceFish.Prepatching;
|
using PerformanceFish.Prepatching;
|
||||||
|
using Verse.Steam;
|
||||||
|
|
||||||
namespace PerformanceFish;
|
namespace PerformanceFish;
|
||||||
|
|
||||||
@@ -20,8 +24,7 @@ public sealed class UtilityPrepatches : ClassWithFishPrepatches
|
|||||||
|
|
||||||
public override bool ShowSettings => false;
|
public override bool ShowSettings => false;
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; } = methodof(LoadedModManager.CreateModClasses);
|
||||||
= methodof(LoadedModManager.CreateModClasses);
|
|
||||||
|
|
||||||
public static void Prefix()
|
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