From ca9321add8d45ccee0037a3a168285abd14a655f Mon Sep 17 00:00:00 2001 From: bbradson <76005458+bbradson@users.noreply.github.com> Date: Sat, 3 Jun 2023 20:24:38 +0200 Subject: [PATCH] Lots of new optimizations utilizing prepatcher To start, this mod now requires Prepatcher and Fishery. New optimizations include: gene ticking improvements, world object improvements, hediff optimizations, world pawn gc optimizations, better mothballing, dds support, ContentFinder caching, additions and changes to reflection caching, as well as various other additions. Multiplayer support has been improved too, but should be considered highly experimental. The WorkGiver_DoBill bug is fixed in this build. Multiple patches aiming to improve RimWorld's load time have been added too. --- 1.3/Assemblies/1Fishery.dll | Bin 200192 -> 200704 bytes 1.3/Assemblies/PerformanceFish.dll | Bin 198656 -> 202752 bytes 1.4/Assemblies/0PrepatcherAPI.dll | Bin 0 -> 5120 bytes 1.4/Assemblies/1Fishery.dll | Bin 200192 -> 282624 bytes 1.4/Assemblies/1Fishery.pdb | Bin 0 -> 115456 bytes 1.4/Assemblies/PerformanceFish.dll | Bin 194048 -> 373760 bytes 1.4/Assemblies/PerformanceFish.pdb | Bin 0 -> 130336 bytes About/About.xml | 34 +- DubsAnalyzer.xml | 49 +- README.md | 6 +- .../PerformanceFish.Tests.csproj | 22 + .../ReflectionCaching.cs | 451 ++++++- Source/PerformanceFish/.editorconfig | 4 +- Source/PerformanceFish/AccessToolsCaching.cs | 48 + Source/PerformanceFish/AnalyzerFixes.cs | 314 +++-- Source/PerformanceFish/Cache/ByIndex.cs | 47 +- Source/PerformanceFish/Cache/ByInt.cs | 419 ++++--- Source/PerformanceFish/Cache/ByMap.cs | 74 +- Source/PerformanceFish/Cache/ByReference.cs | 924 ++++++++++++--- .../Cache/ByReferenceRefreshable.cs | 218 ---- Source/PerformanceFish/Cache/Database.cs | 53 + .../{IIsRefreshable.cs => Extensions.cs} | 13 +- Source/PerformanceFish/Cache/ICacheable.cs | 140 +++ Source/PerformanceFish/Cache/Utility.cs | 297 ++++- .../PerformanceFish/Collections/FishTable.cs | 645 ---------- .../PerformanceFish/ContentFinderCaching.cs | 53 + Source/PerformanceFish/DDSLoader.cs | 189 +++ Source/PerformanceFish/DebugLog.cs | 2 +- Source/PerformanceFish/DefDatabasePatches.cs | 50 + .../DesignationManagerCaching.cs | 88 +- .../Experimental/InlineArray256.cs | 31 + .../Experimental/UnalignedPointer.cs | 58 + Source/PerformanceFish/Experimental/mono.cs | 300 +++++ Source/PerformanceFish/FishDefOf.cs | 2 +- Source/PerformanceFish/FishPatch.cs | 399 ------- Source/PerformanceFish/FishSettings.cs | 236 +++- .../FunctionPointers/IndexGetter.cs | 58 +- Source/PerformanceFish/GenLocalDateCaching.cs | 27 +- Source/PerformanceFish/GenTypesPatches.cs | 136 +++ .../GeneTrackerOptimization.cs | 115 ++ Source/PerformanceFish/GetCompCaching.cs | 472 ++++++++ .../HediffComp_PsychicHarmonizer_Bugfix.cs | 43 - Source/PerformanceFish/HediffSetCaching.cs | 113 -- .../Hediffs/HealthTrackerOptimization.cs | 37 + .../HediffComp_PsychicHarmonizer_Bugfix.cs | 60 + .../Hediffs/HediffSetCaching.cs | 247 ++++ .../Hediffs/ImmunityHandlerCaching.cs | 146 +++ .../Pawn_PsychicEntropyTrackerOptimization.cs | 19 +- Source/PerformanceFish/IdeoUtilityCaching.cs | 47 +- .../DisabledWorkTypesOptimization.cs | 25 + .../JobSystem/GenClosestPatches.cs | 167 ++- .../JobSystem/JobGiver_HaulOptimization.cs | 77 +- .../JobSystem/JobGiver_WorkPatches.cs | 2 +- .../JobSystem/WorkGiver_DoBillOptimization.cs | 382 ++++-- .../JobSystem/WorkGiver_HaulOptimization.cs | 47 +- .../JobSystem/WorkGiver_MergeOptimization.cs | 23 +- Source/PerformanceFish/Listers/Buildings.cs | 194 ++- Source/PerformanceFish/Listers/Haulables.cs | 187 +-- Source/PerformanceFish/Listers/Mergeables.cs | 124 +- Source/PerformanceFish/Listers/Misc.cs | 12 +- .../Listers/StorageSettingsPatches.cs | 49 +- Source/PerformanceFish/Listers/Things.cs | 123 +- Source/PerformanceFish/Log.cs | 201 +++- Source/PerformanceFish/LogPatches.cs | 84 ++ Source/PerformanceFish/MapPawnsPatches.cs | 16 +- Source/PerformanceFish/MassUtilityCaching.cs | 88 +- .../MeditationUtilityCaching.cs | 66 ++ Source/PerformanceFish/MiscOptimizations.cs | 248 ++-- .../ModCompatibility/ActiveMods.cs | 108 ++ .../ModCompatibility/GraphicsSetter.cs | 26 + .../ModCompatibility/PackageIDs.cs | 21 + .../ModCompatibility/PerformanceOptimizer.cs | 64 + .../ModCompatibility/VanillaExpandedFixes.cs | 46 + Source/PerformanceFish/OnAssembliesLoaded.cs | 84 ++ Source/PerformanceFish/OnModuleLoaded.cs | 22 + Source/PerformanceFish/OnStartup.cs | 34 + .../Patching/ClassWithFishPatches.cs | 19 + .../Patching/FirstPriorityFishPatch.cs | 14 + Source/PerformanceFish/Patching/FishPatch.cs | 333 ++++++ .../Patching/FishPatchExtensions.cs | 21 + .../Patching/FishPatchHolder.cs | 74 ++ .../Patching/IHasDescription.cs | 15 + .../PerformanceFish/Patching/IHasFishPatch.cs | 16 + Source/PerformanceFish/PerformanceFish.csproj | 66 +- Source/PerformanceFish/PerformanceFishMod.cs | 175 ++- .../Planet/MothballOptimization.cs | 106 ++ .../Planet/WorldObjectsOptimization.cs | 114 ++ .../Planet/WorldPawnGCOptimization.cs | 106 ++ .../Planet/WorldPawnsOptimization.cs | 111 ++ Source/PerformanceFish/Pools/FishPool.cs | 40 + Source/PerformanceFish/Pools/PooledIList.cs | 48 + .../Pools/PooledStringBuilder.cs | 62 + .../Pools/PooledStringBuilderHandler.cs | 39 + .../Pools/PooledStringHandler.cs | 37 + Source/PerformanceFish/PrepatcherFields.cs | 36 + .../Prepatching/ClassWithFishPrepatches.cs | 19 + .../Prepatching/FishClassPrepatch.cs | 17 + .../Prepatching/FishPrepatch.cs | 27 + .../Prepatching/FishPrepatchBase.cs | 35 + .../Prepatching/FishPrepatchHolder.cs | 438 +++++++ .../PerformanceFish/Prepatching/FishStash.cs | 63 + .../Prepatching/FishStashHandle.cs | 41 + .../Prepatching/FreePatchEntry.cs | 22 + .../Prepatching/PrepatchManager.cs | 376 ++++++ Source/PerformanceFish/RecipeDefCaching.cs | 112 +- .../Rendering/GenDrawPatches.cs | 8 +- .../Rendering/GraphicPatches.cs | 11 +- .../Rendering/PawnCacheRendererPatches.cs | 26 +- Source/PerformanceFish/RoomOptimizations.cs | 319 +++-- Source/PerformanceFish/StoreUtilityPatches.cs | 34 +- .../System/EqualityComparerOptimization.cs | 223 ++-- .../System/ReflectionCaching.cs | 1035 ++++++++++++----- .../PerformanceFish/TextureLoadingPatches.cs | 194 +++ Source/PerformanceFish/ThingCompCaching.cs | 52 +- Source/PerformanceFish/ThingFilterPatches.cs | 232 ++-- .../ThingOwnerUtilityCaching.cs | 256 ++-- ...Worker_Precept_AltarSharingOptimization.cs | 121 +- Source/PerformanceFish/TickHelper.cs | 32 + .../Utility/CollectionExtensions.cs | 302 +++++ Source/PerformanceFish/Utility/Exposable.cs | 21 +- Source/PerformanceFish/Utility/KeyedList.cs | 113 +- .../Utility/ReflectionExtensions.cs | 98 ++ .../Utility/StringExtensions.cs | 12 + Source/PerformanceFish/UtilityPatches.cs | 26 +- Source/PerformanceFish/UtilityPrepatches.cs | 50 + 125 files changed, 11272 insertions(+), 3681 deletions(-) create mode 100644 1.4/Assemblies/0PrepatcherAPI.dll create mode 100644 1.4/Assemblies/1Fishery.pdb create mode 100644 1.4/Assemblies/PerformanceFish.pdb create mode 100644 Source/PerformanceFish/AccessToolsCaching.cs delete mode 100644 Source/PerformanceFish/Cache/ByReferenceRefreshable.cs create mode 100644 Source/PerformanceFish/Cache/Database.cs rename Source/PerformanceFish/Cache/{IIsRefreshable.cs => Extensions.cs} (50%) create mode 100644 Source/PerformanceFish/Cache/ICacheable.cs delete mode 100644 Source/PerformanceFish/Collections/FishTable.cs create mode 100644 Source/PerformanceFish/ContentFinderCaching.cs create mode 100644 Source/PerformanceFish/DDSLoader.cs create mode 100644 Source/PerformanceFish/DefDatabasePatches.cs create mode 100644 Source/PerformanceFish/Experimental/InlineArray256.cs create mode 100644 Source/PerformanceFish/Experimental/UnalignedPointer.cs create mode 100644 Source/PerformanceFish/Experimental/mono.cs delete mode 100644 Source/PerformanceFish/FishPatch.cs create mode 100644 Source/PerformanceFish/GenTypesPatches.cs create mode 100644 Source/PerformanceFish/GeneTrackerOptimization.cs create mode 100644 Source/PerformanceFish/GetCompCaching.cs delete mode 100644 Source/PerformanceFish/HediffComp_PsychicHarmonizer_Bugfix.cs delete mode 100644 Source/PerformanceFish/HediffSetCaching.cs create mode 100644 Source/PerformanceFish/Hediffs/HealthTrackerOptimization.cs create mode 100644 Source/PerformanceFish/Hediffs/HediffComp_PsychicHarmonizer_Bugfix.cs create mode 100644 Source/PerformanceFish/Hediffs/HediffSetCaching.cs create mode 100644 Source/PerformanceFish/Hediffs/ImmunityHandlerCaching.cs rename Source/PerformanceFish/{ => Hediffs}/Pawn_PsychicEntropyTrackerOptimization.cs (53%) create mode 100644 Source/PerformanceFish/JobSystem/DisabledWorkTypesOptimization.cs create mode 100644 Source/PerformanceFish/LogPatches.cs create mode 100644 Source/PerformanceFish/MeditationUtilityCaching.cs create mode 100644 Source/PerformanceFish/ModCompatibility/ActiveMods.cs create mode 100644 Source/PerformanceFish/ModCompatibility/GraphicsSetter.cs create mode 100644 Source/PerformanceFish/ModCompatibility/PackageIDs.cs create mode 100644 Source/PerformanceFish/ModCompatibility/PerformanceOptimizer.cs create mode 100644 Source/PerformanceFish/ModCompatibility/VanillaExpandedFixes.cs create mode 100644 Source/PerformanceFish/OnAssembliesLoaded.cs create mode 100644 Source/PerformanceFish/OnModuleLoaded.cs create mode 100644 Source/PerformanceFish/OnStartup.cs create mode 100644 Source/PerformanceFish/Patching/ClassWithFishPatches.cs create mode 100644 Source/PerformanceFish/Patching/FirstPriorityFishPatch.cs create mode 100644 Source/PerformanceFish/Patching/FishPatch.cs create mode 100644 Source/PerformanceFish/Patching/FishPatchExtensions.cs create mode 100644 Source/PerformanceFish/Patching/FishPatchHolder.cs create mode 100644 Source/PerformanceFish/Patching/IHasDescription.cs create mode 100644 Source/PerformanceFish/Patching/IHasFishPatch.cs create mode 100644 Source/PerformanceFish/Planet/MothballOptimization.cs create mode 100644 Source/PerformanceFish/Planet/WorldObjectsOptimization.cs create mode 100644 Source/PerformanceFish/Planet/WorldPawnGCOptimization.cs create mode 100644 Source/PerformanceFish/Planet/WorldPawnsOptimization.cs create mode 100644 Source/PerformanceFish/Pools/FishPool.cs create mode 100644 Source/PerformanceFish/Pools/PooledIList.cs create mode 100644 Source/PerformanceFish/Pools/PooledStringBuilder.cs create mode 100644 Source/PerformanceFish/Pools/PooledStringBuilderHandler.cs create mode 100644 Source/PerformanceFish/Pools/PooledStringHandler.cs create mode 100644 Source/PerformanceFish/PrepatcherFields.cs create mode 100644 Source/PerformanceFish/Prepatching/ClassWithFishPrepatches.cs create mode 100644 Source/PerformanceFish/Prepatching/FishClassPrepatch.cs create mode 100644 Source/PerformanceFish/Prepatching/FishPrepatch.cs create mode 100644 Source/PerformanceFish/Prepatching/FishPrepatchBase.cs create mode 100644 Source/PerformanceFish/Prepatching/FishPrepatchHolder.cs create mode 100644 Source/PerformanceFish/Prepatching/FishStash.cs create mode 100644 Source/PerformanceFish/Prepatching/FishStashHandle.cs create mode 100644 Source/PerformanceFish/Prepatching/FreePatchEntry.cs create mode 100644 Source/PerformanceFish/Prepatching/PrepatchManager.cs create mode 100644 Source/PerformanceFish/TextureLoadingPatches.cs create mode 100644 Source/PerformanceFish/TickHelper.cs create mode 100644 Source/PerformanceFish/Utility/CollectionExtensions.cs create mode 100644 Source/PerformanceFish/Utility/ReflectionExtensions.cs create mode 100644 Source/PerformanceFish/Utility/StringExtensions.cs create mode 100644 Source/PerformanceFish/UtilityPrepatches.cs diff --git a/1.3/Assemblies/1Fishery.dll b/1.3/Assemblies/1Fishery.dll index a26c4b707b292016a3845c095a837bb3bedffb25..d5c8445116342eb4da1b75117ddfd0696677c1b4 100644 GIT binary patch literal 200704 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�pHcJ=oJp>4^j z+77a?dVws6y%s zyk>N7yPMnrpbp;(8N86%Np^LJI0bg&{W<7msqIZ~qD70i4*!$_hz{8=SjrBUb-xSZ z{SpxEcV@KT3MB>ook)Kjw`7WbRQNgtVfnK~1qn{TCuGVFPu6_Sj`K-Cl+P(qK808V z{RkZV@6t#=dqF=l*zZ)`@0@tQ1XPa*%n!RvW52i2E*Dcq@Sm`jfezLeS;M4GRU87; z=`@!BHRF1~DV1kbPXj$i9H|{~6@}PImJbFx8UsFsGKvldIoBBZcqX z#o7%4Cn;|LC|vug7-6$d&i#-V)GqMKsJ7Sa4c|ZnY_;#9mv|#YcfHG*TxVD35pgCF zD|eIf46(;R;IA-|0vRqWzCjy7mhAAZTE=-IV=y}15dxwz;_)_ZBLvc|wNVYIuiQqL zp-b547VrWa!9PqJLB-kO-8BDu3jc&^ui1_2!XiUU~-nPz0uO+(ct!2`{ zMumDCDNtymnjLcPR2S38vuI=|=Uq*z zy#|igR6f~WLnev9w54>Sy@oszz_hh=oV{itGHuOe9{dV&>P2Hi?6dhh>dhJk74IO~QpBE#|lTNkCQn)vn&m z3Qmqr^?iQUm=c`oM*y|G53#EUa7iTF6YWV4Yc02UhppOh3U`cYg!_^Y0~b$XNdFs* z0B%=4!v0Swr)m!(Q%cr8ip18lEB#CT4g@g`aZp&QBkw(gCK-34_c5g9Db+`WMJlE` z-NhWB!u?z-#;rU|oK)=*BGt6ePs+uNO5X@ItUJS_leTZ}b6PoGdceR2yt z8ED(b)BRh))29?qpWXsb2Abw+2DYPAyemu6cQPX IY|ee|4hDB=gg+4%N-6ur~F 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Ʀ{Hv36b} z7Rx}?qwI~DJRx4{1#1#1vw4}6q&GGlk#;h#kXG(Z8HPx`J(q2@(mQPM8Qz%7Q_`!w zs@kBX5_Oe(Qjs#AQcNBy#J3mE7_e2?+=LDx#mqidh6^LpB=4xHmX{3cUfjr&fN!tV z%FAO+dr3R5OySro8v)9|V_S-mOgoxyqc|xr>7iJc*$?Xs;(C$%+w#~Oc2Eg|d{=U@ zd{l)w_3Ky?K*t4si!P6|-|5tcO-Xn6{LDkXqxwFX_xFI`)$B4n*l*|z+G z^<*9(K`;=#>Lx~a=(h2Pj4@|-rrnkqUSK>7#l(07HGzHcjL?&tEOd~wcoZE=d;V$| zSX|vr+fkbbR*5175L8UXFm|d* z89f!9o3rQ~ck~1dOG?BrBQdYhwRzNn1B?wvBX2OD2{|E6$a}2ob&pWDJ2xgknXEDv z0&fF}-`kr+stEbByXHIc=Osn6gqMX~bKs4#0({H9ne=t4HHB!tt#lwq@} z=6s0lKY@;HaaH)9?m`FcQ{~x7+3jaMJ2PoUvsUr0M3IaIX%U$BPi*bYwHou_?Gofl zvEm^BI7{fiMNNgkx4K7jQdn7CT(SLft<`j832}wbMjq!c&5XX}L$C1Onh&k>&?{&} zW;lgOi3C+JFY}It?wDEaUdwBCujW>9-p#uf&vJX2Am^cITbr~vry+eq_!Yvhgz!4y z^%GD>%=<9bRB?y$QoZSu%ACvlXeJ_{o?!m8hnHziCsD=eBv(4gwTjG1X&4sAJygoL zNVHHB?-QYGy@cU3Ww`a8T3rGE5>zRLzKMFZSLx$TbvYG9)fUCJ^;Fe|q%Ux|PsTS+ z7Dc&>nrWi*d{dH`U6LRU)n<03MP3a{7sDiy$y6q?sieGjCesR<3^tcb@ok^pTfg?$ zly7Wv%dj7P027V!?>K7GTHBVpX}o#tUymwgrEzPKd*9My7cZF`lIM5!o;h7u(>}Yp zbdFlG6XUEYcX%C@>5p^zz_!3{?4-e+h+MmHG*LLAbpk2OcFr{;SH%B&{r)JAGk}0y}$P5&IHeR?+K1aFC48+z#oj9C+aO(idC(zZJWeb0{OXatn05*OS5#v2X0Y zD=W_36FEa|u=nE4uv|0GydNDf?*m`zoO3IY`;%L=<;$2iUn_F#=8Hd&OQrvoIYisBE;Dv<)@8a6pHoli}zqlc=M^3yq z*i(`FqHk z`_s9T@T*gB&Ru?ZXYaz+)}!m2+s;JGV6e2Y#fffwe)B=<$j>aV+_QS`eP_?z|IkMt zzHsqlAXIOij29YN-nnhA)JvNu=G!yf>g*l2SKe1n?wqtV9pZIbqFv{P)2Sj6Mx9e- zj;9Om<9wfbu;hNJM<(I(%NW+^o^(&R%UmyVZ2c2%jq^4iBfiGqIbUuHc<8+NKD`A=KRa&6;V{4mrS zjWN9dwsCE`+I2RV{t&N61y+kJ) zZB1c!w2dWxQD|oaQrBO|0D-fC0jDge)WhI^4LpSDUxU?kV2-Et`OVE$JC$JtOjh{Q z>8Kj}$p|=3?i6012zBXqWon`5mtubOZiU~9jpO6gq4SOX35L_-ezdH{=ZxMa3XU&| zGDk;oGKS8bv$=hU8Pc!B3Xdj7&%HiA?rQ7z^3yN5CW!yw?C&M^e?a>GIRd{0(=vQ& literal 200192 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 z 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

LSoC^v%|XX8Or8+ly4fCN}026A6** zoOCq%w71NrBF$z;vmcODb{MIIU2gogWMwVk)tX5qY*eF@lATTA#AdTnh^)z$A(gN_ z*~L;V))U@~GFHMm)!!}oF)yc}Ll7CjdJ_$4^s^MmZV+t%Unm=G@JKN);cOyON$~!Z z%AjyI14-lwKDayJxm<4CSFX#J5RGcoSFXqQbAr+LWVe%}*xYDAo$61Qo$N%6phMZu z$_7i06*L64$i*s&Hh?dd&4>dZjPMn#5{+Z)8VI@%D4w0<57?$W8?y6Ae0H}e4cTQR zKD(TH!&d-ecGX%9*-E4mme{PjlEnHp0vFEbL?wlt<%IKjOK!%z;1!b7p6mun3s#B5 z=kqZoi>*xNz7lpQ`<9%|;1&>RNTbFy9 zfCjw`X*5yEXL`Dz4d5$adpXg3KEf*b$7Mi#J_}iRYaS``e3UtnX0y2L-+~@x9bpq0 zo5^-IwgnGjeFf>IwY-BLV~-FmZ_G*j+il@nRWG<_X{$FQkH{oC{k9>+GA^hoft?665s22W&OZFrw0Y(UV2;K{5AX*PSum>K*W zJ7Lm<;1^f`e6Geve=Fei;Fs7`qVn3W2hU_W zeI;yQ{CB~xu~8=3f?sFTIl(N ai3# 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 diff --git a/1.3/Assemblies/PerformanceFish.dll b/1.3/Assemblies/PerformanceFish.dll index 2e54c8743f6c511adab400d80532967c0b9fee1d..812d07a522824cd499d71c46aac636d3cdaf5edb 100644 GIT binary patch literal 202752 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

vT;1$ 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

p{Gz0f2X9P6zF(sKz8F8Z4L;O&gQL>DpwS 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 K{_~$62L4}v9O( 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 IzjzG%-{(5FzW@LL diff --git a/1.4/Assemblies/0PrepatcherAPI.dll b/1.4/Assemblies/0PrepatcherAPI.dll new file mode 100644 index 0000000000000000000000000000000000000000..2306a3b7cf6167ce870cd72e5d3ee3fa3e1de36d GIT binary patch literal 5120 zcmeHLZ)_Y#6@PPgzSvIVI89<2r45@nDTy1c?b?L=Ex!2d#A*B|zKc`BiF)sDeBNen z_t@R5eT4>#03uQv@u8xss89r=AF7Hr5`w5|tD+zjp{9a}Pw=G@NC63{NPGYxLHNDd z-Mh1$q==8Gvz|Be{>+=-yqTSS=kc@Op*RsGQ19F!x`vj^kfQe|OHg-w`mGN7N!!is z*Tl)2+q3hgr&V01(;gJ3|&u+5SyHa;QUL$I4(zk*?-o)#^T=c>Jx*Y^Mwbl?fI8`*FS>5&A z0yu?j2p!(24%DVH1oXNqEeD1iGMYtYUk{=-l_8>|O|0s^&T&OyY%yw~YZ}qp04`Yg zKa_2;-Rp_kd%hI3u}{6Q5zcmX#b0?IE)zWmLwS593%rS*X!OUmSV-o?Q4yl#H9tG3 z_vr(D1N{dWbPHLa|A5c#^F&4LYZkRT7+l?b#QS+les$~msH%pM4(%v zxUYn|R>i#*>e}d0>WcS(F2;14(iqFP9{K?I1U*&f`oUG|+&*w$t8=WoT;~ph`)-{} z0_XRaaj!1peq84|d36|jju%NguumUgKQ@3SX$xpO?E&qienk%`no@L3(Nl_kK~Y0d z6Ld3u88k^3LAO#(^>tcHDbi^podDfRCTKUgppPNaIt|cG&_nb)&|&HpIz35!pcy&@ z`b9+xG%1GZ9K8tI4q8zCLfH4{WpP1CE-1+b^5{Kr1*`hD_#yhY#dSshplDpML=!vc zvglU*#1h9KdUFsKcnRJF?sM6Wn1to$xY=E<~8 z-!v@qIq8l|f8Hrl%JXD7Z`HsWMZ-5zKHj-})t59<&dXv^78{abr(7{D=^m4|bPc~* z)tGP8lvdN2`smZ1QId6z(lTEym5jXAB%7#O)^bUNE^GSDhG{uxR4sqTu&PbNM_nnW zc+rhnvW8od{wTg~a=~%WH58HKQLJZ1x}NFStCeH+oa2@aA4-!D(r!{jQ3X!^ zhBZb+u6T@L*BXlHs_mO)spP(yH!ag|(jKkha1@(%fJQB&M46iB%d#Gh)x&V%`d)Du z^`d2k#`Q6B&H{FXj~eHQ^n&lWkxAM#O19(qX2FX@(M&t~c6jhhA6@tiqdABHxCjjeg6NA<-AC&r`6Jcl!QuWq&FfB(H(1H+fy!^!J!?D{$- zG$C5!8i^KkHf&(FqeY8}4p0bEtv`D%H?w8%!;A6cBQ3FH3mdes>Jim_xK)I?#x{Pj zv$3u-u2GqA?2*NSR3{}n?>Y;ffF0k2MB{=U)+a`?^^?3ejPDVAM?a@O4ksO(>dLg~ zRV<@6fm6SUM`?A9rmPW)SHVW!kaXHa`LF=Yv#qmsR zcM-1|nn)DmZ zZV9tuNeJ4;Y}|$>ZY?%Dx#{E05d9$zzAC@@>cUkL<5RpJ7g28@zr3+F&@tudIk)%> z>M7)Cr&MAGCM+tYjTp4knRMotH#cuNao}%HzB_p7@3dv<2kc|`U=FXg%E$R`^0eKH z`}gh1WqfIrb9lpyvQepcIfSI_*tsa;IUIEl1EWhnmvO3YLFTZJIVb;2F5KYU^3dy* zVxBU`Q;&akAB9-9o)59`dOu3szWS^8J5)!Kat!g0-Wa&Jw&v$+$hKw@anCSvmO6-cxs5~5;EDS zT#65(JA=&TY0wdxMsEziv?johfsO{hy6Bz6NB?eQBs>qFT0x%=9VO5PcUCAslSUQr zY9O;3MUG{Wgio91$WcZ+k-zerp|?Us)EnzPqj@SS|Yv=8gQ!z>f=0jkxy%T~s-aqqyOO+Bsj@ z+Q4WvA-&&xI@W68;j$Yi1XnP8l#~Zvc?E0coh@O7{Mpq|C)9kCp`0Vj^F?@>3nKL2 z&ijOl8}F_IG*!gRZ$i)UxZBP*$BTanEg!!!YEiuUfq%c+*VL->a_=#^I literal 0 HcmV?d00001 diff --git a/1.4/Assemblies/1Fishery.dll b/1.4/Assemblies/1Fishery.dll index 46ac14de6fdfd09939405786f8463d5909db6198..25f63b3bd96c81a1647d00ad374606df644b1202 100644 GIT binary patch literal 282624 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 BKYRcH literal 200192 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 oC3`iw8G`j>V47PFy6ywTNxxw~U*Y$a%37 zr9SH4I_ozE57GSLVuf+UD%sELNMTR(Z@|Gs&zZ0}LQHk(hDZhiJsL%ncTXs6iLeE;d-%*EszDf|sHr*b@ycrWJ! z_&7&Bk)BmMiQcHa8eZi!aMTrGwJu)=RG1x+ean`UD1A-BeJTJB1wJ3^04jlE)~@0a2C>AH<0Al8ocvWM27 z@xTh0gF-_FgZ1^-0_0C!v9p7*f&h#a=g}Kh8{sv_3LWvs3LWvs3PyG{R*X*%8)l3Z z6CeaeYvQrubtqB#eN#(b*;p}2zLy;n$2R!mg$kXa54%m0J1!ZPkdU&jK+sZ=0F z+QrTA(L`I|P0~cfn4pO+0yx}6V)XQI{sttq%xY^mr_r~!O2&|73>na{nb(LRJpVV; zW*S@H6WDpnz!*KLeSRBF$Yl0;tg8QC*kde9Xpi57+SM+GLwj5m->PjDKT@lSKdn|5 zf2&4Q+rT00F=MI$=fAVZxTEpEVUMY%u*WpK4KcXu>z4xL+ha@vV2=raJ-$qM<-C{s zi&!x^>T-ZMSvm!S0|jt}#I4$!;oI(&%y6h6nf!tEqWcy?^cb7@Oi#QpLHC^U#P4|FtORDGqSuT$cH82 zF7l;fK9731h&87+!n=&_|gyyEqJIJihBhN0Vzt$E?l>0LD(Oa$CMZ|)u z_hs128`8U1*WU>evM&Ssg09afy8gQ~Vb*;C=wkLJ`-o?-+hcF~-ALv>;(Nr8)ZPmp z`-j^ZH57KWiZBC=nd(MFG(`A}@ORM+tnH=0MAS_{3i7>UTt0#L`Pi=~&d2i9JK;1t z*84#<+^=6YU!ecD=#1<~T(iG?zV4t4etu|e3?x4FX8$rGMZ*0Wyu(lD;nN@OciK}e{e$;P%uZjStoF$v^za+5%oLnguU!u!3#_cpZ0jIhD@FtRia#+``BIHAw7 zsNdEWN|*M6zSs*9x1eSVLyCr>AqHW6{VwG9rwZ=|INu-u_Tugq-Mvw(hHo#<_3gz+ zkW72=QSl?Sd&G~{J_a9l}sND}Z3g><#b}}ZjC7%V>VEuFAj<+bE7x)+G?#ixEKmQxMa=-!FuCN~;4IcSt zR#`nuTy0|ZF{`YqC9XCxusW=^C9aqlS{-%+OI-CnNI+X1UktBq{_#andKtTOv1WJ7 znUka`8^YO_iYAWPFKeF+iS@8%qaea_m#mZ43eFE&Y< zN)YuNhUTXsW@mN%%g8Ttn4191HyDLY`U<`E>Z|b7%}BtqBo( zY)j1Xi~*GIqb3CjGnyU5x^-MWfvnd1To17rmfw(aSD4mlruHB>l%-SsqC7PJ9I&-7c&Tz6$d(eCZX2EYpXasP72!p33ErTm5UOV-nA5nd%X6 zy7?|>Smz}K#$)^uSKJhjE&AD{dA_;pJZAG7QYnY;B^2?Q@wbrcl7~=+`ZhxSXARG; ze+PlyMOpoOp_%%_NL)Lbsec{*f{udPi-Aq8iN_^A~+P66&CfRv(7 zBa4w6%`_OTf0MB8`Zt0wZaJ=yvr6~7D6qlgfuG$NF$7U9MkyyFD@)Bbm??`G8N~~s zjONw|RImlG-F*~v%2Ne$?Rf%Qn_KvJxWom$EquHXFBG=($jtU@+hA>35?Pktg|PLJ z3+%T(Z;2aE{G_#y!~Qi3GRP+JtEZ`A+`JaQ(1gd=Vt5ZLm0kD(9}@jGNFh7yzy2)~ zY|}fs;v--|&({~TbjvAi>P3+}6mFIEm6)(E(joc#3nrY!z z-#hv@Hn+hm=t}-JxLswc$X9=m+u_~XO7-pxh2;mV1-*{TB)CP7yVAI5>{8lv6^`p%*{m2!-8-X?K5UZp=HN>Mx10$yeH@Cg> zK(EI~-$>Rjgj0*M#46pgEmxNd-c>-fP@TW9I^Sn?KCJ8aR|1|S;7-=>2MDEN1BNlx z*tqZj1R@3kY41AZk8Q()SNd(%;=$hg2zmW(Xf^y@zwG2*^+V*{_Qod49PjQh%EIO2 zP`u9rL9UcTp=jT6R>V04e%}U@S`)9XX3Z^lEu*6|Ue3tx<#gcZRHhcE#oQyH9fi32 zBNSOkxCLI+Qu_m@KkRXT40tAzj;Nm?;64QhyWF_d6~Tun_ErYee$-BZneNjhgR*(g zVIAv(KRoh{;UekoL|VNAvJ5HAA1JhA*o+OP@DZuvhzeRNxYnQv=+6|~bJ#j}18)^> zy?zIMhy!BfXCR($Fe>94=gaQTSF=sjM_Wt@ap6+t)O+~!Ti5hMKQ{R+ zSdvebC#cYs1HZ8h!#9k2pr-k){0ey*VrdFy^}B%NFe2h+ZrQ@)wYeH0hz7s9`DbtJdLPUrFVssBycO^)TWAG=?Rd1{V?fKsV=X6$x@Q|07LB(-)-6W7`TO{nWqPD< z7duAqD2Sd)=PJ(wfX~peX^$`Ovb>$Be-bQ?w;wKQ zMtCnv!($_F3NnitZ)Ow=&p(1rgHkPAWMvmb)xXgzA|;$TpHq8`4TKei_`GQ>f!W#0 zI`-Sy)3m?B46>OTaA*r~j)vgoA+u%|#C6~t=!4MmeO%uc*;%r01)avX0!+u$aRZ7f z*|wEigV#d~7h7Af1!eM+0H#}SVoZY2tSq)rH8_A^TS1D!UG7uKGTWzvzfEmylfEFt zF||3DiTDBw9=FzBpfreUHh(|dTZOi2Vf+agqFCIyc${K_{{(&B*d+ifFo~lq(m4SB zH7L{Cf}I1iw5N4DeHQjw6pD91ObIBQG25jqz~=>E1_X@yXirDeRQS~C~W5?~al|QqQp%_P?gN*ZGl?X>$e$Q`Q-`D7^S#7ti!EC{M@N2znwh%3qGt-{gcn>pXpPF7lO{)K}Y=ss^UjL_VEr$MtimVEj1BS8B2(QC6)vp6Zx*V1F#hEmf>QXd z>79(OR%2-}k_ZElbo~95e4AuXQ1o!ViM7PHL-!@{H z>)rr9E2HQeJjae9zo6Mw2Hk%EdC*(Dgue3KNAA=3 zv7R+3uAIQ*^O`A;90jHFZTRXIXa8Fa+MT-`39lfzoiL>U9}DD zu^8`QTVpnfww9XJ9AMe?PUPTq?ugyTTjdc`2%}LtfGYHal1SEcyMS$1nNwt(;;%hp z4y(b;9`j`ljUZ7WuYX`9&5b*JoZc-Yn7B%s!{$wJ>?=r%fMM=UZUDa+pzw|lYl+kE zDezLoJ!5V!+G_JKX6yLbg{c6lkD;378FBYqB+MR4TX;Fh0o+YXB9ut~x?=fjJDu1T zOxLgq!S6))zp$v+(cQ+fV+;pH%9zbLI!0-{(}nd~%JjCPWmONT;cc3!Xd;C4`zt*% z`2fGB0lEe@&A=}iO%`X`Ek-e0)4av-TRmAM`_25+PW%WX8Nv$+5K9mEe~Rs;d5RhM z;acJ806bC=L0jNgm3tv5d}9O-@#e=UdSBMffnb3sy&7d6Q5f&m@d3tR@ODzi*O^rd zKc=iO)dGXasiq-RIHx%ok0RBS@A_*n4_fcBr=Z{7t}-=g_rEw~<7W|adnFD2n91iL z{}A69!<=TskOgu{8Jjx{<2{s%#9g z22tr7Sdtky)A8H0e{@l=S*49%yh(+9 z>ag-n1>9FYyx8gQQwAQ;D zp~@^^T^!GDmwp(}ZCA4azzWj4NMQV6is%lDd!wMLT@rkfnw;%suU9c=Swp3!n(JGb() zorO-&&MYm|&JvY&-WxRD*OZ-ZXSPYJorS8j^OsRJ+qnnnHA+g__UnL#3`-VpzioRF zEPz+;*X(ENJxjAngrt246%;Vz!}$M8{2zeQUV{Ih$NyjAKW+q1s|hi)IvrWm$6?`X zGf3S-@&(?f&^F#5;q>%m)d0j?o7qY{AcDkUIL*XaOw871!`lbXL!hGztttWT9Ar1E z%&+Dmxa6~BE{uVnfFVw0HKjA8Znjw4 zoz+sp8lhSB$#&LewC63(Yb#4i(z2V`j;O1UGBM?rTIjeIzdIl09eC{MOE1YK0*XGQ z#}aMo@lB$~)ETYE)W4PZM|NKJ0d~MG`2Q*VhrPn$i}p%NH>(&pBObQSFHi-DL2D2? z3bl_I7|>|u0{r?*7zcb6OtYz}v0TEg#uCf*9lYoVS-^5Jr?yO27G zg8OrC9RMh~h>2hbW_~Uq{9}|bJ|J`#2;H2*0T(V9t3BWqo-w`HzE}v7AG410#-M3o){>P) zaPkd8<;ATUxi!^%KKH~Osy&g69jd)Z2z6mp*5(obKoYoEmw?aDGWb|0HP2FK?l;N( zyu~25i?dYjLmdWMGeFDohMQFI+1%d9DVtL0dmZvS5c1O?gO+z_7;PV*g^tnnoxLRh zHV?a(B2w6)_Jyab=kAA~f6%(WKv^{x13_|4VZF6w8DiTMu!!)@~HF>gXx>0sKvD?Da1P7TKO{$rJDN zhuBQPRDCfXU|E2S^7>>GiZ)uPsWtsQ{P-~atF3yer4m?(+m#h4DBmDd9YkhD$F4v- zd=TxR4hBfJ9k9!R-&e2|R_%NxUe@e50E9upFUX{dv$0G*6k+!;IHk@Yb~s}4A!c^L zPW*-@5mpkRV7f=buO9_xXqHYHD8bmNqnVtl9|JF^j)luTc5DmSR`ocPGLSi?ANL(s2IM4hn?#_zDQQrz7+ z7z;91skx#!Qa=yfY7!R|GGl^<)G2eR0RgX#q;G{pVc{)E(-VXa@CZSc3*xl1}RTd(rRH zKeKTW)^~IC`VL3nk-SyIfomimsV#!Xy`pPCthOf$tmFPcU?SFn<;prZ`39jilJxhA zpjeCjg7&5lS<%L=g?}q#X4ZGda51z{yz*T6@D9%|do84n7o=JT#=C9@=+iVau zqOD*(kFcC)55;_tRmUDpI3v{WbG)N)E@XKdWLe)2xk7go>KlOFI($?2 zg96S>7mS6U4m5=+XiOgM}LcXwE&JW9FUR|Q5?7oVc)DHpvtB}Epl}#D^gdKzI zn)QQPOm0GXz6=P}GGJxeWk5Fh9WCx=?9r3~d7%sr_5H)>Lk5Sn$Ui_Wb5TaVL1z=X2c78^o3I}xx1Id>{1bF>l$pP0G`NwI?bf}zHC-vG$)Lzc;JOPMDwIL#Obg3f~= zJl4}3IWw^G-UknE4Fs&aFEU8va9<)v_!3#!lE{}SkvF2me1lM5B9zA=kjIB8k1gk* z2w37h?CP6{^Qa&l8|J`vIz*1aW>qkM@R?Mq};OCk?ZB3FZczCmb6Z`hCS z6nVT2$&|-7`05%slm~(8TBficJ*?$%9pbx|2eaAk^>8PV$J>$Aln1fh8{o9dgE-^m z!CIOl=QZW=P)HuEc}pJ0`0_Z`m&b`Md3>GnxDoX84MIcmz;Tdqm&oHCNLKHJLpi*Q zk$1xpIebIQ;XR0l=S8i)LUh}GFWgD$za2@<`V-rIADs636Q@=GZ~A?OwK7Lu*Q|f@ z`4xSmq{iB|WN@4>gH^r^Uel7nBb32SC?nq>G$aE|*9`0zU>$oil3D!}5AffQh*bZZ zy85>uF6Wz^6SnG3T-*Hs5+|wq2a(jQJF(qPIPG;O&iJ~sHs;8NN$bv9w(5SoU-uLI zx}V&t`?py49VjE;AXL{K2i>;<#9zevHfvbF4KdxmrtzBpL))9c$x)R5|C8?7ncc}I zu*qgN2apih(Cm?zgdq8Gna>!wabcFVRa`<`D zK+$E5q%fa8>_KzU$C)tuY8HJ6Tn8JsPP1UC^0I+z)iGzk7NZA>g;yvAV}i*Hsk zxA5t23hFv3k(y-ST$W9gj5m+NpeXUy0A*f5Q(gwOrT8Gcbj(d=a48TwGq@}H%df4R zxyJ-+D@wUxHh>Y{0Vm7#L`!phctX-X_zh+X5Yzql0^W;1rEeF188 zg13oDYlAP7JI~k}-l~jleZT=%9_&t|TNaap?5wb`x8~Uji!3g>R#-9wzC&{xXjoxk zO&Y}FlJ5|)bf*=T*^Pt6!Ogs;nX?;*Bu+DDH{P5$&9pTR@m9aa;S|^U(g_-ieuYE~ zK>|Fn1r(YD_`Vh}LF!g+KI2C;BoOx&f`+$)iANsI;XDjvhwh_8MN9?mjj$l#=9Q{< z-G#qF&$Y<2Z*QkpYP04Sj-Q*jMqVWby^bX83F-PBq%heJzlu=~zsA=OzRoxNFbrYy z`)rKP@Q-}$ZS9!{&9+lK{nf^ugbHXpOZB_3oqa1STf8sO_zOWxSt98{*O3!a5_1nl z*Ka29*&IC~=FG<5ag^(K3)yXkY?eJPUuwK9s~!x&la0-$pusl?ox>7*6SwZ>7o`KK zz|oc-y>(RLNMxmUJ5nEfivWFkexTG~L|3WSj@*LHnqS~mXSc2rej9&|^@psSl!mq` zM|9oAU<4lvn7sbH?kS5*L$Hcq7H8deaUG0Ho78S^eV4+}QFg4hu)?eTjuc`Qm>00} zYiqr4Rq-)2KTM{LjR&EbEf~g;s@B0heCQ|xM!YmL>B2@SdOHygOjb6Q*m0hs<_9Gm zO!^LSsA`O;OIp|1Y)_I^LSEzT85i#*Q2jnWJevSC76sg+N$5IU^g!3!WVYyH2P~!Q zy9r&4{o3d{k_e;eVp3pqeb3RwaMb9!P;@CFuW>|1*Zmn?g2tjCq3i3kd&;*ruh5;f zd!1>!cZ9r(0W*P8{0{7{|Jeb4x;@claneTEDSQ^&tsVQ36;i;=#f_G943W{&yNRvQ zlAPOh*bY@OxJWE%B!e#1*c8cQAfYk%g#2mPPNJw{(cv}Tku@NX6R7?KpZ=zxu?S2W zkdqKMr8rjHjsdDMlS~ttamP$=dd&oNnmN{nWTSn|6t}QYy+zI#G4X3{V=H0X=dSc1A?u&`(2 z_%KQH_#{n^a;LE$QtS>EL`*MPk|rCD!NWvNYNwSu{aiqC%e~`ko}w znoQ)rSYTRJxCyzhuVFmL$Y<+xoKrhzMdA2r@HAPEj|E+6#Oh~A5H zVYk_vvJZBd-5SfL!rD7X$^)Z8`+2o5Kv=9ydc7IB!7o5J@=HF!ulT%oNHZB=dp8hm9^K>Ja*FxZ}pV&3P7IX)wXX0TL$ z!H5wwA;#J?Mt6=c(eFjgANbg#MSqlK6MG9O`V)2zfXn~Se8RuTuip#*DtP!ezDxU@ zzx(#p|E^&F;G=BZ0>!b2^F}_<%7!-SQ1E2?c_KI)o4r5D#z``N=Z!R5aW32^*-&b{ zC(GMENu~ZTKK)HWO`?Mrm9)w3o@xGSpCNWE{A>+{768GK#`M67r5MjGM$a^!O~58pgLF{hCkx7Ze@kLr`xw){r%x&ih?P*lr6q( zU+iZQ2;CX(vLFX>TGvA<_|&%)c*H0t8b;l|nzB7(Mq#C<6ry1|NN=`2_GHMblortQ z@KVXCIEIcv5fWxlf(_NM%(ok5ubr4$u;{`ZUheUVS*)mDIX+#+np61dde9T3{nV`_ zhxQ~ulf5-E*3USTl)V{AEbvSQ52uwasjwTzx;ITB=kBFzE>qUBycctM=bWvGv!{wQ z`AQg2%sqEJTF2_TS<^ebx$7OdMkf#IOi_x{+rhz9fbffk5X6O3&ndh~{L%rZr+-nH zCHr^-kshkj>;^hFuB~JWjOu_E5LX zuL6|$HJK~?)yv_Zu9^1^g#BYd3k0Ixd8XTgvpC$>QVe(CTM>pkOQAvy=~1>1LRfO1 z>c^B$ePd|vZwhMFn9~cZUrB7;^?S*%(8!c}G>uydG~e$HUZx99M&>KuA+R(;+NC%3-V?XSbdlC!?W?-Z0S24m} zDYKliNe3;3efZ^d46b`U>UdjKY(g>f!>o2~NVp^v$ct{yRrh!Mkp;BQZGTwd_41r+1^)7CjofUeu1t9}Le7=L`M zg=0`SY>;sw)M$`#B|+RE3Yy@^OQV$?N8J*WLh<+8_CJ2EU*n z?OLMbuhctvT|o+FwAN85%qwhrKw++&l5YkG1&$Cf7nn_6hi3zTu{P7|;fv zAW02HJhjNBKJFV>pl~w<4L*sln8WJL@v$=P9SFKzEk>N#KV@xiE0~oyN zSk;}TLG0lWpWrF-}}JejWm=uWeUpo~nGluTSNlX+I-6B(Hd zj7s%|pzUu8nvA-1qvT5z07HgP0~K3#ReHjcPw(M3rRfQCmfqSVy~|~u(YP{8k3mnV zzR1!O)Uie7b2mKUs1p6=pxg*yP5v2YGd@5_UFinf5U(5rTL z^{txH#v2%zU5TdUfT_Nh79esGI!28!_Q~~dRM#`^dL35Q`b{o@^crDYiR2rd4)yq>A)KW>b@~<$vK?z z+ns1d%NnylA{`RVo@1f=o0R z)|x0@=|tlyP$s=Aqt8-(57Oyx3Yydy$p&**y<^$qM|s=^s9l?R zbU7kB+(j-`@8khfP`(EO(0=4P)pL(pej+#P)No5acZaB6=;Y1lo+QCS?YKuWkgsdY>nx*7-)krLDiq(mvZH@t23>wz^7Bl+N#yw zuuENs$Hy<(mfy(DY5lIfpo|`>k+w}L7z#3)(Oj5WS>c<;uVA(E#k96G*$Yp=-o5Ms zh-UVCA`WS8cps*f^_=kiScgxTHYtGy%)mQ#!NHfx!VL0NQS1BqBWRF0%0usq=5B=5)?IX?f5o|oh#)$BJm8INFH*wNZ|+Y zryHI&ts`j4TJq>>PsX+Y$SIh^&#oJohT6%Ww_>(PKzb@qO6SH;T7JmZZY-X#vLM+i zkTs3V-NkcE!5`s2m;aymPvx(l;8Xbjng1mCoQIczbpqQc2TvtV(&IJ0Xd+^R(Nm$g zej1gsIu_Bv;#b-w)Xo8zuEB}6q_?uV z%J9p6UNJfoT;YfK#J4JtADt!pmu2@Nfuau!aEkyPvFHLW@91p75p;GW3dQyfu$zDm z{KAW5+K$BVfEQWNEUH!&4QrN4YjbP7J;@u$kGt+NYHy3V!hgSi@l*>7`xna1`l^=J z+AhgY@NM0yVdi1$BYxqugulrJ8KslIVdFL^#GG+<#>A#^z+zP!_#D{p zN+LWL>#Ag*(TCAZHldTj{>RVyD zt(3PWaFOa;xjOh>30$=LR=5tnD9DKQO#;I6p^d%Oj9TBsAiMy$+md0u$HqWkk-3ED zW@(wD@5pgh<4ai$WeQfRUr4O}rl7rUDV}-PA5NBrxRL*$m$u2L73P#^TUxoSz?FtPqpfwpN)$pTbpj_V5zye!z^Z6#Se> zM@}wR7y0EQCp+)5AAW`aGzdx~!867A`Qc}QEntQ7E=OC_Olb4$4d;7ATX-p9sFJ=} zg3E*qy2ZN_6M0T}rwoqe-C81KyqlSl1AaA@ zKlkP&j=?WL8()J>OE-6_0*Z=!)xPoVX%nKf5^C9fzZaMFbpt1$!*ANW!mjj*^UyKr zPBwCtHC7NQt(`Z*AS7U3>&DI<_6{S*r+fT;jfzUCH~a!LEby#ym4aU?vu4*6xa3vY zk5IL?(I% z9553uUFz0smi)FH{E8H%T(VjUt|xqF@M{4&rgZ?g0YGo?oM`HgZop)8XRWgId}A|B1KDvTU-L=D~26&-sW$fn<4jKHUR5q0se# z!tpBmLgA1}m3K@!QTF}3`q!W^mfULt%_!uU*o@ zLhE4FZ5FdPZd0Fkv99dP+N*+>xHiM8!Kn{wrbc^bH_ zPX}(YXl5@J+4tlS_>Qi-K+`&@CKSFkM>hH*#=_uv`0eTCXahNQt{k)jPkHnAd*ywIR z&Lj8+*5I3bIF$M=jPB0h+Ze;F@!f;z7Gjr9?To&I%M0j*OA9-vmiqdq?AJ9=?2PUO z9Q+pgN!egl=0)kfDm(zqnjIPbe5!S&8s5i;l}=$8o?5bFu!FH&gQ(q-Sv15YQ(M%Q z8#NO%XiJ$SR$?R5Q5K8H5}PMk23M6V9~kmm6?~Vk*3`a;^u%Jd*Fb?!x`gpNGE%>v zApK22-GovFkxeM?Pm1ROD;_JK-N@^MDj&NxK3pmGu1wCI1rLDB4$zu=` zJJg>XQ+p6RY4Do1@+^*LaXi0%G{M6WqG~3|J-Op!5#Wt*j(3vV%e&^C*-TPIB_qhB z9NvYg9|{wc5|hDhDrIu6zcW4G`hD|i5-#zQHMoMpjz{F>yQNr3Rs4_uC=SIi37cEF z*mf1-9{{m=%T7ez1bzfpYCX`4guS$cA*FDY7R^PmDVA|ixEZE4fq7+HG`XugmVwI#7-i4$o zx%|bKlK`e`79t@TT{+qw6-~9qompl20aELLL#65n_>ru(cDEO4F!b&T-VIOIE!XLpbdhyZ?b}DC9eVOSY5S9<^>dK% zfYIqVXJyLe!gBR>G#_)1h7=|4>1m0y`FJSHv7oN`5M5dGL9SAQAD5M*A=|(S@%)lw zz$t%k1gS`B_v_wmJU>%4PZ1JK9K~nHi)E(xVYLlT7G{v|ACPYZ1)v{3!WT&jb|dn@ z?BG#_WGF1pa96=9+(>B$|4?DJOA-7?F5xZ~b_Ms=8SY{@2#Z{}XgRnfdb9j;aaE~& z9&^z-mgp5s1Ljy6dckAhTC#3n$7=m?Z0x{3(U#O}b2PTn|8ZN86$%2so)k1=z*BOX zY-RZuEX)7yiU|GcOZzc7zX^{N#z^;#;7X}vG#BfyCiuz9gP5gI<1ov>DYIN^>k>Nh zyfU9+IApw%V|-qTz$-qK@#<+pI$r$*8@zf(xM7P+>2=CaQgAKcPuqf|y!x4-(i=^y z@7@Wwi{Zh~fuFR*%Q^O}9fGKFTmt0vXBBN>ooQI9`BWmNk~dYgOwS_Ye&q2AT9f!0 z1#3DF(kc=CI>UW9@u@S7y{YSYy zix3>gx>pQ^(suSsjKS5_$u8`7Ug0mvgY)aWm{vp6reeBg%P!QOr>N)&%1pUH~bapEMLuB38O1J@i{9yOUcTPnYNYP z^Z3W&1!ea;*_B=Fzn5Kkv~pxdm@mga0Z+>DFId%PX->|3C%I>&0Eu3Oujo|V8(aClZ23pcAWiR?G;o?8@fsF#8z43kRzFzcq-~r8JDWsRI z#Q#)i)Tt@BYM|WV_V`GhnxUGRqB3=AmXZradtDhuotm~>eQI?N}Zb48j8^O zr7vZ=)UxQ(rDlosf<~hvC6+5cb6WYS?)|962UmXLZ&rSW)yj{Vx0T=D@Q=j~%IqJq zE8A9P%w6(j_AlT`nf)8<*fLXqQDw$dWc)HK>TFF?W{Nke%uc|QGP?wWQn8f1Xg%TL z7x_?T0#Rm-c3&@I%MHnA3$z%}B_}0U6p)frB$b@pt&%f@GgE|Ca+aP8m6n_`Ldhwn zD>>$lP7z*OcDLM;m0gBFWoOByWvAqFWoOP--FwW+PO)9tiO*Tt8CEMh=C4+E|G_^N zTPV9pkQOWZ*nQc>1S`ky5n5uyMGi(}y*AGAO}E?Qq*wnL>o65Pwlf&6WhBbPtQ7nO z{W-y30ma{Ac*lae3~NP#U(*Y41oG!vy?G9M@Hc`j82H8r8=`I~m+nt{?m3p)xa)1O~c$lJYrFm40DYSyHe&HtZ3=4`}1)V%xf6kz0|yB1-SI{Ya53t z7WUgpBspfC#IMcIhbdc_JIGURPtL8SFucs>H;neQma-H5HHvtNl;2?cEg41QuAsle zYMWpiYxW}*cCu)&=+;s)1inLKJ70Z4hQfD2D@eFzrpw-K% zP;)A!vt{T)*<@$6M=64TXl(Rng4w)yAJWyl*dg)_5ohrR(BrenVJjIn!{rz@FGSzK{CB-yYqp{y97=6{l@+#p z!TYrfb773&{JWxmB~EK~?Qb}&j=6V5pAi37Q!(ugKbbb?l_jvC8cih!!KLSa8e^Fs z6Q_0!DSGZ4_MtTVBQ!Gjod#0u|!k{6!{m!Ji@?JT+*Bzq6 zTyTkps7mO%2x-nt;3wa=BsG<69nXtr?h}3`+uMQ&SF$FeWMWcCRaR!qaq3 zp0@T7IVZE%b^$n_6u&M4X`WwOq-S4do`allg(b(Y{=S@JL(J>Tp6tmp=h*Vd&RPo9 zah!xr>tWMyxpqBN40hD+#Yo%1Tv910ABR#ApH&*h@BiT7K+mxhf)&c(U@o!R*)y3Y zx1-4irqBzeBw|~6F^tQXmz@9i2F)gN&iCtsO)z4!%0v&T;?E2FT$JE0*wVr zh3^pdDYYRsG#>7CMO*tlj19q4(cO-MXA0&S@NZfQ&jYCAutPl@z5Sd*boNd!J=Fw{ zw|bGJh1*8uVFM!JgcpZ$c(#XHj#=5ip{O~%8eq-*B{WGP}#UEPcWfaur zBe1QyKNc$$sk4=LRIhjFC3&xeF ze?hpeKD{V&y#Dz(5+8ScnhtkEGo!!F8YV}4f4>wGs-_M3= z^nV(rRp1YgCn)4(tjU&_gIat+ZJ0KjiebYvSpti$Ew2oL@6dD;fi15Lh3^OvTw7js zde~BP%RILZ_QSahm%@9avZA@{5+Ga& zxRB@}bHvK(%V{vXJ9lUT?{t)YH>GswIG;yKS50m(UF3KPAFX`)M1NCITSu-mhgqgF}siARe~*2sSfw__EcH^Ld6 zWR3iW)!Dv;N;6p_Usy+MQCzL^O@-w_Kbixo`A}QfW=lW#C|Mca#1GDw+3Q=@(3%;1 zB$q|Y-*O^6W3sUc@!esL-VsipI2v+y5@-9X8Qe;pB}b|@B``x^j+ba4jMj{F59Fu9 zwVDyF0``}p%|zAGr8j+jakaE&E{1!uWvFk`i-_CNepI`ryATv< zslGX}`b1|9)0wBBwHwO#I>^8Px)xOLZR z1~ZfFEW`|MCv8oz(QP0>qAm9B6RQHPjZHMS#qPKmZAT%92U zuap_z=;I-3URv3dqVKQzYjq~cDZ3uHy&-xWM~uFuvm2sj6ou(ITEzJ&BsW%3dLvIl zirdhA-}^IhR`Dj{?B=f5kth9Tr=xc&yo*PxGIDP!k0`tvxi53f8c|LS{o3X>;GC64 zKc)pIx{AyNBDxADiLos)Mp_A(8iaU?kMd74aIQ2Dsl=F!~T z%+M%8hK8of2t>oF)uexAHHkVMs^ZB|rL^)?{ri-RDy5%M^~!{*J^xB5gh1zQY{fIFktx z$2wS-ik6XHxC5WbgYV_k`i|JRuC;$>xDz(ydqr2cGd8Pcuj~vzOXF-ca|$(c7kTIo z9%8*_?7Esk>+xO z51-1l18yuZ2uiLUr24z(6i&Xx9rLGr785B}{_>nycSgnu5bWJI$`Zh&bF3jBP;Q-7(lxG!`8X_HvY&2Wql$EuH4PyhwhzoWG!) zD@Xa9f9uRN=gMP}^AlLjHg3;|#XVe>Pz!o|qKetzxm zEi5d7iH7x6Hr3jfz~+A_O^Gm8yko;0oDU=F$H8-MK8!JIG#)pAr!Hm#c!b~G13xgK zIXUAmTJ4}(+wnC~*LF*RPK;_Gg2n-hhU~E0U^?=bC9rheo4$VG@Udz8Cj0I84wO1| zL0C5@-Jw*co08`WYzv;M&s6%piB0RIT%CGbYQ7GS)`$+>6QHaN?8nrQk)sB0|G{*_ ztv#J%sO&s#2-Q9(BbtIMnxeaCOxOyO7=`ZR)!BZn#QT#V>%j-)(_L79y=XZo4}%uM zXKOZxI=vWPbO@m$MGqzkZ#o)X;fJrIE_u^I6)5Ax;XDE_ANmA8+}lDQN`Ts3 zh>NL9m>oih>SKB$La$ZR(g)^=pu%IIx`1;r4VyX||_hN!Xsw zfiSsZ9DTDyJ>?uTyd#L=hv%y7X%pXJX*G^dCUr;3c}9abSJD>p2%^{bfm8iWK~u#A zI=}o0=y?u$>Tkfi5}W`^2DB$)=md6D?h4lZ+9PSf3{LXtWsCH&`w=O8qhgi@d$@78zpQ1}t?c?90M^(l!@pS9-NVs9 zoZ1z8(oTDKE;-rlgL-Hw+UyiN#!po@*vp$@-qzrjj+)pf})Tw9da zHDDTskjt21O*v24g&}8?J&Fmz^nAYp45!bC4k+A4~Va{~O(+Frjjk)%2oA;U-uZ%QL6-o>wgQrcH|#Ho>ruyDc=-)i{=jNx$nw?@h3- zmids^TYoFGt}h+N*UpkQW+}_=)$k9)kNV;G^fv|V3p&(Y?rGgfa6X=>El??mr!0*Y zINjY-zZ1?e9f&s}Mk&%aIEO&o<1qVq*yB0or!Uj1Mhq#e`JGCQ?+o8ga>G$6l4rCj zpT5P@ovYtD4~hl-z#1Y2^f{H`t(^UScKR0>E{+M@r8Td2;2R#vr>PVM8WG>QT}ygu z7oSJh4!=s(reMyeZq|kI4r1`v!AAw2l!D7ahk@O>TkR?Q-r7WQ zF`1i+8^Nb>2N&|uxn#BmRh;Xl=BZj&aQ;6RnHKUW!pysLqe9{6#j{vQ15I`DR;4yB z5B52a*^8#89*zeN109#0`*i!G^9r3oVJgj&>5yVySLYOa8b_0iHq?1%?LQwtyCsj@ zp9IsFa>t>pvLrT(ERFd^A-K>CxSa>`Bc#QBpe1^J%&ye}9 zu!aK-DYF}lF;wT9X3lOf*3gnP!webkcSb{o&faP+tDjdp<|EWCRT1j+VeS2K9n@KW zJZ_(+I+fJEtLpD1d{gN9Kig5IM_J*3Zvv%lepm#iBoJ zw@*)Ub%xA?lZ<=-%rlp2(>bAgi;V8iWpoQV5#5E4Lbs5QrTa|`mj4I3MPWww2NSx_ zl=-_2lm`0g8J!JAXAzJ^DpxjEO0ICuYV>EByEe<5psmcck6|otB4BLZn*UpwC>KfI z&^;f~wO69cXs`U}6s+M|jpmgBuj(L9)%A@~J8&RVQRkItH8*H-t+kuZl$I`ct@T4- z(Mfih!KGS_9B;j3KT2=h^^#jiasy)~;i4x=?&O`+!opgJXdLG}S=*JFo4`Ktm6==d zZ4J0?SKd?o)5=VKePbsks-Bg&9W=AO%01F?m@I)s&-70Vfp0unPj;>be|PsV@n9Mq{f?Qoly@`Wc|?ZwlJdBbJ{dT%bBAYLU~;+Zu(D z4-)f#sDsLKRtK468Y7r>+dRlOU87_9VAW21_;2&Mb(YU>Wcd_y+wGaw>Lk)x7FS}qP(Z=jWRQ~y-|u;d&BV^cQ!(nz@lgEjSz6IKoM;5 zGi`5#!nd{~*WP>-pxT=YWuD}%ewa09gey2(Ma67|=j~);l%3pb%ImN`7|zDRfb6i6 zl3%m=h@!T1tf%9L>e-Kge?%YK?;Bq;g=ont$j1^2FOsSG)k5kAH%4d=>&(1Nc@i@S zHmHK1sz7tESY1q%Hswh^k8zYWb8GK&7k`X=C>`=a3zMU5g!#cZ`RL8A&5fT05u52( z`M5}j$)Awf2tEeR@FF1|m&@(r&cj8U5R9em<88E!I}TTh2`i~5pTyIe5q?jCdXN`GZ#NoTCst$pqyvZ2V{$Vq8>wfGa+Zmkez4FfBk zmdvFt8TPHq(zRCL)2@@}+VX>&qHtrll}@86KW`dU2un}Sw@F3Q~n>IZu z{S{NSQ)(&PVK~X6+{jAR9+nX*wd<8~dQdu;j^M7fhaiHhAlT|hN1?`A8J8k@f*E+b zJECD1gjIqnfKoUUyS-fegspqoZIpC5T!ei}SMVR0J-l~UbTydMLp{tT zE2|%e55r4?X&`HS-aI)Bb`6&Gr7!TUe^Iced_tyYoVo7`xI{z?mLxN{e{db}uJB8I zwT>R$N;ZdBOb_-{xJ`SCU48-$I)KFT2N>*u!#AbL5j{&K4O0Z%-RYOY3YJpXZQrSU z17FtuaAORenCM#-_F!#XFWR(BUtK)4WWJ?v8Xm9E38EYDv=9zo;I(30t-*cRE4KoL zOx%(i9bq3p-K~w75mj{3?q>K!@H}r}G_+@~NZB3}*+csF%m!45YV7dg~ z-{BYDNJ(+>7o8Q#Mx}8D9MTrcF1}N>Jf)miiosWi z=0v-z95Rin94VgjfMrh+n+IRRJAm3=OlirTI(t}j4(9!f=JkFzs_uz-7W7<=mUg6C76qt(=E@{h4+3X9k|A~_#Sx9*(D@%z zZDoqLrg!&r_btB(w9aW=K?#ZG zFh2(!onk6v0|pFXv-XX><#9PfvFHgde* z66N1h!-pYaXjV^GYMq)=I@RdwnZm7rDLqqqCNH_Zr-+@>Er$%tE6ZXU%eB`}P|CQ>)>KV1H9S!J}j(R`Z^8@eS8^s2oxIXczK+}dt7EDSNH^g`Z(n7Dj=Lwx4Ig zQrbp=j5bEgDYi7Hqd;JTowE^e#(a|s?{`I5aAObJ)4`^gTD#*@3`I7UQ=F>(NKQ_o z4>rY-+TY~lTH9b#Ij_ySBy}o>wb#ixUB1dm?Q}UUt|+d3Tu#F*%35U$9a@iLIoREA zT&;q((oQZYY9}XVL{7o?!rouW7Rep{nveK43gmLSSUKIjc8gNw*3Mv4p)^9dQIchH ztTgAS9%bW`WN0#42jESuI42W_LpMD(tnjiCh3ym?Yt5}0r;V2Tc><+iwei)un!oyQ`JAjs?tBF|hHjT$`8&y5tn7G= z9j)TolKt-#;{`tbO+jPPo{e>Hww#FPA-DT_k`W9}1K3ah3Xz)+iN4Wi*5PJv%RFHOZzYC8}SE4GqzEMS|0{f2& zOeNqob|z2BmhhhmUH=Q8{-&U@n4J`uTesbbd~vFZanBWmLPIYBg1=&A1{*Lv(8@(| zBER781ZdT^L%n%_raShde*kq)i{7TPWlk@1;MY9NjnSd3+rzn<>puLXz`XhePfna`Q-9CL;}_% zkDI{EwP^wq?BISCY-+>5CKG-;-JcwC>(SdAy2HW>2hTA0!751b(CzhgLblCx1+bZJ zYoaTp-pEsFm9Fgv6{*TFCDcCAReX_fXJXzZ^FU8CM=oP<*f2Phr0p3D(KnA$eB}5ohnXdgov)7>5+Bo zLpZ5k;0Q}kAN7xkXC-7_TA8zCj?eQv=#zgJQJGesjxpTT6+I;6)g)*)lY_yfolFdv z?^f&%mcMk<;?PDZ$lU^)oH{2DPD{tcotDg!Z}v{6?IE3Va1<%%feZ0m_qy2N>ft$o z8H{u0NQEmSd$%qbqYkTPA(QFGz7@itx79vol@M zbR@Z8|E+%GU{6|yWAal-A0r9i`|&5UrHXPXW?b$=<8tLo*v|BozwvBsr-)UsujWftM9v?Zm6I+9Rqm zTJIB$@cg?nC4)M%$^tEOWh0D*7HFbns>ai?DuqvjELOaH3a|WJmV$7#jRVjw<<3I; zR@LJB6~Nge7HBH*_XIFi&}be3;5NiAr#8e7$jdko%@8fl9#*kCYjIz#=Om6&tUyVG zm3lnfP)AD9l3L27pnRiYrd8+A`XtjO;HFsFiTVof*)Yx-=0 zyQd(gxY0*4DA~k98-1H9v`$~XEgh7|5?Hzu+*456z7>YsKG;h$x+Hbq(XbhS);$F! z?^{Ww+YMRzC@K|ka|ur;k8CNgyJG5>$W=T-5Vm;KaMOy)Np!eg`k!YxHRmFfwBY^PDh=y zkbY$0`xTxh{SoB~^HIy4>WHqLun9xkzJM^Uk=+U#ozCZ!n{u(wx){F^0PXKy*%@w) z#oxCSJq41Wk5R>_hfqHya?f%Ig8>33xCgODLy8<+tswLph0oJ(h$wG_fmbrFdjMcM zW)-2?9@-C$Y#XbMv)(at{Q3TmERy=MSCCg01@d`KUhf!&s!FLCuOlp5a0^8#yC{|T zX+?(6zlcyJpC|N?YcfKWN;Uv}OwlHybJqBDKAyyJZ!>I&&PZeqarRc!<)}?>_d)OJ zkQR%IJiYV2kkPBu(hfNtkH8)llvS+70R0jyp18jG#|rC3GF*fCgaG3=m`~!R2GfFM zQPg0{D{U~<$I8uRLJR3yN=vBDj%6hl6%*CJ735>wKK2We*#EYVeP0U;?WU3&x7}1F zn7G}%7T;DMd%c8uoIX|=9NTUxuj90vVoF=P$=V+k$mm;w)!NN>rPW@Rz|x(d-BjAX z6-IlzsnmTdp7wTA$@^APX}g)F?>j1%LPgrgjvfD;)#B=?KJCVrE}In+Kz^f|NlE)S zXCUrvr9@MznlR>0r@OCtbH0pjRT-sv@q{?97tf7ygvynGTG*pfR?8Atbgi+t^NvE` zTWz$%L#;Iyq42GC8W-UfZSGG1%6vxVS&e zb=O_4?fAso^YP4M?z~kt2=^kv^eW3gVN0yEyb|5|BVMzYS4!?1}wabKTcFwEO+R0w`71&%w2QhYB= zd{2_Ou>JsCX#UhO$!(QfJ(KCRQ!SY8*vBU z*nwpyEF&MGVY3C7tjlAHw?@YL8GCqzIo^yU7I;o;D_)g;HE0M7G>eK zD#E$o<=(PzIU4uFx^ClAbo%R0cY+l$`B zH<*_f3gT(pu{|aS0<;`+5Mknj`AjQ#@gZi;h~I4H8V3$Yb#Pk2Tv~o36YcWzR$xwk z4#Sq0pTlu`(cA3%cKN*=`4L*==Li{V-oaP$Ge46bc_2SBa`K~CIr$Mqjc}~|9B=X? z%JTAK^#4D}k1%XVek@aKGCsMSAwTaVqLZKTgOs(}_kSQiVyv$PKDX{Xw*NezmU`T= z&Ch5aH!!y0?&RlNQ5n$un}*a4Xk0J&EYVvdm0#gtLUA-?Bq5IxMRa%XO}jf;0t=@J zMHbR`nBT4N3+vCR&0e2xv$=wQiCR<4?^Ealjby!j>t*yncXO^{h(i{qW1cH&)G@l2 zR%bG_kIT`Ep8D@Wt35TJi{iDfk*!iBYjCU)>|tfs><4no{#Z%MD{@S%}eUOVa~j8h<~MGSfYlnGXW<*!a>tXHw+^;dKl z<8WW6UJ?CzlX}|#y@NcE%QbV;Sri@*Ay>X1LXMM0|4Nn3-N(sh?O8tc_uzSw#*rJr zkF&KuIp=M>n|#D!T$^0wA3m_Kw)wRLt0`E@J~SO1WE7X7#H_(_d9rOT!epwz@e%gk zrqJbvbr3MkyOiutF3;QOfvaD+?aK1JzyITi-fb6@=e?rqm)=gC`z@5`o%72@y*qK@ z603Z7;@)0i*}Fkjo_E6u-`#xSk>z>UT(s9NJDqN!g=i;=v9Rp57UU%F=qtC~qCD@u zH-Bfhg(sEg9sRxUynZLou*qvDompG9nFYP$xzn!M=|#ixk=nI4E}UPU_xL?G{dwU* z%g`oWDAg8`Zxf;&pI4+`8*sN4@E-y`V!)LxAPTbz%kDJbK`r3-1-!<9?`Z)a67XXN zJh=t@nt)9Mp4S472zZPEuWbQ;Cg6bv{7MV>pn$s>@S82*GXf47@TnG%L#u^l%%;ia zpIX4D1r)EmksHz>j34$;dZ_HA_^y;N#~V@RQiilKrt>w$6tUa`v;{qwjS8P+lXgCd z)K?R|zbRl5mr=a5n%?!U@_Qt~(2NV-`-(_Ha|Oa5rb+1Z@vDH4ArB7EVwX zE}$1K)h1t8INphG?Jlf(v$+x;o|p7iO_)a1TusRIbM)eo=;x?nRsZVN1~yS$i{y>I z_!kfA+pbvR{8a(_!cwVkp|`H5ux_dsbfRPYNmTO57??yPo%rxJFKEbseR^Srj!W{J z%s&1q3Xc;h30|%OPc8TDBmwb}1??b5m=^e3x@bdXJG`%|gF|?&LDUj&!91bMLGw?-aiD*Y1 z{y}NFNvi1osO)$W_kA5N9d!MSHJs^Y0X3I3+RMt0MGGiyTk@)eO$`iNYSM8-WVqB9s@_GyiHQCDs@x*K~o=z{+#9%b7o^X4*n$N>2#+^ ziZb0PR-C3gr3i_%+0jh=V->IIPw57l(-y;gBbCO@urmG}C65ZQL3x}ePO#}C<0a+s z8SrzC3HpUMk;hX2B9Gs}fMrHeG#BrHW>*qqlH|l_bHHMq0u6@V8o8YqdBIi)Z&x+! zQ|DqbJl_Jp=mG~`B)7?{9b1hzm3N={A|-oEN?h6&PK7`GJugu9D0#!>l9T)lpXPGx zC~(5#1Y;xd!OnT=thi+3vB#W99GPa$(5%lDc`eP7qfw$_G?=I$527?D59Z9|fkR{o zKb<_N95Z>aESWq^g)+&*Jp5zv*W{rJG$#*R08d({eypiHaONb@9i#xsdlr4k8zgg- z=H-Jpj8De zY<-od-kigo-3_bVtvElSk;uiFna0ryK}t_Grv%F|&q_mixloR(;QdgQLpVDP%B7DM z!jauuD`L-#L&Tgk&gJEHBJx zoZ|P|zo4rZ6)(rqTIX0Ww=0eJFtU&`wJ}WLBm==iD1`c(`Sdphtp@#?C7Dt&gta@v4nwB%7pLQc`TosfN5$TWTEvIG`AyPqfozA+`;%*asq z4$-b4Vif`oo=RXZI*o6OgIbtQIQV|779>^(ICwgSI4HBlLHQw*TJzf#8OrbVPsqbF zi1RXdsPxA0@PhzbJUmlGq&yTwV|gef84u|V91mp)EPBR6A@GeY?K~6;KQcra4?hGD zEf9Pi56^PjtGbu(N4s#kD^iG90bt>WF~mZdEf&g8{o~8x;n_rb89Y>iV|aKDz!neB z6%{EDMbcOv3Q5Mp{ZbyvqEmH}s(TV;phAF73q@dYS4VN390~=u{RPi>cpgqK`Uu~a z9G;!Y;fE!M3KA;>OgtY$OqAJTqWoU299}@2m%&4&H-?8F1=!-@g(4#5p(q;5Lm|m{ z_{NlnvIG`AJp4F@cqp^Q zL;1a29)5y2FN23lZwwDV39!Y(i$z4rLs2xAheDF^aQ~EtvIG`A10?4IgzAYYV&oAL2Baup^AhANg!%t&~hca6{l;6wc;b(~RGI*%;#_;g709!n~ zR79ja6h&isC?pvV4@h|^OJLD69twePY-#7AQ23D{N_cpR7hNeyy9}SkT0Tp=8<%5v zj-P!4C*%S&6~JrC-)~;QcVM#_1{(m)7lr0ZfQ_s8fQI8UZD?8nnhM}G^LPzcoVGF9{ot1rtX~cGWhy@~p8qup)i<&K7vw+F`7_W_n#bhR z)H?s(wD0=ON-vG;miWwjA(qSoT_5J8te6BoPqw?#p*>H7j zYgnXpbDf#iuQ=XO}jj1YxiNb|8^3+7`T8uW`OjdjrDTF@CB9u~}!Sgj;YDD<#F&54Q1YW9%zLPd9}wyB0vc4I*VohE|bN7NQU=~jS-kna~xWi&SlJqcJc z30R;Ab>G+f$JTGKk=VM0EW$lqxhSCVuh=pPV$qP1jaam z>sbVhPsZkroZ6Z!eF$=GmKUEQpT-G%lk$C>@b#N4^MY1`t;v0Ss)E&T7I=O67L59r z`BZ}ed7dUu27efCetJ5#a{X2p`Zk4rJyVKmFsRVVz84uq_ARe3ogs!_%0xB%3V~<3 zvQK8U!sTYQIr^_{(SL@p8DZ;7w=1PP_*4TH@CV}$%C8!q>5LCK<1A-<7{m5JP+*01 z_9{nGh*qV_qJvZVC86aZ=8rTk-@vw#@q?n{Y^ou z!7#3$QGN zSt*;_#WgWne%ZfS3yW0rJf|3m-zY(hl*n+U_I-))KD6X^{zwGprn}~J&-jeS8%Mw_+6Pzhj+PpoXv}`)FS2E2}>b@0$gD(oQZQwY>sA>)pDV-Q z6B{h5Pv?v3QWe#n2`@yXm4psed^u&UEP;j7p$b=K3dzJq;l)k5_O&2tUZ7m2T{^UB z+;kA8ezSAqrmV$bY}7cPSV;r-tQxp`Rdt$zR)d9U1Glz+)=LbcNJBE0>b+&CR9*b4 z+=%Hy5W0S0I2|!rUvMAExxQeckra4~g?EFW!`dkOn8z&5NZxpXSFL|n)G{jrQnIH| z28W&h&@5*r@1rO{(cVO{CggiWvq%#N%*O~W(UR#z%kuX!)@VLYiTu}X!1|p0SnkGc z1Fi<;#BIPA@NKmL*WqYQV3dh`#XrZYvN#f+n^xu9Q+bmmu;|$YMhJZ4MLL1WP%t?Z zqVrvC%y!H_2{aixehRBzw2*u}2Ol{zNBgb-wC`P|zMYx?ZJcP4m_Z8TVlc*^5teHF zS&U_$A{3pVfg(Y|?FeGZaw3kXROZgUIObpqjk&%`fT3&3C5i9 z#R}XPD^ct4E%iu&CVS|qvDluu9RjE1{{|M79XiqALdxbDO^&{BTPR!p1~#3n8&yKN zZq%G)M*V=!`Ej^E;fL!-#jmU%HL9#1{gTR`zVp-g$KsLoowul{&+`1?K2t$gOxnKA zoI2UHN*`&KnMm_^{n>=;jklA;OUN?UHM2eIChPufJ~EidHvQ^S+=AHbS+V-FhGQvbfdYHPoq%DyatVQt+9 zOXr2TRD9!O+Fxbq_%632pryac67pSUe*oO(g?B@C^IFJlZ>zn+4)BofowHUn`jy6C zG%oxrpP_y~d&!b2Q!Rym2JGIlTmrj;yK#a$t9>_sKB;{-DJ5-tSpB;M)73u(0hvyz zywfV5#j&wbe6ymX<_N>o|HQWlP>sKgF}SbuKo5hwrD&-yd`Owr5Y=L4KJkr;*cS`8 z)iF!N{9Vlv4u{E$l*p2`R$~+`S7Xd+^<_|UX7xpwTzydvv-)BwSbh1XM9J#NeUg-g z_4~#7TZKC9B^TBo6zsNk*oa_XX@@-|*zN7GM+Cbg53Bl8!MA&=f#YG=A^`Vm*D=*( zy!wcwAZZIvBQI>XQuhT#TA!fR;6}HKU| zKZ7yBZVL1x1?sb;jOT{M0=a2{f_vsJ2akbz$(N1iik9-6#zIb8CCgF&n1ZxqN%S)y zfqub3`OIkg1&98pTJ%3IKs9~>!{|46B&VaF&DKi%V+HDq71`)FEEdSoui!9?Z#j56 zqhHZ-^viknhhR#pu>Dt*3nwrp+|26OllWJby~Spl-=hpmA#cp{I>4x_1H;>vM_Ax; zmm=IN&6|`*$aWrr8kp(z7a3^|cGtOiUmEPFPr~3qzfN9Y8_|osW%m~SHs#T8KF=?Q zPeID?>~gqH=Ctw%9YPK}>Q&0+@KiW6)K?Bq!yI8$mkTibs`3cqE(b|(E0os;)5*_p z_sC9kn|6_5urjhuYR`g&Y*tqWSCqq}wHR{ol&%fo+AfU`u5D9$imS_AjRJmL5(~VIfpup__&boWy6P(l0#mdLwS@07gb_CDxN8_0*KjuV2%a5^GI9P;&K z2uc7tf?rdi2QF|_bVS1DJHyfQGbky>qJjBU*>tEwI{FUwrV);nGjHUZX+hN10BpU# z-Vzu+`FTegW^J!2a*V}{>?YuYN&r@UUncl9j;G)X^Ty-R&y7dVIv%}3JbIF7E2kDa z_NC>qdzdTG6X18Gd3-^6JbDzTe<3WfVp^GW7EV;g_<6N+l{b-u4g_XSiQJhkhhGwF zJY5d0%sQRE{e9K5mE|no5QSfUKA3Vnw zFqsgI+}Vod&fN8YJ@0V-)_{H!aJ zS=Jv)vi`7T-O7I@^voyk9mA8Sb_`9K+Hvkt)y1W$brx91wO=A9qI@i$ z{y@BpPjk+>nD(u|+M8=dB~GUEV>~(OpV>QdSDWN99Ua5c{@J}FM{StP;%CO?M-nbS zDpT^Mu^uO6m}5?sUdyRP$Kz6-$9{*Cxj%{(9l@V45V|E2x~IT{dX1?QRlG^)rx`7E zeR8qM7>0BO)C4upgn{Ch}|wEFSFP^ryr-&L|Fn0rzN>8 zB$uOwd6L49NB@Z}B1Wwj&w6?EtDDZ#KPONBW{!T&{X@Tn6az2aH2m8$z^k4=Gg!E z^glWg{ad{}`qc#G>EAj}|B$1f3m?#LA-fX#*QNB!5?DCVpM~UFxG+}~eorr(C&W(! z*7}XiU>n-cf%!}&4Z6ZXS2yV&ozBVe*SY;KPf&N3Y&h?d<7}^DaSIC{5 z1Z@nX9jJeLab-_YBkC#f&lQa{bjMchJ@eq6#!ms}+G{`97<^u^t$I7*jP=Pk)alv# z&ceLFwU-ALBcUGheT~L=*J>AW$K46iz8G8H%8MThLKQ-=x`Txmc8>u5_HxrrIyEgc7;#6Z>sJ>X!somHw4X@s9eYc`&Ct(lHBH zDKx%1PzBIaa^oXf3&h+#k{s-ZPdJ-Uy!C-hUjfr%;q@8^WiqV=i#k_HkZD0zgVcem<1{F;HmbFQ6Cfkd%M16uc83KRksm0)UdEJ77*MtlkJ$f!$(8?Xa(8 zN{*CLR`$P5(*IqWzT}TnJjkD=zm-e>eeLP*n516{-mBz`;Tah2t(k0Hp+=&y@H1zj z7s;=yJkP>iHeb%>EHm1IF<B*|uht-ntJWBA zIEXcA?1l^~9)!bKO@>+XbuWX-5?FM}aE8ElXciIy8O~7n#?^2Ua3{lmNSSOFxc3M8DM`5g3vr!wm5I*4?luwMBXIn0d{^q@^8JLIM;qTI?i2Gp#<#`yCJy+%m>hYL z7!WSOn(%$8LB)k|8?46nU!{DPC9vq?dxpSwXtpH+e9ut$#@BE=;PAa-J;qN*A!qDq)8+v()4Rd(+=Xuc=OSl z@0g$te@yK7pX#vE9#@t=lJjVjC9!^DS^7A>Em^u42ePyy`SBuUKimmxB1=0PR6GcG z!D_O^-9=Z2WeF_0WGO@7J2bB(0*{9T`olb$s@Wb*o{aHZq#ucxU)ie?T$Ox z84(P2;nOVOL(HobbCT>S2@C6=6U=LeT_c#Ehh@9MMZn3fa7iJOeaQRB2Pb{l$5Vi0 zPx#%o0ckP3sH})l7vJDN>}$o7Ejsa!#RBXV*+rx6E8jo}%I0SAd@8_J z*s9kK(z|=Xh?;l;`YB!E+_}|IzjyU{xJkpolaPEZ9&{Q4xE=1}a$T-Pjcy_JRd$F?Q@RMWfL~V~dF;8cU2ZMx*hX z#6*oIn#AaTtu@2pkbCd@|GxKoKA$Hm`!{RNnwd3i?|t^(sF+hM%3{t<=9ur(F|UmJ z$rJ*N`D=7cb>x^IQZfICVqOgstuZg17kqDX2=+7n6ok)GjK)dG(~&=z%FnjaAC5HJ zTpoMmPsE@4rcOfP-xIHP{EUfS=$Ze3z5g3%i(fuK&P+g8H!GIAa8}pNH6Xl?x^AZL zg0#l6>7D==V{=bH74tv(F1wG5dyVpKfuKgpcMGH1Ev;fl2PdV3d(We1bF%#!h#mV$y1dia-o4 z(i+;Pm5vH}#ex0=AwPqNExtvTjcqME`n%yOqdw!5*@0q+{Ycjw_*EppUwCjV(MPCo zEDbZ`hXfnNoAvhtFBDm~{&Wm+aC`j?LVT7-pAm3!u)Fl{h;FBZW2yWO2o?X*dQa7Mvc8*6i78D94we(UAdCrC`%ew? z1^jVNU*VV!Td#1`N1kXi>WbF+-B4JkP;Ke%k5`yw3EcivtONZsc@B{3O@tx(5l&+g zzLjY5?@o2#cVs%?6QAmV`zq>1d`@j3P5Lo>{~lJ;YWR|kel}lu=~=3x#?yIL36_a> zJ6*l>v#V2jp#^?U5sO@qh)v7wjf_`8`1&&@UD#RLT7UWrI(SYY{riy1zHC)hTm5de ziX|nEWpv>=1#erFJ_XyBJ_FVdm38we)x*CH?ZwKlnyj*rimvOS>am%?zdw~=^Ym3) z%%<1;_IRlFSu6-&jZx2DkpaFpV}}jIH2MR0fIpl;3jVhZ|KqKfofs3N&{VxG(~-PR z!8gT9E7}cR<__SBQmTi4Bvy2CCo=Px9IgJ>8UM$hoiZlUA-zsPez+SW@Zlr+pdwka zR=vo?kL3=e;Ls}?&7V;vZH%mMf>1%~g^J7nFwvc@UiPp);M;sRnc2Eh?_IQ`@~cdy z_VTWz8(vl|UE*Xmtojc_o0{ojrwqcn^SW2AFs)TyJpbXPii%fhkS0^C_?3&YTG&*! z+COr0@YM5clu>!H33QvO`0xvU3lnwtiiI!EMSpysO}%14jk4J^88J?@Z_=}&c>fsp zO$v$HH)%YT?VB7b-Z$xwpU}BV2h>ldF~fW1BTU7K{)E=LE|_SY=pnhkQ5ue*vNXP* z{SlUlcZspgcljFKbzz|&C7W2i4y;_SdDy@GD`GbU0he%fae{om#zk(3GD^uqr+**o6!tXzRU z+{v!k1^x-bKB&9tUOw4U`VCLoJMqhPFntMl(pHM!;T(ka`a7HsvnUZ%T_TpZ1E<`jt-;c;=qpzkHgcv_a+5>?kd+d|CxcqYD6Q_Y9H~6EGeZ@Vdoj z2ci9_U7r|?9L`((=ZOEk-Y_IIq)BMghBR2fj-Qi1`iFozQ}903p8}S=h0iqf$;(O3 z7@bQNhqoJW89TX7Y#&&XB>B6hbvng%jzZmfEsErKZArdj*`8CrX+tTAqg&yhXz$dHWS2JNr%D_0 zzo6BMMErJ6G06`*kZiMzWI-E}D_DE31sy-A*xRcjY?~GCPOuxABX9^#sO75Vm*ny|C=oQ2MCXKqeJO}JcixoJ&k0hOh zy+d<4E@_Yx5eRV(y}d38inbe!Eog~#yNY#lgD{q_wwbV=u4!cdy(87|a{h^x9&pZ~ zF1EcwWAZjGg2qfF_wo)G8Ux!ys1<8_QLc`IDc4gTtGAlch<(^-e10us7e2mchAG)J zD#m3|3`cjMcy0|O`!}P=(r3tuL}&PPP)%(;41zpp=(b=c$GoZ~i!9-M}-6 z`sieR>Z4fp6ETw7n1;RY22c0Utm(9we0IZ{o8c6WQfGL$ky2VNC3!ZR(tk&9&hSxB zs!1~(Cm8=mlzxHd`#p|TkRwG5Qb@Mp`8$^9*K4h7%{Rjc9&i6@Od~UI4$YS~*(BF< z$_bW-B55`7&Y|?4xg=+DUo_%Tz21TRtYLX+75VwsAd+3srXu|A6prOSb@_UO8zgg& z2Ro)?8^3v-#=r!OgBd<(GCto8%*m8;1!H1{L5&CHhd^9po7_NnIpFOnZjjWL(&INf zEUF1h=1@OA;IVoa$CxwB97(d$Hj*#0sqgaFkleg^1_;#k6;*l~q4!YBMgENoG3Bhz+EstuoKQAYuifJrfrr~_V8Q}~E za!7VVOEc^qO>5MMZL~U>U7q3!U%_EK{ypAfJV){zX)Y{exdZzm5ax61TJCgMv>37+ zXdbnB-C}+h#5ZN5#=|GJZ~Hwjy=0{0k!BwPim3Th3Ynk z=TXmTv@*YHwm-oG_BATbGDBk@8Yg3%NqQvGO1N?ut%QN|Xfo|U#!F?8ukVM>ho#DhDD&;~JS?=Mee&onC$=(}R1`lu>S$Claycj|963hE2arb0D zY3%0_A8*`5K7(+zcZ2jL6#tuCpA@chGu)${p}7yq**i(j;Sv+E?QU>z81?M>5Gm21YT-^({#r4xwIn$t(ZOF%;X=snp))cuwUE@faC1oLoS?a}vh} zo&$8ITJ_;N07l*!YVKLv#0r;w?eYA_4f0EZaO^!oiTmkDT36FXQm&X# zk{{<%N*u4cVk#Y1#W9BCwm;>nJci_=@sxKb_vtx|fEji*xzWuT&hRW-&28_2QF4Z> zA>?ygONw(Pj5GE%jyl{Q$CEtC{eFa3fW0BKT5jQy`~%y}4<(z!Y`Hp@{45S8xsQ9l z8jdSG8<`N~Rmq6hj^7(Loba&%JSW6m)eLVHl04y0=cO-k?zq7pSybo2z1-uc5gN?QrO|Q&ZnVn@9bs+WAVzzg>~b z%q7%=Xr}5tl&&+E5WywXgBbk#ST1m@TC;%xA^-+p3tix12P(Zj#Bsjo(wnXbW$0U51RIy0nG$tXsui+QL?*qa69Rc$G-C z{$as@-DJ6qb>BBq^a;}imjS!?+y+yqV73U64FbgznLZ>8R*jxrSP2gvw$XMq=Ca^*t6L)nvLnV0{k|o$wtO(tV)Gm+4x|^*xrNE)cz7B(m4{ zh{X{YChU+G74t|wkaQ_N=UBI*tCOiOYU4Q48F$e$kzeIQkAz?!)= z%Ab$d-jIntU0|P6{`>$D3t3DnvWW)Z+jFFwlSecVW-{e54T8B`deo@zi=W~cU&ItK znrJYrV7fh0Q4v#J)(wG`+&(#dWRHzv2&`gi)qiAr!xz+I+hQKcSmBaUNU_+XL=#gE2gtEh^B*`L6*}sRb&dTu5=Delcy_k zWLh~_krPve(YZK6W`GOR>!TF8G7Z$^#N&=TOd)7H>fTqXRNZn$g> zbv7158>W$!w}!eIh0w{M@p2G#0b&{SWa^H(08s>qOq<<_RznU`tJjEdUBL1H20Bs+ zTVM&79*tgNaqh2TdR~cW7wqAD71-xn@G;XY_W3T{U~0fV55ljUZz_8^3KmK2b6QAV zjzc9$Gr+y^)=+GhrmfgMA{W+ujQNP%nEGLPMD9$xusk9UCO-#7RhbfXK2N5`I$t%W zWjdc1Q*B*(b*5t#m6sY!0oqGVrq{HWT1=m7OK+xdZRyKYTU*wZ^cRf$(o}2xxb41s zh)zI5iGvq=dTVGeTs=E*zJx|p`g!Qg^l^fs7)iZ(3@JdIhc1#v`9y4OEPM{#qy=?P zx2{MG5Eo$}Q)Vd97l5}FQP;3-H{AO#LoQPXO?gby+bP``ruvbJUT6A+=?aWvdV}*_ zf$>aUOjltd(|b%`!W1SSrmtW+lh8DS=>w+gFpCLK`mttT!)&I24vOY5b>z~2fCWsu z+fVH|5ci6OOsn`D!pGH;4ljyT0Djgk|y9|{4MM&_zkwn z=~zM*{_qluKQbwL<8`7Juv5-J-7Zf>`=mq~gr{(^AcdltZbWwCeb(K?JHN5uB91b3 z#Rm^#@e5F&GPS`wz_Iua??tYQeLq0|^~7~q5q%EF6FNK#61SxEae?l5mKlpraQ@8s zCgCq&vG_d3uT0PIlra`U#Z&gOxIcLb6VI4h^r7}O7Q#fW9nqI4T$q`t?JuC2a9~;x zNz_ufGIcE=YAvcVEh{2wCu%Z%&LwmdzD!4$qD3H6DE?uJ7Z4*tnVzt&s|aVh$<$p$ zFfC%so}wMoXr|sGnrR|aU(ub(8-L7w0sTZ@6UC)NM~ce;F~~%H6y1&5K1d8VQ9q34 zxC|C4oG%`|Jcl77jp?gcfG9Ck1BV-i!-#em?@`B|>ivuEv>p&Gxb5A z(c&Y~f~gYEm5)U`roVX}9Ty#$BG~ezh~e_a@R;~S^kCgEuGwcIfoUw4a8`_D%LuO7 zd6B}p%IxKm$YrwQNL&%)m`pqau8PS_H+E3luZijSJeCXmGMf7Kx|q#$f$3{eWH%Gr z{%$_$Zip>Ruc{J#gH$$pz7gB(sNbt`Pk$?RGU2Ttw7f6gWm}VcsolE(u zX;Q0q9i%cZzC>s_49{p{@$Bpc(;++qh=qE_OD>^uBPs#^ZUTP)zyh)uo`NK}{4@KXrUVx*D4$F@-6e^W_*bmt57^5rV=L3!yrRrDc5-=zJa6Y3du{T~HxAiYE;NqXcv)+*TNFEg$%`5{G#6~taV!zQ2v{Y|49S{J@rL_c`?i1n0~??mgsl1#H+|Wx&vvJ&nnQIp3nVmmVYrVUP3<2@yomKBi7Rt9^CmyzgG=_+X&z_rG|gvz?(uTegCX<~Z63Q=|G%rtMJoQZ}%m zc1UG2APR|QhdP7rARF3Pc9Ib`x@e@bHpIw|HoC4zWxDRNr;V-$Qkkxo9Au;GjZ~)V zD@WMqVv)*p{bh!QM)C*T+5t#qx;U9_ql-r>(+!n*Hk5!=*7n45x?ys>jpguiG@=}h zEJvftQBpZdK`JXRl?dCQMo+q&WK&+IoMJ;+qX zS0T|jQ{`=x@7q{zkw|MWWINy3$PjxE!65->^=d+nkYi zcuxP$`|VlzJyT{Z?E&ZIPfWf{7vy6m$@GPM#&n%`!^`q-refaFuS#i8>%_@$-btjr zJ@v{N-hsZBu1w!D-H=t8o-%zSYcbX3(Q{M!F%4nex3WI_+*d$8Z^>q?8^gLgGLq?G zD_XDb%1&%qzZqHnOLk{nZ}$0v?9IBhy-D|z9Lltl>1R2dEl1;?8Xz9Z(M*$=9?Lv? zx*ziSINAfA$Z1Tz;}y+fFMZg{6FHaj_2qoO$wh40h4+qUa*h2AjD*?|p2;`u%f`fW zq%z%~a-$9XRgPYiqrc11OQbTNuaKr=t%LD896Qal*+wT!TWrWMmDrFpZMUb<^DfUS zlWC`o&e`-X>t?a8l4+k!Jv~hy+E`X2LNDt2!OL`nqqJuu)uo2%n2lvk({URwKBiA> zbhVMn;!?--sg2GTsZ3YbbjC*Khg7DkXF6x2^G7Pv1(-g!(FG!v>4HobZFIrqsJ`iv zjV{D=#fBP~zO=o%7Xl+=3N$n=ekuCeJ`Cbj!EGu^fE(!%sF8)}I} zUjE{7@yE>!%Bcku%Uik^thM{y~`F9mMcCAX%!g4oC z5BPY(QLMxJK2BjC5Rgj8U(}@I3q72|T)|SyDJ%>Q_>DxLhleMfPE=pH;6Km2h{~o9GM;Q)FJ*T42LG?0GCe)sZa;N`Xlz;mz zLRquk^1(jvMqTnS)AK0$EW!Gi;p|3|H(9Qn>+9kN``4~U%RlN}arJ?A^=`U`!|cN=q+SgF1Dq6SNM=if3mr^r)`P=??%Vaf6$UoP#({|j1gFk zHFt&Ev%kWqzPacQdYkI^leIp1^&Vl$N|XW~Vtnu!11tsKRb2e%U!+Ki*K~`iH~Rolarl&}=-(%R5L;=i?HVZE`7P(_rf5F|{3sgkgkThxE&L#QOBh zUt5jjUwpirk1z6Z6_($yJjF5!Wv~2iFjisUP&cu47`&?UQQI(B>9@-_46gg#YvBsl zaJ0BW-<=*qTp?^h5SF!@Z7#9gUAr-+T&UfA2=&PMny%oN-x@9NOo>5xvUq(hSJ>pW zzLp6-#Mqi(+@_B)Hz2S=^qFNuDg;&BwdF&CqQ-&9all?R?0xoew^zw}r-r zXP`>2QJd0h)V57GLq@RDHmpP1hIRg3i%+->BAd^`M*6zK60eci(nG$V`u58o>Pa)A z$zWB(Rdvb3s=EK4LbWRJCvAa0`R|R-|9A!lqy5R^aRHP*4xjBQOQ%}3_7kusMAhx% zK(hHb@ZW8yZa-)HBYFe>j(eIHDru{Y9x)({`)Qk&#NUz$xr zSe~qRuSNOeMRks%z0ywdr{kOT=Hb{oyn;qugCde|^0AqZm-BHNu2KE+3-}mH=-8XJ zt}M3S)-*pE8XK|mxow_#d=3n}f?BMr%yctYeX&kC( zl#R*K6pDZuzAh~FAfPplpYkawm||V;Ap)tfsdN7 ztM%$(&1G9>W%*5yJ_!Qe=X!p_vKpSv3ixE)%KsSQve{i)qVoBCb3q#)AlWB>Jf34v z@At=E`xN5Qf9d-3QQOj$?Jk~`nc)kR0&35^*8<;@LWyfpr63$jPhD+St^b^B6Rz#} zO7RoTBFoEluNVm3H|byZXvmD70JgfNY36t(xsSW%}pfl%qMv%k>t`X zv=6RZMe=bAlCyV^bSWlTa}&us^GRMxBq`9w1A?|pNuXLS#rpgzim@iof;OPsIBP4) z_p|qw8#!dT4=qa!TK{{=5AfL4MDlIZiE@^B0gt1|1HQyDkJmD@Xcr!d(gWr%o`HEE zE-l*^Y{%sP<`|!~#!~RzE|$w#wm|6tL(zN%`GIjrT+i9rcod=b{<-_xIWU5WLa+4+rf_4!Yw|2NMui`R4^540Y($8v4+QmZ#& zd%cVqiFCXKSMpxl>ALBM9j*P!AJ-222~$eXI_B3aZ$nxEqbqIbN@pOqx|Yvt>$AL{ zSiDlRE$iR?B#$oPy1k9{OgHUExyAJu%5k_pm?33a3hwf17gO@kq_n^Bdr9Vp9NezQ3D zA1U89&;LhC(b|Roma=-7E0^dBH3FLT!254pZqeEjYkH5So~~u-?_ufY`X6&`&*O3A zG&;LJQg{iE&#{b*zlq0BcKn3WZYRm4Ra0>MOk$a}?Iq_m5BGpCEDpo*ZSIU7xZ}J) z=>f}DSHtvWt9?;+N(~uK(QA&!$DCWEG}+VI^0ui59*2a+p`1A{hixXKpMzO+omhH? zR@Q?<__}f+Usu}prn<&#=tjA|8eHSpSh z^(W}F-->G}hp`N9@Dm<4Nc;_Dx0V$~;28zWWr6M^D8fxJ#nsdT<+9ZsSjLVhtIx7l zrS)l8iCWjkjanV%uGakF>>-I|I?EiEW6{sQU=qrnmI9Q|wl6{Xr$3FBy|ZlZGtg?; zeqKo%0o^K{L;vN^nS<++_VaRDI$T$-K^GXA!g~mfBw~n1^efI%jUOigt>3ai*7+NR|RB`G1HW+SWZeqBPxh z?^EX(mHfY8n=esT^1sc;_pJ7%Hjg;{H;ktx;T1>Qb)*Nh0S}Y|)TM7g8>2|V+hkN5GY=1Z~WUF?cc2HI~xIWles+waF? zpBQy~2#;NAsNjMj|s%|fWSDE#bc5%@3w}tzU>_nhWiNZ8n`yb6^$y3 z_J3Zf)avwt?|?F&@2);2n`Cel3^ZGryYlt4b|UB#hR`W?{wsp7^F)#byfyVLshm_1mT0+IgY>WJfY?*{qr0L+!F-R9RO;06D$GR%t(Ud7joixpyK)UIQ;7Q&*q@|jM zdap{V4BMFs!852N=`}d4$+Y~PBoCZzJJ?fMm|DpMg;4JLZeU+L~gyhM7)r25?xo+}+Z@pMY|gm`?uj^f#2 ze%0jau#>3>E;pFsToc~aw7=&R=UQ+`)1e7doW0?=BFokqabZ628B?Lm=8KnO5>o_Jt8mly1q;-Hv{6SksIqM4{oz zOUTyN$$k*2$nsvzxUfLzr0ICgI>~|1L(@&v1wmg;Pf!;GZ)iFFcn$?>&GK?Vp0(dfu2goGc^R(m(y)4M|;cBfpT=T9GzxTkq?0@ zRvMPv0Pbs|eh7u%tTZe+3@rFu2}PHBr6Ig#rD4g9z@MoQK5dYk+!!J>4M7Tr7n&wF z$WCsC-%n6&51ck7IRZkN3gK)Aq8VCuZd+Vf1kBU)<+eJgTdFKM60Kk-QvqygFeAAY ze60vq8;9^#Pz66B4{%S6z-#5!(15AH;oG_A@fI>)5SYX>)&PQb5t726Ih_glPV~ z$+TJ1jhLe3_Hc)(i1T%T@0E^Y-2r}9I?FqA`zLpRr%cN%N9M*Oz0kTIf$Nexg2XpZ zumns0z|BYwOhuLxJ9Z{_0ynL@u;Xo{YFc+{)`8?`@L^hJxjySia%Tw8I!nPP$uSVB zb+ro4CwGBnOf);jI(LPxOw%AI5YIE=fXWAbHtu%p1~1yG*8V;IYH~O5Z%4EYewlU4 zy$9qm6+y@1CZ0XueWp{eZD(`G9&lPy`{gc37nsye*^^x0zapp_{~)=HPK5oL+zTFQ zpW`;0QhLKPO@(2VkX|aEmMJ~rpf`y2N5M=*;GXds z`fP5a>jQ1eQD>%8TwWjOs`LFGS~H~&#MxN(#qR>B7Tw%NE$=q5 z@Tnq8_qXDpKU`xv1rK^QckB`%utn=u^{bx;%QZFF zd_H_EoYj`=R?SG60FL++sj@6inG6wD+L|&I@~m_)Wg4us(y^53aFhs3U$^S>lo@be z>y~cmSAQnBo~yim=A57N@z4&(I? zS>6+^Vq)w+f;^@ISRZ|)%@J5hBnlvG`}Imk;jX4#(M#MuhT6R--)(zX5p5iUKutYo z_eN^WR0KzZ2BaQ?)ta(Gl2cCsy*)!C%duN>>S;J_)xDnj8Qin#UQay-CVYyNET48R zNWB1+nN&Q#052jz>x8T87Z9Xrame*bm!T0`sx{;awAMtuc@=sxDKA%HfX>%t{-V^Y zuwK*RkoBq8;9Z@M#>7|fktXtT9WF2_FW2Fk&PQIZgCo8PLNy~VH=q`gUeCUPKuzT3 zCPXkPFE=4d=j$?mXX;JprfG4=+o`tzKP+kWavKsfk(civhe>()4kqY)UFPpk{SNXq zEe;tFa~EdleAJ@vVUZ^Cau3!sDKGb6o6gr|{^8Vnut(G4kYlO$;ccCdy!-(4whpzA zy!;5~nUt3w;i}F@ee@&T(?osr0G{Z4l4Fo;R{{1uYQ(G5lL*P2gKe}#B_SeUO+T3k$h z3VVrgjXbdZtJL4%nx<1o&!BogrF&fTUFvg))pTc5je38;I89f!*Qoa=yr*gL_8(IJ zf)_-%X8o4(Tj~pN?oXuZ`2swcPT4P+_5yV^HPJc43-Do@$=A~_C=>oW<TdaVxP`OXDo&|peZe!=#ZwhHRJGGF2^-(tGTeMAL))d9gk>OE7Q)Z{I;Yu*8*z&A zO8TT#5SIrlifuR|%|SR0Q8c?@UYfIr(Dcjev(qYzBu$T(EJ|}1%QRJL@k^wK*rw?Y z`m7=n5|kIWv1`-3#05=*#=f6cTZ~9lx;A4^rum81!xU9&aXBqO^s>^ev>=hAX+g6e z(t^cuP0s@#r_~p`H1!|wcUlACF`T>v2m6hw$kMLZ7j-Tp z=}WYQ@LCh1OeEYJEq^o)LV8_OM&mG~$xMZosg0jGH5Idz4tg|clHN=#S2}=JyB(X0 z`-=EVqq%4@ihLGWd|#<^ zU@gTpCN=w8in~m7ozpqJrFg8!G77KXBgAuMX_<)E?|A7rn(Cq~TZvsvg zK+|`LJ=5EW{z+t62!Cwrrv^Q#C5HM4wKTmibtBxx1X2ZLj-40KDw6a=GjY()wCtGAJV%> z)MFdx?{@4hPP0x$qPM!y2dC`3Rto4XuCPw6M7_m#Oe&teg=2PE3B5&sO;kc}u?(rS zgg#;$>r@GS#6G41OHTGA&pzUqA{Za=p-Ufe!AfUb`ih%Yiua5a53E$2-cS6cX-Cr! zUHS|A9I8tJ+}Je2bAYI7r8Lh$qMoK!F?pWxB3#pFSppwaj$%@0fI~ztrQ@|{h!|oe zJG7j~q}o1IylvHuZZcF{QUpEmes7|W7lL+Q!lElrOa97<0SH#Gg(?NEBEFvf7LITC52VmWdvN7b2B-=>M=a=L6L z+PRLUr-|{JXy-~3lbF;xpC(o)ouzP3uw$B7t7!+*CQSpFc4!*I^fptemxD@Yzj!c} za99)VIcegACfak-g#GK(zEk$J=cEfqO;mci@YF=5rwcz#RC>B-q=`yT7m-W_mdm(P zri*TxJQtr%PZzP8eq`M+O`)tyVVY*Cfi+7PIhq2nX6a(0rY5M%5HmD&MqP$jz;w!< z_S8(VSQG8HnPRCXBkNLnrdXk=;*x7fD>a=9x}BaS)@X{%e~_Ln)@jO4w@=Fvr!_rV z^jCV0C>X1v^mAIEeU5lXlMl8iN8Hpz=L|Wb;y7hV=L|U_T$6=qn5K?Qiw5|~bj zho~DX#xb1`hkFoJouVx32GzgH!vzhVYJ+1q4Pe8^bQ4zn7f+K{_JSK}eiuk_9 zWD&|V4W7%;jLD)s6W$XjX__%bOw#nMq+Le7IIHP;X1k1O0;Y56ysJ$YRhf!l($LNs z(?x=&Sx7U)HYPgL>6{Y~PIy1#tO;I%xQFlwzB>c5`miSfEEGE+oDv!_IbbQc)jD@17rm@?XWh@r! zH5D)0?YLA_FQ9ydFg)jtjHMz^)8Y}k9ao6enkF(G&@^JimW&m`bGFJCoioLGr3lru zblVi?RbrT?y-2G?o+9qCHR4C6BFnj;-5G1dV@*8*-$i<^2zCx2U6(mjLJ?FOGcRe4 z=*y(m$hD$C=`2?|$AzsG3pMQ>bTDJBSgxsS^CL)WG_9R>0%@bB3Nt^;cth-BQa;}h zUFWLub`FROdqX_Z^hv+Z(Xz@s(y2ZCO;L*ouU=|xzMAo-XvVrCc)RDlj16Lm)-{>? zFk_SW8j1Gs-v&L**dm^5%gp7^GfG6{{4$^0MKn_pw5?%eZWnVjHAUJfj%sQVV`T0Y zPc(%hy(Iz`lzDkugq5S_<*02r>dI6ErU?$2Z;Jy&cxT9Kf_LV-;+I@LGQF z%>ClICYQKYNIjQu+qu^J#UUa=XZP`_yQpdG4x(%2ET6EB?pZZ=*e@)F7G6PE^|b@sVQ_v z92^mwnQq&6-IR`WMbppTq`Rgp@gCV-^AYi_COXSIDqd>3Ge5@usBl}#^%Qhh>|^1h zY1QQMa9lLf)TPVyN+(2HO*b|@t#wj#(M0#iJ`sI2bz5Nvr^QqzwGMtN7HC?YR^9xm zSi_`7$QiMRh@5Q-}qgoeO=#Jny5ln>9rTcuJ ziv&$xA*o&$MZU7+bGJ)k4(q5lGcqrUb(+TRAlkx~s=Q0$DC?-aIQvWDbJkILdum@5 zw^%m~T8|mx{gtS`T$Sz>GAZ-A2-f7&xghf!F@#93Hn+qm)=}x_yl;sqn#S%p=Y3nu zQUveTzuxdW@rIQOGw+BUR$8BVSG!+E)4%&thLhACeOuQwxrfBw*OPyvC`-!FT@ijYAw`$Da^%Gx@zq!QJqM)_LWFt zQmuU@K4PNQ-tz(DGfhrNhH+Wvqt=>?S4^t4c81eRDuMcTSZzC_uBNd&hT(^*f)!!= zPGwdwMqBCgO#DklNUD9OG98Tftkk%dvvHa&seOBDyBf}`s03<}YaMqZlu5PdHKQw$ zZqaMTN+#8!*9^DSq@xx+sqJCZ)Z~Oz75`eP%114#X7piFE%GuFiMT~m>v$RCHI3ad zwN7;-UlF$GPG$|`xRrj+^f9g>sTSSItZh_XQ%ca=&j>}rnTjpCR434w$3!iP@eMH! zGN~3dFs>2l7Bw(@*Al4~H874bQH%V2LyhyAoRAtC*O=5D&&Gy)gYu~sH8EU?utgqYC-9c1I5yo?*OBiM7W}S7W#AD?#6pe*rKqX>-ID@Zy;SEeD4+5wYSl8BT*6j?$xSZUt`23rTcpD zTYmkGH#AjGTje*%*u_LX$MhLwL~K@;-+OiGI@nmkR0Q9~>~>5rOj}sT`)z_zorp(W zAFl+Xu_lUAg3(eDM=8OGwvt0uq7jQ!8l_>zFs-8~4L7D+$t!DwvB^rlS)+{8nkY)i z25jZhF)o%?DMm9ZHONXcvWRq)GL2hIDoWYLg%Z-KDCHWHwkb=B(io%bc10A4vBp^@ zvfLOu!SLRpbQFmx#&RY#TBjJhh&U1l{HGWvG>zSHz<;W7RuM*GYLk59g_YboOgCJ1 zmPTS~lNm-sE4g);WkhMZ9TAaLV2rbpZ-+U?UMqFTns3}k!qPDkE;Sb$p}R_{cUGY> z&PqeGmK#Ty)GEBv_?!rTJNq_fvHfb}7L)4fwMN=*@ORj02kX2Rt>eGmdKdDc}#JvrKB|def-7hrFnD;!UGJ6Ri_%1J)bE zH8~+|G_sh~Imy^1 zmEO)eW?W=a`@<*3b0Qp{G?qRymb^{*DAwx(KQqoSsaT&eE)(gs^^D>D4v~u48DkC; z#cX=uS!1~-C#3VndL|XK3&t5H6|;-RmqZ*h=b($mLrr6MI0t=UJXeHKg5H;mmhZA9 z(y^>Bjh;v(Gb_$6`++gZ zO77W@jMG-CoBf+nc^_G-9q2D3ib%)om2sbS6tibRuZ*_)$x_7(WLF{`GmtBpRLnru ze4lg_vnxSD25NFblCm+AikY1p%A{gumgz(sv!GzJoS|v#4x)LAFiOp`&2poa9I`C( zJtP&gX4&@gx|JNV9OMJC)H|M|q+@J@9sS~GC8xnS$I56C&apDOg>%fQG@sLJfq&Rg zLU8+RXR9tHINF9r4Ql6y-=+x0Ox~PW1D!(ub>(pthAxuexj2ge5pu z2d6qp$F!wuU1=PkCee7Y1AkeW>4g2@qEPz)>CL3po&cGqbb_Am36Lw9XiW6Y4wRcU zIUxnh13Dk|N{GCpiO#`7$m~$rh>7k2rXqD=DuDXMIoV-y z08^1A+k0GgL%BfHOr%Egs3KVHJtMoZboqcrPa)K(y4$gdbU#ETAiXs=yNPVURD^fh z3bVsylp^>nYqw*#T%zgJNTLCURlaU9YteG~hl)PS+M3-=R{cm(deH9d=5mT6%kNV^ z%x)={BSqtkv)|gpv!y&>rB6}!KvQa?CZ4S&98s33jXuq8E&VlZ?D}hVTiI2U{nl66 z?c`jf(lOtj`>*s#?NXvAuCG_>pmnFjt1wGWM=Sjn=AP5p+V6Cg9HU5_a;n=g4rwQq zAnw^K+Rs#AY0zLrUPUG(=-SUAezNgfVGay86X#G@(mzbp$Oa85osHfDt(ZA z%0#92%!!ks$Ehx=^mv)3iOP$YJDG|gV@3a*czHlmE2P2lq#~Rv2{}XLH6pwRadF@% zr1mGsCyfaK39?H$>Qj#5thBIdf=p3lxf>k^i89Bk6G#)Rw6JQToU1KQqvbGJXw~7H zPjb7Jme~!L2ef6wCD}P6)ce1QSNz%zm_>P;b zuE_EO{t}ZceXY8MRg+~Sts|c)vZYlgklI_x18InrX5^&GJe{v-Yja2ZPNk;p%jV^z z%b8Y70U2^ZIa+Qdd^1FD()l_zz+Z*rPEBVLh~Cw73E$Aml!uvSV2^n~mOP%x;VfRAkw{ zEIa3Q+0d#J@VabKj@p%@m~zyYX&G1sE=Dg4m`+*f_lL1^xhDEWY^+>MglG4&OP1%1 zmDe;qE7_1UUfxBb{o%*Z-8mEGZ=6q!5FCX=VhfYmeEYN?L9XH z+Ao$p6>;f{Wq+-s(ih7Ft*bZLK6i=Sq=`x|lslPJ=}YBFt)tSH$tgdxb`bwFqiArB3bD7laSS=@Nq9?#s%b7$xJL=@F zmS;6RD`}9sPToLLv!hw=2Kkcnsj;+4HoZbMBZ|!3Bs(dx{81wgw#XiuO!(XN7CAsu z-Hu7HRVHX^(DAN$t4w23XO$)L0F%1XD3K0VsRTN!bP3xgt7vjU+97?I)IPRL#%Q9m z%3X386YY4l!*8f4-+_&Xb zO+D;~6vYosfL#v|tC zekdCg;o9RrvoQA~xtvM$-%)v7>G)36QTc#L)#a#ss)^Rmqw<9&dJY2rQq5Q7lYZaa zl6zG8Fe#tMWh|5Oa$FA9L|%@|BqAPXALJgF=QKSlIhlJ({=}Bb%cru|bt*x5`Bc`^ zL~;34HXvdzyB$B3Ej2wWA?n1IrC#!wR8M~@^O;J0&e5cNE>Xm>{#3rlR0Nf_&u(}| zHv5|DQiNaeI-h$+4rEd#oRLeJR0(I~Iwn=Z8M##xm2gJxQN$&jk(ZgMgrbmhGX4gY zpnRT}E0~ne^Kw6v@_Al_*pRUnCm4KZZWdy&(^>PDSEd`74w1`K|m@6Z!mB8n?*HDfaoT^k*u9 z?-twT-IiHM%BOSQ9eIUy%I7_4x{cZN$hs#THIdJI(p?ezyeFG66~W!+wes%Ed?e-5 zKkrBRJ?oUupQZbEWj=qF)isgNpQW!N_W84nW-5X;Yn$gil=(=?XS=+|@_W`PpHF4g zJ7qqfN^ec%^QrV##6F+Ou1w^!Pu}lx5|Z*6pZ8qeW}Wi+mvp~d=JPLET@(5IOZqBe zpMS|tL^#LRtWD4RTP7eWpXqt8;VcE0}I-BA*pZ_lbD+PtB`fioB=jS;>Mt2UB+>wO1AA zRWc1Bot{;0rea07MsCP+Gacf51^nKUyXiFR)DGlsa{m|QQ#+8mDV>RSplx}tnZ|2! zLaJh#!K8K|Ptz_fPXAo|nn)K9yHwS=DoB zQgxG~rY$`W=2bVjYs$iRb8DEqG)hTr=o^=eypfu0Il~61+`AnUqg&(`Ze! z&U>4(h`5hF%kwssXnIz1C9jU@JtWmfH}m{V$2p(!>2G?*qD!hXM(7iv1n`3j+4>tFI3n(Q8s&jOh6)^B-@O#Vdr zj%Q<29+MgcjZJHqXcWB2Yho(V4J^tR4Ncek3G9&4ge(A?zsGx?-ZU>?)Ll*m-T zb!ll@K!p2rAFN9&(_to6m)53RY^g?M8&mKvSRd01)~OctF-1S3(p8K4m^Ls`iz3JLHSN~qgw)UU zK9g$E0Mji^)S>~V=So!{bdO zS*O}C*tAaPqpQflrmdRD%V5(UMeJp;=^_z&S-0wZ_)ybz)+sN;OqG8vtIIHxmnM1w zbC}7ONj)7l%(Pz97<{{VxamHV@-p1i=1G~C;igWS=vmF-rXEb>rPr9@rW_)yOXoGi z$BZ<+!8+w-lh?+Iy|F_u13g=MLBHTBgjMq{JeLaWq+3L&RHmva(SS zYMHETCE_)jtekTyH$`dx3u@uzrYK>8uqCD_y@|N>Dar^zOP0=@`;4-fh}U77@*5GK z>!vBnuMW?qDORE^_|2Szxzm(YM0npQOVgE#8<_LF{B!4_@^s}a(K>x^(@`Nal=DPf z=M3csB5rSn@(WRx6lP4DJ44ZaLoHd-Yx?rJGZh~}yWI-r&QkmZt@JFIJ4Xo=^m5&T zx$~3|K_|Vo%uP@t1Wl{AWp0u(T#(6o%iLsTvmitLEptCoH^ z1C<%Dh!d77^E(tGr0Wb>=FE1fkAcYbjKE6LBqtN|YefQm70j;(fkQnTrHkiZ?{gdrnzEGOp!$ zWxptgLB%M#M-5a(|`3y;DDOTL!`HX!`7c2Dzp_XE$2@pNY7=mzA4>(B8|6!mu2_UwK(6B*NP6n0H7Cg0nb#>mF7n5^*hu zm1hLubw8{mAVKRc+Hr8+VdaFN8#~^bcSQLFi1+LF=9MYmQ915exzd2X!v}XBXXceF z8A$M%WqjWFj#5f8zB0d~=qB`qugvc#F+{jB*UUSvJR`^kbW%wm;wy870zWlG^~069 zLTO5bEAx-@-c^op8SUraQ;rG38oj5yON1-eFZ140&I-bJPQS1GLB#9ufzk@Tx!T_A zK2X{VLMMl zo_(s!hVNQ~aZ9H$pDN3UxV=x6HG^+UAe5w=?aV=+*W^g`hZ>2LzD?wQ9 zjMAQnmph|`6Y+9ql{cKqomGwt!q3Z`RelzPZT*>YiwL!JivLXM>5lfemd}-Ekl?u? z_N>p9Brc=xiuhbvPxP&PKD=lAm&(pN^ho?UMe(4zOLZ1M8vm6tTaa{etox5%0+tl+d~;<30I;l1YR;c~<;)%1S{tpi9bnBHoiP zD`kSPCtp_15MfVFj{iZic#)rUjeb<>6Y*>GqtZkWj`tsxmP9yGv*LeLss+IlhxlKV zlzI-YYn7LYxRzSwbwSwHwaRfK8t?J7NiArY^GiJcNTxt8|?aV;MU!d1q^t`pg{g!rH@ zb}bP^ygf}US`gRys35#XCN`W1TNiyPC9-SzMdakx{}9BrNDZm(I@h8Tp)Xl8P0WLc zYcaEj?$A|FGkXjPMhCuQ-oj>*jK5>v$`XVO-}&yw76=)>^WBXt=0xvqcVjt%@Ts~x zE9B*5e0RG$D-kk$ce@AsO~~-w?RA(2-+BSoWqfyg9aaws`UJkay)N?=gzs*z$65)( zcei`9E=2s@?e$qtBCOw4&-&~!B&yMc_y%kaF9)9i_#xhhZ4)xAQ6sir$goC@*h`$C zMzv}qRw@W<Q1_@e9%{yh3BnpZz+#DbjRII4(HiOI4Gr zM*o9sFA=}e53*N?vZN2CoAD2_SMSK+T{NtUh(A{gWS$qX-jwz{yu*fl z&WWDx1hN_-!>2oe?6Q!(KHregigjyD<>)int=Ypw=nIpA*cc(hXG?9^ctL2d4O`X3 zVXqBa&x!1{Vb2R0+H1ph3K`mK%ZyDO_S!KE5w{o2S_&E33t{60p}i0m>W}64bHfn! z7!j{+2pi6ctcS31LWb5u*d!rC>+RVqg78^Z2lfULw;sx>g$%8CWM0j?TcV>%-`25m^<#2*eezh`nVR=OSJ&9e| z1)^LjV4gLh3u_R7)_H#kV?jiGtqNlaM10;2WA72+yjwS+8#^n=2GpI^h;n$^^boT( zN1c2Yeuy)DrG7GwkJ$9@y#a7~S34?KukxG()#5YamL zCbFP}{%mwhl&zD-b?=xkfHi1^lq-F)u}8wAERcwo8^l71)=A#o`y~uw0YNO>)*<2!QiKi3Buna*qpmA&&5np{{*g-+qE-|cv2wQ$;!UT4ki0=oV zWaf5gfqVTVt3#9}-I5a$o@9YevRD=(WFKm239+oVlWZc360&DCCQW2xh`yEa)BaDf zsZKecVhKdNmp{eQiPq^AkB$m?iY*c{T>qY8t2oh})KhFoF#5SpvW;JuFo{(V!F~Aj zyo5OB58rKySM%E!HYPm7LWy{drn4uxjIP6UHr+)QPn0E97#^EFos|emFm6kj!G04I zu53$~$vU=23t7^uN=d>j)}4sgVK(a{2)&-o25=&;XR{eZ>!h)x4~;S z8{dI@9X$Hggm_jeXlZ^~LISHIS|<(PTbVGQ8ACBAuTe4!CF0jFnf*w_eMx4&3PN9! z*=-`+hkTll%se|fw4||~M7&%Y>n8}yrLo6|u-vGSG&bC&+#;89IWFb$IZ@ry*sDbN z-0=H^4E7@tuR|vD>x8~=>zS;%Ahe#zf{Ae7b1fm0{YJ#gWwQr6JIZCVc7m{6HtWoZ ztY@>)L}>kH!a{Zch(8rFB`#v0pbYM-hVS)CT*8ztj&@nbLWsC$%UD-IXki)a#fdB| zW3fcs!U|Rf#4R*QT**E|8CmF@n9FWcPF{yR*1D_1ULK1k;^RG!ttaB+J&$z?!<;zY zA5C1#dJD1v<+EraKHdx13xaUG7qH_*INnDjZeSg|p%&g>HnLts+?S0kk|;|mWaASz zvO$7Y$~WT+*;9fNVU~K9B?vkTv($4eT~H9a8UH+6ENBPpQ8uw>1(kx9&1@UdLTT2F zNr_w7VNUd}h^?%QhG zYCUaw;x^VmP_=1#;&#@Wh}V4w>mX#msq+$dFkI#N7-P~-HkF9)tcuwTLAdr8!~3T( z=XrY9PBFVEWcVJXUF;_z!_|Hl`%TF3eKWgRyY5tuc2*^9qn?Ac?d#3 zkFf?s+|OgIn;;$hJkEv?aX(M8H$*wS8$8M05ro(FBzs?!yRqYg#FOk3r*fy5{4lqU zec}|WCkVBiVt#_qms6}c5$_?V*hoR2kNzaFg2f4X4d`8#7lAt0Nrok#B)-Q!5Oe`# z@3Z7SD9e>%wtSKJ0sDh!o&J)qkL5$A^d*^I?&}9+C0eJuMO{ezh}9L;*bk|p$oc#5 zt3dvO0><0`YDvV~ zi;ty>wG^~siyu(1ptdt^CZ1+ph`9CBEQg5COQ+dpBHklUvtmJL?=;(o1oM3QD8uyA z>|2td^+9t#Vb*?Vo$EZq;)%GHGb~LIYB|FeA%PbBHiI+lIg)WLpRsZxuH|#qE)sQe zEuXW_f>6un>>(u3QV+f%;d3^hWL(RaY!eaJa-LbDPz#=znUc=4Zba}dw#Y_FU$H@g z@OxNm*mNXu?)EK9CK+F=zGcUW_^A4p`S(YiII5Z@U0`hl*?=yx&P05z`ksv!grn+v zmOzA~Dk$kP+r?#cqWc5eF9_@R11lxMw(gMh18W{l_MokoPP)SS5%K!{#10VgR{Dv( zCJ43s#L7jvdVU>}eqy!(4lUPMFcH`CGuueSwfxMs3PLSEvl3A*EUH7&&+G?5=*ut6 zV<76}T7G5oiLkB1lYV8pxs2NS1}hVUZC%U0L=tU%lU*hmZ|j?^%cH21xAjeyON4Db zFzFV1R*((oHrqkO+gegT5QJ?lsoxP{TMti?)rOCuPF_EQ+Ds7E&!DyIN zo1z@f`gPTS$5AK0_pPgj6JeWeN%B&saT)c`dTN3oJk74BZWV-e@K#HZs3nS%ywxg_ z@fP$^rwt(sq{T;_CkSVKA2po_Tky3cA9XtsFXyYCb}Hwqe(qGxSN%?uYdBY)Wupr;2my#Y-pAvMe=xS0+HJNCg6gByFQlMHb=>6$+=Le~e zjo@A%DGHh&qTUj8DR|WU4r=x&Zedf@s`;H&Wi%46QJ6Z2h}Q^y?t}i`W_|OYoqvaH zFHbF+-_0(wy)ku{6J1T(H$VK2%sBrwC;EKqC<%VFgzL0jow}ldgLXH8Z+X8Xd$S3A z%ljQV06&uV&>i}qJ(C=CwcFeCd)|@N2|w*b1ILY$dfkzY8n>c>g97I7o8SA6tTXu8 zTb)UD=ig`bu$oBpHhibkv-2NT7jx3F?h)!nL0I<)b*~_-dmr^JL0I=b>c@hx?tRsZ z_MA}nzUm)>uFi}VcjFuv4XJfk?Jf#SobJ( zfgr4Vlv*GN>)u~25rlQ`uf9pddqK2%oQU@V`0+D-#h@2_Gye|R28_Qvf1q7v8#ewL z(15X62i}5@s>6w}1;3g9sG7w|$8v+zb%Lq^B-4N5OJMD)G{JIHixJcf>6s4^@bqSGE|kub1jQ@D9JL=7fL2X;vl1HjNVlXGSFj}311ZVdb!|ErGR$p_Hja83wf@ize=8siBqn!NN z?l|>3lJS{#oEka-b@G{ZoSI35Gi{UP@#;!JHlPXWdLq6$#j0h3aCM4R&k*5E8<;#% zwLFPBd3!#k)+gd#KczM$%92_cPkBG3{w8Rn+s*i=RckDk%aV5Mr@SYrBLt;--i)8D zP9@^|$SG=?AiPFX)J1}DuA8E+BEqW@oIFL{Pn0YDzO{F9occZy_hp)To6BgYI!!fC zMC<&DO;hU#LY>pphD12CKbAaA4JLvebyn8Q8R}>vu5+fkOq4^srQq>YcXdzWS$cZeZs-GZ%&d7~f$?0kh$+(s*HEt5>*mVjEcIPMH+Jk!UaFo2;-0;hyh6Q5<+#oqwZUYE&K$L=AZ)W7wIvZ= z&6CMFYJVbLZk3vh1n<>6A6}WfO0DHY?_gN1HkyJudCt{p1QGA^tJPIRyw9&zzaYXs ze=d2AdP$HCXsvo(l*8RczB+6w`ANS*kgv`l!ajc~dA)iLiF~=5T%ZQU5s@z&)qzOD zmyPNSBJRsZwUmhavQafZgE`Tco5_W$w;&tPb81r}KK?eTnSyZqZBh$~&=;1nSv5^_ z__9TXuZg^4y-00I#H|;pbBMV0B6TAXTDPWbQ+Eim0qs!tiE`Ky#p+Q(XuVkdj0mlJ zr|eRH<}%tL?N)CJ!mGJkRi>k7IF9^McB_vIf=?Btyr7N;;_p>?C}p3Tg)-4*2h{yU zyw@F2O9kOakcpzt_3X|SGyCTg%v3$)ahJCSM#JAF9=)dq?$&==d+XQ7D1U~ z@={Kz9~1F5t5A(|skTyF{_td6>JmY?x^S;_rP}soY zkPp;gK|}rar&Oxl1^qUyJmf>QkDx=d4ySyi4i+>Z`puLob)=w8G4G_DRwoEL6Zc-q z$LbV8sr^rlyX*GMa0**&(uO9Ufa*qBBCs* z7o5I+rk)h^opLp$T0JAEk*PN2bG2H~88<`f7wR{HKJxTT{Zg$n58I4eIHxuy;ug-S zEr_zDHcCOjIkkxyT+9vhUO@Ad4g7?~6UJh9;mr{=V4rYHuQL z?<+NmC`&?}U#VkUWRrxfKIr^PEg|AMzf$KUIdp!dW)tz=`nCE25$~;EtCxwew>C|! zQGXL;1Nv4q%ttMJJbb5m3Bum`of<@hy|q>9MKywmx7j82F(O`vOX_f{RXtb(N59HuX&XL9LgJzHpsCs(wUS(j51`sXwa0PO>X% z7a@CA?VEZMgZ0%~AJ1)H6g`(lG62{2%JoJ2EESR;}s$y5q_GpK7ciJcIvJt;s+cfBycb zDrIu&?}<4Z5%Ev){;58|iJr#)skRa_JfHtl4HYu{6tASk3&N-IvX(}K^<$DjD-<&H z#i(5uguVb}Ieal{ZrKiBj9P0V?u$|Dz=?b@YGFc#z8JL#AwypjZIK}Kg=slN+!s|V z5i;~e(`p2vFF*?%zGxbJAuqHNx2|bAC$g?-?m~vvHLZb=p>>PaK*;bpiB+2|2(8<+`9yp) zx@n7uc&~HQa)`2|a(2qwP0QypNojw|+g;n{l+#1oL&UXsXop>7Wkgw03;C3{hgN|^ zpWV6G-c$P$2!1sZ)@ijv9nH85Ys9U4X$^?@Oys4-67iYHOItvMGttO_^|V|;HlX_2 zMo|veO&_gX5Y9wCS~U^QL~#QfYHrKzzC7Nck>(G?eVNq3SL=f^;Y(v}G!gfuv9^}f4Q!&F7GwkR*S-|x(3b}^eFf^|CtnX}O^MK#+<^hwU?ggn7dkZ8CIWF^ zj&^9FEvB5jU0Q0Jh`28;waY}@mzJ7sC0amViUtO1K7wpOt+fY;_zc}fix!0K(ngCV zLSJ4U*j8JIM84QU+iAr>+?Qsd!P>i&ll#(Mt0CgPwAX@j&^q^}y*7vleK|3(gEmHx z4XC5`G!gfuv$jeQ`qEk3Nrb+9Hn59!7KwbB653V!0f_sO78<5`u0ng1FGoWk)8!FOb%Zuakj(_| z_R;bLopQ@f9i_ca#I=mpP7-k~qqPrR=&UH$T6->awD#Q{8I#6nhPCJme_B0OQv~6h zJXX81o@Dg2daQPX6V1Y7wcA35v+!6=FW_2m79OWPAqZ#T@!EJIoCk|iW3)0P@h;#e zwOb_Ped$T9-v-pe`_hxzIwI^#`%`1JErM)7PiZAYe2h)f+%{r4-j^n6Es3x%m8MSC z)*=aeQ?xB4TL|y|KAJj3dsCFd_Zd&stc4EiQ?*`1X#HeroHjs^4QQJ71QEABLz^rJ zN7W21g9xoxrOwm}i1;;{rEMeP*JzgZB2kuv{biO`?^(3YWwW)WL|GE{m)TkZmq81D zmO5Aa2}#r_UXz}~a=ZoOwP+&Vg7Ml0BCNy3)C6t2ARADU_M#|ldAt1neCxCo+6+$AE-SQnA;WfAp`{C%lpK<_QhP-Zwo8uo z1`+pcm3E4Vd$vmZ$VK+0i|m4n>}MC*O&6I~gnrs}x)a&$HFA+XK$Iova(LP*El|)% z{o%A+E$mKCCau;YiFmuL(V_)myR6ZwIhm$xo&;1QWcY5FHQM(=hV8OO`;7=aV^W@` zZKL+2?*v+_Sp}iawOS!3(;LrUSJ!GqLWVlmYP*FDb*|M85%Jot)7~V?k`Ag-Y3sD! zJJ13@sadZ@3Bpr{_1X|5I-41uwqE-{(2X5a(w@~W0P!=VDQTNE>rO{|Zqp_bah=<= zX9VFBzinCq66uUj+ol~7bYsV&v|ZXMAg*(5+FtEDD#yJ(piM1y_<2B^EeLfU(2|iz z=a#es+G~Pt?AV`nNP7>6>wGiqi1r7Qv4KwM`? z`Z;Yp%7o4u&9n!J>#Wf{1)Ju_DrwQMhm*J@DlJ1mS!p>s~LS zJ)F-9(+zqM5w~E}6NtD4qn<~E7Ph7<`X)g(AXVQb%Ao~aFBOCqbiIlQE$m4*>6e8J zS5dRxWglAS7R>q(BD8QY-J*{dWCOD4Q;7JS?5-yZLJRJC9uZo2J>5eu5i+#kssBdA zEqLl)`_Ud+IF?>VZz{+JsRqm+-nlgjbd_0g{ranF48-UsZSeVpDT;(2*M|KP4yBa>X8@Ho9Z41?LG3h^yd0_Ae7zM;gRv6 zo*-nGg8edD>6cL^dSn~D=}SnwN4C*}hpkEd;yqckUmzU8xw-BoDAVLe>GCJx91lfQ(>qm%qn|0Oy z5QG-G>J45&Eoh-nMwtE#5uX{l>2n2Pn|0IEiSP+PbVfJ*O(K5XyX)79zBL^w7?ROl zza{8+!I+F5Iy*!bOr>L<&Ui?-2|6}rZbnbtTM&N2u$S)7Nw)fCXY|rTiFkWP=;=hf zwh{UTBCKtGMj!oWK{lXA^gl#7Y|lu&#bLD0ces&y48~dU!a1wI zo{mI4WM@Wyy^LhMwgdH_oXQQzsVXfv(oiUBJ3dvnd$mcLB|VHfz}8r9kU=aL*FRq*qBwBnR<~RZ0jt2FDGd0!ptna zl!&+WLS26Y?eVr=s81)tw%(JuNS`mr2DC(9K*Za6nf`(xZ0lwE+eFybuVgNFX|ok~ z+C^z!IUwf_{TcFlX6_xrwqC3MMq2p%nXfBvI$9!Mw-Uj7v!zp+`FbxRo^!oEz$xc? zeW;6Uz6&jK$(iFK+vpLT0gB75ZyF=@SCNyPW%1$vbr+(Q@W#bsEB^QP{bKFlo8 z_i>{8l>+^ckl`M>Kz~!n`Xqmvxj}b-i^|cv)Hmw&iFj=b^#_P}4=L1J-ytRy>U~5x zJQsde?hR@Ry*VfHjV`jSqTCSnT3WYZPkBpk=44$h)Y?{w}gW7gF5j zi)@UGY@&;7ri(1mMYh02w!%eL;3C`XBHQC4d&z~45M@c%OwXlm)lWH|)C+g)T zdW?`^FE7#KgbYvG_UL;BVK09{e}#zGVXuCah}U7Se#%AmiHqzU5r1a7S1&z9zR>p* z?bS~bah)&f9}{t%FX~^o$S%3auDQr=yU5fEv|!g+hsdt8p^L1Ai>$4SEX+mL+eJ3e zMK;VuHo-+Ug(ypUR37v2i!igxk||%33{sCE=ePk<@XuA+IKz>%Yy?X47GX-CK^R{* z4MWspkbd9&*Lc5`_B;mZYFm4p=a;4Gc32A2`5!zvj6ol!T6EMzOW94j&pQe`I))C`a?gVtlbd?tp&A#RXzDaAQnG7_axq^-#q zj31hYVcAl9Xpq9F6sGXFQRJDj6H_X}G3TNpN8CBR)+wFG4-UutJkJv`jt{M+c)bL}TTf#+sMsDF#7Myudz@bp9u6wTamxE4 zzam#?gx)LeS{AK1_ve~0v~mtcrBU=5p3*EEbKV`_3h744S?Zi(f?jG6wbQ5{F4>EE zcsua$|CRKI$6;Og9E+j7hrCCl<3ESrOu|}!lY|<&cXE`)cD9dAeg&`7xWha$i4&nz=2WJ_FJf>5JBw-#HIY!D}i@Z{0&bdSZ}Pfd-?* z^%S(1Bu08v=_!b7LNm{ad5n~zNFPL_X0&40!0VL`d3Za>62@i8pM2oGs}=dlweh-h z51qr4G}B@V|Ff?qBVmrUr(m47p}^Rq*YM@nk4vl+sht z7Ed_&vwj|T zcnf=Cu|_-mcSv39<(mHASa;T7l;#BeopnriwR`WB|Ah}Y`z4ROw25MFMsvfUVHrS=+$@kqPtTt ze~25lZC_8kBD_uhnxfn_3jW(t&f}W*N#|Aa-_0J5n)1H%ceVScaqV2w)pc?7-Q5C? zbaq!e*!%GRO3!~cP93ev{rTrP*$V z<9e|yKN;h`^`>1hFSU-!^7KxHm=jCM62JTCy}_}o#~reLhtGG*&S#R%=xmLjKw@Z+ z2B%}&o}<%3yl;}F0Jxe4DUVJ%<>g#OLIX)5Yh@DS4;P-3) zlESYSAHU9V`>xEP%`W{D#-=^KD+fL2d%3%FVy*2t6)1J@obC2Fa$@}6Ilo!w$cgcL z=fqaxfA%)Km!FMcblY=c+_}Z<>5svBhg&)8EOGm;}kmUug0iYz%o*Iw$J?%Y?M^~Mo zHQmu;|C%mKZ_}MJ*T8o>_dntsy-t>}9bB(Mi)i#}Iqg08tP%+8zCntn)gL{v=jT%H z8*eL2u|F?!-kr8vkEI&HnVT#;NiR4j| zq)MMJHo%EDAL)1JDW2&nE~r!Ut4(!Z{hpDBjByXpO^jh^zU9Ld-n8uo$38= zwK=Yi{XT;4RsL6dvwP{`_X51<-2WBFr*8&nW-9g*+{xRYwEnXO``C1T9>mYO9Z!qw zdESMcq%2`*kWdPD6Vot-=S&OxYy4acrtt8eJ+b?Yqe+%{i05F=s|I}D7)GB#8efRk zPs9ACh*b&G`EzRfI>kqjLU$89tQY)OZG5Ej5O+uR=Y!auMhTw-+UHN_5pj;@Rp(iY zYrt_~w|=*jpD*#8&hdNY+zE4qQF-rrS=s51c6`E`>*@mnw|Ba)IP&I z_XJ1WUN4@)M3p`iybQJBQyBYR_usy=;;;VL@1z_a{$0xd z$~tdP-sWiW@78X6y6f6)KZ#vUrv&$Zefd@37T3TY+rDGxDFb)mjtfVn{W{{cv%gR2 zc~}|kqm}F7SCEeau3|?@qCw0`-S_+2d|a z(P!Q=xNGHYYrhl!6LvCosq=dzc>epp%DngTxyw23Jm2#6$K8nQ?v&@}d)>dzwEXF( zQR*6srTEyv8OEN%`+y_GerM{e=dXF}bKO$d(HW)juzo30&jHvI_QRT`(w(M#_r=TJ zYxj;)xW52B260|@|1HK#@ip=P?^3)L&UeuE6l?+K{Uly5d(Yu{c-#Jcy1nj?0dFTvaYKd-rb zS1ImQyZpPF@;fyiax46P(>a~TozH|{gdQ$S7`mPb^Ax_5_i;mMBX~+>-@kp)(h)D{ zAtnr$fA`uO{QGoX3m%@!MY|`c58$;h(4F1C=?}atUW@;>EI*6mec=9k7q`n#=l;90 z{JieJE6eZf{%u))nunpit?(RAmN2xZ-2Lv^%WW|aUt9T(-FcQilZaA0L%4TO;B)W4 zy-(+(@BXjI-F~8me?Pn4tN-IlYLNb|SG>jUr4!dsSsJ+7we_4^QkE9LeT_k!rSo}} z-@nXE#XEPLnc)*7FlOy*6PMzwYd`UGE_JW;|Evw~J^A|hceWf-S0A=4_)i{gfwQK2 z>vTw6b#hw|&cZbp^Qh93a1W?aJLrGY4qOkGVzgIO?<0@hB9<~rXiFhmjBNegD$jE| ze0FW6ze?@;@8>#V*?YH=Lu%LQyi)UV$JdDeylUV7DEq&glka=)-v`|B+TZusz2L9C z5$}xbPfU2u-W%XdS@e*Skupe~_iy)a_x^L@I>_&x?R#?P9lLX!d&}3&``6E}JJ!zi z?un=H_H?%59OoAKyA}y^X!y`1 zIh6WiOY&W1^GO)z=iAP!JYP@mrJ3((uSR=IOs)^F2c+S0ptMVBCmk~cORq}7((CYg z3tpabPe|(t@?P-2m$VN4d&&<(nSSt!gjW>2`orrnX|y~T@(hN&L*f5WX&3zWgw4*| z(sb!0yxxNsa-cFx@;1zt8XD$EjSX|9W`;y*FuVrBD+*r23`x=y!+a^#kPZx=iiKB{ zAyZlhuYvH2GGxIk8{*mUe*vT~fcOH4FO)XGYaP4>!Yj(KSn@S2f!9)aErZu`={b1$ z8dgYK;N@#rDQ!2bk@^|(qzHHgDtX}FT4}#wo%FKdWvQ9;GVse%WBAYDzbwBZHI!a~ zv{xi=_|M?KEFY50(jiDYB(;G54F1dVVMsd+X@?>0Fr*!ZwAZ9o()&`BbXpoFeJqWX zK9wd&Ur9cOU!~R3ZRt69`5JCZTj2k8$h}`u;THi_`6RsFgO{hQ$sbFad=_3`z{^v% z$d@Dw{r?vJGhkV^LYfuQtn#<;pTU1wwn3T=(rl1sgESkYxj~v6q`5(w8>G2Gnui=F z*Of=g4do58zw9fwmYbfR{mX{B&&wWve__0_A$(c*Bto;L!#Wz zkOZ%6xuKK|wz6e!_|M?KEH8kx1(3D?(iTA40!Uj3X$v83A*3yYw1tqi6#QHYX-mP+ zrI5B1(pJlDr98Pk^w>_)dO1wmAoq|8<=)ak$a@g-9)!FHA@4!R`;t6GdRb0_uL($% zj=(qA9Rc|fITQXf_%A~(mrBRw4e;`mkHe?Lk3;%#NUxMXlqw-!3Gol*ucQwl{vpIa zlY7Y3a&P%_xi7Rrl>DVUP(CjYhSnG+*T`exH343}h8p=P`GPzJUen;^Yq%iKlE0JZ z!7B+~zJ~AQRQa-;39p6l@-ChnFY3UVs;amn`3cv|EsN3({^u+AT5${WK$6po-UhQi4d z&ZIDr!X*$kf^UyO%>@*eLFg}K$X8N^N+v@rd=gqV;AcaFq_^PPkz@n@9-?e`3qAuH zD&cSE`Wp)1GoOJ5)DTX2A}BqY;$9SwA!#mz-ckX@i=}H(@H|36D$^r z!ahmgdNzYx_a}%w9CuP(*pk zDclEAS*oCT6_u)`lmgXm@q{wki27rVsJ}qN^urLYVT1IkTc42z=~n+y5FUy1Rr8cF z+a^hQ%F1muYN2v2@H&L`TGguibSzZ{;Us+@wdWcA-LPc6QtH<+OZSlLHMZ$EQNp98+->&X=Q$OpE0n9l3bQ+-T<@TC#( z%eZpMI`|d{`TC?*CRzETYp|(O^4ZhTl+Ey$j+Zl~{V3B#J#B5Q>8iec+Z2;*s%kgg zln3eHErdCc6MxP4jQ(|8zR5>^YV{^lj2zhgO;fIXt;@$IPgD5bYOwXwnp)7z!xdqz z%$PISTq)VWR*Y-|ZwnxN98#jXi~(u;po!+q&~xTMxFaRi+||&gFdM>;*Dr(c>v3x# ztZex_gqx@CG$%qkT!Zk^!deJtxcv!XeqEy_(cl)NSprROkFY^Fd5ou}tLd}#J`i48 z?`MgGQq3@I7-Xqt_-oM@b^lclSvDICjby2pY3Z18mPpgkQDv4vrfJ*Xv5YXix30oc zY*;a(65`oWr!D&o+Rn3(GJky~#Fy;F_?48;EitC=xBUoViyD@>~=ctHGt zCiSh8O-HBsTW6XEbZTKOGfasJf-pXcNrw%)#s-7*rOy7=VrfNKFkBJ*zMjhr`_fF) znDu=@TGSA&n07)Q!@~9c))>?1#3<`Uz3#|C5WYPk2*U1B!>re-)M3N-+hVPWrX?Mw zT5qvM1Lj*5=%x9(r|D`~j&+G?%=SH?b6_C6kHzEx@m$mH3Hz)CrmZtx0k2Y_rp1Q* z5&l*ymGYv{sw(Xzn-_&KO6}&lwnXJD__^6MwsWlMJX|$zso1nA4}KlcRMZ1L^FpNp z)S-hyY@1CLOCGUprZx$Hba-#1=|-P8+dk8gb~7OynK926q!y$sv4yHw>rfSEjBwK6 zX?m_;yRE>eY=6^s5xyDYD8y^RPS|qQU)#dFfYcVgpV-PwStHLw`jwP#A-u5q3Z$$a zc@5&f1icCI>tk-%PMD$-jBY1PeG1HOWu^;b>bq5%_OJJ)uo;E*A^n>%E#1zTE=8H$ zifQykkPqigLkB(V7NX&GNu;Y2sYuV9pgR6_ZS z)L}crJZ6H=13g0Ms+CgNT2s?$<2|g>fi@F8WTSQVGaj-zHD{Jbxr)CrE*rb#E%2xi z@jgq|dt|V2GoJORqLgZN$EYi|$-K`S5(AHTRI3A5m3vHv_$iNK`US#jwPMQ^TebQH zh3l6Dko05B3Ez@Zt->$gL%jd8D;~GxsDj@i%xQbpy+*}&jf(MG@cky9o;8ri)6>)3 ze}j)_hOuN;V^1IR*iJ##%jC&*6-V?f=%rE60_b^>dSqFIr@y)XvPjP&k_MXn*H7{c zF=LNi!f@0V(=SmJOZXi{UCm3!6nkFOtAgI}tdy#Qy28i`rcu^C>X@gZ)joI{!rl*l z;TZ?5CrehCfvZ{)cs{$iQbKjM`%jqA--ga0(+RjHYl1g`+8q zrLa)l1Y^E$<=QX1I>ExtMlei>d?bFE%8&6x}8dIib{8^QOX$roEq zG$)!rn&jt|Xg=DyrPm1c{LYSECyehTb@R#~PnMX6LrJ#{PN3>XliP ze+6h8-ejFuGI-e4l4zc{c9&PK`Rj29Kw7cp5QOvAe&{tq58r!^;uk;~)$o8W+GpOFy0RX&$@+SEbd__>UBG&t z7U2hLJ^4^Xp(oqc_Vs!NW}iLfU}b3VdypS{buxvy=EhKJrg>qb^Yt+N4s@oi{Sj;( zSY`F@rQ*zaQMbYDjV)8(`-HJgd<$=Ew?FD0HV>vcVxA>xm$sv%T=V0gE!X@Yge7GC zqM>Em9^OmTwUG0oVMW+T?>Xe{9IYT_vG+j@SB+AwP5wdeN@-WjDUkLljxq%p`{q|d zyqDjnke;4>4&q@^zk}VClT%^N&rWcwA839(x-NvVOt|-NRZ$sHzDVonM?c@wux@bUcUX-U8+E(-N(rLETu}zBA zz0d*y`Vm+)&gj}Ep3L~nXNZXZ zmh-z$nR+PvtYtK$=yf7!9y=lJY6bgn%ahNj4Pz;19EF}HThV}qo~B_tM>Y(Ez5UY- z=TLewgmCghWj)PGk1WWuXxEa4**e~LEZ1=?EbFHAZWO2~d#~6^AZLwQqT@7N9FjJy{!C^2`gM(FDxXexw$Wx4@9#WKtG8{p4WZe%9Im5eE7x(B zF4u9EE+_xX!P~{&736J|jw82R$1xjCW42t!F;C{@MHOx1}aw&6^Zw;k))l8kI`SzmSS`FpzYH2$w)Au^H!)5)Y?a%r4vJ8B( z*mtJVcEVoYN@-~E8@^q&4>OPX-l9Ccw6;%v<{N2gJK?@|=Dz(y#r;Uw$r5U3<8q4y> zS-T+}=cOtYujXXSWAN+4K_>6MHENZLb5s@e_E1wn%D~3qChQ5d<~l_&5XT*Lt+}n= z3cm>O3O;RQLJiTPMvu(h*LV=@0lcL|OL&_%8xJvUD|oN*X!0jeJ`j4d@l`fBL2eR9 z`75P|;GS^~#gi%YG(EDrLz8TZFQ+iiw6)QuCV8e~`J0*)lC3=W;VSsnV-xO(@~G~4 zrXLfTw8T<8trT)5Y%Ob&YndBd-lV{S9*)pY&ZulMLhs-IEQEO1>}fjCp{7Y8-F@eo zTEtw3QrK?L{&0p+1QuD-BCyDs`YPDAz6$0k0$Y;JS83EX7}8O53FLgZX$j=Su*cl7 zO%GC@gP=3VR7!r9lAi~mF0ypcggw6m@{BSCkcM*5u%~G`Xuz;{{<}>pOb6ObZG_=% zh-3b8C|jeJn+6nLv6Y)}KU{9I?a}!kBK>AVa&Uubsgj?T+a3cbAyI)8>Z?6)6K%v&3^hwxZFhNx39qo0Zy zbt>k$BQflqpZ0*&jNV$!*a8a7{~tZzWyZdQ-Zl+T%&1v`wX7w?VUHT%YhKl8B7`SH zCI$qUlk2a75Pyv+2+HDmv1~y}fRC(o-v@bK3pfGcj*y=NYR%#8S@TeHQ@6oBq2^|u zLCt;5P5gtKhnsP=3OCaVVooaQ2kBo#k82)jnwS&c+}DI-$JhLBRA%!aGv3dJf|VRo zILzcVYPcEuOa#=lpm_w;6hoZ-qM=4pDZJe@n(8$~IUoK~^U)BNHjjny805tJrXl9i zF<(QxNyrb)XKG%%MoFX1M_?8mVm@9_qYj~X99Wd4IP;+5!9Ee@j$_K2M401uJ>DqJ zd~sKg7I9|$WvDq+_nF%MsgJf8Vm>w|rbV1$^_6TmLgDjz!isNPL5pOP=316*EN&5D z{%p+N7TKgfoAhUs&TMnDR^LN9{#MoUJDSx8ai{I~V1)VCM%^FGBj1wA&t!@hnWgSC zA1pDyF>@J&=xqsUD8ewnT^hPRv6fb|^H zWw_S2o0ihGE~jf@*Uhu1(4?*;Mx%SHuyS++Jxfbdv8 zhG@~tf)>3j(}H@m@UryIe=D#S?4AKBj>KB9dnT}k>|Q3j*U2vFZ{=m#*!GIe7c9Q; zU;u?dWIdEZFAI(@FN#-DulMmtG!)eIbf7y-J zao~ejP#pMxVekCbLAaNDI0$P!08+3Ij4*c^wZ}Tgg7^D#EO@_vLORlUMbHVUd(?`c zA$0bbYgr5Z+|&H{hD||*urCVp7)0xPA*^?aQnCftyKL|uPr3r~Ynm*lcpim?BK@Nn zuQrD*k*)mN6u|l-!#&`qp`F@PvgUo|CPmN!Fwa}?oD=VoN zzeQ~hTEdzxYJ;`d+6Gt5(l&*(Qj}U2M_05d2MwRMDTSJ1_|%-QAspO#kF^5QUus?{ zU4hjT&pD;GrIs$;ZEa6j@H?daO&C^zl^j#0C4IspZ7VG|GY7Qwku3{`w5=xpvl;rF z%_97w;0zYMI%A1mxUOvkw7{0OHPiw=a#oYq>eN_p#V@6^+$snUwyl8hg=T@44Y}vq zUbbM1U8nqKHu_yNUzFoEn%d>c)#>i-ZjtqiaL4+lEmy`n zHN~p5pW3Jv*5WKFS01^r2ju*9TUVpAT%>aVXq*{&9BRkk~$Z|U?&yFj_uBs@3xdfb(EzE+%{dl_I2 z4ZaGea~{C~R=n0IeK5F}Vf*@s;2=r~rO?-ks{o!kYzz*kln5)%LeW=2&s{NhZyS=58bVh6Gw3ZZayQl+LM2`F_uE z8us3`hK6I{y^DtWapOX2rT8YJAYL678{%mVojWBY(e&G>=^;MWbqnT&1X_b$m=7AZ z#$K_N(%$c?;o1yo}T?F}Yy^FNgA5$E%oczIf zc}O0`@ubx3<|EgG9c&?`oUu&n`FqG2%Wv7=qPlXVFjqHqNdCx`d4+;&P7XBV`LOPlM4D{Gp>rmU8b?e%elGMj|BInKa zwNmvA*eSs^I@`X&nzZ*Kgk{qQ`&7|&udw3pk5$n%t)@~TP%5NDpm9QjE4CUcbs0)M z-r+Kpx=y7oL#as}u2ZQP>)z=zJKUnO8J314)^t#8Xv=C#+WSHWs|{^=*|3jUZRf*} zcJQ*%F2aU=ED~&e-yzbt%=d~dfJy~HDO+d|l?tU&K~Sn$XegBmr&1$~@NJ;B2+BVK zGz<@ohEh`?L|a3sR5X-I3mrnGMpLO++ZT!ZL*pRlQ3x?-1@($t=i?SI9#9LChs z9hbui@G}s8-6pN$WUHmkl8!~Br^JRmyu^k*yu^mRI**<%UDUr`Hpp5Accc3{9yZoX zEA4pDhP^A%+Aj5jj=AQm-Uqx&K?B_VQJ83M+4f3DJVE-sV}|umlG$w^+`m}hqyKm< z3c>C~k0sXf+=iVh$Zip(7m4%_A-&T0!`Kd;a;=|_?b4~rhCRR1xO-!Cr_I(Q0|s@f z7AeM(>79zL&F9YPRAVcIGw>=tMnKz&PNkr2MW=n%KBL!mx=dECQ&?+$aCd2^5tb$p z##o*!c&}4eL%qcBAgpY8taqqy-o=i#*tI+t04xA<62Skrfo3aPX%-SBYd!`6(^ z@LW<3?HJv;()#tdQ4sEHIJ2{rN}bXBtX$pM(|DwCQ|GJ3hGUrIMd??q^T)o>xz@U| zvn}MR_1!pIh`)Ae<$=y7+ZS<%I?J}_Qjc`5HI_vNS!<1!(Lq*E@T45_`%SFqdjQ{^wc$+ zEJnEDvx#Uood3`0p>b7RDkXf*IK&N4;YPdRlZaS1>>CwyO)Kb{7Ar4x^y+$9>YeY? zwOGON66c0v@Unz=5H*xO#|`h@ij_ve@N`MpNa3Wtwvb}wO6o&hYba+j`EW+B8uv8l zk(V!ou-TebU9;VA#avE04=cYcE9ttNS|E?|7gAWPyt8<>UolCm$zl;%ETNp)B1EtD zDg4=o_YxLXLOP3;lKdWF2gynaXoI^;itkebmOKi1u)YT&j55`lH^vSPt5SOf>vd|) zz9VBASHXFgUZ<3Nkd5o6Wrc;&%%;abtzeznr__W!2l4v(Tf+`3g)zlpy=+gf-5XXby)*6g zFxmKaR(Y5#hee$Ti?oHbdq1q4>{hrf@Ahrj2pF@M!ir5b6X2Xy88f1*HO6LmGS)QN zwkhw|uqwBKF-o^;%3nj_MFpSq&9t@N`)k-m<@&;I-4boh+V$*q!de&_Z11!k*lmgJm$7iyZ`+dUZ;dpJ-sK@xkq?t?*B3t1t;#LE z+w5+a$x4i+Z~uke6nAXZ>uv=p7rI&9-`e~~H!pX5l7de^PuB5r$LA^6-SF&4amO=2 zUw3SCt2^$0eBE(h6ab|rfd)Jw383^uWqm9E@F38fulvH@&3?RUjuQ} zgS~DmHQ(r$;Q+G{f>@r#EdGmQP2<^r*De z>+)2O0wrP1^d5y|y~usmxaWM&*s6+_^(di~|HIt7M@Lnj@88d!Lng>E2_8~GCMKvT z2N66VATuUGIYWS23)UGEI;GNDMcZ1n&X`aEQ4>O;D7F(5R6NuwSZmcf(}Y%9t)*c7 zS`}xS@U>KIYeB8mw(oV_`)c33tE=Dj{k`k`D8C_{Frke{a{9&z1OOYXyjeTr0^Rdm$$2K#EO0T_Z%Aoi$X?*t=8rJkcU0G<@UoLHng@*C7 zUTB!TGZ{Ackxhuqx$Qpm^BMdU$5w9!-Y{(SzC7y-ctT!(*NcdM9p9!+3H;;!{jpa| zBa?o2?0_Ld&;LVgN?=*lcaGU=-)*t7<(TX7KDjbJB{1l|YUswq1Nc^M>D^;N^Ip!CPosmlwHi-?*?c^47#9ibM*IX?*xE9!4A+O=3nxa> zBXER7{BJhT#C30#^*I7Zm*=hz4ZY^%+G~fsGI3h%tjOo?kJsLav0d+4Y;21nHS;g4 zT@<-?(K2`akVmg-uDvw!UJ_d!@vdOJFsH3{gYD%FwwE{9US1UGM%=@-@nPt@n4e9y z#wmf{f9aXpt0R0i$PD@3h#jYFv-s7Kl20E+JiRhdH+I0lnM3P3O734j7(bl2|32Ks zJnyOrbzbnd*G#TkH>6|Ul)5h4gI%@?ycj>O7~HlD-`e<}sjdGW z)5^i0zTxY2yReMk?2UgJ);_%>v}n@ci5;OCP|q7bXtb;N9sK9 z*pYkAAL01cWrX89kQ#fVjxSx#+EzEfyPfaVtSt9y8FzJwS3oC zeqaBg8@F;To+$p>8JWo^pX!;9MaC|>Xl60eaQ)nwp|YpLmqKqm{t77n=EI;eo~LyK zvYS@V+=Wq9PT3XY_4De14eK5ndG)|1=um z%n{Z@Jbc2J9+`RdzzK=`%>5;IE_wmVyVcbL|GbvZLOg@^4jFk?VP;2Y_qjOht#Nv2^W_|zHXmH*OkJx0iHo!-2t$?pD#+#{PVtL?ITT&4H+8GpWr&sv_FY)4dG z*^Y5|`&xF~s(}~J#n`Sqv99dc8&1A>iI&5w;ukO1^2?)^T->DPcg|XJab4M@X{n1> zDe}3?&4ev&)1fHDe=n1H(JfEAg?N8%XojET*l{?$u{z<2Y&Io_^cM2 z&nbax&w6my4)3ndJTdFxAb;v(Yv=@A39lZw?YckA+F&(%LK{+ZXYUE!IchB46xFW! z)a=J1{N%f-jMwK)WxPIbD&zIJ(%XC5^|QC)3cvW6S%a&-v|;v#Zb22F0B>x*YxXwl z?Ze0gYrAK6*$j8t9{DiBw+%1g$o%8r7lM4Z{{oKWFP#0ll+Q&k1gRfJ9-UJ)=VA5p z-rOm3_M_%EXYEIJ#jw4>u@5bnQ&oE3a=afd<27xUlzV?y8LxOCF4?p7VMc*Z8qUHRsd2=6FND zyydAmA-s#e{hTqS#jc;v>9euDZX-EteIB+x4_lu{Z5yks=dz(o#{V7LHT0y&+;}Ok zX8ifFL32O0efqH-&o%ben(qV8t_+p)jdZA-Z}sbrIe6mfa|c;ukVOWS^PN27bLI{& z=ezXb<*A_^SZ4ijs~m1MEqL?4eeQ_zE#G>2ZcX_cU-`w{vE{t>4llp#m;&ngetvQ} zKckH;AAi^9+_Z9j*D$%9pV20lpLZ9R`IE7A)@Pmdzahl&OfKge{<+q}-16G>v*yjU zkxVY**3S|f+Y%eY67;b2wBWvwb6;h#vD+O?zYBAbVrO zpWpa!z`O3s+eXa4ww!nKDS_!%OqhQo`oVW1<$Uk7*G-x8^W*jp;!)R9eu*jHpR|e|x?KV^}r+sMTz+nhoWw*MA4XA0GKiu9VY_gh7Rx~s?JN8u^4bHe4F!uFK&JCs|)s*^UD0XMaJS&7_Qj% z>9x-yhoPQ3T+TCV#2|i?J{F(L=AU%bviR#n<3}tk27i9lh=paL!H-le{Mh1C0*9tf znBQbiJN$G|S{|yn4{rmuhVE}&ws24dkB~tXJVFLl@CX@R!DFKsjD2Fn!fB|-H+t4; zgjJ5P${Jg)vE>ogs>ZYJ}uX--x_|Bzb{K&r`(mT#Iu>#dnbv_Q}X*X}QhP za+{wfi!@oJ$s(&PvdSW>EOMUu7P-+PH(KOIi`3Y7YOwZUi)*m+d_61vh!IpchhZjuu zTjlGPeciHsw%ljSuUk!@>2m9Tx%Ge8S{=2@qgMH`Eq`pwN3H&2)2d*o^@b&(3j0ay z;XJk%4PA5BeM^eL-pfZUtg|-&mkzz^j$KOzS(Z;hzgV&-;-5|Lc8*xMC&F`fII^A_ zKAcZ<@j!6odrPJSN=H6$OgwPR)c4TaGMq2`X2e^%Xy|3P4OkivG~9OF(kX${wmmp& zgx1?|+q9)Mwp7x&b!sv;k^5d9nO0b?-BPdooiS4j?iD)eiR?z7P_~V z4sc7{Vd$sayU^wCeQ3gc1Z_e#;2v|MmJe|`=RP^a^|(`^FSzm0U%Ca*Ke<((4B+2K zvF0@IIz(1^8(8Db{uFAwM(BR;tI+@Q9)rH_{SsR6*tXAm7t6o%#$P%FKf$^f`X}$c zOG{lg@Y1CL_eh{>dAU0_7+5yMT^rnw>{o+Fpf?7GeR_yX2ggBMf)_&91}}kb2qvJd z!5g5Pg5Q9CE7%F$7R*7j!Jk5Z5c~!7nPBgyOWl8==Ti52@bJ=7R|vif?F&W_`Cah+ zrR6RhV)n&wHO8Kr%f~HG<18pgN8@jgi z<nIgE*4+O#f!;4Gt^|xcJ~v%S&BH`Dx2f#XnbU#{ML)aj$P8|2pFEQv70g*Wfm{ z;+F`xfyk2;?vz`}6Ws)3wc=;E`Q|6N)yAprYsQTudc7#F*d z#$|50u~EFz%`?9m#4m-xzoe^7)q=NS{Y$_GH!9UAzrynEsb+cndQHoB7}vLUDo$RA z_S?bPu055v{N~nfrS8N;&a-5Tl2iC^Ldc~6<_g5qMY0MAF`W0fv z^6OjI$ak3E+}a7Uo@9L&c>0LS)OO21p5h;7=J@jFF+U*dN!ITci;+g-7?%JP^8kn15?H(H!ztW3qh2DiSo7GxdCx_WVmvldNAMrY(M8?Hc)Zb|*wWE#gsWI}C#JE@wHn?X~v*Z^W zD^p7>eoiV0@_1?lZM!Xx?Y8{0snzByQ_aeg9M>A<*BLjrwp$$YVg8)dcJuA29m?;P z?**q~f0MP~QHxim@K3X`ANtmMpq&pO#}fp(K9a{th4}+(ha0i~LDrG1BL`x8M$1n& zR;FU$-EP#ndc~J0o&;_CLEC=iSAY#}YwB9_m8pyo`(N=*ig$u+M{@n!EsyiUJodNZ z`{axL$o2NZvpvc72SqQec>tLYg3O1-3M2ME$m3(O@tjn>acgR^@+*{Y2H6kEe%4t0 z`P4e|2iA6g98V``+hcKT56F4gVgA6{onkl0I+Fd7ocG-z$5Wv9magjsSx4ITDUV<9 zVfi4)JlWv38*lU0ZKUV^4qJR{Dhjf{YLN9L`y;ttlJ%oOu78r{53G%W+^)D-3$nf* zY;ez~5{j=dzcsbS_;{)VoZ~&yy50QCsXS=cIgtJBw)pd@J>V7a`;;&CBkOwMZTmrv z>!5LK>WI~~r@V;YUl?S50LcC-K)dc+d~<6R$o?iN9#@=XycXnoNcK|?a()&Y53Egs ztXrY@YQ@*cx65~e?1yAO+r@6hN%p&2+-E$nwgB4pg0{WN9|YOY5%UMudZqN6TZ7=I zaXloN4}0BbW3+sX9(8fC7G%F9>ywJFkY6KqfP60K1Z{g2 z-!A4s_S>yI$^P~ku^j{ad=O-NlKBwG?F-9SD^D_Cqx@)jJTxPY{RXlhl6B;3Ui(J! zw|L&F1KDrA^0SP%Pl0SlvK`5GB-=HCY_~%BW{~wH>(_|ujJU7M=RwK=wnjpV7+4I)0RK5w%LezQJx%#e%8pZ1Nl6W1=)Wm$n7E7 zFUjL_yL^rwb$PJCz0}$ba{RlM-($pe(c+b$Nk+{nc6A83v6(; zsT|0~~0%ZG@#>&*S7Eh$s zD8J75Qfrs|c5x@j`^_%%UrKd@91qFUQ**vZkY{E+vRr}vHw7B zXAWe$ZqW9l<#FE@_ZTZv`;6F+AnSWU){`73$@TP^Kd|(_|wMm#@*?6=E2?xV`@P=2Sl3*>y|LDu(} ze>}C@^6jZT=4(^?%pX`=G{3o(WPLBlar9Xp_YGrv>JZ55?oneV+&Gmb%SiT2V}c_Alns{KLmc{ozO-$ zxHH<0DDRcg<9Qxro@BlPWczB6{f;(|_clfx&x+S8o&Y(Hr1Fi*H-YSbg~i)b8S|T4 zN$!Vcko~VwzFq79`8?98e3yAV=PTcBaa{N1_gH>Ws#kH6$3Mw=J81dssY6EGugls0 z`c{(Tr~tXWlZ@@DxaA*hC7G``zdbd}m`Ej!wW%hM&x0!!&lrExO4{~;c0Pg}PcvA7 z{?>uqPwf`R^9mS5oSYiK`DuAZ5Ibt54yN0tq(nfMS zgCP4SSx>gR6WdAl69PHDu=3TGe|{s$e2v9%-dX&@c9QWrko&6$>WP>};$nEcj zZ+9=Y_kbM7Zjk*ILC#w*$nlemlkM)Uc9QEksJJ`UKc9o3%_qou`gS*D6Uq9p;uS{R z$H9SEPc_JPB-@c(56O0;6_0_eueJQyn@HyCl}{+21bH1H?RW-j1Isrxf~;Qway@B~ z{WXK_?(vNz^J|pPD&J}O6`M%rw=2KXi2DG@e)1sKOWOQ`?QX-SZjkMIEPqDZZjkFQ zfNWQ^IIff6ZSV(`$FBplJbon|`u$xiX~!+d{T~F`PgsnC?62DL_#B}4Br#@jJimgh zt5v*STx`U1D#-px)+Iq+PZ}+c&w1i%Bc4-1)|0HyfV`fN9B(st8{%s~jyDUko@9Ne z^4pcqDNi!rt^97~3ySwD-e++@4S3IkDr{X&;j^jk}ZpC+79G_>E?*;9AQ~aQKMET$e+TUV@ z5zhniqs2*L9OUsx+V&`3XT)=^e505KxxE>X*JF~~)vSEG5!W60F3_&q;DaY(KPkRj z>=paKotV#q^1(`NhgdDvfUFxWKgNjfV?eH_R`GhVQCw*}uy(b42DEX@w}9+-jr=;} zfwk@O9r7gSsZ;Uo;!fprAp7kGxt~ZL7rVuMVgY1bA86aH_z@%SL!aRG)VBpewkIpF z-mrXy5&IQnKcf|&B*qo5m9G~Ui%I1hL5`E;dRB-ljhHVn1KNCntZ%kBKBtK7Vh6~& z?eaT}c&{(!mEW!WKE;cQ_kwnOD}PA&Bk~^p4ix*Z0QsC!C0}hGpC1&TBp+A4R(`Sa zNyQr#U#a+N`3%VG18Li>c)JnDxBL$Io$}q{ZjkT)_bK12_(6+rO&wDFh#16g!nf@L zxgVqyqEu>9kxmCCOc)8O6iqNyab|apz z6z^1gr`Rp-7WWzPeoFqJctmmhN?#j?2XY)id6L@|7AwRmkagAaqs1{s92fF&(6(Q` zUR-R%=V2qxbH!JPtHowB zc_8LN)^#htPw@iCc`GX3EA}aWMBa<~+aCm3PjY@MjJUpmeE(Qu+}t`w@kwIbi1$&uwO=UcJei0hmD4*8w(-Tlb-q5I@}`;qY@=5ZgZ()NL$=61%@5KOr_N-!9)NztcQ^js#@?-HP`pzR!r~ zZP50ce6M+&&+aw7Aco<9VQ4btKonM|Jze0?4`}%DZZ=7vl$6AC)IrR|Rr@szJ{GB#_$~SG*SF zew?NFVk5o>6&sabt$f-%-V1xYbG`6R5ig$}W%I^{P8S%MO z`98&uh`}1a-w?>{7$8;{ai0abJ(CoVD_$!uRy--+2y&cFimz5Y19IM)LGD+Q^V=?F zEspyL$huC&cN(!jmCq~Rt^7W*2y(r>@<)s~zeaOCcn=FQA2g5i%!uoU;?B+IF1c zGb&br_B{Gq~a=b<5d&NGGb>6A| zdPy5U$axG}9Os#NTrcEnK(>p6Ht&ir7L!KYx0P>He6`ptwu>Dg`{@LEJnvLIZ^U&^ z`EJGciM`?x(K}7!2RSblVzoHNi02OZxIAgwq4;94QCux%K-M+OuQTFvmV75@Vp91= z@Ve4%6EdSBW*m2cPmaZ-YZYqdd88suYerq82Pw-Ey#XJ);IR!Yu>$^npM{! zc8YniTP%WH56SiPihUrTlf3cTelY|xUL{{G)>s_xx8!TZMzI;>dEWxEeFw<(l3Z`6 z*rj}4zFRDUHct6o(VL+0i6PMDtsmKcjeJ~nNwE>+xSBwYE2DU?7(CPOKML}>ty;cT zOeo$c-z;{D-C_}JaF@6B%6k(zPJ9jrIle0SYWcW)t$Y$}a9?a|ly9;;u5*evE8ZdB zDc=QhKJtopgWTStd@soL_kpa#pHs2*1VI~@e6@TWt~1J zogn+ogWR5O(8i;Dukzj`zb*(eAC<2XtHroj3)*<(8^w&+tb7Y-$DiVzVqWY9xqU_X zUeTMZ@qo-%$ybYUu~tlqjbanX`OWkr=b>5o4zW}0Qe9rYTP%vbqIb66FUkIbVwG4e z#z0;#NY=$cj)$bL6_aA4m;~DsV8r_j#d}5XJdH=J605};klP!VuN9MGqu2!6`&Rj8 zu><7(A-Ub1VqWYPdqp?JpQj+m`T-!@M-{JDJTBIXNwHCE0@*GN@;J*V-YjM=?iYLWJko6?%n?SxV z&KMt0wa9nKcY-_)NY>}YZpBH)3n2GX&2+!NB*=K9m=T*nZhr@8pO@wH^4%ckwJ6^! zk3S`5^+AyNka_%km3$S*^;OH)C?Aur73&mlluv_pJq7P}XW!PWc&FF}^5-r{ey@=S zxt?y&t`DG%SMgqXH^Yy6Ah$Cp9~G+y>`H;T<-7PS2$zXRlPkvC#| ziuZsVcTw?P#SfXs^F>VK1Gzm_^3|Y?L%vo%Dc>lck#Cmo5Ie;#%O_GhjQDqKiuWkK z$GE<=SMgxnUvIS-2YG*}m9GQ2KS;hGPFfzvnerKn<9(F;I?Llas(7d3JIrrx%`4xn zcu~Gr{*Zj|LVsKXKt8Wj%h!qtu~AHm&0-74@n+>a#T_8`TTZ@P+yinwz4F0Y&5Kwo zHi}IkzqfCeZ;|hm?-qN-V4dGz4aoT<`98l^@uYmCdl11$)79gktgZBi_{Nj;}>Hf+sBnBnNNyokn77RPcq*Ea(yKG&w{M$ zP(BaZ`7PfAvYuppQE~5LjSIAKiE+hA8;|JCVm;1#F%I&2m6Xqj9g640qUg=`>#M{V z$Z^Nz6Y^=X#p0V=v+_ByAQnN6$D5;mLHf9u0C~KUwjGM66(N(DoZ>_hIFWVjsvlZ=uF1#>G02?MdDr zlZt1=4zUYl9clM}#fzf1$nQS}vX10mk`6 z$$VZcie7`(FDAu|*dgY{qUbI5+a<+}*aC9;fC`^Km_50pxp| z=u)3`pzQ~c=UoD<_HMYXM{$zz_$M_kF(Y<}Merl<7jswt+1I6>8-6!M=AnQohk?f}^_9;*DKIxYEb);F8RfIe zlgyLMcPO7zoMb$&ctLTJ@uK4HGyeKW_D3@Bf&4uIRiNF+#H5%JJH(>sU9RmC<6=_G zfb6%!i04?vi(;R}7q)o`^&`eW+fShFCy?WegLeH^KA}9xd=ljN(~6UfXB5vWPBPx1 zcn;+HNUo1$KCgUH^pbx6A&~V|AjcCo;`%H$DW8$=5c8sUg|-*8_VP(F2l75%5Q~a? zjeff*$b6N2Og;|U{KzMj&&ub-f>>1EYtnXse4ek8uaS=%A5S$|9PeE~u0JbwS^n8n z-tstqLC#-M-mUPx2Xg;JB?z4V8D#pY($nn*IoQH(sNyVEK&lvH00>m6RkjD$i_63mZEy`D={OyX136S+k z^IKC*Ag@0eBfjsj_<3y|@?G+Il5oh)+Isq)1i1?>;XUWp4?Pa-dp8wca<0e zIUjNPq}T-VdxwmChnNFxK0w|V3-U$tyW09dwsT+b_qzx3dz+|ymH8jH#X-)00_6IW z@)^rNpK1Zw56SnfU1AR8`itftPkCQt{_#|mac^53WWPxgAp6aOHh$2KPvyO< z{Pr=B@vPVZay@ym2y#8{OWID5J}O@&pO(*wv8(<1gxCbKU5nTy=8ZV6K^vcZpYrY+ zKOO>Yz4BG^HDV0pe8uIHAkT*;`83G>TjaaMoZ@-&7>9fjWcxmgWBXV8?LE-WGmzt{ z5$lYVsRYRNWEAfZ^J0$?KOc0hwg+T=jd{FhlTU!$o}|Tb{guy{$8}h~1LSz}#s}Jp zmdEGR>-_$!zy|k5Til5AR`HD3A?C#bXxA5z{d!*}@%vc7yWOA-3FG1oY2!T`GM0Zl zRRCYZb`(L*i~EZ2J&?XijEf27lk#c#jC@wULp~>;2OHe)+Y0hU<=t1cKG2R6`J{YW zJ_GXi&Sd3tVnKA*s~^z%k*@;TE+(H;J}sXCxgWFg9r8K(ynI2vDDS?e^?__3m9LVI z$;aiBViU;gaYjB1a{fEyyOhr#meUzG0ydA_?FG!D?7t3ex&;#Kl7u?}Qi0_1T@ zvOcMJMr;u~#Jt!8a=k@)ccXvYd7!NqAbIj)@IB;$FI_v?auQFJ%?c~1<1Tu&6V@he^h+H;nCOnH+1$1T6U zm87o&ZGJ)9F2$2#T5*!wOR`-?Yyr6)9rAgx0P=aa2(o{Y?fT@sulwU98Lt9aN7C2G z$3bon$#_z1viRevjC=>k_9W}_mdED;`5xs9Ah&~LT@mE<$KC9E59Ik6GGaXPagf_t zCnm*=5$^@WE|BBMoByz_Xudtwr?{79zA_aBx&A8i_`E0|mrsgKM$9{SxBK0OF8LhD z_64zMaa@lx{`jlJxR?O#cm>%m337YV@)?lZofo6G`1K^~V`3a+T~f@59bz72zeRcc zl1E!F$$p|@707<#Vp7bAIgsNe*)FemLB1&OwP<@m-aq2tK{QiM(hxCpv?E+YhonA!d~C5c43nr_Xp` zty`z<1=%ht#zFjh>y1f}^-V_HXDq%o)uDI}wCx3LdzCN9_kld$y!F~%F)k*>G-%r? zpOx>B&w*S|UcM-Lw{e_!A8y2P0NV2($Z;pdG|2w5VoofGeIUo_Hu%?B(&j@xDj$<4 znNNsmF)QZ8g6MAd>+3-KdOP5p?vHP)qxyOLcT@5M|5}kbs>;-QTaOg7BL5M zK6>N}@~+j7M?t$kf!v;$JZak}rp2uCB=b3WlD-GzcnXS>jJr0iPmGBP(C%v>>(cTh zeOAng1?5TWccb0_Xs7xD=a|G3-cB?huCA*RKwm;*UJl63`nlHP67`a$}r z7!$id&PNWk^F;B2nE0mOE-kizJU;qBwsV{PcBJ($)_}}6iCK{SldQ{$u`Pa_WV{Y! zyM%mBEQsz~ypNrGdjaHgWMV7Z<2V(wVoofG?k>MBD#pZw*aULDY5A;}6ANPWZoi*| zm%_F;S+M}#?QXt3`W?SMCN_Z_Zvo`Ik{pN2 z`m6zMd*rjC>+tJI)hXd&E95dY|8}2DJ6a*U2ZqyWMYYZ<6m4 zb0F8(BVUm3laJl+=M!R9%!#oFlo!)rvp3<67LfPLoZO+OzU1BG+`sWV3OmW$ z@5t`*-vj2vnkQIanW{75{TRsWPLq6EzC}JKUl85*{rV8d^+x4mVnR%dIk6zRC;j$O zF(xL&v{(?`4^%J4#Dtg@v!Z)S^rp$<+KC0lNygo7+3*% z&rCMBUu;S!-lRCmc#k~E@1tWs)&2o(Jn{+ow3roh%9E@w$dfki=e1uz>kqW&2qW%S zVoofGZV&75`CE*MEg;W_oOyhHk#|4y`^}0ukk^lb82!2LV`4&V0_{2_pA`$BUC&=2 z@%#mH{4p^n7XBH3QQIdrfi_P0g4m~c>=&vRTR>j_vf$nBwL1zR&nMF6@g>b8NFNnr zVnTV6`LsMqpOq)+yFeSCd_j!gVi(Bz0?2;whkyP4#F&@>?Y=6XmM7URD^Jq*f$Z1)p6kQ= zDlsP3fy^i5vmo2$#KhaaPm4Vu=eJLEhy8pAWIQS#1Mhab?@Y^Q#R6#C|Bm_<-5>lo z$@NFYoY({MzFClu{ZaD=+IYo+*azBi@h6QBwDEzQzo_ChipLZu*s36bctLbW{B}(skB=yPuZ$Oo&;K z^(6OOPM);=ExL&Bqaf=^=40}t?RWWvd>Z6>vtk#>@%D%Xko}Qt-)Hglt*+FMlZ=Nz z{(i%#e2w|NZ87--$bLxHr{zidtUO7d139jO7#*PP0-2A=lk^F(3FPxfT0RTf{K@C! zNw(_&Szl0`WZWI&uRki*ft;5FXxjm@u1WE<;w0lO7JoLCmCuO<(B@Mfe+=`#_G@4f5k5(2hfp>yIj4WASHGF~v#tlaMFr)1VzEVnKP5 zdHf}Be>*_>s2CFy%9GZgd|Ey$Pcq*F@_JG;#Alxv8tVHNBksGPZRaqK56>}T4aj&* zzD~Z$_-rbtcmd>f2!E>37!wm>TFi<$u>f*DLgkuQkmIfaIqsNzo$?9!w3r3$xRmb# z*)FFz$^HuRBz+WrNRI2R0r?yd0~xQAPslgPr{%L^m*P2)>nGX1NAZHKs(CMLwRm<4UW$mirqj;A0`(#K9~XXQ!y zoIFWikSFO~jn)U!N5zX?c=9 z2Xem@#ONu0JSHZ@w3r3$_?OSglkBe`Ptv0@F- zOe;?^pOq)+bMhp8L7t>{W3)byJ}Sn4nN9Z4S((;&AaE1we!q8qRFptYBe$&>6aAy3k$q6dPfmzkb`Cq*_50;VZ#jkl+m4*gPA8_-4!vmtn%s6KEF?9p48u;v>R|Z7} zR}P*wc*)>j4E}iV&>@!$xopUtL!KGx4T}wXa@eniRhOMw)=>7Tvdhbw%2t=%RMuR! zuI!s-on=2PD=%MI{$P2rd_qNC#iuGhThUT+d&NT)FILPSe(JH89{bv{?;qQ6+!e?D z=(t121&=@Z_`2g)9KY%Ky~l4nVMygEl}9Uwe`3=o{{D%PC$2tm$B92aF*st_h%qB( zkGOWkQzM3rykO)HN4`07z)53H`t?cgoHTUQi0J6(8PP9Cw?h*Uh+LM%#?XXS_Yb#RkR7W0kQ{ zvC*-|Vo%3jkG&UL9=|bud;ACSAIEuk#Ibr7aXTCb~gPG}z-oEJli@!aqYu3}V>Sy0Fd(-SkXa9P3 ze9o*nx6SF9vtsV4^B$V_U-J&m`}@4m{9*Htn_oA-V?li3MGIRN-nsC*3!hr}+`?BE zzP0c#3uB9}S@hhZQyae6kZpLN;n{}w8m27%;^NmAzqk0}CF_@LT=K0Y-(K>_lDC$8 zwB)j-jZ5#eV=o(hFfb5HL)|cUEdDb~OEBfA0T)EzjVHlk{i zyAeOwbTfYFDDAeojJwO-g15=Hx^1`y-Gi#{;K$susJz#;;|F3kxi0rj_W)WvjLJt* z^*vNQh9C8L996qqhkF85-*?;H58VCkhp2oSKi2Uqs(yrM&OM1QZ-3x=+*9r+$Ucwk z&+zr}3&{M!;k*}rn{qV-X?-@T94f5F#6e?<=;x!>SL+G}{}^}6%CH*lptfEPV)x`@~7 z26%_uG2ZXpAn$EA#5?SUdGEM#?+^Ik^hbA`_a}FP_pbYd_h&c4JK|3Aj=HG#o;%rl z-_>{@xY6EU+^OD&ZjAR=cZTE5>+1@{RG4{^$Jnwuj;7#>{-UVLB zi+f?O&MWaQ@*>`BZ=g5N8{{qU278OVA>LwdsJGNR+54nd<6Y{F_Lh04c$ayndY5~r zc}Z`K*XW(@t?%4mJHgBf40c*X(yVz^>W_cU2_I7WM_f2oEw*{lv>dp7=!btA%7JA>oXga(GuL~o3 z*jwW5!l<6WsJ`!A;ys0tJ%f=w>n-W& z@4O#*zekU6dwaciykB{L^!9u2dM|rN(9?V9=>zY-ybsaaN9gTu=;a?C-Yo`ty}Tb%2BFx2ea zl81*vH$PknJ$dTM&}Sc-0R6_5jP!i|Jm}i5aXD^!&6GMU|MccdpjB6`gqA(L8hUuz z&Cq8@-U0pTNz@Na_hvR?dE2;qpyM<5LSK7;*^@f&hwireiR+oY@AQYD`yYG^`j<6N zLaV>=Bj_y)c0&_a?)lg9nnxMA;+z+u=Y4Z8bnUILK^u|>p+DVp7#g|teQ5R;*YeLE zUOc}9%g;<52wne$GU)eA{hnu^%l^wpvFGLss83&hJZc(}HPD$`#zOx#c@ngF@)YP< zmrjQ+KD!Qjvu){rLA)<68Q!@jq35VB?&A z=jG`054JqnmY1Hy9_E?)J44HY7iU`bW#;2f4j9 zo&P}OxhR)sZV9eoCDq^iGq()22(=+O5;}9sDHfs5+%g5adG36RaCzpICR?V?+;W30 zQy-eR3HsRwsc+7{4?1(pPUsB}QvaXJ&sm)M_pbfWHDg}2ngbT6&fM~zEmLQ18S)MG zL*2NY^_8cy{)=Njfyi~A9tHJ}@WN#^Se|+pYm%FJ^gez0ctrks^Au>yEY5(hGq=x;4&w92P+Mm4IE;<)iYeK)Tiwl>yOR9GWb`m zf8Qei9Pc0Zd;6ca%fA}1vHhog(Le6@(|>OL?DHe| zR8T&<8FO{=Nb27|bvrbESv&OK>)Y;S{aIhyg6#ih?fs9m{^R-g<^Ok;{s{Z`)_=zO zFZZ9%vQ^tL{>TjvLEpQQ*WR8{yxQ+Si+bF&ClJ5l1x&Hrirm5Cno;Hy7N{@VZj zc=jXyw)day{v-cS%m14r&~NMa-+zYt_t}4D`j57MTR-kc`rk?W-=fZSLD@i^Iqm>XWqkS0Drmv*!<=G$HV{WvcHY~^Pzta_519< z@BHPdcl{b;D_r&_^gqq5zxJtjyY0atMIphdR;dXl#YI?7uNt@4&aYrIRKqrFR^r+CYt zr+b${&+smXj`fo0XB;$uPxp<`iQWomy_bT{^HxF^cvnIfdS5{0A}Hp;y9)YA?`r5} z-fF}@1I0Fa*Fo>_z5?CqT@Ss>y8*h*y9s)acQf?cUIzLduNm6mt%2U_-HIMMp#j(B zt%Gj&ZiC+E-44CqyA%3=*9P6;wL>5Dz6pKE+X8*q+X{WeyBj@x7aDMndiP-Yd(eP; z%=-@Xajyfq)9Zxp^0q^t@a~6x-`fFw(t8N{1Md;&Q{JP{A9{~LpZ0b_pYfi6KI=UR z{gL++^f~WoXwG{Un)jZA?)UQ0SG{g%(c2CEt@l%CueS&KuJ?23pS>5M?|UynKk)WK z|Kja~e(1dn{j2v1v^?-Cv?A~tba>zm=&^w}p~nS^&{G1v&{G2kp<@DXVH;0}2Hb?e zA?TTbx1kdQ??BHA{1G}M@GdkKI0C&e@E+=Gp#gVM-~;Hzfe)dx10Nwi2a5e0_#1Rl z;2+Rr0IwR{6@ehMF%X7c6No@p2L?bhfq~Fl0)wHgfuYd0KpC_>PyyW0^xR+^Iz3nmogJ))&Iw+Gnz_(`n;)D7T@suFT^gJR{bX7l2gXM~=Go*8-$Ix&=oo)hYZo*UW?ofi5jbb4qH^uo~3p|znG zp%;f1-dl!Ds)-sHE3h#4Qy8vG~iZ*-o)}}p#hf)6|wv| zXuz!s^+LZ8Itcw@=q>0qp+nHsp|_!54ZQ=sKJ-WE#?ZUa_RtaN*3f&W z?+bkdy+8Cf=tH4@Kpzfyfq?s7C>*zg?axbQsa#P9;>S>Z*{bHabY?gSy(ruWogH3*nmOSVmgk07LKlRugeJmYfF{FNL05#YhJH4@8hT~;I_T=~ zSD@F1uZLb2z5)8x@J-O`!#6{}7S2F#3O7T)9$o{zIeaVhmhd`gbNDuBOZax^+VGuN z`#LDj&~O{{ws1RiOZc0Je+!EJ9^L}IE4&r@-SFMe?}hJyen0#j=nujj(C5OP(0q71 z^!f1p(4U2OKwk_$1Pzot0u7ct3JsM!1|3$i6IxdC1oRUnPeM;Dc?x<`$lU5=%kVtp_i7t1YK6L7n&&92Thi|485Y{ z73gP6UWKMgUW0zV>2UoANU`kRt*&;unCpl_B;g#NaKPo{5| zoQ?4uE;$#=@03h|{;6at^xcx_(4!?YpzoE$p?@u@Mde2&^;rJ6R3^6C;;kxe|&y zUF1?MkA&g~i7dnNC}_Y%BbQ;h3W}p8aygc(p*UM3Ni2_s;^`vNh~-nEIDR54usjBe zt8pZS ziaZ2;Ao2+G2a!jiPemSsz8BdEeLwO9bU^8o(1E2-K`Tn1h8|n`EObQabI_AY^U%?y z-Oy7@cSF~g{uH{QbPu$(^ykoTmA(kQzw{;OyVLHT|@ z&r|ijZ+Fii@cZjGNxfC|)KhoWQ%^nhRP`V6t*d($-+6U^#<#KVd9=fs_+p){^C}}| zXI%xpyXva&Jy2JJZ(m&70Gq6h2h@r~6j#`og7rTC849gAy`s^0$=b#bt~b%1Yh)K-70(wbteKcjW7DK?j*R6;0yX(cM87O)t!d#^>u6U zeRJJi3GN&51tqCF6W>qPZNc}`bxru*QP+&`=j(Rh`^&mr`2MPH zcjfUoW$ngE^jEOE*=+s|yPH7Z=&+`_#Uk!ATX5DpRkI%7T+If257+F%_h`+T_+D1C1>Y-bcH?_h zO*2k}UkA+F%^QGuyLnU1CVa04=I!PNVBT)tTGNE@+ktt9c^5G6Fz*KD9T>xV@qHgK z?=XK0%sViW&&KzIz`PTqcRjuz1LmFP6TrOF{1Y(mG@k_Ko#xZPywiLZn2(qn>h|ON z*1BF(?VT~d8Rx?L@SX2nFn>M1ub969-&f7g;`{3PXX1PD{4My7%)b=h1TYtP6Tn>H zT>{JnUK*GSybLfGcvHY!;N^h1z$*ZAp?4IR3%$#LxzM``m)e5_CC2_J-(k_umRuCF4&9j zZ41uC_j3!j;Csh{v+@1>f@U-2-B!OJ-_O!TknrUc-8P7c^|ZcS*x0 ze2-~36W`++w&1(0p$Xp=4bA2nZ#6Kl@zwzI8t-&qUgNC;<~80Kz`VxW2+V7|Mqpm+ zZ3X7F-Zo%f>+J;Qwcc64yw=+T%xk?CU|#E;1I+8a4q#sIbpi8w@7#vH`1SzvdhY-* zulM>J&c=5Dn74Tii`V13Wbp=kk6FAI-{TgaiSM$-Tku`6_-uSnSlo;q>BYeOk~ad( zFL{Z@7vehx%rAKp!2FVT$>LYyn+E1Ly*B~#o8I-n{HAxq;*0QoD=@$5y&ag}^xm;J zhVQ$8d9U|rVBYI}7MS;Xw=GWL`#E6V>)ip&?|WZF==Z&UMdtc{yM{ z=M{kYoOc+Q&v{3Ixia$Kz+4&m0WeoaezDl-`0?d;lzgW5- z-=~-MLUOYea+PiqgMP?$(CK&|v^qXzzG&_?{|&v0Uqeq~zITkb#@h%zgBIupB)v;< zVt+041wP5v{neB3UtAaYMxiEB>kC zixuCjxVPem6^~Z@qT+WIf2pXcY^YpS*;sj2Wqaj7!;t@2&2szNmVt`kLxDRlmLZebpbU{&e*htG{0T{puf8 zKUMvFbzRMhnpHK8HLW%0*Q9FFHLt08SIs}xe5K}VH9x5NRZVT}lG@d^8*8`K?y2pp z?X5k(Hdgz?+F#YS*7esNt-Ge~_PTG>eXs8Mx`uh5oOj2(t@9tBzjwha7vvUPv*2qB z?pbi+!gUL;Uii+1f4lIkMgO$ukBgSopIX1IzPJA7`VZBAz5e0)MGYr3Y--rwaIoQu zhPO3*yx~&~m5Vnn-nMwp;%_hh>EgdE-n3-LlEX_ryX1i-Pb~Sv60>yS($!0^So*=G zA3Ej_$5bBscgKG0*v}rj^|<#Qcgt~mk3W3;YmUGD_(jY1F6&?Rtz}Ov`_-~zmakgA zX?f@J*DZhZ@|%}`XZhpHpI^Rm#aSyZU2)lp&#bs}#lNk%XT=jMHm>Ym8C!Y#%Ac+L z!^+kZk|*47!c8Z9=Y-#!aKfs>s(V-ca@9$zJ6B(@`h%3=-EXYJ71)Y{9}er)X>Yady=cwN)F z&UKfqyJubX`bFzcT)%exCF^_67&>G8jO-boIb-dHcW?N>hFdp$VZ*$QlN;~Y_^pjU z-uTSMicNbqb#1zG)4y-JZ__U}H8)<=_)m>bHU75o*fZCj*>UDIXFh-CahrQKAKm=X z&3A9UfAdqD|FpSk%cd>cw(Q+<^OjF-`Sz9{Zn7EsPTK2qk&&_*2xaX65zP;y%dw#m7WpDT1{NB6w{&DY;mYJ5A??H_2rwf*Pq9UT{R#5*QC4tM;0$Co?4)$wr0_Ri0Ce!la(u0vf{b$y`g zS6y4X4|FHHEA}0~Z|lDO`(C~8ihbYP_vpS%blJHVo%?s^-gEAT{X_dN+J9*O%>LKy z|LFev_CL1&xBD0OoY1qo=f<8}d%oWDgPtdQ7WXdi-PN1!y`uO1y|?$Ce&E~#0|$;A z_}c?tJ@CYV`F)%F_V#u5P4wN|cT3;>eZTDcOW%h6f&OHFrvIk?5B2}L|GM+OaNYyw zojmZFfo~1`=fIB!9v^sm;DW)i!M6>*d+^r5FAsiuuy$zi(2AjTLtBQ<8hYhWVrX*c z>Y=-aemQjVaQE;#hAXdzqA29(Ybu86z~9abjj2?pe7`f?x>CJitgHT!Fb|k}Pg*+~1bv zbiJeD4Col1A;9~F-}Dwbs>P?DhlZbR40)|{<#swgZRSD8Z$5PV=9^n#ZiD$3n9sr7 z4s!?0oiLv_3(OZ_zKGL@FTwmP%$IR)@fDmyd==*3%o6i8n16@43+C%MnfM0GH(~CE z`4-H#VZH-%56pMXG3I+P|ADiMdttthQ;+||*~Nds+z0dDF!$s1;|DMg;56fhFc0Dc zzH1k`SXJGyp=65(t`8~}4VO51Dp!p-rpJ1Mac@E~!Fn@u09_K8^I|I6h zXPAh$5&DN4O{KR9I*6OVac%~`x!Kgh)WOX2$Xk+YBtJ+_Z;Pq-nxL1s$1H|f0<#q6 z7?@*Wj)OTKW*N+K?`-HQwwje*C-fCN%_^AHFek#SfjP;GLU*ypoC0&I*Nan+132?I zVAjH{gIVwO;q;>qXCHky`53^t#{f<}2F;l;n_;%VZ1pZMO)%TM3&B-h2pz{)njJ7Z zy;qrCI5{~BW;e_pn7uG9FlWP@|rkQCUEkSGBKEo zVdCB;W&~ywCIOSg$;}wdILri03g!|oZ4P-ElZKh}4x0>4YqBs?Fqe8qO%5gxQ^2{) zG|XX`BQQr{W^n#;8O-G#G-|z@%$44|%~de3@jh*?_C9M~i!+ng!Mq;k4KUZhTnqC? z@AKwOI4il%`wBSSub4OE#N-B?m%Ih$t=_+zx52y}Cnq<;yumiZf)zs0%A`(ZxdeFvvW-!UJ=3Co9I{@(j8&XWEECrRJOInsaP z6zRWkhIBtpkRHJK(GPKY^dQcT9>mGfgE%+(5l)SMj5DK$aANcj&WnD6)1sf?tmr4; zvwwnfq9<`m^c2pBp27*yQ#c=b3a3Lq$Jx*?a5D5uaM-`ZsnF9n6M7mu%U|I<=-1d= z{?>dAy0rfeUD~@~z79RwZ$OXsn=p4nm-buGrTsQ^X}<$q+Iyf!`(5bKe$T6b9%&^s zL93w&S_6I3dT4_#hBoM8XoD_>Ht1q#gDwVFy4d?6%!4pLg84DbLoh#qdDvS5J<}!N zTbDrB^ce3kn8&?ip>KLD^i7ZTeg^X-%u_Hw_m1;^0rN|kr=gGgE0|wHC-*nd#r-Yx zaG!x5?*Bpu_jl02{XKMW{}1L5(8K*B^l<+K^DH>$=V1N}UEIIGJP%_ct)2%HiL`kY zFqM&Z=&81Q)sYUb2BtRB<<-H=i*$SQVHQO8c?)3{Mb7o=Bm2FENRPKTasYa){oYcT zV_=SroaY?}bA04{=(Qg7mcy)wT;#2UIU(|DZxzhyNX$DCW=-T0@1#i1J2`TncS_{H zy;CFid#6Qy;GG_Mz*`%6*joqP+4azwJp;P38zR5=Nb@UAZ!PU=*aTFY%NNmKYyGwG zbG@rI|NLIU2TXni?TgK+w-Engv$_j9j%GsO%Ww|N@MX|ZSG^#tV&3yn`X4cC&R{tnvGB(b&od36@mj7YYGw#xz-fv$c0v z_%0hSDR!m#n6*D4c7yq$4gV2rj`tr${`tKew?B&P>f*TkQRGiTj~=q&583dCZ1_Ww z-HXXHKVluz0@*9*SueR;YcvwO3Rex{B~qt$&@hH(GnMwYOP&r?vN3`y6ZkeTDY# z-&bh=e#H7eV*URpc$H>jrRLuTo9*3JsqNiXsqNiXsqNWTsqNlw@jI;@wf5esf02GU z8#d|o*;QA`deUnBt?;uSTdOV=`qOUlJ8gKU#qYNMZj0Y-@uO9{x|nXXO3Sg|`uAIZ zul4s@f1maDRqbBPeETf?JPSY1!q2ntL2D1&@L?N1Y~cqj{Gf#&wD56TkCZKU%GTqM z^&hhJIArTFY4Ni*JZteUwf;*j{-qW_Z}ZLD{HCpc+WL=J{}Jn-vHlrb&KV28+`=!n z@XIayN(;Zz!mqUOx7l*uXv=k@E$2I}|DCp+@3iH-$>QH^!*90u@3sE-TKxA~{JSjv zH*EMfEdJfrf49ZI+v0!Q;@@M#@3Hvbv;OZ{{O?)(-&p)-Z1^)4|995^JB$B2i~oNX z|Bp8Oj~4$~>wnhbKP&jS{a5`ptj7j+Z!O2yvDIA18q9JFUvA;c1%8aVvRdcaYpQi# zzNVV<@)+}43xBb*nrFZ4dX;I&@g z!kfI$FEn1`BI9ja^i|k*dzUV{&wF6e{oXUM>+0|Kmel{nJhSL8W=Z{V6`Skt^E&Du z^>X!(dpFi!2=l1-kM+Ovo~!?=*W7Rw?5km32Xip;T>TqhzX|4Am>XcOhj}I7$;fSs zuZ*l)auDW?ktpnmC2xWIEwHbQ%q+PPIJdxmOXTS#JK=sjGO+ZgFi*n$)5xVupO4&F z|9qsm;kb&{W6rGj&M{3D%?;O8Y&`DyNEb{G%)I0OrQ(F+2fVIjQ(n)qocD)i?Un16 zL}4C}+_L;g<;NFX;N7?U0`KYNmv~RqJ?d>-@u=6i;+>T@*53rvRB>bdtI-x8g8SCW zJu8o^c>BtGE1y}E@$Ng}q^g~(PN`~cI1T0%sPm?(53D+_;x?G(hSsW;tB0!!tFNe@ zS$$#E+g5iVzkQX@)o-eLX7z>0=SbBBCmyMKam_50R6S@rO`$0JX# zJEbOya=mr^j+$Srf3oT?>vzL`JhJ+X_L}xHuBv|Ej3=vFHas2~*^sSy^M-?w|JZOv z^@$sAt=_TmjWu66{kodi#wSs?9Z2Kx$k@i+u-{ho?v0On_ig-mO>@Je-V+-y^!^P0 z!cAYUd7|#t>ak7jXsf$x{&CYymCX%nYoDmQ-+SuJ{k2;*@2`!+ybk7{H-E9_x%wjr zUsrqImQ7Xjw%%3S+;B$ibM@bVeK*X10)C+OLtFa+dqr)1)2m=t)YUhQ)!w&!0`6Sx zcbW>Yud3a!?P}Q9*S=}n4Y1!``-5%of&KTj$2NZi_P)BFWp~y+SN{dr127k3?0yM$ zQ^oL(riw#5?yGzKjz8BuQCBzbz|Ldmy=muPOwY0pRYrFWSKYqr_45j=-w1R2yft-C z&U^3fwe#=V(=>nS-re(WTh%xJb$f5EzHQZo^FOmU27he+qL$?Rrj{!sx2UVqNQ z1wG5^VU{k~-MVx^YwHDGebdqf_qHuva6inC+iqHLWBn_lVGL=R=Er)AM*_ zUhik%|H7hI^xm`R4ZV+gU+w+yLiXp*14|dITe82lr*E|W&3*f8zYOy~Ft6XSzji_Y z{@P}kqy2Z)pLpI~^@A|)fjMX3uKL#w+*-Y+?ymY%2Y1(;I@r|k+QD57pBU_IcyjQ( zhUW$^ZRi`i(7SEbO_jH;y0PJZhW6Js4)3o$Z}>|MEYGdOHRei4XnzZ{-kT45KI{cB zi(u*@4QwzMR_uUQ?L}sN<(mP!0p_hRZ-coJ(#e~_t>0`~B7Xz>Z=ezTLD(OJ{ZZH- z1^gD+x4{2N*q?;`8KiR??w$V&q*kAUWa@TEo9=*w=}t(IJ`YLJ7a$$_A|yaxg=Ft8 z#JLM`z76}^(98V}?C+Sy$ai6X7y7vO!oC;q@5BB+;Qs~tzW~1<_Wfo%^nM|wfc+rs z2Vwsh_K(e3k%wVF4Er(IkHP*a?4QDZ3ieY-@8__8j`V&B`qxG>uze}Mf5*nfikC)m%yeh%q95Bqu85lBlS-dT|5I|=GBn}uonQn2=*et7ke3)Tx1FCCEno(_N_3NMUIDkJmAY=l5o@?HmX zZDcj<)!y}yHL%xsZ;hM+`xL-Wg?%dEr^7xS@b%t1A{$}O^ww8yg=vP_4s#adv%6vT z;0E|!m=^Cw?`)WJy!U&pFm1Ty-44^?-Qsn^ba|igx?%R=E_M{=T-?Ln57Q6*^z&fw zx2HXw%_Lh+HG5ii#bR5VV_P0~_D zn=DLZ6Oy3>G4<`@8c!BtL%Db+Kb1-+EnYM;mL;xY5LZx#ax>k@LXec7(v~eOXB5SZ zj~?nt?3Oc{rKNC#3_l7OtX-BaPx?ccn7%bn3FQHkma$KesJSEKO@7fyPfx zj-+Q=3x!;2WV(>dOC=?k8Ht0X>dz&|&^XdTt~?Tmw#NU0D}cn;=q3H`r`f!XnU_{| zuHIE_mPMlV=Te!%Kr%m_F67Z+gN3XNH5-2&r;GSQJ7lfp>QU?HB9ku1K_ zZkQFgrCE9m8DmzUz`+<$|7??lUChpO73|OE3uVnFp(WF$KbM_K<_alof5GU;4x(K# zo$?1$nMC$Tdp1)@W(ra#K`5<-0-61GKmS>JEfk8z$`oW$ve&muG#OruA9RQW7B_*Q z_GI(P{2+&GsxZ@^%yq%auWUR z!%ExNPZX6NNM1Uf%on6lVs7A(0oXDJ0{}Qi!swx{G(J7a_@O*90nxJDz!ozE49*s;sG7!HE2_oA zk_neTPo39I)Gb>X%mi7ga0{z{HUexTY$ty4G!#(jkOxVzF~s2)#rj-He5{@2=?aL2 z+{LisiN~~eqCI9G$pn*lCFUNuK=~__5@T)l6%kQq8OBnS@GvG|mca+`;Pl8S`L!h0 zpTfj}td8S8Q2Sgek!(pwy@sa}n420DV$f`8%QTU6fFZ@w*58Un;o{lgpvDfq3y!Yo z%qXnxWC1iEOlcwspO6jMQE_m((<5{$3^_2J7Fxi-&dl^A$cshoymUHVupX?fW2y0J z&{1LhNGDp4#8WOf>d!fg58k4Kk>h#h>eFAikbZ$->i>N~Xa>j|pZP z6NwYl`CZJm_&CD-l1Q}tM1MA$hLcn&iRaRU8Hg5=lYk9Q#WS!vk51)4X^|p49m#YO zKX0BSA&6&2lhMhkw9tQ9Wq~j>0rHJZQs}{KZcemTDI)Nr?O9h~_{ihObE&-KB}v&O zgUlhq+h%&>Q?NmeY+0q?Ie?^(*u*+)+NMDalYW3-?b6U7rF~f&!KTbrM-mWf%`O7{ zEntnn&W7nq@%oOa2i0K7B_PT%kOuP-f}t~#;7dl6vxNCW#*L~M!qJL=aLJ~U+GZp*aY>$988$;}E*GC+ zggDmObTW-0nWKLYJ)ce%vYDAdMGPgLEo4TH5L&;qp~im8?6?QK^qR3Y%LU{ zz*&^HK`FGBbLW!dvi2$(<|TG47VU*eI*JhFIUDKawkMe$BaH=aDN!YteDQ2>u$+Jk zEyY`eGyXUyFqNMe64JJn1S}y_C!RitHQs?kWOi#AnJr3hI5WzvmTlT zF+#>Z5oH-HW!!2k$&s>Px#1+)YW62*Bx-*=MK4Ev0d~>U5UgZECWfM5nh~iZB~&bq zp)RRf5?uri&48U(0&QZeKLgmDb_SpVAllg+#&2tG9P3G@u$7a7D8ZzF53}2@H^7$l zu*nQHne&_cAr4nC_%X!cpKXZS2UOKcXV!0UjrEu<>2v=rA)?3}LaE5G|Gh`b=WI8kzO^2p1O94*_O94nRH_+t9 zE0f`%1V)%%E0_b3MelM{cH5Oi%oH%32)DzliDRkBCzIQ^fqNQ_MYoxuZT`4{YqoI$ zFj%v!6QZI00ZGl-S?G9W3}RC%d(cjl){8+hG!V~VGp(s$P(T`HQ?&L-G9ev~O!CvC zqu@JizzcVIhRvKcU%*$kMQzA-GcFO2W-o`?bPYC)2}#Ov-JtUb5=Z7Oxe*Xz%K3-03_!F5TPU!+`MZU@+5dWb=U)^WtU$L zxMnK{z?8XjDux8_fQI|LBz=T}Rh~{KTYv@$1or?%ynja$osrJBn2wJAu2^eN|Grjr z4s^GvqpNRtpuL@}0OXI;$?kTO&W<0<=F*9NEE({|M!BiU4`neXIQ1b00dy#N6iilf zYlrD?ZQtM8-5HB^AZ9!PYYH-Vh=Aal&ZHn6f+OKeU*PHQ9O&vB=xsgF-WltP4(^K` z?Cfdp>+OsU4k4|ff!6j;EnClUZ}b4s=8n?aIXH;iP3z!bXK!22g|P#zz3?4q-`6*Q z^ai2_x?|_J_6&DoXOPPt*_TX1_S$9II){cj2Vy;a-DOE*(Z?KXY>k@D*z8Totjx9c zM@{!|)STaXAllQ@8tc5Ezx6;zXGg4S0Qnv48`y79)M)Q;Z>+twrzh6lH+*0yhFY>t z14AsuQ0w4+RBI4%hdK`oM*9w!^E(H`ZfhNkw#Pa;2ipgt{X-1d)6!=KQj^jTjnO_c zID^qONmu_sXMgKZ`@YTr(>vTV6z%V6y%6qjoAx1%-smd;h>~iLU8mlMEl|h#ia$?IXzSYaQtAJ8+@lFz6h((12OH z(8v-Li>*E<(zQ6bNN#>1Y$Vs&jg87gYE+<@R95(Cy>ZN@BX*#=co1WY zvyJ^Ku05MuVllhlyXIq4%*ga@E>^miu5J~dc3}AX921!Vvt+Owq^xF5@LpdA0Ho%az*_HvpNhNa_Y=>+td%Pgc<=o63E#|z;IpmE^20_Hs zu0qCP{4!vyAd=V+S}dt28>XG3VI+IBhav+MYl@?@H(Qt(!RWNbY)fSe$x-Z}z)}lP z>C*uWzXX{4R6#aK!#t`T2OA`<51}1IQZmLnl904Q_^1OUnHeuk?91j-myvBs4?;%e z!Uj~RU{_s4VCBnzF)Mor&_tLyoXKOr_JO@3I{?N+vWw;ee>_cb^vqzELVZ(oQf^wO zcvgaieliZR6^iXfn`KPgcyr!8+dx8vO;O$l@}i?QnoOw<5TIOk2B4g%6zD_?pgLSj zP}!jYyP24c*oMTEjnO>Mny|)aaCr1ru_f{17&3z(c1R_-0O3oV^RsYhqjy6Tj`CB| zBTfcG?g@?pxnbX*6sZOnGM`$t9E!!-K=5QH5DjMla4Uf+4^#qB4lkbi<>5Mm%i(sq zmIn+L&)sr_(y3aG(V0n<12Hqpf#E~V@=%#Y5Wtp)#1bV!?O9L?Y)6)J=a^P8eHVCE<0LhQn6HJD_QhVnf8MQQfnQkgzE|MEm+K$W$? z?6`eXh18@yd2hpM#_U+({vuX+p2!Xog0eOn5UJ}NfqrG@2zL`hDN1?b0U;<4I~dPV zBcMEh3L{6z=amOxsM-SsawO&9Jy@2?p>n2NR`F<_Et|!FD31XSryMw(n*|7JIGRgI zLgg{Sogx}JWrYZ1bmy|uQ{{=YC(~(J;N)x}-!?Ot&KBmvwFf8VsdQ5AR1PjZ|Ie4B zC_S1bKo-m-_M0LADUax{;B#VxQ{Z%(B@^_7M0W@1Wi3bq%TCXUBcpeg#2tw)FT34I z=(@Qd^i~v8@;6BYOlXcR><{ z)p!U4C}TBs%$_a9oGXP5!*aE6>WIbzE*CWmjpUFwHlZc0jkNU8!8&xl@^`Y3D}mxbrWfGuo)b z;H9yjzTibEkVABG65_!E4kraDTP+@ec3||30u_p2JX9s97tcmuaU4n7c_<9n z7q=+i1r5M7!KuBv^F`{x z3bT~1^XTXVg;6FaUo5_dx}h+cLsNomNs(|cJq>5cDxnLc8IpC1~|rS36Pwk z1GWoM5k`b!!@Qz$fFY`SM5{-gpo>smq8QH}$(a2^xm41COQ5beiL&?^f8R7XjCATy z(qIB<=}a3$7a8kz5&&s^3NDpYv7RdIU~0)6m@{-6gHa%#APi^FajCJC3&21sKr4estmjtGu}H;H zB-R7jg6KP1^#|+^G$9#d;$+L8pUS70*W`4*(3W(*j_fpnmhy<^V|TlfB$>w{Cr3tV z)B@ODMF3_YEWAaq)Hw_yicYa&oFW*JxC|DfgdjwiLuSK)?Tc|Z{2iW648fWg!vYu+ z4?!k`AX`;Cb3P_2R1L|{a@|ViW$_U&RsqU&phcU7UY82_B|y&{A;cYHtZ4pxj7i*+ z0KJ6zpNWbNl^G_x)S1CFPGZM^<4B}}vtM13)EP!;hfLC9guy%z8%R=%5)gbbPZ9~#^}X8@oA%rL`wX6oC(ghZ%q8$=n2#AW9z;cvtA(G$-*$HKzhbOWZNrYy4Ly; zRkZ73iFUOg=1(+%fWRSL+lR$L{v34aIJ;AI)x#-bxfgFREyde6#huAC$)NaUvsB=S zWMf>A%w)x5c}{b10@~e<_a`+pBS{Ef0OD~CMmkfoQUOUpuBH`c$bJpVT0af6*fcM! zl|rSpd17Pr3sjO227qvKOHI}cXkc~2fvwP@o028C^C(bAX!9;5<-M?f#!=C5Uh5X# z>T?j9N!gBzQ^~w)7y|sNZWy%%%Af2NnUSaNh7$+A- z33%ip5LbW@NE5;+(EC~05Hk2*T;t0Z{OB@gSjo;b1e8gz2cr=G0hck3RY?Jyqr5KAlg&;k1l7W|gwzz(ojDXm z`vko~xn3y{xM({d;$lGKq^9Ss&UH43FG zzxYlKLpaJ1R76REzY}*Ss1XrB++N@E98Nr0j^H{V6xW>xKx2%Xx*$$?3q)B(5Q2?v zdO9J@Ap_cRfu3T60Ks<+^O~gZL(NB~$H&Qg2Qar$2_T)tJsku%K4mCX2&W)>WNBr) z>IX4!-2($FOF4kg69l0h*(B66!6@=7MRY6(fT&~?f^A-G3V>Y@vWO5f0F82|D*BKj z!V3aKTN_ikf6IAR#Aa>=YSl|aC0d{9e*iB+{UYw5W3kYf^De|8! z5A=xaJtdc*2t|V`07u6&SdU5Ske8fLQbocsJCO0(69s9D0B(ti_FVuzfJ_hLDqJCn zMJ52t#%_w+D$rK&QJHbw&V^8Ps}mergo1?4nl%JcwjdKs3Jy^SwXrV?!Cjk`fMr`} z*&psHLNRQm^B_ToQ^_NidW8_s#MHAcf>5d{O-||gQE`RBu{h<6+cFd=Lmi6&FI%T) zhH_XuaPv?GLm10$fUtxlaN#(pxbl60-;Kof?9>dF`-u?h;Gxu%EcuYGrb2LVr>RL? z5$9?d&x8Wz?m2rYC&<>dv#Myv`Q|_nsB-PV@2D$V5_{qxK!=B(SO!q8l#?Ql3t&++ zC@zj?LXT<~uxvQnn8Nx6QXD+C!4n}2;I!U9%!C5$X-tr+-E{~XTS$!v_Zft?&0v8< zUrtYzp1F%G210BnG*-Xe+^8pZDQ~J$tl5ECMW>ZYn;{wE!aS)fkM&wzDtuLU@v(`B zBP|lhx!akCz$L^m6oL`wG%2B$7PmgU)?642vd`S;(A6QVEskYU}ud)-cdX z4#O~e%s@tx{C_Aj-JfH0<-*WzKBW@}+2nGodHEb!2^_14J*eb7FN#rg6elN0;b5HZ zjAcis!yqtzp&Yen!4@MFBy>d|Xd#0b3PBQmIpvVG+^YNI>VQjBePikoi3tdy^^%bc zDH~xQM;=8{8732zs=x}Ganh`>KlueIE zfh^HE3~}6r6{H$)IBEkB>2#iZl%x&)~jkaT+ad+WASwk zQV=UT(&su14pLK-(~}ZNtT9RkiAg%*d+-8P9w=!6auVaB zwmEglemhuzrQRVgiWBk;PEQHnf-^ZBup%9(y3n#RYjU1o9{R?N6B(!voy1+adRPbE zhz1r^OYQpz@W_dV+`@)O2pD;G#4*%(raM==t%fo>q zc`5Oe`%zTR_i1M7<*A~;B7$l(3FZx!9Or{{xik5p_&B{X6zGt1X*zn*`0;cb&ZzKq z$`}L=MP3}cii8C0A43PHuzKS=2i;tUuWodmuFFb3Bejpr9|SB?qNkBr{F@-k7$WG zLPRBRjxfySU=ton)QVz!O*tt;8wDrKvOPYHr#*Zxju$X3tO^Vgv6?4>X%}LgY8_UY zC85%-`W4}ym6L%av+Ia~QSOUz9Lyi_0YC)87%~nb36sxp0FH)`7fnq88qe1C(3E+( zmD}>~E~;)HhH#oE3l%Z7=RwLH4&~;QAY?PP3NC=Fb_s~9X9>tp4>cdPHJ2*j)EpW^ z%(C?OT6V<@AU$zRy$A#oJSaiy`xs?WtxS)xp+c5+ib*atcQ1kGf~xUG6*HV+^6sV8 zD0H}(Ghq-H6)LaCI4!Xmb&n|76`>zD9H1!?$4=mY4k2PNEg{2tXFw}HH)^{0!7e9< zwajg!-FdnKS|wgtEu32p2?9@c8v6*l6te?Bs(ox>s*WWWSX!lG|$RQXQ zUPH;rDdFPL^sERY#z5!GfSk4rlpQ;kcBLt4I0S<8U1 z3aE^&%%|d@HY`q9+yvQlR-0*M2M*RTgg9 zB{GW^WPn-B-COu0`S-!nXa~(FtB*y&z5Xae>(); zFtTn;K#XIiYyli{q1hM;AyMQ@ULlaxxw29Hc_{1z%bxE}XK`)GhNDH%YS|XbP6nS|BEt~U8ECLdH5#2}BZ6Zs~MV*HsB8aq+kWzpU4v zuv5t>4Jt}X@+vJD@^<9Q*OK#Sq_fYw{I|8q%#BW<-e{K#2a zuw#(PJ6n*eXjYG?ER-`@%E%=QZVPs!%6gG7qHA4H%xOp<4=2+zE)D8pb5dEAy&#g? zbXy402)09gAQf@F8=$LC6gn|vxh+idq{P!|4{J}$f~j4p0AEB6mQXJDhTmQ#?;K>Q z5u#un+3TbhA3WvokcQRd7TycfR4)U`I*QkCl_fwY;}@>LA&4@n#UzX=<7fn~ z622^0t7Bd1X@qk}ArUG5La`?d--Z&$m?)))a~zWn&%-7Y2U7`)e?(k)1gMKBmM=p^jDS&Sn`UX)yqFb3L4S5>7- z!`!5k1;p^fA-Up=I!7B0S5a$3!@RMs&P*O!0E5$GV-Vla&xxzzUdH2Eu4P%9bGU_u$w9``=s=k9RSq z0LuWKR0$44RcZnrr})Ju)5lZgJeVA&MM8uF7jch=+?eW=)pM3gm?Y+8f!);lR?4Mh zIYfU#q|UHy^mTxow3BrQ36_w-;%n*Pt_4%~wX?sSX~kCdbsN~_#I3p4{1i0N3$EznDgSW zU9)mZrgpc62h%bbcyYZ~lvL%hBajbCxv0h|b4naB^q@!s36-fT-~o9Od(_S-!&T3@ z?X?)xBA%v@F$)D%NO#g8V7dGRYUC8kYm}OsqTlt zq6rzK#j*O4;**Q^$aoCeYU&0_m{b?^17aym7hJ`6n@4}e00{uJ6-$t+FGyk-f*BMF zl6ut7+N42f)O@xF%IT=8Jj(%DRl2b~r3LC6Q{l}Z$Sy<;&@L9*uWx=dwi7oLO)_dt zYg7<7uyF8LN~a!QX$Dl4a#bS8YDR}$lIZ02fcz50WZ1%sb5J0{rt^#^#Gr@Xv{1H& zBo1B2cRYl4Q$Qy$nTf*9pk2`s)XxCbbK(LWnaDSL#0Qluag7Ni4e~{LC~Eo;Xcl;V z6zZtf!HL3Psk%6jt<~x8$O<4f!f>g%l`tubCYnoJE2S-Y3Wo<>u=61=Zcn-)a&;~% z*}@6Cs%y7Fz#|cHi&iqHjUAg_C!m}{B&s1Vc|)u;$@nCA2j3|bfVA9(&`WWz4stq# z+)+{^;9$)OX>&;k#{Xm~kI<;4E(4>SHiY?ce-ZLhYRm?nMD1JU`WV?X($Vw0(ZM(TG;btDbVg%_5nk4Y+gD8Q& zo3fF#+|Or<*=elqRvS-JvN9@vcfg&9gN167mgmFqp0FxYV8|D}8sfmXALhR#>|mIN z!M?)*V~OG`a6TMN1cdI~`5*%ar3e$(Eul2#Aj~MVOLSmG6zX8NBrYTdQi6GaV^Xj> zB6)TpXd$vs&g(?4VVKAe9fEK>U`dn z*8r4Kx;vnRfu;&|53$|AzzxAbV)N802!X(5hJ4rp1saNFDvL%hC9>V!M#*4*I1MR2 zg@hte<^>Vo9chN)V25`$h;NhQQ44--ra?O-T=!C$7oq?T1(w`!s)?ZT~ z-E&*eux@gQ9?CIvQP>bh{R@j#i_qPQQ3OQ3MU=lNu!uAlg#^*ql0`uwrmzUk4j>l= zS^H=wPJ2KW?H0NSO-?+qPv=kpH;^j|0!Ibfpxdfgoj_?Heu~D&~S$$0@IKmjTZ9Z zPEW`;8bpisP`BRxUI5mF1P_dTKQxMbx5$k_Lvg4erDTOl?plXJF-qBOId@q*6y&aU zg+QRoP>~7&iV|nPgu;TP%L7Uvg|e7Dr4Oa$Q-=@?tF5T!gg`6~1g=`~7@p=p1NwfR zu!CQN&=D;7Xu*er(SqQ@#gbc+I7OZ2fbrKRvc>et;RZx?Jc;YTqjpdn4TtniVINOK z6}Tj^iWP-}J>n@h?iPcCC*21B}7m|Gq11T&F`_F~g&Wbmb2=VoNp#rW3Jl6{D z7Ko4KP{kHJtT`J6Lz>b9sP%YsLC{?~O{HPcCUS^By zw(4RN@vPR~Nz|R208)hddSTKYVF|Bt3e@V7Agmf?zb{r6XHFz1HV^$cnQj-l-SI(? zQ9O8-!3SbfR`g3zyv|SzU_C)dihoebOqD^1XZfm<8v5tG<-uT%@Y8Ej8Wfluu(-K7 z5(gpLG2#VlskZ3fiqj{e;uGd7P_nbk60QVDsN)$mVo1Hty>);tfd~~$EMD5fqL79B z?UF?fR=8@)`zZ8qq~b|D{|ghStvz;sVe zfl&k(iJy{!$Xnu)qnL>m;IlVI=nObERe<0W;OG~z4`PcSaw`0GQZ9a&0~O&&3Hl1y zNpT_xMEDA2AjB_J1g#%S#w9?IYXX=#}2WgC$h1rK-i=VO4VA5 zc3{cDZ)ZSti38lqMVyzyYV0AQNMiKp-FonBt#0Xsy2-f1#3vfqGg=sxw4x=dtYF{| zgXkc@vOWH!@IyEtWuFZ!5lizqK`opE>+gGD^M#@0U@BO1f^rAl(^81VM)U_%a3h!p zk$R*g;wlldg#EeX6epDhg@&J%&QiXEKB1zA#X+mEI=E&^i)AMxdzg2KAxU5xiH4qX zE*Lb?3?6PF{}TjZ9|r+M;15ZE?T^_YF)IS*Z7wOI^Hb96+!V$-H`Z0BYUV&CTQ3Km zDBh7daA=-85asc9>O0V+ykVOw+xrdabv3`ZXdCbc`1<{gI^j74K+vRqlTo06g$bfJ z3F3l`dxidyAKEu?OW-~^Q^C;ViJ-_3Vpf>xV3O}JHr-uwjuPP-dD**BhHeq%Z7@a~6+YuvlqzEd;I39ULara; z(f0JcLLb~##E0@_q1kpWJ_3fAF!q?<*bHGh$pR%Al>LB`l|J5qumCEZM0QJmSt`&09Lp}7HSW}67Cewf+bPX zStN=XEUT)0V?J-eQGZJ%r^K?C5}km3xaWjB&J%?wq|Q*d#nF#0qu7p#da&umcu3)} z2EVi8v{3x5gr8mLEXGQjh%I{CoTg6vrKAh>&f#!5CBqZqqa=<5@v>l+4ar8KODHBz z3h0)$#seE@5vt2b52_UoVQULE81YuopA^D_y)^hIUI>5?k~g^JEl}{^v%|+Rg%l(2 zy9jeTU>kedUawa#Qnm~k#-$HZN0LvEvdGaMbQ>=V!Yf@AcvE=9(*-dz5^ziwePvB3 zW-$ajWf*}`q$P^ulz>oUlrBI43z3@QFPEY`p&xj2tZjIAYk9>+hXzUH9{-0Pgw;dJ zd_cYhKP3?QRjC)7mfcWOD)ur{05QYQUr3EHO8!43;_CA<6S1o^etMy?L`?ZI6V$4e z$mL#UT4f^JM#mK4m&*To--Y_W1X;$n3=DlJ$oh1$WP?q%e!xuRj_Lv{2-}ZIl)Tm-p9Vo1eh*|KtVaUdKT^62n^%kIGryK zaLI>ikCJC3fZt2Q?OoYPU^I)nHBbE@9EAXY%%dH7%v}33F?ooYxTTg-PNt}v)0TqQ zHgQKPjtjy#TN%yER>z*qTQY(tCxT$`H+IpW@5lHZI6oginN{W|I)H9dD86d0T4%gwkkYR4+Y2+8^u9LUY?B9)JiYcqLcYUG0Lim zqLsbJl{e7TlZP2GfcctYp2{_HIG5M0k#(aQqUewqfVGU@bRS zE(ej$aKB^WfcAuxQySCEL9wJdr>IJDXUct$1EREHZ|P42E2Bl}or@rAgR+MXz)h<{-8SyqXjR9vU$%+;8Bg%tJ$Xim$t5J61UkTJ6iqYyxzd zt#Wv%$-@Imx_N^XG%g`JiAp%V!cWq2WSV)iWX>%T>(Elm|Z&yJrZ;r5p88gqB9xZi9%)Bh>mTg1_p$q=Z;e zo0tsv!@LNdFVp*ko;a$B%gZy^-*t&k+SpBxG0UywMB7tl3_v6RI(^`gEef}JdOiZe ziyd9m-|I^V@4ty7K|Blyev`Se@B{Tq1M~L!ED1S8C>XDDqs6#4G}On(i*Z&H9pfP| z)K~E)6{ZO$hz+LHj)|++J(7W|EGke@uo67S=Yp6E!3+F&{tZ`9$h}fTr_uv1xp_Xg zYkz1-Rw$5C-3t0h_RWj)`0#~`Xxr7spic{60Z6mRucfhp&`>4sf>R&89FvEfs>qAg zDF>Bpzp!Yx;u}8(I%hVq0{4`8!*dX2dvtYjE2d0x-@|c@S>*d6t01)xgC;}4C9AKn z(SBUH&M3F=?c!|C!@&|?TXKH33QCD7*|IIh&96WBL(I4A(PYf#5#F1^@l8HERwxF` z<4>X_)&(_|Lp``$jsXf)Km6j95LvCXeFKd$RiTjTzzzW3kP7brw5{{PC}9*^Efs}X zhKWZ$f>ZW`u%`4kP92x3LJo9ABgJbS-t(6U?eZg@6G?E&>)QI)y@5bxH=X z@cZMMv#>Ex5A@+q#j6oy(!PGBji?HO{uzuZ)!`&%z-F3RoD0S396P76HIvD5i%lpf zx4T~EEBkR8&gw+m%pculQaGLDC3YcG;prN!9*xWc_IOjTk ziJ(O8B1~Kv%-=s1#Rugq@?&g)^C|+R!ujcaLp(YK6*`{(@=yw74ss2QM6m3E=4+tn zm^Vk9&SM@w5B_^gPrIa%Qh!9;%~}plJ<@c>25=IjQg~Y~&Uo@OOcF z3XhuFlTZ>WcS4spdV`hPOnl;4kG(|TGvVAJZDmXpzC$ecTcrT~?wCZ&BQzs5S^|ai zs63$C*GLRu0~qp61&Jr@%YJwsP9HPT9j+AgK-PIe6RPBFqnyD1RRrZMv|iv+{j*~b z6J0z4*4Jl;^|>b}jIeKS@~sM!pB@pWos675j;dZ^EYvy0y`?%ih}4BC6dblFh#iks zf_PBVE6}Mz_=9+0PJaS4NCX3{=<)8 zsQm(Pq{v;~yy<`-R0TMC=aKw|6dCD4>ookln9qw~jG#fV)X49=Ykl;-kN=Vjrayic z$O7f+2(Mh=HCu>Dz~<{CM7@O(T-BRx?ScRud_-!r5kHC`qFubJQM`Y{Uhd^ZWb;Vm zPH^r8bh%u$5|>=eTyMiTHRe%8|-g@;V%i|MInpD+{jObynM>g zdRd;3mo0%q#7VTRYN~((;?HtmBw~&Wr4Ekwp-4wysq6rH!L1`0Y7BGFzy@N3a42)b zEhvX<6|gZ*3Ybn}9l_2ri|rR6p$V=0fX-*D0^qi&ZP}w1o|Oyv22VUOHnV~VNhUs2 z7kP&RP>7)1tk5p*PAV#GDCt_!G?arV(?nXT`p*YG>DOH|UAmUjg?rAphf5c4?YVk8 zSWFS2X+?}#H@OU;cwUOER0!Hi_DAAz#GFA;>Gs`%XAye16DXYDG=h{NE@&ga zaJOZhSj!XOEak2_1&P*`RX{p9rbPnNjMbed;Qq~U>qVhaKM6rb&M6Or21*U0zrfpE zPB1_>cyM`16EBQmOX@ah0o&impZb;t!Rmu!2PXmtS6tBZq;fEB%i)?=4&oJV?DTCV zt`@q*6If6DGI>xA(--_RkW$tLk<#oy)~FuC<`97n->FcD$n8W+CLIT+f}s_&7yhBxUx&?I~`AL3z-3P?qxD zLXu_ClAxmZKS2J1r$2n+8XzNBc~9(_a%9$-IGnU*p=18y&y`J2$>-9juovfEY$`T0 zGE6OvFEOr@9KXb9rOFpCE4mxqFS*b!q;T=FN>Zk3@e)(9JrE-I!3O;$*NzhX7mO5M z5np0K{bks-y{hn#47|jYiU!@wi0=m8%ZOS!_+EArt`S-qUq%uyGzecta&zeXyo{7y zcrd<<1a(0Em5AX%8AjdHvNh&5$FmVMZa4IW5aEuvrjme;T3eE$ex)!;vadH|vbW&7 z?-D?iF{;z5H(|H`szazSr&^%CZk*K>{W`SftfeRkAroU~MoEY*bqUlLh1-n5r@PB0 z3K0>7-egcf)ZcciKiGz=Kc>5rR(q1DK-dwo6u!5{rt@)Q!lN-3 zI0~N6;l~Z+=YQOxv+sxChL=3e7?`m(2<0Jiz*9xaZ_0iW@4008!!0>(#-uCRGM;cI7L&aartqQ2$ z$FnzoAp4N#;7pWq4>BwKxh-)^>xxT{u(?c&Hzi+q#Rh}On4)$sE(|kyfk4L&hLid* zwro77%`09ubUSpzG!grZxp?G=U0m`&LD|MgSmM$j-fn4>S}Rkc&e9Fl9PwC?mlS~$ zKqCy{T`*iJz(X213`EC3AvTL2>cp=$;~tq~X?mee%zJTes&k(sIvsA#T0ll@ys%w> z8Ut}yFx5|>ufPww9giRI@h`n&#j`*CdC-twpC;nA2=ags90D{w-L)4ppeJ)6dxY#K zuJz)U(6~y5jGe~m89tblu|TlEG6!_zLoj|+%7>|fABQ)XC$w-A5LaNJT>(`-h!d%2 zL6<(9#d~G#Gq_`-K)GiY^2@ByAbI;toheBGXJ1HW&vxv@M4uSv>J;c;Hb9s-0uWwGEQUWXfm0#@e47`z?g zhuU5#4&<<=lxs$6qb|aD%MZPZqj0$gAYMIr3IR&Doq{#Om|T*jptNQ1^o`LeZ}ZZ@ zbrgHkJl%(2n5SDpI3MlvaL{_S>k4NE;J!ER;L;aPI7OxU^b8ZgV_b)2I(w@sz}e16lki_T%`y66nPy2B+mTdJMPQwQ9i+oY)Sg zbc9ktGv)Sr;*)_VD5!#vNIej>$pGm4@k^Ih^ai#08ME0AQ;Ce8qCUR6(hSYY5&+BV zB9MYQx6}+51$m=HcdEQFu65@k!29|Np&+IRVppPKAf7M^y)nXH+jW9v4ET^oZvxm0 z6(KkTCb)e;MHpUFfXvivu_s3uoS~~sP(?wQl)fD^U>ujG(Kzn6KP-Ti#>+??dVGcg z7cU&=X>p*~`|(J~dUwIADx&zTnti-=Zo*uzIS-YAJ*7RMO+%RK{^Sua*kCptASrMaR*F z@+PNq>ySY?8~|J)B(@j??kLBIj+!1V`@REby5D z@E`?M$-x6Rf)UNDjhU^E%MAb4_8a^PsTng_d?!uZWXvcat+3-JZD!165^hc^g*2L8 z#7USD;6XjtnBH=Xex#X2+9{-zLW<*tv62QS5)yX^;mi@53BaHWT)5zw<->p_04o4L z9LMZ_aryL1dP$@kGd;*{9J!d&29W+ZatDtqWg0byOox;buM+_42y!>^P6G}Q`Qpt6 z;Aa6_1J{J$5n~PT@WL)qNJ>Z!?WE-~O>rs^y+&zOt$p^S$m z4YqX-t~lyEBk@`DQRI>YWDU|H9`2wbp9EZaz_ib_WV7UP_3PQZr3GUqQ!q@P#dO)Y z;+GdBT#>no!m5^5auXn4vI{wALou#44u=bKpGN*`P?ds|LYq<>&y}jQ;;xk8=0vx? z=phlViw+7?=h8CAk4w2?hTX!+z>c3WruD@QxNb9Sq8L_;RY3eKx|9PXXUyVObXvl6 z+7P69{2+!<9Q7XpUI^a&;_@0s4mzm{EaCEQDfu8^wF`tWmabiJ z@XJleonyq*9RwtYFmvJnaHa%~y0eW|g*(^M?rv#?LEXT>e2n zZN#{Jk%Y5`JvC*_mY14`iN>Y20TGTN&tL@WT!@86kE`7pUDgPOp!>LwVc}{Ai>4(vRrC(hL}wW#2l5;&3VKz2__KB z5$8`?tX*@@ITt7Bvv8lAh2c?R>N-ID2a&!xDT-VVBVR69%+ZB7Qf}6kVvt0WY;*LH znx$mPWSSSCO}AifsjJz#wwTSvT>XtcWW=srg8|%bb{GyR7bS2v3%u3r6nnsQ1AD94 zCcZZK27I4}wVEbC7@whS2zP!BZ#LVFOH(1+kQ)E3K_xO$WtUPogj0%@BguBefh&_a zDLlD#NV3V<=_G!@IE{P)*FJIz9FCoKEd#3}t0?&bvy^1VEewihP9t%|uNFy4EFWWs z7cCiLSKq=S=!oYib6Il+kV>sVAs8Bl&z=Va5d9rU_nWR*<7p`X2XfaD+_6eTj?ZxA!oRQnE4_1v7Gs&%Pv&#*OoTz z8sI3SP}I1uRBF3M>c!R=!#}nUX)&oWXPhz`BQhjebJo~(8EdZ05NYpaQVL~^%-&Y% z7P3y70%wAz?iiHP)UMvozi!qC7@z!*{q>$BjEyZ-S|40THTFwIhHKvz6NlHjM zjl%)hrDf-AF@3>QceUbL#a410BH7|BQej$SGK+LhvS(NW5+crkMzYbb?<<`KZYYtp zV|7>*Wj#-Z9mlj)YKABL;9}A)2AhMdsS*MXdNvFvo!bYDSPbr!7>nuA)=IO=WMP>m zV>u{?A8|$`=V@SRV{1&UoL1GbZjI(-;B&t+X$~UI9EJv)gXwC09MQ{T%989Pff?)y zj)dZHa&QRhlxH`Q=7tGd96quOlTt!9v$g;S2v-GmsI~yp(1F0|!O7-o#&k&%U7g2J zX0|X}gKh5GnB>6`9S(>gX@90$JY3eukR@$JS*Iy4a`T*SgQvItk$GfM);ePH&;z+EH2Uji*ZT5cB zT#8;v+1NU+ICg^((eL+d2e8M4Mbt%rV^iCqbbf@_P&YHU8R0G*n}PFY+q4DY+WwQk z)_F04T(}$-Wa&_-jt#c}nA5sZ1E#<+J1VU(h|=OL$d)i~Hh@^qMYj47lbE{ra7LP| z{}{!<)M3fhT)8ez5DsMyBsWMn969tk2vxY@2Dh7bb7kwOrK82AgOx%Ia@fq3e1Q%% zouX_^4oAk}TB13VfpJ6g1c0l0GGrnjMSr*Q-fJM|mt3j-yxU zmCl@C2D53o2~@^Gxmf0-<5Tkww)Kt1y!QCNp7|JBiP`H!P;$*gOZ!--Ifnxaz|390 z=o-cmz!2gwE~hM;XB1yeizJI=gzK&L5?$;e-L;f99>+`2z--)rBP2P_*=E=Ebhe4% z(j_q%R+k7=-b%~~B{frLVd3@3jIt2WaosWR(l!y_+>!~J5+P34%B zU~tq@G^f3S2V68=U~4^ssje-o(2}&zoEv-Avh11*fA$iww{_cM=FExQIu}j$Tu^3H z#g;OjS?|UV$=O=LA`RgPB+cMR(Z$e1iw5x-n=xB4)#BiZb)uaFjs!eObV$aXE-#!? zhkvGZ02S3W-_3tkP$w#zSBLOin+bHs zNo+W6r~zPe^a_gAdHE1H&Ie;AZVfq>f@R-1*mOhEg-v3_7}WZ6uL?IlMi)q?{5H z!YZ<>#-xIQ?1rlz*#PZK-L(uOl@T-tlpZ98lLiXgv63;^p?zoob_^vkEIamb3;Pk{ z|FQQzuyWRCzTZ1@{>?eI$7kl?U?=C8;|&zJU0fW84fbnqxU9y3gljUqM z;h(p7cilVh%sDpbCTh^Vt`;e@K@C#Sjip#kbb}hCpf0*W4eCY>ZD@m(sEHa&8+B0= z-5_n$em~FiKJ$CunK{P!b9-BzfDVJ)l_P0_wAaA zP_$_p!A3b~NE!Fzz*6Ab0v`mDm?REL~$Vuww$|FjC*s50fg+_oI1 ztt$KkLL^xQ&YwPWW8TblLX+e~bsXRz)R>N+vI^j6K!Xlg)gLqTTPN-!dD z3EjCLY9|#&-1@@h)W4Vnt4e$Luy6b7Q*% z4vxkxGTjXT<`8&UFm(svCWGmI86|KkVd1^{W~Q6e?!wgN7%FNIQ%h~Frj4?`e$FB(oQk0?9%`{<|KqWbLo z&*erRF+qUU>7YXn%lf}eBq&DMV=&_5!6Fb}CL7R?c=>-kVzbUBn-q`Vk@0`ldB(MQ z*$rfXgQy642#q*wyh7-`U zb9%%TV!326OcJ?+sq zacsCVT!L%?Y9@f;&c%(&CEQflR2x!m+61!Ou;C@u_Z8Lm@I7xb&bveHvGCy|%r-^t zJ+}Ny>qvsErC;{coPcd)mlovTf6(IM4`m-Scv+YWY3R}ReX4nr!TEsXA9TPNvKH=< z%Qg3bvn_0JAPC8*nRS>ZoBODq?$dM3qo6Q1i&9vu`e94eiKu_JjXp?40|BQFbL(5R zHXl@quOcw7**4s}( z(zodQKk<_9FAIlrc|k4|`mWnu!~PB7AXX|uc$BTHL0up1B%XBKN?t9OTXstg+Jy7` z8$M!As@yk9dj-eQHZ%>rfM=v){`PD~1qjPmJt2Z%KA7sa@%PhkWo&FJn-JLq!_JQW z`+|zGutxi!5uB@BN4Y~%G7AJ3gz#BSrSe)#9a9=pMg$bI0I}w=x@1Cl*oHi6WLG2f zczQ_K@@TQw3zynA>cJd%`I?r^u(gXh+d;J%WtL*H*%n`+g`rEOws|TBH;W(i1sjW1 zFZz_8X`iwFm3lzVZUgt`5G+j3lT9IzUeL2`60;cli({H zKX(Cu#rLuxx=C|fI#IJ5jyk@~K$~C-QjPh6+%eBAwmo;7T7OKrZrssgc2Twd5KNyP zh6y9F+(yjq+tZ&5iCDbwWvlA4Mb`P32_Wao-)7we^bfDoMSHO-18T~X;^48o%`_sj zL7zzLx+S%T-N;ZM6v90$w##CiNb*=kBl=igL(6tNBJcl*p`RE75d$`#A@7cm8HU^n~g>I!0voG}4^c+ANDUD8B|yjV%UMQtSQoXTEd zn-IylUApGxQY#jEY z)o>Tq2o$eby?itiSNk%$?fiG6izXWyxj(#u$P_|O>o0!}cNS|vtQfg*gjtNtO!~u_ zo53a*)EPQCqVA}sy(+aEMvq|_g7qgko7-oM0uuXM?EYxjG(t;xfH8K*CK*jgjVa?h zyQFNyFm-6baPGs^Mw3$Ci7WIA_0#TKZ$G>6VqGb1?9uZYT#UIf-60Gcs zR>=bngUTJ5sB$I)s~RO7c7C}&CKg1Toa> zniV1Y?*|c84q=VRQ4y{yRu8ChV>~9vsm53M%Se)P{OIqF3tw3g?H_ik_D?uMXMGEG z%*0{WB*l!{bSZGis57DxZYQ#hCpJHo`6TP~K&;0<87kg5EP=RUd#Dc?^dnM2Sd^KP z5~IXTnO@Fi8e}O{yIs z-{Br&M{a-4R?1SiN?m29V>hZ>|Cn9u{$K{=|ub;+SY>YiV^B-`hM7E71IE-YE zpl4*hmLp5&qOgY6$)8xvE>nanqVgY+gBj1naLVud{eA56EL#~?{NeDj?F<$~Y+aUJ zF3dtsF6xN0VC^nPVQF+NuUpg2defAKb15h30w+m`XC+J_u9KEaWs20^#2#ZL z%qBPD{1`+T4KN={4wQS>E=jO|e8UcG>b8+1%_Gz9Ah-jB`VIpYgDve-Mf!RDkY|Vq zRQIBV@#xuE|5j@Ov>=Rt7rp0fpCZL>^OxIVcY6vYQWMA_05%XL*hXTT`|Z9fjDLm zc1v;8VN;-rs2DmD$d^`pH}t4b0(t`3iJ1rWU_8JCu?|o+9h^0hTQCCB92pJGgq$Re zqs30i(N`BDOxoDROxX3m+Ih5C=kVR~z^v#Ks!G6Llz>9F>=Q>TyRZ3%!VNN)5I4&sT_Ud!>pT?1Ftn3O5 zSo|Abkz6dcp}W*`a!Kd^4++(WwDWkM5ga{3Y7m?gG>5E1=p^Ny9Zfw$1mw60_vh*x z!RC4dC?K1?O2a>kmi|Ix!vk_(3m=YkT8Pl$bFasxK2ipfU{PYdUTHVNXNh|2i+7)gGp^FkD>v#UwlU7-YQ%&l=-e7}8R52R zvPZ>{Ix<}yO&V(ng%=H$y19V2KxyaysEU^)9FwkrZpub%2pHcj7DqE4)6k%nxI0Nm;u zl+teZ`c`o_^<7d2dqFc{Xfb5wD9k{28Oky}m=PMm`0|6_pv2GMxuioAIGYT@h%1;D z?*nFMMh|Z~&0;zr<-^$>-5RX=fF>j2V{Sx%p%fu-m$tkM64(jmx-*9HLVfTQWy3_F zj6+PD7!bI(J_n9%^jp~YZfHO>E%4J`%@1tmRynit7Xq5f zjt4}D$DqJdbXb)*k>ivK3P442W1tR@=qn%2zzh&UP`EptXDfAvl#`?KL zxn;N8DFy1bR*1!D09|~xS>b1eCkGt%p?%mz3ZMLbd`hz@S^nG-wI(0HR|l?!fvD7 zJ8Y!BAtPZVd$&s_MzvexxKEfP=)%4p!Uj;gjw-I_D8k4L91w?h=_H>6=M#&HP2JH! z$RU?dNzO49&`_YsnWnl(aacW;ThFd#lAJ+(&m+OupnW7;niOuA@s*w|zZ^lx{FIFk z-T~Fs9NqgodQ690x6qRz5*@khfX`!py-OeGc^Bgd*qVMTAha6m7@$UAft6SkW{{CmQCMLj62Jp8Dz&mmoE;xndm!E zkW4g7?1>ow{4%OZgGfVwCN-Rp>)?mrJQouGGpXfsp%IE3Hmrx0y(0jhn6dEY2agh? z#)PL4!v!!`lF3eu$TLPrD{cy)7pd>kk|%J4SaUowwdF+;;G+yv0~X5uHBuJRaE;AW zWt(nO?SPzX+UUWjb3NB1+l+7^R^S#umOph+kE4(UZ8H=APR^D~Ll`f@zdq%oN(IFw zhIBXorUFQ4-)R;(ABNweTrAdI*pUb_n2L~}iR(ZBE98_ZqZujhkkO{w0xoIYB=#we zGCtXZ-cpcn7`N+ek>?u`%6jrgw}Wf2#fVf_5{FFlnHmwokhmM+Ll}7 z<=ts%-GiAM)Gy_0dSu1sI%dZ7x(tQ%_o@4Rs^+01$N@Hm^bhJEGZ8URCm+a?Av43Y z6d=LUp{N`=j#_6ym;n8P;Bqh8J#KQ74-Fdehb~pGpcdx9xO2UQC`?q6CV;eH)iT6P z#*NA=d)`d2n{H{xfcFY?NwY9M_sJ1v?g_g2;E-YXfd$~Ff49?tIpG1(MvB74ifX3EJ_E-w@!qrfu0?~u38vc+MDmOCKmR5DuM|^Vj~&h z16n}N`VwH7Rvi*od=IHF4;(N=CdC=vr3Fa9r0_J9Bg2Pw@w#XMgfhMEnN`>1oQpjE zMR|B4c5AxqgJ@{MjmR>*gJUkF*L#qd>(@#cFUr3Q?BI?id;vGO$rj?GT1E#c6P)*Y zmu9u8OKmS*?cKL2-Eqy>_X%6B@|P{hjZ2>$3mxHD2b04-6p-Wp>^3a;eGwr_my`z! zV_baFSQV)g%P(L&n508ND!KeJ7vKz+QPP~bpG3e0z6dm zdUg(ozC%sKB_5!-YN9}{aCFtAcBAKh^CPV1M#LqtZ*`f%_aQU zirA8{+LDOXb6kdqV=pX4qjU!>LRJdd|gQ)_rr>N_Xw8^0#A@Jo!KV%p%h2a5@x-T`X!-Jy<&mrKWF0JANF<;@nyXbcQ4XeQW z^O*^1@ZA!Olw6=IM;f3v#5MHjGn3|vcFJ%21$p(PtB)CIu{Mzav?oh*$4Ar?EvEVb zkEdr-K8M-EdqzaofRr)$I<9+zy{Fo{pTvo+7jCk65bq|3qC5U&d^-h3*Usvs2rOjbU23q<1r5Te3oOy~KXbRi1JIcy=H9LOLM{_nnNHOt=@ z193S4#)nM|i~^i^^xdDE#SLhv1?bTwD-?_Sjlb!4_JRG=;rzzt&B8riJR0Iqk|s~s2y5IQn+ko*3;6v zON`MHwx(YUNh61veB9X;qtg?HOBB^g6I#&Jv0DWVjyc9GctleMUngni^F(boDZxJ) z#U&sFlg!=Fd9ji@T9Rek&{-^uAd5LNvCnX={U7;zcB{G6d5DZqeLmG75%%{8PBnIglOFi!O zi*5R;HDA?2B&Kn?4d&~W5CEn?cGoDuNBhAHC{_s1Rpz3teYxe1aK;P*3dB)5{%;pH z=6}6cNq)aVP=$M&&M$(AOO=0Oz$b`FJV+|v#2txhQ#A}yCN1%rKf zKqMgt%LCh^JR}cc`2>Li+VSPXp!y{4;9?)8b13=_apyW14Nq;K+QC*l&p8-G0|h*P zgPwf16{9JWwbvNrB%dVYMbg@%>~009CMBYPc)2RMH^rUa4&#I zg$6+#G(KqI9N?O|O08x`mJAK_>PH+HKJ*H?Tv{xwT&m~TD;s{Z8_`UpkIE6}NcAP= zWDvQ?Qq*WVR$H?TX|*-G0`UUbxWj}(MN&}933W-(AaLb6T?AHAASOIq$})vn{%;Rj zQEY=GwQ!8~m@M=(z!x(Px41?rzMVu_mj8k2*E!wXZ`3N8b!kDg6Z_pcKMYxV4PugJmAWI;(dr97P(B9C)yH)o-o!EgS zEF9fRd4-0wi+@vzB6ShYBt9@#}a-i`&A5$*kg3TF&ZGXhL;IohCdc zhnF~tQW!t*Q_CaZ2n^tHj}akoi{5^dql*}Wv3?Yq-$~Y;iWrZ(fjb3x`qdaj8e*M8 zPs1iwoGtJc+}IfHg8XlfmaEAp0PFWXWmdi8mp*>fm{zHre zZ?Ja=WTcgWL6|%^>9{T9-^Fhk&$TNM2j}-IFIn&DB@GC0-5d7hiL!=HuH5lxV``wZ?Xp*37w zK{{v;c7b|W!^zwylrq5Kq#W#8wn}`n9u^G*03Zf{n zCc=RBQgk3;hh7rS@SvUj);~7tvYhb%2ik*nfh3KH(bF=yA##GH978dOwDWd{OJhT! z3yv5^b@s}ls2nBsnH{T{oTvg%24rjOM!bBg9TZf|@S#phspm0?HRD(a61gX0v;noclR2AaxzCs0#RmTGk zNYa$hnj8DjhL<>>X!K~z(-yW6^v^;A7(r0_Y$mD&_N2dK83{<43q}P^B1B_>A%sh$ zGp}wLpq_Az*~I6KAv#C^Cx`R`2AF5PP z7RFU4J`_We45Rqe1!HyT2fQSx%9!_iVO&|BFYXnex%B)bM%LYN>kv@~v6d~1nAi}0 zZc;^*!o|$h8bT`P(FTz2ni+x!2@m=MFru|w-_ULx2+7-M!j0gl2#OmtabmBmwX_0}gHSQ>mpEG1xsj1gEc zk28ylwP2lu;j*Z?MxzK!C#*q8Za_!tVXR{y`GE#zRlKS1|u~AJ@<^I9=+)k7;6%hE<~^kPUYSfq=R+%tTI! zn4CBqh$@LVad^DmHG}K3Q4<}cQS~?n$ponaG)}z3S^{LcoQ$zB=0T`}IYxP95l*9i ztX!;5S~2Zan82R5hz%c|k@(C6iB$uv1nrnl5z@9B z^c=*%%s@`)5^gt#Kqy(YX|kS9m1D>_ZG-4UV}OJJcVvchJWN!s9!5{E6G*IkCzr8o z27u`G0Q3p*p(8sSwU9P^3T*CIh0!#uu+tcZ3CB6om0nW($gME+&NYQ@+6=23SF{lq zW*QUDTy30dw8%C_8Jk;|43;oa&s7tQcr-UkeZ<^bX<{=hGqk`bW4;i59$+^qghOy$}YH>(vc@2GMt zY!1|SC^z8IdC~LT!3|mfEG?}#$RHXujRg)$Q;e{Dyc|gv7G?~&O7cm41)zi)+}BDe zy!*OZl{B(f-Vddw()%^7Om_s+A%l_i-bcOlAsboe>QQxK)FW%mQKOoZjlD;Q<@F01)iWiq z(=7rX)6&7Bgd1B`gofy0i2^^)7gJc~tlrMfrYE#1li|wjGsW#M<&t5z-E3v!?s~4O z!iESj6N-gX*!1dRplr_GYjBXhEI52~BV)pIitwc?KffLds2P_p7A9Y&ECPuN!;Of{ z!zvvX5BxaV_F&nRUV##W*;N;jjKE~bf@DDgIr0L`BY26V^Gsb-`94AEw%1=OjWI+C zMvGVLsR3P8Y*Ie<=qGXMa6!)C5Tce3cF3>bzQIzkfX;uOKDaLJWck&%2uMfQBiim= zy>Hy7WWDQsIH-;0j!p1*NyAd#V{gcC;%pK6hw@_qA?&5tC+ML74?3V{K!m&4uA#C2 zAr3#vf~Y2%9Knwe-H>OjKv$9O=*7&BV^_%nN@V1uz@(sgu^C+_*E+BNE-1T0jfF7* z#W;FM`AybeJm%qE4>~Dr@nt`Tdpw}d_fB9+x+xH*jzLK+P zc487Js9*PG)6ih={VL5C6s+B_z+pmyhh>I@ZCL>iBOsXG-?PsDaL^W5+*Uy8al02o zrcuCf!YU7XueybmLgvDJ?x=9RI*tVZTt8_c~FaS09ANJNE4;c39`$UfTOpiUz-XCqopxxqL$TBte3b;<5l* zW8A1q;H4FCgbJsBa1DWFe||o^IXth%R1C<+D;1LI*CHKaqmlz z)i7JT=x`PY)`MjR6+rDh2i0C;goEk_apo=4N?3-TlKt`dmS;?`KO!Qvn7!}Z^+%%q z64$}5xj82GSy}w)lxFDBpRY$BOEz%4ad#2i7}(W6b(``rMas()>(LF<8f87|fQ(BA z%3Lx%WReGD#ywVUb|mc^Ca}v)(9LA9GsdCv^i zdz`+<an!)Ju$*h%WbED2lp85~%I_Mo@L5Ih{? z-jDxK1hiPDC+fxU6J?VUr#di3Tv$U#&|!5=hY4jO!q9OzX&1zYf#XU6t?r5oI4)I7 zcvG~Sj+LRRV*9tNUGN14Sv95wCOPDD9wU=Ka{0~H-rHj|n+8HcHd*I?KWGSd zDPSatqAh$s+ir8tzQSUtDaRav@-9L6Qk`#1Mxc32VF=;*^_km-} zGv*GZpqykN;pfBb2%VUsSc@o!I$e(YGzSNfMo-XZ7-P{!8Cpi3^a|4ice*W&l=Yx% zqVd3#%W=jE9%AHWi~$MQ^R{lWmff^k&s{A$wc?(4#loC>O>i6YgEZpDBpv^0=J%;5 z1io?64zSI>d?(=BRR=Q{XpoRv6uTNTw0vs^gqLdUHXf&skc8QFXnA6v5ntM;XWw_1 z<~8n)x^ezN%zHB(aMX6fOA# zaYZXb9yAwWGN!b=)^dknI#T!+zHK}0e<*K83ic1%pXqlNN2Jkjw#7}y+w`0!Aj@xt zF4lK@yb&aoUjz=79afWYY5Xd+pW6kI?z3^bJ42>!NV4GlIOK<^ghLc4fuBN3!JUIP zHWG*Q>iKZw#?`vE>Nu{B5oZRwstc}ZxvKGRb+&#Gn^ zn@D^o7<5lBONJ?~>3t!wEMN=|BT=Z|^@g3qDTjlG#PB$oD8g%x8Iwkt+f>e3zKV3f z-KS|l8T!(`tN2)-N`XE^Nwij0G795$p8H)v->dFjs-!N;wDJ1^^WXfgV7do-tEP_o z#>)yZ3`1t__YS!I#K2>Acwqg0v(A475N^@9o#0v#5rl>hG3Gpq7<*9K&vmtnB%Yag zcs4jLG}3w;?vfj;h;0k-$>>O{$YjfnY8CUFGp~>vB$d7I+~`atOM0wTMz`s&9cZMW zoso6s$|VY|r_*{&V(d^Uml)Yb=f6+MZ_lIf73MsWnn6S4_&cDOAA7r1-vG_OYFXd; z4q|){c@mPpEhG5h5szg7i>Bk!^XorbM%z+xRQ_jbd;d{&R&Gavr5R2XS0p z#Xz+tE6=XMwcD=q^lj_VSW>4@D;0cQ!!0G8>%&SZvuEfM)IFNT<2;lBWCoX%HLcNZ z*rBxQE-?IJDAl#UJiR?CvYms&-6zt3*Aju1={<6R!R@ud`b<(Fx{R)o?b*(e!4|?u zs_qYo@b6k`=uzDP2_n)03}|rVfU-dWGYa9TQ;#9BeLAk&EXQFM_2XghwN`x*&&=M< zx2q?9c*liTre%>3Si3d+Ftf9{@>?;4cf~KHe$Z~W`)ip5$91p3d-a)Z_)AS|v6lQt z@ZE#jzJ`0uz6@Gr(GRPze5w-yDf5M*ESrD^ixK>A7yoW?#~zulw<(}~mwu&nNl&-Q zKv)vT->zS)D84VeQKb%Q^6#*8Qg4%d$n>)@dsKF>-fmQWwyFHiEq;5f#>e!1qcl>b z?1m-vc)QY6Prn-H&)K$Wok&-iEw@@(;Js0O7UeG-m8#O6|K?9MIKPo4eyj8oLeXvw zod6al1&DLM#x)TLbDIjG_!<2}z&)s13H@as8>9F}q5NFGi;6!5vY8$NPD~Un(zE|s z0Oq)x>RIRiq_`WA)#Y8-+UJpdP$y!F>qSw#nAX5Uq%5v!(`}9$K#xyEc9HMA*eB!w!Q-N< zM?@dE!c1DWIrbq@LV@CGFaM}$8S~*W-O#wtO6vai&0)$G{Ph_Yq}@qlwZpwA*KkM& zCNp}1qp9sN+E{O61h*;8OJOMZ(F$O5BgQxI2+nsuW(4MuNRNOr4EeXpLQVQv}}2{l!lzV`RI1h8o{5 zP>J8T`5wGNTM(n83`!2A3A#dGVd+qF`GiFqH*AIRwIawo?jLJ^afHW7*-uqu^>F%n-N)G|93CAG$) zNTB7vJ{MYCx1_lo^b&okdU$9)JD}kAGQdK|B{;IqIl*?*Ar=1*1oq{_a?KIy6qe>F z8&NXMABeqFG9TEpS0!UeG45R&J?O*6xxNoG2=TWiJeMV9iwgpcog>I1-`WbNvP&0b z;2sZkh($Z}DVPe2!P_tetVg_ojRJ5u&aJQN;tcNB{|Dt}V$!Fz(e>=sK^oWMzJ94C zF=-iZ6?9*Ld}0mPCEyMEMs?j_QixSD6bmX@PQijJO_=*pL5FaFZ5_*5_g}9E+_{Zv zsz0i3eSJR6YWMfO`NKj$*&ay?3QOu7AY(Vyr}h4LVVguC$SsnG)PB`RLGNSXBsqwD z3wiXQ+N4CB!K$w9#AdP}uw>x-gr^lKpJ2jKOa@BJ(FbYm(>u}+1iR@=E+#$_5gzi1 z=M(wo*TA^_hHb^fC6Y@pV3Dw9FxV$!0MFEkz!&6N-WZ3iK`FQPAWQaa0TB!9oZksp z(E)C0rM=(fWy>-=uDS2Z~Wngwg2U*-T&&6xBl$ESlaQOY-};BjW5<}jR}3UwoEM6YxOz_ zO-tBqpP!i7($0TWZy#;-el@wJeWcbtwrNW%|H)YUc)OFg)-FhIYpu15sncFj%k{>V zcIRxZ-8nP9n9b4G{FGJioS9zCEN?EGSj^gaElR#z6yTex$2O|#owY5k?myJ#vUoYM zxBPN@`Q^^?%RS|n`^zs6=>@32%NX*PTfGkn0(GQ^4NCq_y`7(J^_EJ{cSeDwc(}7@ zAV1qa-s;^G$@fe${_eKM;zVs?VtRVIXr#Npb>jO)p%dTVpzgk1Z+8#1PW-C${Ik;Y z52A4Y;b%$#E8Tk?P2GE=$gi3{nC?>sc+}|eU^NaDHTdo>*4jsv(Y;u6+9;an{VK5A z`Ld8xz#-JEju$0X$D3;G4JKovK9|karq#r&{={^4zo&XOu{_mOSl|7^o0$3jo1n<| zs5sD2&B)4L1+9IPT`RI{p3N)S#dmwfiE)KeKP_tQ)VBtrcOhiiTh>ccMfKJ2uPH|N z!+NWGwsrCgZ58~aCNj(a0}bC%@=< zUZdGM`9LY}O9Ociy+iH%!$eWG-~2;!(W$1g0OX-)=B?JruMITwc;smee-ktG;o{-s z1M%=sJUkW;kHzizEPQO){7RCyvEJ{J#PNaAcwSU2e>;YEYYn}PVH zc=+;^bU}M{XG5Um-xAY_90cT?7@e=$TwU{~Dr)9CCIiT~*YA!Y9Oml6nJ4`HxF^hw zT@Xp%pNOQnaW$qsgnCI2L*87ry#}pariQlHw%19$N_}|hH3O-0W0WOzZftH`Wu>ZU z9Jv2UZGup5IkR+hQOy!ffX!Bc|49LSeXb7u7xdu3LIlOsdcyXa1|%?Twj*hK8u-up z1Fz4G^+_nDQrj9#N!x3p5pS}bxV_#--9J=NH%=P{WjUQTtl@Iv_S)8Hx|-}5&g$SA zT#gqwkjU;f`a@ujt}JA-z6+vmcR!$v&a)+2{riH1x=ZnJr?KbiCpJ!vw>vM^Wf16Z zGOM>&n$4*UncF?PAsbU(du5@$QpXIL-%5^3E>l?#7c5R##Z&EG`kT(`MRU!J>}Ip6 z2J|Pibea^sFhhhQ3rppQ3PqD?auyv{yD;f@Z*Rb7p$VyPt1P zFip+oYz?XPRDFJXrq{WtNg)E{y=tG|Yj zgz8qlqoH~l(QA6tlRiPBro*0Iolr*H1SZ z)6)ysTl%lgf319XeP(81qS4MT(O;uppPra7Vp=_==~9L)(rNwM03fTU^faRxn3-v< zK0TIYjqyx&@$}+&tgkh8)j?c zwb|`ib9+`7Db){5y|a}cYUiJjc`4Hk1ErM<6JD#G->G3qH|2ZvS8r0(sFJ2>E)|f; zEUJPmGeQ15#C3XnQWZ3YjXc~{pWHYl&^PM8en;(WQb;+}6mFt#&5b}fG^-l=s)HSU zPs?;|&eoe7H{uFxq?7z!HQi_|j?uuzre2Kp4O$^ zXtwi~l2>0+@{wsR!lM67Gwhj~sR2517rA z7Ewj57>+D#YkmYuUae1xG3!DvbN)(Ws;-G_wC{$4di&;Et8a*gn_D$&nwwXe&H5xn zy|t;2+SWWj11elb*oLo-6l=?sJR>9 zTC2b8L-#BKXG;BD$!G5gShsu0)K#?_oS@1|GbZd_PoD_J{D3E1JwJs2HAENRB02j{ z%^{nZ=(|le<$DFRiK*?H5GNTU7JI2RW+XDS zsjM~&m+57#2JMr#7KPR(A}Pldw|Zw(wENQZ_r5XDQ5*5?^(=UZxE%rwcuyz$?+1X5EmntF3=j87>);YxNn)`ROP0aZL;=dNzG zyp1luydcT*mOO3AM}&Y)Q}uH5N{u~O~X zeg*2+8ds^Y1&vQdH3&UT${uPonzB^JEQ6p^LvaP72^JpcKh>rLm&n@G%1?z6k%*jg z!A+{3n7C$Z?UG6HyD$&M{5>=`#iW|H(*R~7C_wi2EvMc20mZUfgVP$=Ya&m zPL{xfT9b{GO<#G$PawhlUI*jb zfTrW#4$5F-MCO>fc(^gId0*Qvr8Cyh$~>8#d)G0j-X1>J4$X)-iC#dPc@)8UnTSr9aJ*L5!R?xzW2` zGb1SXAZl=2G}HQT%hW^x(e8Fne|tnQz3UV7yPFJcca#xV3M;z}zt^Y!d%LG3XQ@n1 z{)Xl66rURTi60@C<|R`agvixIff2HO47|>QxhRDoLCKaY&7=y`a*}2hotmqeM}yV9 zB1b@?Lp^J}QhzIj7_T*u5ArU5K07rL=AXDmWY;i3uSO=PY8Xw@>|4hdv&}V0a+p>& zk4zP%Pu1jzj>!~~YoRg8EmL!n%r-mH8CzRZ6YbssT8|!ZTe_l_1LpeKOssbL=*{0K zh&q1*)I3{jY|6%DL78aN+lBqvDOgAq#9WPj1HGL^1HGL#Gdue^-6yY@+R|vR{*8F7 zwfX>jC=TiE!5e~IdVA(&1Zogx^oEibh9C7GC6DD|dc=Hp9YzDX62CXk3oErbb`PogS1X6->rNDq@!C^ytWzE$KkA)&H8lhP6$qRtTHxP#;RS>MzpOB4PfKvSHP<(u zo+$>GKf~biXCkAw9HU;gQ7_vl@0D%SyO(}W*P5DE6Qj8zj`=n*gb4W*v4OWVb)KmF zO*7>;40T!K%gPXJ_PtS@<3P#J<3-3RfJCsNnk;f5nvuh+`Z5D0#N;2Wt;xP2vVeU$ zUq}qE0AT3O!X(lhDDdm;NC$RK3671{_Ubn$yz)RH=hzUs{0?-qX$c#wo?pn6U2kjfQ@-wf_ZCEA(rXlxGbk9 zMFs~-kSRwo(Cz^XVklP@bH5Z)(rO|L)PS5K@y+oqGR1+$=Ab{uSwxJcrk|kh>2!r* zN!qN`u}p;Rc-5pT8mLnF*O9oBa;)U_Vx}+4xnZ;`?PX5xvlP7NGoPbAc=m{nGZ)W}i4~Y5MayG~Ri7!dhV-Bqq6r%MDJZU` zHB;+X=7<#w4ppPevol&kNCgaJ)?D0!;{n&jmSJ$|SNDndFUMzT)FGI*@qR%9Gm>nr%wbDOnn4Uv{ejigqtue4%sy?IOT@1b9j~5v$&^xQMhNz@F=c7^oX2>d2DvXlCS75lUQeWTaLrrDh!=DVJ6E@?zzMeWa9A z=f>x(yK35^A*P%*(Ad_5oN}qTajoAudaB7SLr^)bWhg4AY*5tSku*2<&Z+TXG+D}6 zfvhn@)>wN8`k8UD&Wt=paMsx2NGtatOwIn|-&kG?;EydZ|E5yNMeRa;v*Ed=#M1+-Dgg)(gq0oA%LrEL_eeWEcSHNLW zOI&8DlAyHPFD*kUD$262O$iTj#&Ybb@`&D%Nx&(6Ao;?qF6lCy-Z!A~e4~L=i~^bb z!@f)w1u&6HES?CmnE;ny5UwQ0^(FZ&wy=k}x>TG-BH_8+mejA*w}sGpEum9EDp?)d z<)o+~X$FcYW4oe|HHq5owH;H~Jm0hxU-IXRm}OB+1+^kv$`?koS4_+9YDnvj+iS9V zG_$k`HkFe3dWm&EJ2gd<9*C0itZDU7);@h`5!tK3!&+pP0=30UYYEVdr)L|prh7>h zX0VVNGsMpH3LmuBzGX|}o}wwLybIW|J}$;tKzy=*wf>F)u!|vhd~DuSuR$Vv&*;Pe z*j{hWj>!P%LsMW=Hk?7zTKjeh8u^H;*9%U}U7qGL36@F;{0lP1jmaxnYI)7}R>{8c z@H8nb7Ws_*-P_Dq%3s8{TKi53u<9Jw!CE=nA8V5uQtc z6==4M*UA|Uv67<9+iQhtjmAoKn=TL|2i2N3bf8+Xe#>cdWA8ws4JW&PEmbbpGW|MS z2cJa&X*W~_r$*_DJ2f*m=K40JxV}yA;0tMM_)5uYYetM{|C3Rd)c6D4%b9G!! z0f*z;vkMG|2`5oi^}0yQLhH<$47!Ez@vW&ctFKE0lFPePE!4 zvM!fE_55RB^@yO*EancxGm0vn3qqAHsZC1?7BYB%=4@4nBBpLEex?Pt_KFsXBz+Z- zIbQw3Ltf^8lekuZVE>wyy5{Ts0Lbd)@_q0J<#+2r!GyIRByUZ*D|mQ(htsGXJaWC7 zisH4`el(+j;_a^J9CGO{Y}RhXN^tnTrCZF;oQjy=ZfBV$SrD z97%2XQ#?(DMGT#v>rau(H~FXSGHFj7f#aR1YZk1RTdr|w%l+s$$ehQJ4Hq^9_W2(Hxz8%@Gr|S)x z>;6E2I=T1Jsrps>3V5;>T+?P7V(tu=c#~E5p(I25*yc>zM0RKe$fI3YTcG)vIIrG5 zrYuFi^jd5S(;$mh2qq|3i~MaPW$hVDnNx}$Q%o zif4oLM_M}>diWig@z%VUgD+avbuIhnV(~44XmDaIG7r@6m<4aJ(_%4EgdQ7BnN$Z zf4IGzf^x?j+O1Sq5H3erv`DsK&iPAZ0u;?6$Y-e(bV`nGXu{RUz^A!D4MizO- zHCEV{8&2#LikVPtUl1h-lb%p~sawr}+y1IWQOgl^N?-I_)ij>CM!7azLAs*7N0uZc zo3(E}A|l8vKq1(fYl+s!rUl<8wPQtdsuLg*oZgm3l|PALz^RST7kR>pVmqwOSkVnk zqah%F5-d}h`LnusQmSdY659Dw(c39KZ-!r=rbA?Lsl%tD!>5ZpdZJ9Jr>CNar@V)! zC5`23$fC;on}v>Osiqjxoz_Eraw?iWq9#eCX}(KMM^HU~#+yD;0RL&wBIE70o{%&7W@P&pUUWxL;MH$N)m z#-9(gJnxO4E*kIew94E0nWFKBR8^#`ID*>YMcP?PP4rH~B$OIH`DjOxdLaLnI=oxn+(@yNOzG0pd`wJAD*iXiRen$TJz|H4vqgwuR*?SZ@ z1lQY}u2nZS=MJo;3z?srrBMVYXJy$XQ3)i zd`+W8nXELmrP$PpwlW){t*{gz3Iw@REDHUzOv@copv7yoXr&|#woe?9B?j(0r?jSk z%sQcMLNXp6)`K+Hg1#*EDCU8N{YycnG^HOk3AYVoJ*x?MYg3`RkFlisnh@rQB=6Ut z&v3}&2m^MXv~b{dOd)L#)-131Ze$#0Xm z2;PEurGC0>*0q(LR#3brY2Ldnb`&N<$MDZklrc3aD{)7wD?6;%b7W*GF5D&XH z5wtR*dF>w2;N-Z0?x&3)mi5>@!uvB5iuHEyVvC>l^{CBH*ykbE?{#7_8Y9tGIy<=; zuE%91VI2t?;pI5y?TP2bX=9TMO)bu95f`US@lh3Hc~_Y-|28r?-D4t)cK2vZ-BC+Y zZlMOcM=7bDJ~~R!c~k4b6AFCl1OWzJeu>`1b?ihHnY^Nn;R_Vh;IyE)FtN~VG$pgI z6m$y{O?W|#bdLsyK3N~$oNK;m_em9z1tl~2O=0@PtF`7ole5#`CZaYkmX(@Y0XB1klE{v0@D3IP-BIZ*;^ZD6=vbwfQM0A&*l~k4nG`bsjQz6KVXtuaP2a^lK zQ4c@UZUXxMkYsHmHo>3PW0P7Fk_($wY8mKg<507^aSgtY~z&_L14=(<~&40 z2P-6Tb!-n3dYvadbf4y7)@bQz(UN6psY68zi0tdc8zM@qZr|dH?~UpigykyaO|Ti0#|9iH*O-x&66=f zKT~_WELEl{d*EbqSm|xE^|7LO)GDwb{A^UCvKlr@WL~7*EZzM+LEY0La-|h-XUJzX z*F*B%{p}M!4|uz$fmgo1KWM7ErxQh-iA;MS^Gulzx(6;UZAQS%LbAx1)z)w+lj2*k zMl-?6dD~-EZJo|pDDT1 z&on|?9Y^1xs5N)TB_EmW)p4W*`=IoX1^ABZ8e3*g$E#1?Xgo`S*ud>Bt#hr&St~-4 z5-0bja14`u5$0&H^N+l8eWAs z+b3^9=d6CncJrP1wFt8N2Bw;whQRB$*LL^E9O1F!ASZEj1i~{I7u_G5^KON}={)=O z#M#au`|c@$qDzt?M+J!w}_YA2e(ITNl#?kWrrA+&Kir*M0y&ijJ)|N`_eXx zRNyQ90vHC1QyXMQv~}3f>f5H=6%HP=KZAzq3-hwfwNj-Lvg~P959}q@6PvolgY1uK zpPaVOO+Fay2DUxH#Xd*d9fVn~{m!zwrCAV``6*T2Ct=1~UAu?Q%a1@GJ=>U<+cqe1 zM*fsB%W0jUZfT!_K2-S^C#XOC*n zzD0$n+r6Xh-Z8;*av}IsR2{6AKd&;+w|mE3CkYrk|0TP$XVz$KyRh0@liTqmf}6kI z=e2k2DdCZiVl@3h*h5j+h7W?woY_|Zb=It@)vvw-EwcL6NY%GQc7C=mFXGWsZ9aC| z=hgOkt*nvaoRBm>*joKQw*GTt^Jt7Wv?+{LPaf>kHn_EW&+&FvYINo*PEnq`PQ*-m zI!0)3dJv|1&uMCq=Q@gEUof(2(M|UynF9!G&WUwjLMAunM3UW?ydT?=)$W~!p2Q)x zt*duhdrnM0^t40wKw0Ybi14ZZ9AJg@PJV$7ZCTmENkKi-(GU5yD1zsB@}Jth7u&rT zih8{lHZ5w-UhpEl7Yz3=;sYLU9|vMW#(H}~ke&TkY zRu@Lv@g}KbKBH#Va3}F_jGF`s--uwrS-sNk)*?Pzyrv8@Ie{)!26l)v56q3@zC3A3YWqPxG3j2}ZVs?z(hp_br=&?Z$sGo>@VYxfWkRYU`K z01RwgtZSQ^St;7#V3E}0I^JqJ_pa7S1=`Nyr>Mc>9~|&=%z|S?X4KhEyum9P3MxPT zE(2FalD6{=meIu?qmmVsRxHMc$z=~H5PRF6{n^xP$$XNADDMYVnQsqwBFnAZTKlN~ zAI(c8YZU%=oUTy9L~dbmm6ZZV`K_pgc(HQ^f?7k#+76nhWJU;2@Ky0hoYfstT#M-kWM=#30rI_HqXn z9zhQuGrevq~ zUXmizc-^aCQ$$0-gSK6IBE5D>`jamQK0hVd^SNZ;^u5MYf1ntP8UJxx3I(ev+ik;} z{D3G@$^HWIsI@Ps*7PDB%0weTm|&^P1ysh7x>@*ZPuNtgwj{q+Eoi3ktZpi+@BCEx zZ8a>$9H6m-*FL9}27&n{pM}{=op9_YgDrUN9C~**vB#Te6|#0Ft1& zDfUtu5U3+{sJ!@y`bElRG7d_FFmv~v*Ar)w7SzQ?0aaopJEX| zaXNb0rcx0FP>PD7hM%@+WhrFC_OW%?(-PVQH<#2jIAKjL<>TXrb z?lU^Bi^~f6dGcuy7@Hu?3+uMn4OV_98a0g{tS_FFsR{3HDz&-Ek%33OiAb%kd6+yp-!g%rQnxXr7=@wb2DtU5I=UYUZ zMP#J0LH7lKL8L^HO7=W)QAolsmwc44cUc?X(&*kTB1IC#&J+M z#AJeBL02l38dA7M(W8}Z#jdiV`%-)TP}hiy=G4s8q5?zm^mJN!R+87oqA@Khq+(FE zdqjj5TD^&JKDH*HHH3aQkJaBm})_^Ile zdOi6!b?wB_Mpa#OC)&{NDaL#9Z*+2vB+n2Xh)`rx!~?-YOL?()=s}T6nSZB_{jr6z z-Hn_4)Xb{(BP`6%;r)%VM4Pr8#)PKgD$O!ZwT=0@7M>R7RZ1-(x@EZ7v+vOQX^oXu z9W_4rg$>#~>HDyW=Z-1G%yC_?=aPij7_7E3&v7i4<}?=}U33W_`cEZ_3;E1}(lhZT zo_nn#(|h|O)?)XU8|deYm|Q2n>=|FOq%T?0S8yt}6u0()w3A;B<)h~G7Wpb@NIm(L zO8!^8$*)(b`}{|xwRNVu*BDJFOe_8%Q@;1Qoo?#Cv6y~LK@1gQ7%bX13c=>j^!mgDyQc7iDz?G8 z#*i66)v{(*fl1r)F0-m^E>!G`#=rTiyFa$=njbx>ZSr6G!TiLne#0%l zRrv=h=f45LUAJ6Y@7F%Z-NfA3%RR&X!-xE+$g=JL=6+%SX;QyCnG(Q9E!yKd{bCON z01dSwl{<DS0UoU$B=LuNF^JV90b9szT|oLzRl0FF1r)7z=Wk8|*w zn@r)dx}qUS{jqHRM*CW}UmxPPK1OCPsU}QS&b(lcVBT{Eh8 z-f9)V3-ehnGjoTU=LYP@v-#-lHdPq{DKZZh*eN$6!9>fGcM7TpvI}UEp9p1+jv8_w z&9;+R{^hd$dOuu^-8`qX;c@ca}>0^fL;{AtA=hNeDO5$fd(v|x&A2%2w zipkw-?pU@F4uO*(r5UE)FRq{EF^RQDv&n-t6;~cqr4Tc_^y6$W(!+u%Cf5ZeKWep{ z)v{V`{QuH_#QC;_UL2FM|K@tK790%mB)_lx_$YPBP)+Sy7JivAMGcm zwB3(A_Q=Yojy|@0<>HY?4}bQN2Ub3H<>JSdAEEs9*X+8A|F2xU;phX89erf^`UjVf zKK97H4_vu;`_WH5uyXjL%g2^J`()e3SFWQtL!K<#iD>wUdmh(7zwknq+6VRr9+kH&LyBIc0rvIy|9# zuVUOE(!aawlUv6pH@EJ@#kx+0P^ZJk)9iZ5@8+}f3ng@4#zqS9y!8im(xr#2C5A_pMApO+^lFIwH>n}Za&IBu_X`qNc7 zq*i%6o&n;gpXQ`|$cK9-c{8t{zsp&RXz_dN$F?;R)E_SOB&N5LDwc>!w zreiOoHIZB!L<&p30=OHb|!O9J|>;osW^9^d|=N!%#Jut#zAGAm$lroafK zvw9!Uis)Ob4{+|UJRGhIqR8V>wRcFb-OuxC`&-}8Ykn<7J{Lubi*D3UZ`r22a%T+` z8rQ>E4MJ((q=B!K1vZv_&U*(){Wc%pY294f46Vln8Kl1e+WO0# zN?E&yX zt=I~YN~{#`bZ}#zfVA8A$^MH1=}+}vv>^9P|K+*1wyQ_4S~hvU|B|=!M6B%O?&rMJ z&R?__Nn|YmzO0LH+b2HPhyCov$g~Z_{Ye$j#SY>^=G&I$uNG*EHPJpY-s(U7I-1s| zTDtmn!8ZM{T|FiSfoQxM(k6xM`fY5oZokuiKVj|#RX$$SM{l~A zzImYUM?GlT)(~3QUk4M_O*s_!R=`v2!I8}9J#1+_<$d(+^BK}k z7&Rj~78^=viV!;RndLRbO){Rc4Ydp> zp~myZnz;@3oNcOcIhC2p%4^ni!lZc9>*kt{X~wJGwS^294ZE8EvaD6hblR$)pcM72 z|BCLczoI*pQdR*HFvX)lj3U-%0g?5gYs?klvfC0BRn^iI_a-WWUen=4MVdMAR#tPe z0JRKTo0a^EoX*RxI9{ID{yL)AS`xr+l9{%Flb%|vXNAMBv@ko{kB?z!Ypg0DOWSX` zC`@fnm96dd-PmsWqDhF~I0m6`v<$P@Kd(0}K`9z)aO$L^^6b_habL+yZ&|lxJKaIT zQ1?FFQg2$hzNtt+?oWzU#y@|tLBSybCKG-i!NEGgryc+k3|!f9Nvp04xQ zJE0}j57p&Vwm+mtJqu|HDsqn+=XPG%s49H7^H;fZJ8R!P!QN2aXs%+M!BLd^P72Em zv}3iM4lT$-;Eb&<{I?f<>8{rNZsv+_d+q$fUuEwp4yu@=G(mrPNfV}9{wz)B3){Jl zS1K00`c>^kOweopynZ;~t1Jua^lbO2jo_#dP;YQY3j62P0%6r6(rQH z!t4cn+tjGNdjnj_Y_FVYv`b~Q-V;TsYBw(xWi>C-Q!m@)THTkMbzMs!T$X>%Xi@uV z**It4x<4UxmspjXzFN?@f0t-YrbKurZ$UfIaHSnvh-+QT6$C*-5#}V zmV4fnpd-t=eM0v~))h4;Vai$ZHSJ0j35Ywoaf|8fJeQcv&(5ldetC5KN@>*d4`mG+K;;%yyz}tzqEHJj~cD9nPC3a)S_~=&cgU{zS*S) zG02P42qm_45iI{gw3Cqpx!E7UTuan!%w(4IZ@yHhB$?Y~G52`x7 zTjEh&otl-eWFubvrX;j+gyiE!8ZKyMuT#n1DF(1N5Uh*&O9U-o zFpr)T9_$uBNrigDR$EE=yD?vH6|ohEKXs^6ikSsj(Xu|_RKK!rv%Rt&>F--aixxLg zN?h8eBa^lwt(|{q`7&K#Hf>A1Y}MaFW8*Ydxuq+UxM3fMcctwN5*13~iW_}xuW{Fl zJ#4RU)T14_;mQ&_L;%=I^()6n-KgENRzx>d={H2i#``aJsZ(6wrebkTnH8H*4=kvA z6SZhU2vNbJ339a@Hc@)j4UGLkm2WeVB8cYa#?(YKuj_1RVzTIsSMG7Dr7pDktK$7G zD52{*Co2hzM7o0!Zm*B)0xTWy5X$^>%xUWby3@+IrTYQ+%U(oLdf{-KvkTjtIkTou z$rK&i(d{D;jYr7Tj$O^6Yegusdb!A&FA)G0S1&hHrih_krr9#WU>CR*Ft2p@EmWjk zA>vxJy?SWFqGZuF(LqnE)Y2$=k?K)H#YG~zF%oM;aXP`GPwg|(F%R7h!s7B0VpCuG z^W-~4OU2zG)VWhlX)%~9LnJK4rknt&>$IFc#k2ZqYR8f?=4EP3b9n_@Rm4Lpvvmn` zZeQX>Mww9O&xVoCFa7J4cX5`PFrUY1^_{{UDk6zBZ4$ZjYe!w@6{e)~a$m@oANj3H z-9CX3L;fF==9I~1H=#lD5=q*uqfPsQ8YQSPh*$JA%g%Ymk8HLtgrDA|Ks0^%F@c~r zZMjCCHzO+cFHx+btf9^uaOtF?xHTaX`nMWH(Ib&_grc^KyJ+OO$a#(&?h1%Hrz7VyIUMwloIG-Ja_s1S z(b@}9tH-QrBn6#GYac{Nam}Px#$NZzv}on zOGA#2v%1aIP^_ct)<}NVajbi4sBzZYT+(58Z*$2L?rin$^n^PNJc37k@GO#~i*i6X zr_#O3J+0n7Uge(fnu?wE?G8V0rNVFd*RS=(9x9bq;@xrDL#na#BGUW?8XqHSZ#-mC zR+}q|R6RJs>F3H8@r6#w>{RRfok^slF0ZQe>aZL#$hwET?jf(6pGAvO z_fWx$igH-xeu2I_(Y;HhZ2yF~w0m#76#DnjR*c~r*kR__Ns zM?ct5xSFyEWVecAfct<=>QOzY8KrqZTSe*E&UUIDw$@uwL4I_$IOj3jJ_>>9OAf73 zrMMSMp{6sK8UUvyRhO@J$1D$>FV&wz^Q(o~ns(+BrF+(}Q$Acc%SJ(suaawL8oDns zeo z`LMW1$CtZ5$9UqX-K+MXBXzH;15xQ~k@R&@p#21eNR-qwW_Rgn#UZ(0_P@7jUov!g zlYMS8n#aL7Bg-7?T+_@+IV+Vv+&@j~9xdc(jSJ#^@?Pt8^OL~Y$~w$)joYat3IuPCQ%9CeO~iz)~Y|ZibNw&I}m1ut*Dikmz=D0*zyR{5@Ij-EO+?FY%#M`|C@?!K`4x;k@Vg)ylNlur3`V?xBaJC_>U7)j6*2UTd zKB)>dR;zHd%F7lfth9mtV#oOzlcT*?pw>dn^HQ&S_A71m)bHC*VR=i}=v0;WdX3?t z`;wSMBEqDJ$txYBEZ%$HljC>|~W(_~Z3mrS@6r?5~Wf^tbh@yKiLUcvVS}YfSEq$Pg6jdvqn(M}oxvG<5kI z^VllJ1W?}p-QKmt#&I0c9+8xnl1P)(Cz3-Uo4`RRGa)ESRA`A&#IHz4{8(Jdfdq# z4)MWpURBM^4u^Es0%F65EV(n?)!kLq|LX3lUXe?P9|%>%Cpmd#2ba)(@zF3O@H3uC z#5 ztTI-bktGP?Br-rUr1d_05BRn8bAGLSQ(m~!3}H*t{KCC3>>HyO6lDeYy1ZU_1Z|2= zrvkA8TRa5dwF1M*t1Dh%?j0P`3v=&CdMruOpa`P`k~=I4g9oo2cKn)()aRvhO4>ZeAhGC9wzT15BU7{*4 z1Wyb6J>1eF@b@IyG!qMiy_+94-~3R5KjcN38La@8Oq#>divvXeA_0X7@T+C%B~B3B zZ-jLLZa^UJmR^_FeIkdshK%A{mj2qT=(c~FHL;ccHl~nM7z0tzkYC=02R|WJ3BKP1 zxvY)?Nm&{ec|lb11yM1z(WZP975-RM@g2Y~s|8ph^=(Piq;P)$&yN8KVBG-NPd$%* zC#j;Wjg)Uti>HVPU=nYj#ZxvSe>@}0^$~ZBs6L*N<$CGo4NX{$W8yD6I>iD1Z`F%mq*q04a_O;Hm�D1wufZzW@Rgq$L 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 Z)!CQ!d9zN@JazuWmqzjbguX&0@E?+PtHS^Q literal 194048 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ⅅ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 Mz7qfTmn|eSE>%+V$uFh5ma=}Tk@BC( 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

~&XddDg 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 diff --git a/1.4/Assemblies/PerformanceFish.pdb b/1.4/Assemblies/PerformanceFish.pdb new file mode 100644 index 0000000000000000000000000000000000000000..0c87319852447f39c7a27f9c5ba58e32a38a5b88 GIT binary patch literal 130336 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 GmHz?JXoj)? literal 0 HcmV?d00001 diff --git a/About/About.xml b/About/About.xml index 968c5d4..3f7aa32 100644 --- a/About/About.xml +++ b/About/About.xml @@ -15,13 +15,43 @@

  • Ludeon.RimWorld.Ideology
  • Ludeon.RimWorld.Biotech
  • UnlimitedHugs.HugsLib
  • +
  • OskarPotocki.VanillaFactionsExpanded.Core
  • +
  • Taranchuk.PerformanceOptimizer
  • -
  • brrainz.harmony
  • -
  • me.samboycoding.betterloading
  • zetrith.prepatcher
  • +
  • brrainz.harmony
  • +
  • bs.fishery
  • +
  • me.samboycoding.betterloading
  • Ludeon.RimWorld
  • bs.betterlog
  • + +
  • + brrainz.harmony + Harmony + steam://url/CommunityFilePage/2009463077 + https://github.com/pardeike/HarmonyRimWorld/releases/latest +
  • +
  • + zetrith.prepatcher + Prepatcher + steam://url/CommunityFilePage/2934420800 + https://github.com/Zetrith/Prepatcher +
  • +
  • + bs.fishery + Fishery + n/a + https://github.com/bbradson/Fishery +
  • +
    + +
  • majorhoff.rimthreaded
  • +
  • telardo.NoLaggyBed
  • +
  • GwinnBleidd.MothballedAndDeadPawns
  • +
  • VSE.OskarObnoxious
  • +
  • vanillaexpanded.storytellers.rick
  • +
    download more tps \ No newline at end of file diff --git a/DubsAnalyzer.xml b/DubsAnalyzer.xml index e991d11..be4f39f 100644 --- a/DubsAnalyzer.xml +++ b/DubsAnalyzer.xml @@ -118,7 +118,17 @@ - + + Tick methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones + Tick + +
  • Verse.ThingComp:CompTick
  • +
  • Verse.ThingComp:CompTickRare
  • +
  • Verse.ThingComp:CompTickLong
  • +
    +
    + + Update methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones Update @@ -127,8 +137,8 @@
  • Verse.ThingComp:PostDrawExtraSelectionOverlays
  • Verse.ThingComp:PostPrintOnto
  • -
    - + + Ticking health stuff ond other things implanted in pawn bodies Tick @@ -138,6 +148,31 @@ + + Ticking methods of abilities. Cooldown, effects, combat + Tick + +
  • RimWorld.Ability:AbilityTick
  • +
  • VFECore.Abilities.Ability:Tick
  • +
    +
    + + + Ticking methods of precepts. Ideology stuff. + Tick + +
  • RimWorld.Precept:Tick
  • +
    +
    + + + Ticking methods of genes. Yes, all the genes tick. + Tick + +
  • Verse.Gene:Tick
  • +
    +
    + Comps attached to Hediffs. Often responsible for the actual logic Tick @@ -146,6 +181,14 @@ + + Comps attached to abilities + Tick + +
  • RimWorld.AbilityComp:CompTick
  • +
    +
    + Settlements, caravans etc Tick diff --git a/README.md b/README.md index 817c55b..d9db69f 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,15 @@ ![](About/Preview.png?raw=true) Performance Mod for Rimworld. +Requires Prepatcher and Fishery. + Performance Fish attempts to improve overall framerates and tick times by patching various methods for improved efficiency, while keeping functionality identical. Designed to be used alongside other performance mods, like RocketMan, and intended to be compatible with very large modlists. Most patches become more impactful as the game progresses further into the lategame. A settings menu includes short descriptions for every patch, and each of them can be freely toggled in there. They get entirely unpatched in a disabled state, immediately. Nothing is stored in savefiles of specific game-sessions, ensuring that no errors get thrown when removing this mod. Special patches exist to add new entries and features to Dub's Performance Analyzer, including a right-click function allowing to profile overrides of functions. -Multiplayer and RimThreaded currently aren't supported. All other mods, including Combat Extended, Vanilla Expanded, RocketMan and Performance Optimizer, are compatible. +Almost all mods, including Combat Extended, Vanilla Expanded, RocketMan and Performance Optimizer, are compatible. +Multiplayer needs testing. +RimThreaded, RimWorld Rick, Oskar Obnoxious, No Laggy Beds and Better GC are currently marked as incompatible. Licensed under [MPL-2.0](https://tldrlegal.com/license/mozilla-public-license-2.0-(mpl-2)) \ No newline at end of file diff --git a/Source/PerformanceFish.Tests/PerformanceFish.Tests.csproj b/Source/PerformanceFish.Tests/PerformanceFish.Tests.csproj index 1e8c988..32284c9 100644 --- a/Source/PerformanceFish.Tests/PerformanceFish.Tests.csproj +++ b/Source/PerformanceFish.Tests/PerformanceFish.Tests.csproj @@ -58,8 +58,30 @@ + + + + + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Source/PerformanceFish.Tests/ReflectionCaching.cs b/Source/PerformanceFish.Tests/ReflectionCaching.cs index 7ec0ba0..d68fad0 100644 --- a/Source/PerformanceFish.Tests/ReflectionCaching.cs +++ b/Source/PerformanceFish.Tests/ReflectionCaching.cs @@ -2,34 +2,242 @@ global using System; global using System.Collections.Generic; global using Xunit; global using Xunit.Abstractions; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Security; +using System.Security.Permissions; +using System.Threading; +using FisheryLib; using HarmonyLib; +using JetBrains.Annotations; +using PerformanceFish.Collections; +using Verse; +// ReSharper disable InconsistentNaming + +//[assembly: AllowPartiallyTrustedCallers] +//[assembly: SecurityTransparent] +[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)] +[assembly: Debuggable(false, false)] + +[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] +[assembly: SecurityCritical(SecurityCriticalScope.Everything)] namespace PerformanceFish.Tests; +[UsedImplicitly] public class ReflectionCaching { internal static void InitializeLog(ITestOutputHelper output) { - Log.Config.Message = output.WriteLine; - Log.Config.Warning = output.WriteLine; - Log.Config.Error = message => throw new(message); + Log.Config.Message = output.WriteLine; // message => output.WriteLine("\u001b[33m" + message); + Log.Config.Warning = output.WriteLine; // message => output.WriteLine("\u001b[33m" + message); + Log.Config.Error = static message => throw new(message); } + // [Collection("Fish")] + // public class FishTableTest + // { + // public FishTableTest(ITestOutputHelper output) => InitializeLog(output); + // + // [Fact] + // public void Test() + // { + // const int COUNT = 538762; + // + // var randomBuffer = new byte[4 * COUNT]; + // new Random(42).NextBytes(randomBuffer); + // var randomNumbers = new int[COUNT]; + // Unsafe.CopyBlock(ref randomBuffer[0], ref Unsafe.As(ref randomNumbers[0]), 4 * COUNT); + // + // var fishTable = new FishTable(); + // foreach (var i in randomNumbers) + // { + // fishTable.GetOrAddReference(i) = -i; + // } + // + // foreach (var i in randomNumbers) + // { + // Assert.Equal(-i, fishTable.GetOrAddReference(i)); + // } + // } + // } + +#if false + [Collection("Fish")] + public class FishTableTest2 + { + public FishTableTest2(ITestOutputHelper output) => InitializeLog(output); + + [Fact] + public void Test() + { + var allTypes = GenTypes.AllTypes.ToArray(); + var allTypeHandles = Array.ConvertAll(allTypes, static type => type.TypeHandle); + var hashCodes = Array.ConvertAll(allTypes, static type => type.TypeHandle.GetHashCode()); + + var hashCodeCounts = new Dictionary(); + foreach (var code in hashCodes) + hashCodeCounts.GetOrAddReference(code)++; + + var count = allTypes.Length; + + Log.Message($"allTypes: {count}, hashCodes: {hashCodeCounts.Count}, max: { + hashCodeCounts.Max(static pair => pair.Value)}{Environment.NewLine}"); + + var random = new Random(42); + var randomNumbers = new int[count]; + for (var i = 0; i < count; i++) + { + var randomNumber = random.Next(); + + while (randomNumber == 0) + randomNumber = random.Next(); + + randomNumbers[i] = randomNumber; + } + + var success = true; + + var fishTable = new FishTable(); + var addedEntries = 0; + + fishTable.EntryAdded += _ => addedEntries++; + for (var i = 0; i < count; i++) + { + Exception caughtException = null; + try + { + try + { + fishTable.GetOrAddReference(allTypes[i].TypeHandle.Value) = randomNumbers[i]; + + try + { + VerifyCount(fishTable, i + 1); + } + catch (Exception e) + { + caughtException = e; + + VerifyNumbers(randomNumbers, allTypeHandles, fishTable, i); + + throw; + } + VerifyNumbers(randomNumbers, allTypeHandles, fishTable, i); + } + catch (Exception e) + { + throw new($"Inserted number: {randomNumbers[i]}, Retrieved number: { + fishTable.GetOrAddReference(allTypes[i].TypeHandle.Value)} {e}\n\n{caughtException}"); + } + } + catch (Exception ex) + { + Log.Warning($"{ex}"); + Log.Message(Environment.NewLine); + success = false; + + // throw; + } + } + + var failureCount = 0; + + for (var i = 0; i < count; i++) + { + try + { + Assert.Equal(randomNumbers[i], fishTable.GetOrAddReference(allTypes[i].TypeHandle.Value)); + } + catch (Exception ex) + { + failureCount++; + + Log.Warning($"{ex}"); + Log.Message(Environment.NewLine); + success = false; + } + } + + Log.Message($"FailureCount: {failureCount}"); + + for (var i = 0; i < count; i++) + { + Assert.True(hashCodes[i].Equals(allTypes[i].TypeHandle.GetHashCode())); + } + + for (var i = 0; i < count; i++) + { + Assert.True(allTypeHandles[i].Equals(allTypes[i].TypeHandle)); + } + + Assert.True(success); + + VerifyCount(fishTable, allTypeHandles.Length); + + Assert.Equal(count, addedEntries); + } + + private static void VerifyNumbers(int[] expectedNumbers, RuntimeTypeHandle[] types, + FishTable fishTable, int count) + { + for (var i = 0; i < count; i++) + { + var number = expectedNumbers[i]; + var type = types[i]; + + try + { + Assert.True(number.Equals(fishTable[type.Value])); + } + catch (Exception e) + { + throw new($"number: {number}, hashCode: {type.GetHashCode()}, i: {i}, size: { + fishTable.Count} {e}"); + } + } + } + + private static void VerifyCount(FishTable fishTable, int expectedCount) + { + var realCount = 0; + var returnedCount = fishTable.Count; + + foreach (var _ in fishTable) + { + realCount++; + } + + if (realCount != returnedCount) + { + throw new($"Invalid count! real: {realCount}, returned: {returnedCount}, expected: { + expectedCount}"); + } + } + } +#endif + + + [Collection("Fish")] public class GetValue { public GetValue(ITestOutputHelper output) => InitializeLog(output); - private const int COUNT = 500; + private const int COUNT = 50000; public static IEnumerable Data - => new object[][] + => new[] { - new[] { nameof(_enum) }, - new[] { nameof(_nullableWithoutValue) }, - new[] { nameof(_nullableWithValue) }, - new[] { nameof(_primitive) }, - new[] { nameof(_reference) }, - new[] { nameof(CONST) } + new object[] { nameof(_enum) }, + new object[] { nameof(_nullableWithoutValue) }, + new object[] { nameof(_nullableWithValue) }, + new object[] { nameof(_primitive) }, + new object[] { nameof(_reference) }, + new object[] { nameof(CONST) } }; [Theory] @@ -48,9 +256,9 @@ public class ReflectionCaching object result = null; for (var i = 0; i < COUNT; i++) - System.ReflectionCaching.FieldInfoPatches.GetValue_Patch.Prefix(field, null, ref result); - - Assert.Equal(result, field.GetValue(null)); + System.ReflectionCaching.Test.GetValue(field, null, ref result); + + Assert.Equal(field.GetValue(null), result); } private static TypeCode _enum = TypeCode.Int32; @@ -62,6 +270,7 @@ public class ReflectionCaching private static Type _type = typeof(GetValue); } + [Collection("Fish")] public class SetValue { public SetValue(ITestOutputHelper output) => InitializeLog(output); @@ -69,7 +278,7 @@ public class ReflectionCaching private const int COUNT = 500; public static IEnumerable Data - => new object[][] + => new[] { new object[] { nameof(_enum), TypeCode.Byte }, new object[] { nameof(_nullableWithoutValue), 1 }, @@ -86,11 +295,87 @@ public class ReflectionCaching Assert.NotNull(field); for (var i = 0; i < COUNT; i++) - System.ReflectionCaching.FieldInfoPatches.SetValue_Patch.Prefix(field, null, input); + System.ReflectionCaching.Test.SetValue(field, null, input); Assert.Equal(input, field.GetValue(null)); } + [Fact] + public void Test2() + { + SimpleThingDefOf.Initialize(); + var def = SimpleThingDefOf.Instance; + + Assert.NotNull(def.Description); + a(def, "descriptionDetailedCached", null); + Assert.Null(def.Description); + + Assert.NotEqual(default, def.Label); + a(def, "cachedLabelCap", null); + Assert.Equal(default, def.Label); + } + + [Fact] + public void Test3() + { + for (var i = 0; i < COUNT; i++) + { + SimpleThingDefOf.Initialize(); + var def = SimpleThingDefOf.Instance; + + Assert.NotNull(def.Description); + a_Optimized(def, "descriptionDetailedCached", null); + Assert.Null(def.Description); + + Assert.NotEqual(default, def.Label); + a_Optimized(def, "cachedLabelCap", null); + Assert.Equal(default, def.Label); + } + } + + [Fact] + public void Test4() + { + for (var i = 0; i < 2; i++) + { + _primitive = 0; + var field = GetType().GetField(nameof(_primitive), AccessTools.all); + + IntPtr value = (nint)(-476); + + var failed = false; + try + { + if (i == 0) + field.SetValue(null, value); + else + System.ReflectionCaching.Test.SetValue(field, null, value); + } + catch (Exception e) + { + failed = true; + Log.Message(e + "\n"); + } + + Assert.True(failed); + + // Assert.Equal(-476, field.GetValue(null)); + } + } + + private static void a(object A_0, string A_1, object A_2) + => (A_0?.GetType().GetField(A_1, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) + ?.SetValue(A_0, A_2); + + private static void a_Optimized(object A_0, string A_1, object A_2) + { + if (A_0?.GetType().GetField(A_1, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + is { } field) + { + System.ReflectionCaching.Test.SetValue(field, A_0, A_2); + } + } + private static TypeCode _enum = TypeCode.Int32; private static int? _nullableWithoutValue = null; private static int? _nullableWithValue = 1; @@ -98,4 +383,138 @@ public class ReflectionCaching private static object _reference = new(); private static Type _type = typeof(SetValue); } + + public class SimpleThingDef + { + private string descriptionDetailedCached; + private TaggedString cachedLabelCap; + + public string Description => descriptionDetailedCached; + + public TaggedString Label + { + get => cachedLabelCap; + set => cachedLabelCap = value; + } + + public SimpleThingDef(string description, string label) + { + descriptionDetailedCached = description; + cachedLabelCap = label; + } + } + + public static class SimpleThingDefOf + { + public static SimpleThingDef Instance; + + public static void Initialize() => Instance = new("InitialDescription", "InitialLabel"); + } + + [Collection("Fish")] + public class Invoke + { + public Invoke(ITestOutputHelper output) => InitializeLog(output); + + private const int COUNT = 500; + + public static IEnumerable Data + => new[] + { + new object[] + { + AccessTools.Method(typeof(Scribe_Values), nameof(Scribe_Values.Look), + generics: new[] { typeof(string) }), + new object[] { "", "", "", false } + } + }; + + // [Theory] + // [MemberData(nameof(Data))] + // public void Test(MethodBase method, object[] input) + // { + // Assert.NotNull(method); + // + // object result = null; + // + // for (var i = 0; i < COUNT; i++) + // System.ReflectionCaching.Test.Invoke(method, null, input, ref result); + // } + + [Fact] + public void TestString() + { + var method = AccessTools.Method(GetType(), nameof(Look), generics: new[] { typeof(string) }); + + var value = "Input"; + var label = "Label"; + var defaultValue = "DefaultValue"; + var forceSave = true; + + var parameters = new object[] { value, label, defaultValue, forceSave }; + + var input = MakeString(value, label, defaultValue, forceSave); + + object output = null; + + System.ReflectionCaching.Test.Invoke(method, null, parameters, ref output); + + Assert.Equal(input, output); + Assert.Equal("Test", parameters[0]); + } + + [Fact] + public void TestIntOptimized() + { + var method = AccessTools.Method(GetType(), nameof(Look), generics: new[] { typeof(int) }); + + var value = 5; + var label = "Label"; + var defaultValue = 0; + var forceSave = false; + + var parameters = new object[] { value, label, defaultValue, forceSave }; + + var input = MakeString(value, label, defaultValue, forceSave); + + object output = null; + + System.ReflectionCaching.Test.Invoke(method, null, parameters, ref output); + + Assert.Equal(input, output); + Assert.Equal(17, parameters[0]); + } + + [Fact] + public void TestIntExpected() + { + var value = 5; + var label = "Label"; + var defaultValue = 0; + var forceSave = false; + + var input = MakeString(value, label, defaultValue, forceSave); + + var output = Look(ref value, label, defaultValue, forceSave); + + Assert.Equal(input, output); + Assert.Equal(17, value); + } + + private static string Look(ref T value, string label, T defaultValue = default, params object[] ctorArgs) + { + var input = MakeString(value, label, defaultValue, ctorArgs); + + if (typeof(string).IsAssignableFrom(typeof(T))) + value = (T)(object)"Test"; + else if (typeof(int).IsAssignableFrom(typeof(T))) + value = (T)(object)17; + + return input; + } + + private static string MakeString(T value, string label, T defaultValue, params object[] ctorArgs) + => $"value: {value}, label: {label}, defaultValue: {defaultValue}, ctorArgs: { + ctorArgs.ToStringSafeEnumerable()}"; + } } \ No newline at end of file diff --git a/Source/PerformanceFish/.editorconfig b/Source/PerformanceFish/.editorconfig index 14c2131..7178472 100644 --- a/Source/PerformanceFish/.editorconfig +++ b/Source/PerformanceFish/.editorconfig @@ -1,6 +1,6 @@ [*.cs] -file_header_template = Copyright (c) 2022 bradson\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at https://mozilla.org/MPL/2.0/. +file_header_template = Copyright (c) 2023 bradson\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at https://mozilla.org/MPL/2.0/. # CA2211: Non-constant fields should not be visible dotnet_diagnostic.CA2211.severity = none @@ -126,7 +126,7 @@ dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.severity = dotnet_naming_rule.types_should_be_pascal_case.symbols = types dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case diff --git a/Source/PerformanceFish/AccessToolsCaching.cs b/Source/PerformanceFish/AccessToolsCaching.cs new file mode 100644 index 0000000..80912db --- /dev/null +++ b/Source/PerformanceFish/AccessToolsCaching.cs @@ -0,0 +1,48 @@ +// 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 System.Linq; + +namespace PerformanceFish; + +public class AccessToolsCaching : ClassWithFishPatches +{ + public class AllTypes : FishPatch + { + public override string? Description { get; } = "Caches AccessTools.AllTypes to only recalculate when the " + + "loaded assembly count changes. Improves load times."; + + public override Delegate? TargetMethodGroup { get; } = AccessTools.AllTypes; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(ref IEnumerable __result, out bool __state) + { + var currentAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Length; + if (currentAssemblyCount != _cachedAssemblyCount) + { + _cachedAssemblyCount = currentAssemblyCount; + return __state = true; + } + + __result = _cachedTypes; + return __state = false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Postfix(ref IEnumerable __result, bool __state) + { + if (!__state) + return; + + UpdateCache(ref __result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(ref IEnumerable __result) => __result = _cachedTypes = __result.ToArray(); + + private static Type[] _cachedTypes = Type.EmptyTypes; + private static int _cachedAssemblyCount; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/AnalyzerFixes.cs b/Source/PerformanceFish/AnalyzerFixes.cs index 418f892..8dfe3b0 100644 --- a/Source/PerformanceFish/AnalyzerFixes.cs +++ b/Source/PerformanceFish/AnalyzerFixes.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Emit; using System.Xml; +// ReSharper disable InconsistentNaming namespace PerformanceFish; @@ -14,22 +15,27 @@ public static class AnalyzerFixes { public static void Patch() { - var modBase = AccessTools.Constructor(Reflection.Type("PerformanceAnalyzer", "Analyzer.Modbase"), new[] { typeof(ModContentPack) }); + var modBase = AccessTools.Constructor(Reflection.Type("PerformanceAnalyzer", "Analyzer.Modbase"), + new[] { typeof(ModContentPack) }); if (modBase is null) return /*false*/; - var success = PerformanceFishMod.Harmony.Patch(modBase, postfix: new(methodof(XmlParser.CollectXmlData))) != null; + var success = PerformanceFishMod.Harmony!.Patch(modBase, postfix: new(methodof(XmlParser.CollectXmlData))) + != null; - var methodTransplanting = Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.MethodTransplanting", "UpdateMethod", - new[] { typeof(Type), typeof(MethodInfo) }); + var methodTransplanting = Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.MethodTransplanting", + "UpdateMethod", + new[] { typeof(Type), typeof(MethodInfo) }); if (methodTransplanting is null - || PerformanceFishMod.Harmony.Patch(methodTransplanting, transpiler: new(methodof(UpdateMethod_Transpiler))) is null) + || PerformanceFishMod.Harmony.Patch(methodTransplanting, transpiler: new(methodof(UpdateMethod_Transpiler))) + is null) { success = false; } - var panelLogs = Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Panel_Logs", "RightClickDropDown"); + var panelLogs + = Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Panel_Logs", "RightClickDropDown"); if (panelLogs is null || PerformanceFishMod.Harmony.Patch(panelLogs, postfix: new(methodof(PanelLogs_Postfix))) is null) @@ -47,14 +53,22 @@ public static class AnalyzerFixes { yield return FishTranspiler.ArgumentAddress(method, "meth"); yield return FishTranspiler.Call(UpdateMethod_Prefix); + foreach (var code in codes) yield return code; } public static void UpdateMethod_Prefix(ref MethodInfo meth) { - if (AccessTools.EnumeratorMoveNext(meth) is { } moveNext) - meth = moveNext; + try + { + if (AccessTools.EnumeratorMoveNext(meth) is { } moveNext) + meth = moveNext; + } + catch (Exception ex) + { + Log.Error($"Exception thrown while trying to profile method {meth.FullDescription()}:\n{ex}"); + } } public static IEnumerable PanelLogs_Postfix(IEnumerable values, object log) @@ -64,6 +78,7 @@ public static class AnalyzerFixes foreach (var value in values) { yield return value; + if (IsValidMethodForOverriding(method) && !returned && value.Label == "Profile the internal methods of") { returned = true; @@ -75,7 +90,7 @@ public static class AnalyzerFixes private static bool IsValidMethodForOverriding([NotNullWhen(true)] MethodInfo? info) => info is not null - && (info.IsVirtual || info.IsAbstract || info.GetBaseDefinition().DeclaringType != info.DeclaringType); + && (info.IsVirtual || info.IsAbstract || info.GetBaseDefinition().DeclaringType != info.DeclaringType); public enum Category { @@ -86,12 +101,16 @@ public static class AnalyzerFixes Modder } - public static void PatchOverridesForMethod(MethodInfo method, Category category) + public static void PatchOverridesForMethod(MethodInfo? method, Category category) { + if (method is null) + return; + if (OverridingMethodUtility.PatchedOverrides.Contains(method)) { var signature = Utility_GetSignature(method, true); - Messages.Message("Have already patched overrides for the method " + signature, MessageTypeDefOf.CautionInput, false); + Messages.Message("Have already patched overrides for the method " + signature, + MessageTypeDefOf.CautionInput, false); Utility_Warn("Trying to repeat patching already profiled overrides of method - " + signature); } else @@ -141,7 +160,8 @@ public static class AnalyzerFixes foreach (var subClass in subClasses) { - var overridingMethod = Array.Find(subClass.GetMethods(AccessTools.allDeclared), m => m.GetBaseDefinition() == method.GetBaseDefinition()); + var overridingMethod = Array.Find(subClass.GetMethods(AccessTools.allDeclared), + m => m.GetBaseDefinition() == method.GetBaseDefinition()); if (overridingMethod is null || overridingMethod.IsAbstract) continue; @@ -159,55 +179,70 @@ public static class AnalyzerFixes public static AccessTools.FieldRef ProfileLog_meth => _profileLog_meth - ??= AccessTools.FieldRefAccess(Reflection.FieldInfo("PerformanceAnalyzer", "Analyzer.Profiling.ProfileLog", "meth")); + ??= AccessTools.FieldRefAccess(Reflection.FieldInfo("PerformanceAnalyzer", + "Analyzer.Profiling.ProfileLog", "meth")); + private static AccessTools.FieldRef? _profileLog_meth; public static Func Modbase_Harmony => _modbase_Harmony - ??= (Func)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Modbase", "get_Harmony") - .CreateDelegate(typeof(Func)); + ??= (Func)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Modbase", "get_Harmony")! + .CreateDelegate(typeof(Func)); + private static Func? _modbase_Harmony; public static Action GUIController_AddEntry => _guiController_AddEntry - ??= (Action)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.GUIController", "AddEntry") - .CreateDelegate(typeof(Action)); + ??= (Action)Reflection + .MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.GUIController", "AddEntry")! + .CreateDelegate(typeof(Action)); + private static Action? _guiController_AddEntry; public static Action GUIController_SwapToEntry => _guiController_SwapToEntry - ??= (Action)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.GUIController", "SwapToEntry") - .CreateDelegate(typeof(Action)); + ??= (Action)Reflection + .MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.GUIController", "SwapToEntry")! + .CreateDelegate(typeof(Action)); + private static Action? _guiController_SwapToEntry; public static Func Utility_GetSignature => _utility_GetSignature - ??= (Func)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Utility", "GetSignature") - .CreateDelegate(typeof(Func)); + ??= (Func)Reflection + .MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Utility", "GetSignature")! + .CreateDelegate(typeof(Func)); + private static Func? _utility_GetSignature; public static Func GUIController_CurrentCategory => _guiController_CurrentCategory - ??= (Func)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.GUIController", "get_CurrentCategory") - .CreateDelegate(typeof(Func)); + ??= (Func)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.GUIController", + "get_CurrentCategory")! + .CreateDelegate(typeof(Func)); + private static Func? _guiController_CurrentCategory; public static Action Utility_Warn => _utility_Warn - ??= (Action)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Utility", "Warn") - .CreateDelegate(typeof(Action)); + ??= (Action)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Utility", "Warn")! + .CreateDelegate(typeof(Action)); + private static Action? _utility_Warn; public static Action Utility_ReportException => _utility_ReportException - ??= (Action)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Utility", "ReportException") - .CreateDelegate(typeof(Action)); + ??= (Action)Reflection + .MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Utility", "ReportException")! + .CreateDelegate(typeof(Action)); + private static Action? _utility_ReportException; public static Action Utility_Error => _utility_Error - ??= (Action)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Utility", "Error") - .CreateDelegate(typeof(Action)); + ??= (Action)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Utility", "Error")! + .CreateDelegate(typeof(Action)); + private static Action? _utility_Error; public static CodeInstructions Parse_Transpiler(CodeInstructions CodeInstructions) @@ -222,7 +257,9 @@ public static class AnalyzerFixes { yield return new(codes[i - 2]); //Ldloc xmlNode yield return FishTranspiler.Call(ParseMethod_Fixed); - yield return FishTranspiler.Call, IEnumerable>>(GenCollection.AddRange); + yield return FishTranspiler.Call, IEnumerable>>(GenCollection + .AddRange); + i += 2; //removing OpCodes.Pop } else if (codes[i] == errorMethod) @@ -239,24 +276,41 @@ public static class AnalyzerFixes public static IEnumerable ParseMethod_Fixed(string info, XmlNode node) => node.NodeType == XmlNodeType.Comment - ? Enumerable.Empty() - : ParseMethod(info); + ? Enumerable.Empty() + : ParseMethod(info); private static IEnumerable ParseMethod(string info) { var array = info.Split(':'); if (array.Length != 2) - throw new ArgumentException($"Method must be specified as 'Namespace.Type1.Type2:MethodName. Got {info} instead.", nameof(info)); + { + throw new ArgumentException($"Method must be specified as 'Namespace.Type1.Type2:MethodName. Got { + info} instead.", nameof(info)); + } var methods = Reflection.MethodsOfName(AccessTools.TypeByName(array[0]), array[1]); foreach (var method in methods) { yield return method.IsGenericMethodDefinition - ? method.MakeGenericMethod(method.GetGenericArguments().Select(t => t.GetGenericParameterConstraints() is var constraints2 && constraints2.Length != 0 ? constraints2[0] : typeof(object)).ToArray()) + ? method.MakeGenericMethod(GetTypeArguments(method)) : method; } } + private static Type[] GetTypeArguments(MethodBase method) + { + var genericArguments = method.GetGenericArguments(); + var resultArray = new Type[genericArguments.Length]; + for (var i = 0; i < genericArguments.Length; i++) + { + resultArray[i] = genericArguments[i].GetGenericArguments() is var constraints && constraints.Length != 0 + ? constraints[0] + : typeof(object); + } + + return resultArray; + } + public static void ErrorFix(string message, XmlNode node) { if (node.NodeType == XmlNodeType.Comment) @@ -267,29 +321,40 @@ public static class AnalyzerFixes public static Action ThreadSafeLogger_Error => _threadSafeLogger_Error - ??= (Action)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.ThreadSafeLogger", "Error") - .CreateDelegate(typeof(Action)); + ??= (Action)Reflection + .MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.ThreadSafeLogger", "Error")! + .CreateDelegate(typeof(Action)); + private static Action? _threadSafeLogger_Error; public static Func, Type> DynamicTypeBuilder_CreateType => _dynamicTypeBuilder_CreateType - ??= (Func, Type>)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.DynamicTypeBuilder", "CreateType") - .CreateDelegate(typeof(Func, Type>)); + ??= (Func, Type>)Reflection.MethodInfo("PerformanceAnalyzer", + "Analyzer.Profiling.DynamicTypeBuilder", "CreateType")! + .CreateDelegate(typeof(Func, Type>)); + private static Func, Type>? _dynamicTypeBuilder_CreateType; public static Func> Utility_GetTypeMethods => _utility_GetTypeMethods - ??= (Func>)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Utility", "GetTypeMethods") - .CreateDelegate(typeof(Func>)); + ??= (Func>)Reflection.MethodInfo("PerformanceAnalyzer", + "Analyzer.Profiling.Utility", "GetTypeMethods")! + .CreateDelegate(typeof(Func>)); + private static Func>? _utility_GetTypeMethods; public static AccessTools.FieldRef Entry_tip => _entry_tip - ??= AccessTools.FieldRefAccess(Type.GetType("Analyzer.Profiling.Entry, PerformanceAnalyzer"), "tip"); + ??= AccessTools.FieldRefAccess(Type.GetType("Analyzer.Profiling.Entry, PerformanceAnalyzer"), + "tip"); + private static AccessTools.FieldRef? _entry_tip; - public static Action GUIController_AddSpecificEntry => _guiController_AddSpecificEntry ??= MakeGUIController_AddEntryMethod(); + public static Action GUIController_AddSpecificEntry + => _guiController_AddSpecificEntry ??= MakeGUIController_AddEntryMethod(); + private static Action? _guiController_AddSpecificEntry; + private static Action MakeGUIController_AddEntryMethod() { var analyzer_Profiling_EntryType = Reflection.Type("PerformanceAnalyzer", "Analyzer.Profiling.Entry"); @@ -312,17 +377,19 @@ public static class AnalyzerFixes if (entry_Create == null) Log.Error("entry_Create is null");*/ - var dictionary_Add = AccessTools.Method(typeof(Dictionary<,>).MakeGenericType(new[] { analyzer_Profiling_EntryType, typeof(Type) }), - "Add", new[] { analyzer_Profiling_EntryType, typeof(Type) }); + var dictionary_Add = AccessTools.Method( + typeof(Dictionary<,>).MakeGenericType(new[] { analyzer_Profiling_EntryType, typeof(Type) }), + "Add", new[] { analyzer_Profiling_EntryType, typeof(Type) }); if (dictionary_Add == null) Log.Error("dictionary_Add is null"); - var dynamicMethod = new DynamicMethod("GUIController_AddEntryMethod", typeof(void), new[] { typeof(int), typeof(object), typeof(Type) }); + var dynamicMethod = new DynamicMethod("GUIController_AddEntryMethod", typeof(void), + new[] { typeof(int), typeof(object), typeof(Type) }); var il = dynamicMethod.GetILGenerator(); - il.Emit(FishTranspiler.Argument(0)); //Category + il.Emit(FishTranspiler.Argument(0)); //Category il.Emit(FishTranspiler.Call(gUIController_Tab!)); //Tab(Category) - il.Emit(FishTranspiler.Field(tab_entries!)); //tab.entries + il.Emit(FishTranspiler.Field(tab_entries!)); //tab.entries /*il.Emit(FishTranspiler.Argument(2)); //type il.Emit(FishTranspiler.Call(memberInfo_Name)); //type.Name il.Emit(FishTranspiler.LoadConstant(4)); //Category.Modder @@ -330,16 +397,20 @@ public static class AnalyzerFixes il.Emit(FishTranspiler.LoadConstant(0)); //closeable = false il.Emit(FishTranspiler.LoadConstant(1)); //dynGen = true il.Emit(FishTranspiler.Call(entry_Create)); //Create(string, Category, Type, bool, bool)*/ - il.Emit(FishTranspiler.Argument(1)); //entry - il.Emit(FishTranspiler.Argument(2)); //type + il.Emit(FishTranspiler.Argument(1)); //entry + il.Emit(FishTranspiler.Argument(2)); //type il.Emit(FishTranspiler.Call(dictionary_Add!)); //entries.Add(Entry, Type) il.Emit(FishTranspiler.Return); return (Action)dynamicMethod.CreateDelegate(typeof(Action)); } - public static Func Entry_Create => _entry_Create - ??= (Func)Reflection.MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Entry", "Create").CreateDelegate(typeof(Func)); + public static Func Entry_Create + => _entry_Create + ??= (Func)Reflection + .MethodInfo("PerformanceAnalyzer", "Analyzer.Profiling.Entry", "Create")! + .CreateDelegate(typeof(Func)); + private static Func? _entry_Create; //Copied from Wiri's analyzer fork, with above fixes integrated. The steam version lacks this functionality @@ -350,72 +421,93 @@ public static class AnalyzerFixes foreach (var dir in ModLister.AllActiveModDirs) { var xmlFiles = dir.GetFiles("DubsAnalyzer.xml"); - if (xmlFiles.Length != 0) - { - foreach (var file in xmlFiles) - { - var doc = new XmlDocument(); - doc.Load(file.OpenRead()); + if (xmlFiles.Length == 0) + continue; - Parse(doc); - } + foreach (var file in xmlFiles) + { + var doc = new XmlDocument(); + doc.Load(file.OpenRead()); + + Parse(doc); } } } - // Iterates through each child element in the document and attempts to extract method(s) from the strings inside the children private static void Parse(XmlDocument doc) - => ForEach(doc.DocumentElement.ChildNodes, node => + => ForEach(doc.DocumentElement!.ChildNodes, ParseChildNodes); + + private static void ParseChildNodes(XmlNode node) + { + var methods = new HashSet(); + var tip = string.Empty; + var category = (int)Category.Modder; + + ForEach(node.ChildNodes, child => { - var methods = new HashSet(); - var tip = string.Empty; - var category = (int)Category.Modder; - ForEach(node.ChildNodes, child => + switch (child.Name.ToUpperInvariant()) { - switch (child.Name.ToLowerInvariant()) - { - case "category": - category = (int)Enum.Parse(typeof(Category), child.InnerText, true); //child.InnerText; - break; - case "tooltip": - case "tip": - case "description": - tip += child.InnerText; - break; - case "methods": - case "method": - AddMethodsFromChildNodes(child, methods, ParseMethod); - break; - case "derivedmethods": - AddMethodsFromChildNodes(child, methods, ParseDerivedMethods); - break; - case "types": - case "type": - AddMethodsFromChildNodes(child, methods, ParseTypeMethods); - break; - case "derivedtypes": - AddMethodsFromChildNodes(child, methods, ParseDerivedTypeMethods); - break; - case "nestedtype": - case "nestedtypes": - AddMethodsFromChildNodes(child, methods, ParseNestedTypeMethods); - break; - default: - ThreadSafeLogger_Error($"[Analyzer] Attempting to read unknown value from a DubsAnalyzer.xml, the given input was {child.Name}, it should have been either 'methods', 'types', 'derivedMethods', 'derivedTypes', 'nestedTypes', 'category' or 'tooltip'"); - break; - } - }); - - var type = DynamicTypeBuilder_CreateType(node.Name, methods); - var entry = Entry_Create(node.Name, category, type, false, true); - if (tip != string.Empty) - Entry_tip(entry) = tip; - GUIController_AddSpecificEntry(category, entry, type); + case "CATEGORY": + category = (int)Enum.Parse(typeof(Category), child.InnerText, true); + break; + case "TOOLTIP": + case "TIP": + case "DESCRIPTION": + tip += child.InnerText; + break; + case "METHODS": + case "METHOD": + AddMethodsFromChildNodes(child, methods, ParseMethod); + break; + case "DERIVEDMETHODS": + AddMethodsFromChildNodes(child, methods, ParseDerivedMethods); + break; + case "TYPES": + case "TYPE": + AddMethodsFromChildNodes(child, methods, ParseTypeMethods); + break; + case "DERIVEDTYPES": + AddMethodsFromChildNodes(child, methods, ParseDerivedTypeMethods); + break; + case "NESTEDTYPE": + case "NESTEDTYPES": + AddMethodsFromChildNodes(child, methods, ParseNestedTypeMethods); + break; + default: + ThreadSafeLogger_Error("[Analyzer] Attempting to read unknown value from a DubsAnalyzer.xml, " + + $"the given input was {child.Name}, it should have been either 'methods', 'types', " + + "'derivedMethods', 'derivedTypes', 'nestedTypes', 'category' or 'tooltip'"); + break; + } }); + + var type = DynamicTypeBuilder_CreateType(node.Name, methods); + + var entry = Entry_Create(node.Name, category, type, false, true); + if (tip != string.Empty) + Entry_tip(entry) = tip; - private static void AddMethodsFromChildNodes(XmlNode node, HashSet methods, Func> func) - => ForEach(node.ChildNodes, childNode - => methods.AddRange(func(childNode.InnerText))); + GUIController_AddSpecificEntry(category, entry, type); + } + + private static void AddMethodsFromChildNodes(XmlNode node, HashSet methods, + Func> func) + { + var nodes = node.ChildNodes; + + foreach (XmlNode childNode in nodes) + { + if (MissingRequiredMod(childNode.Attributes)) + continue; + + if (childNode.NodeType != XmlNodeType.Comment) + methods.AddRange(func(childNode.InnerText)); + } + } + + private static bool MissingRequiredMod(XmlAttributeCollection? attributes) + => attributes?["MayRequire"] is { } mayRequireAttribute + && ModLister.GetActiveModWithIdentifier(mayRequireAttribute.Value, true) is null; private static void ForEach(XmlNodeList nodes, Action action) { @@ -434,7 +526,8 @@ public static class AnalyzerFixes foreach (var subClass in subClasses) { - var overridingMethod = Array.Find(subClass.GetMethods(AccessTools.allDeclared), m => m.GetBaseDefinition() == method.GetBaseDefinition()); + var overridingMethod = Array.Find(subClass.GetMethods(AccessTools.allDeclared), + m => m.GetBaseDefinition() == method.GetBaseDefinition()); if (overridingMethod is null || overridingMethod.IsAbstract) continue; @@ -443,7 +536,8 @@ public static class AnalyzerFixes } } - private static IEnumerable ParseTypeMethods(string str) => Utility_GetTypeMethods(AccessTools.TypeByName(str), false); + private static IEnumerable ParseTypeMethods(string str) + => Utility_GetTypeMethods(AccessTools.TypeByName(str), false); private static IEnumerable ParseNestedTypeMethods(string str) { diff --git a/Source/PerformanceFish/Cache/ByIndex.cs b/Source/PerformanceFish/Cache/ByIndex.cs index 2de54f8..b50b7e3 100644 --- a/Source/PerformanceFish/Cache/ByIndex.cs +++ b/Source/PerformanceFish/Cache/ByIndex.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -9,15 +9,21 @@ using System.Linq; namespace PerformanceFish.Cache; public class ByIndex : IList, ICollection { - private static ByIndex _get = new(); + private static ByIndex _get = Utility.AddNew>(); [ThreadStatic] private static ByIndex? _getThreadStatic; public static ByIndex Get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew>(); + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew>(); + } - public static ByIndex GetDirectly => _get; + public static ByIndex GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } static ByIndex() => Utility.All.Add(_get); @@ -112,15 +118,21 @@ public class ByIndex : IList, ICollection public class ByIndex : ByIndex where T_in : notnull { - private static ByIndex _get = new(); + private static ByIndex _get = Utility.AddNew>(); [ThreadStatic] private static ByIndex? _getThreadStatic; public new static ByIndex Get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew>(); + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew>(); + } - public new static ByIndex GetDirectly => _get; + public new static ByIndex GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } static ByIndex() => Utility.All.Add(_get); @@ -142,7 +154,6 @@ public class ByIndex : ByIndex [return: MaybeNull] public unsafe ref T_out GetReference(T_in key) => ref GetReference(FunctionPointers.IndexGetter.Default(key))!; - } /// @@ -151,15 +162,21 @@ public class ByIndex : ByIndex public class ByIndex : ByIndex> where T_first : notnull where T_second : notnull { - private static ByIndex _get = new(); + private static ByIndex _get = Utility.AddNew>(); [ThreadStatic] private static ByIndex? _getThreadStatic; public new static ByIndex Get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew>(); + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew>(); + } - public new static ByIndex GetDirectly => _get; + public new static ByIndex GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } static ByIndex() => Utility.All.Add(_get); @@ -179,7 +196,7 @@ public class ByIndex : ByIndex - where T_in : notnull - where T_result : IIsRefreshable + +#pragma warning disable CS9091 +[PublicAPI] +public record struct ByInt where T : notnull where TResult : new() { - private static Dictionary, T_result> _get = new(); + private static FishTable, TResult> _get + = Utility.AddNew, TResult>(); + [ThreadStatic] - private static Dictionary, T_result>? _getThreadStatic; + private static FishTable, TResult>? _getThreadStatic; - public static Dictionary, T_result> Get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew, T_result>>(); + public static FishTable, TResult> Get + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew, TResult>(); + } - public static Dictionary, T_result> GetDirectly => _get; + public static FishTable, TResult> GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } - static ByInt() => Utility.All.Add(_get); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(int key) + => ref Get.GetOrAddReference(Unsafe.As>(ref key)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(ByInt key) => ref Get.GetOrAddReference(key); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(ref ByInt key) => ref Get.GetOrAddReference(ref key); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TResult GetExistingReference(int key) + => ref Get.GetReference(Unsafe.As>(ref key)); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TResult GetExistingReference(T key) => ref Get.GetReference(new(key)); public int Key; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe ByInt(T_in key) => Key = FunctionPointers.IndexGetter.Default(key); + public unsafe ByInt(T key) => Key = FunctionPointers.IndexGetter.Default(key); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T_result GetValue(T_in key) - { - ref var cache = ref Get.TryGetReferenceUnsafe(new(key)); - - if (Unsafe.IsNullRef(ref cache)) - { - Get[new(key)] = Reflection.New(); - cache = ref Get.GetReference(new(key))!; - goto RefreshCache; - } - - if (!cache.ShouldRefreshNow) - return ref cache!; - - RefreshCache: - cache.ShouldRefreshNow = false; - Get[new(key)] = cache.SetNewValue(key); - - return ref cache!; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static unsafe ref T_result TryGetReferenceUnsafe(int key) - => ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As>(ref key)))); - - [MethodImpl(MethodImplOptions.NoInlining)] - public static unsafe ref T_result TryGetReferenceChecked(int key, out bool result) - { - ref var value = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As>(ref key)))); - result = !Unsafe.IsNullRef(ref value) && !value.ShouldRefreshNow; - return ref value!; - } + public ByInt(int key) => Key = key; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGetValue(T_in key, [MaybeNullWhen(false)] out T_result value) - => Get.TryGetValue(new(key), out value) - && !value.ShouldRefreshNow; + public bool Equals(ByInt other) => Key == other.Key; - public static T_result GetValueAndRefreshNow(T_in key) - { - var cache = Get.TryGetValue(new(key)); - cache ??= Reflection.New(); - cache.ShouldRefreshNow = false; - Get[new(key)] = cache.SetNewValue(key); - return cache; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => Key; } -public struct ByIntRefreshable : IEquatable> - where T_first : notnull where T_second : notnull - where T_result : IIsRefreshable, T_result> +[PublicAPI] +public record struct ByInt + where T1 : notnull where T2 : notnull where TResult : new() { - private static EqualityComparer> _comparer = new Comparer(); - private class Comparer : EqualityComparer> - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(ByIntRefreshable x, ByIntRefreshable y) - => x.First == y.First - && x.Second == y.Second; + private static FishTable, TResult> _get + = Utility.AddNew, TResult>(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(ByIntRefreshable obj) - => HashCode.Combine(obj.First, obj.Second); - } - - private static Dictionary, T_result> _get = new(_comparer); [ThreadStatic] - private static Dictionary, T_result>? _getThreadStatic; + private static FishTable, TResult>? _getThreadStatic; - public static Dictionary, T_result> Get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew, T_result>>(_comparer); - - public static Dictionary, T_result> GetDirectly => _get; - - static ByIntRefreshable() => Utility.All.Add(_get); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ref T_result GetValue(ref ByIntRefreshable key) + public static FishTable, TResult> Get { - // CS8347 without this crap - ref var cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key))); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew, TResult>(); + } - if (Unsafe.IsNullRef(ref cache)) + private static ref FishTable, TResult> GetCacheRef + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - Get[key] = Reflection.New(); - cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!; - goto RefreshCache; + _getThreadStatic ??= Utility.AddNew, TResult>(); + return ref _getThreadStatic!; } - - if (!cache.ShouldRefreshNow) - return ref cache!; - - RefreshCache: - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - - return ref cache!; } + public static FishTable, TResult> GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(int first, int second) + => ref Get.GetOrAddReference(new() { First = first, Second = second }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(in ByInt key) + => ref Get.GetOrAddReference(ref Unsafe.AsRef(in key)); + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static ref TResult GetOrAddReference(long key) // different, worse, GetHashCode method + // => ref Unsafe.As, TResult>, FishTable>(ref GetCacheRef) + // .GetOrAddReference(key); + [MethodImpl(MethodImplOptions.NoInlining)] - public static unsafe ref T_result TryGetReferenceUnsafe(int second, int first) - => ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As>(ref first)))); + public static ref TResult GetExistingReference(int first, int second) + => ref Get.GetReference(new() { First = first, Second = second }); [MethodImpl(MethodImplOptions.NoInlining)] - public static unsafe ref T_result TryGetReferenceChecked(int second, int first, out bool result) + public static ref TResult GetExistingReference(T1 first, T2 second) + => ref Get.GetReference(new(first, second)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe ByInt(T1 first, T2 second) { - ref var value = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As>(ref first)))); - result = !Unsafe.IsNullRef(ref value) && !value.ShouldRefreshNow; - return ref value!; + First = FunctionPointers.IndexGetter.Default(first); + Second = FunctionPointers.IndexGetter.Default(second); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGetValue(ByIntRefreshable key, [MaybeNullWhen(false)] out T_result value) - => Get.TryGetValue(key, out value) - && !value.ShouldRefreshNow; - - public static T_result GetValueAndRefreshNow(ByIntRefreshable key) - { - var cache = Get.TryGetValue(ref key); - cache ??= Reflection.New(); - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - return cache; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe ByIntRefreshable(T_first first, T_second second) - { - First = FunctionPointers.IndexGetter.Default(first); - Second = FunctionPointers.IndexGetter.Default(second); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ByIntRefreshable(int first, int second) + public ByInt(int first, int second) { First = first; Second = second; } - - public int First; - public int Second; + + public int First, Second; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ByIntRefreshable other) - => First == other.First - && Second == other.Second; + public bool Equals(ByInt other) + => Unsafe.As, long>(ref this) == Unsafe.As, long>(ref other); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - => HashCode.Combine(First, Second); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - => obj is ByIntRefreshable cache - && Equals(cache); - - public static bool operator ==(ByIntRefreshable left, ByIntRefreshable right) - => left.Equals(right); - - public static bool operator !=(ByIntRefreshable left, ByIntRefreshable right) - => !(left == right); + public override int GetHashCode() => HashCode.Combine(First, Second); } -public struct ByInt : IEquatable> - where T_first : notnull where T_second : notnull where T_third : notnull - where T_result : IIsRefreshable, T_result> +[PublicAPI] +public record struct ByInt + where T1 : notnull where T2 : notnull where T3 : notnull where TResult : new() { - private static Dictionary, T_result> _get = new(); + private static FishTable, TResult> _get + = Utility.AddNew, TResult>(); + [ThreadStatic] - private static Dictionary, T_result>? _getThreadStatic; + private static FishTable, TResult>? _getThreadStatic; - public static Dictionary, T_result> Get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew, T_result>>(); + public static FishTable, TResult> Get + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew, TResult>(); + } - public static Dictionary, T_result> GetDirectly => _get; - - static ByInt() => Utility.All.Add(_get); + public static FishTable, TResult> GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ref T_result GetValue(ByInt key) + public static unsafe ref TResult GetOrAddReference(int first, int second, int third) { - ref var cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key))); + var key = new ByInt { First = first, Second = second, Third = third }; + return ref Get.GetOrAddReference(ref key); + } - if (Unsafe.IsNullRef(ref cache)) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(in ByInt key) + => ref Get.GetOrAddReference(ref Unsafe.AsRef(in key)); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TResult GetExistingReference(int first, int second, int third) + => ref Get.GetReference(new() { First = first, Second = second, Third = third }); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TResult GetExistingReference(T1 first, T2 second, T3 third) + => ref Get.GetReference(new(first, second, third)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe ByInt(T1 first, T2 second, T3 third) + { + First = FunctionPointers.IndexGetter.Default(first); + Second = FunctionPointers.IndexGetter.Default(second); + Third = FunctionPointers.IndexGetter.Default(third); + } + + public int First, Second, Third; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(ByInt other) + => (Unsafe.As, long>(ref this) + == Unsafe.As, long>(ref other)) + & (Third == other.Third); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => HashCode.Combine(First, Second, Third); +} + +[PublicAPI] +public record struct ByInt + where T1 : notnull where T2 : notnull where T3 : notnull where T4 : notnull + where TResult : new() +{ + private static FishTable, TResult> _get + = Utility.AddNew, TResult>(); + + [ThreadStatic] + private static FishTable, TResult>? _getThreadStatic; + + public static FishTable, TResult> Get + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew, TResult>(); + } + + public static FishTable, TResult> GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref TResult GetOrAddReference(int first, int second, int third, int fourth) + { + var key = new ByInt { - Get[key] = Reflection.New(); - cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!; - goto RefreshCache; - } - - if (!cache.ShouldRefreshNow) - return ref cache!; - - RefreshCache: - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - - return ref cache!; + First = first, Second = second, Third = third, Fourth = fourth + }; + return ref Get.GetOrAddReference(ref key); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(in ByInt key) + => ref Get.GetOrAddReference(ref Unsafe.AsRef(in key)); + [MethodImpl(MethodImplOptions.NoInlining)] - public static unsafe ref T_result TryGetReferenceUnsafe(int second, int first, int third) - => ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As>(ref first)))); + 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 }); [MethodImpl(MethodImplOptions.NoInlining)] - public static unsafe ref T_result TryGetReferenceChecked(int second, int first, out bool result) + public static ref TResult GetExistingReference(T1 first, T2 second, T3 third, T4 fourth) + => ref Get.GetReference(new(first, second, third, fourth)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe ByInt(T1 first, T2 second, T3 third, T4 fourth) { - ref var value = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As>(ref first)))); - result = !Unsafe.IsNullRef(ref value) && !value.ShouldRefreshNow; - return ref value!; + First = FunctionPointers.IndexGetter.Default(first); + Second = FunctionPointers.IndexGetter.Default(second); + Third = FunctionPointers.IndexGetter.Default(third); + Fourth = FunctionPointers.IndexGetter.Default(fourth); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGetValue(ByInt key, out T_result value) - => Get.TryGetValue(key, out value) - && !value.ShouldRefreshNow; - - public static T_result GetValueAndRefreshNow(ByInt key) - { - var cache = Get.TryGetValue(ref key); - cache ??= Reflection.New(); - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - return cache; - } + public int First, Second, Third, Fourth; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe ByInt(T_first first, T_second second, T_third third) - { - First = FunctionPointers.IndexGetter.Default(first); - Second = FunctionPointers.IndexGetter.Default(second); - Third = FunctionPointers.IndexGetter.Default(third); - } - - public int First; - public int Second; - public int Third; + public bool Equals(ByInt other) + => (Unsafe.As, long>(ref this) + == Unsafe.As, long>(ref other)) + & (Unsafe.As(ref Third) == Unsafe.As(ref other.Third)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ByInt other) - => First == other.First - && Second == other.Second - && Third == other.Third; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - => HashCode.Combine(First, Second, Third); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - => obj is ByInt cache - && Equals(cache); - - public static bool operator ==(ByInt left, ByInt right) - => left.Equals(right); - - public static bool operator !=(ByInt left, ByInt right) - => !(left == right); -} \ No newline at end of file + public override int GetHashCode() => HashCode.Combine(First, Second, Third, Fourth); +} +#pragma warning restore CS9091 \ No newline at end of file diff --git a/Source/PerformanceFish/Cache/ByMap.cs b/Source/PerformanceFish/Cache/ByMap.cs index 0eea91d..ed800bc 100644 --- a/Source/PerformanceFish/Cache/ByMap.cs +++ b/Source/PerformanceFish/Cache/ByMap.cs @@ -1,46 +1,90 @@ -// Copyright (c) 2022 bradson +// 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.Cache; + public static class ByMap where T : new() { - private static List<(Map map, T value)> _cache = new(); + private static List<(Map map, T value)> _cache = Utility.AddNew>(); + [ThreadStatic] private static List<(Map map, T value)>? _cacheThreadStatic; public static List<(Map map, T value)> GetCache - => UnityData.IsInMainThread ? _cache - : _cacheThreadStatic ??= Utility.AddNew>(); + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _cacheThreadStatic ??= Utility.AddNew>(); + } - public static List<(Map map, T value)> GetCacheDirectly => _cache; + public static List<(Map map, T value)> GetCacheDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _cache; + } static ByMap() => Utility.All.Add(_cache); private static Func Initialize { get; } = typeof(MapComponent).IsAssignableFrom(typeof(T)) - ? AccessTools.MethodDelegate>(AccessTools.Method(typeof(Map), nameof(Map.GetComponent), Array.Empty(), new[] { typeof(T) })) - // missing T constraints cause the compiler to complain when trying to just use map => map.GetComponent - : _ => new(); + ? AccessTools.MethodDelegate>(AccessTools.Method(typeof(Map), nameof(Map.GetComponent), + Type.EmptyTypes, new[] { typeof(T) })) + // missing T constraints cause the compiler to complain when trying to just use map => map.GetComponent + : static _ => new(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T GetFor(Map map) { var cache = GetCache; for (var i = 0; i < cache.Count; i++) { - var cacheEntry = cache[i]; + ref var cacheEntry = ref cache.GetReferenceUnverifiable(i); if (cacheEntry.map == map) return cacheEntry.value; - if (Find.Maps.Count <= i + 1 - && !Find.Maps.Contains(cacheEntry.map)) - { - cache.RemoveAt(i); - i--; - } + if (EntryValid(i, cacheEntry.map)) + continue; + + cache.RemoveAt(i); + i--; } + + return AddEntry(map, cache); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T GetReferenceFor(Map map) + { + var cache = GetCache; + for (var i = 0; i < cache.Count; i++) + { + ref var cacheEntry = ref cache.GetReferenceUnverifiable(i); + if (cacheEntry.map == map) + return ref cacheEntry.value; + + if (EntryValid(i, cacheEntry.map)) + continue; + + cache.RemoveAt(i); + i--; + } + + return ref AddEntryByRef(map, cache); + } + + private static bool EntryValid(int i, Map map) + => Find.Maps.Count > i && Find.Maps.Contains(map); + + private static T AddEntry(Map map, List<(Map map, T value)> cache) + { cache.Add((map, Initialize(map))); return cache[^1].value; } + + private static ref T AddEntryByRef(Map map, List<(Map map, T value)> cache) + { + cache.Add((map, Initialize(map))); + return ref cache.GetReferenceUnverifiable(cache.Count - 1).value; + } } \ No newline at end of file diff --git a/Source/PerformanceFish/Cache/ByReference.cs b/Source/PerformanceFish/Cache/ByReference.cs index 3c741af..15e3138 100644 --- a/Source/PerformanceFish/Cache/ByReference.cs +++ b/Source/PerformanceFish/Cache/ByReference.cs @@ -1,64 +1,540 @@ -// Copyright (c) 2022 bradson +// 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 System.Runtime.InteropServices; +using System.Security; +using System.Threading.Tasks; +using JetBrains.Annotations; +// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + namespace PerformanceFish.Cache; -public static class ByReference - where T_first : notnull +#pragma warning disable CS9091, 9082 +[PublicAPI] +public static class ByReferenceUnclearable + where T : notnull where TResult : new() { - private static Dictionary _get = new(); - [ThreadStatic] - private static Dictionary? _getThreadStatic; + public const int MINIMUM_CALL_COUNT_FOR_STARTING_TASK = 8; + + public static object SyncLock = new(); - public static Dictionary Get + private static FishTable _get = Utility.AddNewUnclearable(); + + [ThreadStatic] + private static FishTable? _getThreadStatic; + + public static FishTable Get { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew>(); + get => _getThreadStatic ??= Utility.AddNewUnclearable(); } - public static Dictionary GetDirectly => _get; + public static FishTable GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } - static ByReference() => Utility.All.Add(_get); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(T key) => ref Get.GetOrAddReference(key); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(ref T key) => ref Get.GetOrAddReference(ref key); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TResult GetExistingReference(T key) => ref Get.GetReference(key); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Update(T key) where VResult : TResult, ICacheable + => Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Update(T key, V2 second) where VResult : TResult, ICacheable + => Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Update(T key, V2 second, V3 third) + where VResult : TResult, ICacheable + => Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref VResult GetAndCheck(T key) where VResult : TResult, ICacheable + { + ref var cache = ref Unsafe.As(ref GetOrAddReference(key)); + if (cache.Dirty) + cache.Update(ref key); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref VResult GetAndCheck(T key, V2 second) where VResult : TResult, ICacheable + { + ref var cache = ref Unsafe.As(ref GetOrAddReference(key)); + if (cache.Dirty) + cache.Update(ref key, second); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref VResult GetAndCheck(T key, V2 second, V3 third) + where VResult : TResult, ICacheable + { + ref var cache = ref Unsafe.As(ref GetOrAddReference(key)); + if (cache.Dirty) + cache.Update(ref key, second, third); + + return ref cache!; + } + + public static bool UpdateAsyncCache(ref TCacheValue cache, T key) + where TCacheValue : TResult, IAsyncCacheable + { + if (cache.Task is null) + return SyncWithCentralCache(ref cache, key); + + if (cache.Task.IsCompletedSuccessfully) + cache.Result = cache.Task.Result; + + return !cache.Result.Equals(default); + } + + private static bool SyncWithCentralCache(ref TCacheValue cache, T key) + where TCacheValue : TResult, IAsyncCacheable + { + lock (SyncLock) + { + ref var centralCache = ref Unsafe.As(ref GetDirectly.GetOrAddReference(key)); + var cacheCopy = centralCache; + + centralCache.Task ??= Task.Run(() => UpdateCacheAsync(key, cacheCopy)); + + cache.Task = centralCache.Task; + cache.Result = centralCache.Result; + return !cache.Result.Equals(default); + } + } + + private static TCacheResult UpdateCacheAsync(T key, TCacheValue cacheCopy) + where TCacheValue : TResult, IAsyncCacheable + { + TCacheResult? result = default; + try + { + result = cacheCopy.MakeResultAsync(key); + + lock (SyncLock) + Unsafe.As(ref GetDirectly.GetOrAddReference(key)).Result = result; + } + catch (Exception e) + { + Log.Error($"{e}"); + } + + return result ?? throw new(); + } + + [SecuritySafeCritical] + public static Task? RequestFromCacheAsync(T key, TArgument2 second) + where TCacheValue : TResult, IAsyncCacheable + { + TCacheValue cacheCopy; + lock (SyncLock) + { + ref var centralCache = ref Unsafe.As(ref GetDirectly.GetOrAddReference(key)); + cacheCopy = centralCache; + + if (centralCache.Task is null) + { + centralCache.Task = CreateTaskCountdown(); + cacheCopy.Task = null; + } + // ReSharper disable once SuspiciousTypeConversion.Global + else if (centralCache.Task is Countdown taskCountdown) + { + Interlocked.Decrement(ref taskCountdown.Value); + + if (taskCountdown.Value <= 0) + { + cacheCopy.Task = centralCache.Task + = StartTask(key, second, cacheCopy); + } + else + { + cacheCopy.Task = null; + } + } + else + { + cacheCopy.Task = centralCache.Task; + } + } + + return cacheCopy.Task; + } + + private static Task StartTask(T key, TArgument2 second, + TCacheValue cacheCopy) where TCacheValue : TResult, IAsyncCacheable + => Task.Run(() => UpdateCacheAsync(key, cacheCopy, second)); + + [SecuritySafeCritical] + private static Task CreateTaskCountdown() + => Unsafe.As>(new Countdown(MINIMUM_CALL_COUNT_FOR_STARTING_TASK)); + + public static bool UpdateAsyncCache(ref TCacheValue cache, T key, + TArgument2 second) + where TCacheValue : TResult, IAsyncCacheable + { + cache.Task ??= RequestFromCacheAsync(key, second); + + if (cache.Task is null) + return false; + + if (cache.Task.IsCompletedSuccessfully) + cache.Result = cache.Task.Result; + + return !cache.Result.Equals(default); + } + + private static async Task UpdateCacheAsync(T key, TCacheValue cacheCopy, + TArgument2 second) + where TCacheValue : TResult, IAsyncCacheable + { + TCacheResult? result = default; + try + { + result = await cacheCopy.MakeResultAsync(key, second).ConfigureAwait(false); + + lock (SyncLock) + Unsafe.As(ref GetDirectly.GetOrAddReference(key)).Result = result; + } + catch (Exception e) + { + Log.Error($"{e}"); + } + + return result ?? throw new(); + } public static unsafe void Initialize() { try { - _ = FisheryLib.FunctionPointers.Equals.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; } catch (Exception ex) { - Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference).FullDescription()}\n{ex}"); + Log.Error( + $"Performance Fish encountered an exception while trying to initialize { + typeof(ByReferenceUnclearable).FullName}\n{ex}"); } } } -public struct ByReference : IEquatable> - where T_first : notnull where T_second : notnull +[PublicAPI] +public static class ByReference + where T : notnull where TResult : new() { - private static Dictionary, T_result> _get = new(); - [ThreadStatic] - private static Dictionary, T_result>? _getThreadStatic; + public static object SyncLock = new(); + + private static FishTable _get = Utility.AddNew(); - public static Dictionary, T_result> Get + [ThreadStatic] + private static FishTable? _getThreadStatic; + + public static FishTable Get { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew, T_result>>(); + get => _getThreadStatic ??= Utility.AddNew(); } - public static Dictionary, T_result> GetDirectly => _get; - - static ByReference() => Utility.All.Add(_get); + public static FishTable GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ByReference(T_first first, T_second second) + public static ref TResult GetOrAddReference(T key) => ref Get.GetOrAddReference(key); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(ref T key) => ref Get.GetOrAddReference(ref key); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TResult GetExistingReference(T key) => ref Get.GetReference(key); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Update(T key) where VResult : TResult, ICacheable + => Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Update(T key, V2 second) where VResult : TResult, ICacheable + => Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Update(T key, V2 second, V3 third) + where VResult : TResult, ICacheable + => Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref VResult GetAndCheck(T key) where VResult : TResult, ICacheable + { + ref var cache = ref Unsafe.As(ref GetOrAddReference(key)); + if (cache.Dirty) + cache.Update(ref key); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref VResult GetAndCheck(T key, V2 second) where VResult : TResult, ICacheable + { + ref var cache = ref Unsafe.As(ref GetOrAddReference(key)); + if (cache.Dirty) + cache.Update(ref key, second); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref VResult GetAndCheck(T key, V2 second, V3 third) + where VResult : TResult, ICacheable + { + ref var cache = ref Unsafe.As(ref GetOrAddReference(key)); + if (cache.Dirty) + cache.Update(ref key, second, third); + + return ref cache!; + } + + public static bool UpdateAsyncCache(ref TCacheValue cache, T key) + where TCacheValue : TResult, IAsyncCacheable + { + if (cache.Task is null) + return SyncWithCentralCache(ref cache, key); + + if (cache.Task.IsCompletedSuccessfully) + cache.Result = cache.Task.Result; + + return !cache.Result.Equals(default); + } + + private static bool SyncWithCentralCache(ref TCacheValue cache, T key) + where TCacheValue : TResult, IAsyncCacheable + { + lock (SyncLock) + { + ref var centralCache = ref Unsafe.As(ref GetDirectly.GetOrAddReference(key)); + var cacheCopy = centralCache; + + centralCache.Task ??= Task.Run(() => UpdateCacheAsync(key, cacheCopy)); + + cache.Task = centralCache.Task; + cache.Result = centralCache.Result; + return !cache.Result.Equals(default); + } + } + + private static TCacheResult UpdateCacheAsync(T key, TCacheValue cacheCopy) + where TCacheValue : TResult, IAsyncCacheable + { + TCacheResult? result = default; + try + { + result = cacheCopy.MakeResultAsync(key); + + lock (SyncLock) + Unsafe.As(ref GetDirectly.GetOrAddReference(key)).Result = result; + } + catch (Exception e) + { + Log.Error($"{e}"); + } + + return result ?? throw new(); + } + + public static Task RequestFromCacheAsync(T key, TArgument2 second) + where TCacheValue : TResult, IAsyncCacheable + { + TCacheValue cacheCopy; + lock (SyncLock) + { + ref var centralCache = ref Unsafe.As(ref GetDirectly.GetOrAddReference(key)); + cacheCopy = centralCache; + + centralCache.Task ??= Task.Run(async () + => await UpdateCacheAsync(key, cacheCopy, second) + .ConfigureAwait(false)); + + cacheCopy.Task = centralCache.Task; + } + + return cacheCopy.Task; + } + + public static bool UpdateAsyncCache(ref TCacheValue cache, T key, + TArgument2 second) + where TCacheValue : TResult, IAsyncCacheable + { + cache.Task ??= RequestFromCacheAsync(key, second); + + if (cache.Task.IsCompletedSuccessfully) + cache.Result = cache.Task.Result; + + return !cache.Result.Equals(default); + } + + private static async ValueTask UpdateCacheAsync(T key, TCacheValue cacheCopy, + TArgument2 second) + where TCacheValue : TResult, IAsyncCacheable + { + TCacheResult? result = default; + try + { + result = await cacheCopy.MakeResultAsync(key, second).ConfigureAwait(false); + + lock (SyncLock) + Unsafe.As(ref GetDirectly.GetOrAddReference(key)).Result = result; + } + catch (Exception e) + { + Log.Error($"{e}"); + } + + return result ?? throw new(); + } + + public static unsafe void Initialize() + { + try + { + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + } + catch (Exception ex) + { + Log.Error( + $"Performance Fish encountered an exception while trying to initialize { + typeof(ByReference).FullName}\n{ex}"); + } + } +} + +[PublicAPI] +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public record struct ByReference : IInitializable + where T1 : notnull where T2 : notnull where TResult : new() +{ + private static FishTable, TResult> _get + = Utility.AddNew, TResult>(); + + [ThreadStatic] + private static FishTable, TResult>? _getThreadStatic; + + public static FishTable, TResult> Get + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew, TResult>(); + } + + public static FishTable, TResult> GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(T1 first, T2 second) + => ref Get.GetOrAddReference(new() { First = first, Second = second }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(in ByReference key) + => ref Get.GetOrAddReference(ref Unsafe.AsRef(in key)); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe ref TResult GetExistingReference(T1 first, T2 second) + { + var key = new ByReference(first, second); + return ref Get.GetReference(ref key); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TResult GetExistingReference(in ByReference key) + => ref Get.GetReference(ref Unsafe.AsRef(in key)); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(T1 first, T2 second) + where VResult : TResult, ICacheable> + { + var key = new ByReference(first, second); + Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(T1 key1, T2 key2, V2 second) + where VResult : TResult, ICacheable, V2> + { + var key = new ByReference(key1, key2); + Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(T1 key1, T2 key2, V2 second, V3 third) + where VResult : TResult, ICacheable, V2, V3> + { + var key = new ByReference(key1, key2); + Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref VResult GetAndCheck(T1 first, T2 second) + where VResult : TResult, ICacheable> + { + var key = new ByReference(first, second); + ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key)); + if (cache.Dirty) + cache.Update(ref key); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref VResult GetAndCheck(T1 key1, T2 key2, V2 second) + where VResult : TResult, ICacheable, V2> + { + var key = new ByReference(key1, key2); + ref var cache = ref Unsafe.As(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(T1 key1, T2 key2, V2 second, V3 third) + where VResult : TResult, ICacheable, V2, V3> + { + var key = new ByReference(key1, key2); + ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key)); + if (cache.Dirty) + cache.Update(ref key, second, third); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ByReference(T1 first, T2 second) + { + First = first; + Second = second; + } + + public void Initialize(T1 first, T2 second) { First = first; Second = second; @@ -68,67 +544,156 @@ public struct ByReference : IEquatable.Default; - _ = FisheryLib.FunctionPointers.Equals.Default; - _ = FisheryLib.FunctionPointers.Equals>.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; - _ = FisheryLib.FunctionPointers.GetHashCode>.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.Equals>.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.GetHashCode>.Default; } catch (Exception ex) { - Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference).FullDescription()}\n{ex}"); + Log.Error( + $"Performance Fish encountered an exception while trying to initialize { + typeof(ByReference).FullName}\n{ex}"); } } - public T_first First; + public T1 First; - public T_second Second; + public T2 Second; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ByReference other) - => First.Equals(other.First) - && Second.Equals(other.Second); + public bool Equals(ByReference other) + => First.Equals(other.First) + && Second.Equals(other.Second); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - => HashCode.Combine(First, Second); + public override int GetHashCode() => HashCode.Combine(First, Second); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - => obj is ByReference cache - && Equals(cache); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(ByReference left, ByReference right) - => left.Equals(right); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(ByReference left, ByReference right) - => !(left == right); -} - -public struct ByReference : IEquatable> - where T_first : notnull where T_second : notnull where T_third : notnull -{ - private static Dictionary, T_result> _get = new(); - [ThreadStatic] - private static Dictionary, T_result>? _getThreadStatic; - - public static Dictionary, T_result> Get + T1 IMemberCount.First { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - /*UnityData.IsInMainThread ? _get - :*/ => _getThreadStatic ??= Utility.AddNew, T_result>>(); + get => First; + set => First = value; } - public static Dictionary, T_result> GetDirectly => _get; + T2 IMemberCount.Second + { + get => Second; + set => Second = value; + } +} - static ByReference() => Utility.All.Add(_get); +[PublicAPI] +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public record struct ByReference + where T1 : notnull where T2 : notnull where T3 : notnull where TResult : new() +{ + // TODO: Switch to FishTable. This is used by some of the reflection patches and currently crashes when not used + // with standard dictionaries + + private static Dictionary, TResult> _get + = Utility.AddNew, TResult>>(); + + [ThreadStatic] + private static Dictionary, TResult>? _getThreadStatic; + + public static Dictionary, TResult> Get + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew, TResult>>(); + } + + public static Dictionary, TResult> GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ByReference(T_first first, T_second second, T_third third) + public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third) + { + var key = new ByReference { First = first, Second = second, Third = third }; + return ref Get.GetOrAddReference(ref key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(in ByReference 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 = new ByReference(first, second, third); + return ref Get.GetReference(ref key); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TResult GetExistingReference(in ByReference key) + => ref Get.GetReference(ref Unsafe.AsRef(in key)); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(T1 first, T2 second, T3 third) + where VResult : TResult, ICacheable> + { + var key = new ByReference(first, second, third); + Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(T1 key1, T2 key2, T3 key3, V2 second) + where VResult : TResult, ICacheable, V2> + { + var key = new ByReference(key1, key2, key3); + Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(T1 key1, T2 key2, T3 key3, V2 second, V3 third) + where VResult : TResult, ICacheable, V2, V3> + { + var key = new ByReference(key1, key2, key3); + Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref VResult GetAndCheck(T1 first, T2 second, T3 third) + where VResult : TResult, ICacheable> + { + var key = new ByReference(first, second, third); + ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key)); + if (cache.Dirty) + cache.Update(ref key); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref VResult GetAndCheck(T1 key1, T2 key2, T3 key3, V2 second) + where VResult : TResult, ICacheable, V2> + { + var key = new ByReference(key1, key2, key3); + ref var cache = ref Unsafe.As(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(T1 key1, T2 key2, T3 key3, V2 second, V3 third) + where VResult : TResult, ICacheable, V2, V3> + { + var key = new ByReference(key1, key2, key3); + ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key)); + if (cache.Dirty) + cache.Update(ref key, second, third); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ByReference(T1 first, T2 second, T3 third) { First = first; Second = second; @@ -140,73 +705,148 @@ public struct ByReference : IEquatable.Default; - _ = FisheryLib.FunctionPointers.Equals.Default; - _ = FisheryLib.FunctionPointers.Equals.Default; - _ = FisheryLib.FunctionPointers.Equals>.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; - _ = FisheryLib.FunctionPointers.GetHashCode>.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.Equals>.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.GetHashCode>.Default; } catch (Exception ex) { - Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference).FullDescription()}\n{ex}"); + Log.Error($"Performance Fish encountered an exception while trying to initialize { + typeof(ByReference).FullName}\n{ex}"); } } - public T_first First; + public T1 First; - public T_second Second; + public T2 Second; - public T_third Third; + public T3 Third; private int _hashCode; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ByReference other) - => First.Equals(other.First) - && Second.Equals(other.Second) - && Third.Equals(other.Third); + public bool Equals(ByReference other) + => First.Equals(other.First) + && Second.Equals(other.Second) + && Third.Equals(other.Third); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => _hashCode; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - => obj is ByReference cache - && Equals(cache); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(ByReference left, ByReference right) - => left.Equals(right); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(ByReference left, ByReference right) - => !(left == right); } -public struct ByReference : IEquatable> - where T_first : notnull where T_second : notnull where T_third : notnull where T_fourth : notnull +[PublicAPI] +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public record struct ByReference + where T1 : notnull where T2 : notnull where T3 : notnull where T4 : notnull where TResult : new() { - private static Dictionary, T_result> _get = new(); - [ThreadStatic] - private static Dictionary, T_result>? _getThreadStatic; + private static FishTable, TResult> _get + = Utility.AddNew, TResult>(); - public static Dictionary, T_result> Get + [ThreadStatic] + private static FishTable, TResult>? _getThreadStatic; + + public static FishTable, TResult> Get { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - /*UnityData.IsInMainThread ? _get - :*/ => _getThreadStatic ??= Utility.AddNew, T_result>>(); + get => _getThreadStatic ??= Utility.AddNew, TResult>(); } - public static Dictionary, T_result> GetDirectly => _get; - - static ByReference() => Utility.All.Add(_get); + public static FishTable, TResult> GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ByReference(T_first first, T_second second, T_third third, T_fourth fourth) + public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third, T4 fourth) + { + var key = new ByReference { First = first, Second = second, Third = third, Fourth = fourth}; + return ref Get.GetOrAddReference(ref key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TResult GetOrAddReference(in ByReference key) + => ref Get.GetOrAddReference(ref Unsafe.AsRef(in key)); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe ref TResult GetExistingReference(T1 first, T2 second, T3 third, T4 fourth) + { + var key = new ByReference(first, second, third, fourth); + return ref Get.GetReference(ref key); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TResult GetExistingReference(in ByReference key) + => ref Get.GetReference(ref Unsafe.AsRef(in key)); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(T1 first, T2 second, T3 third, T4 fourth) + where VResult : TResult, ICacheable> + { + var key = new ByReference(first, second, third, fourth); + Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(T1 key1, T2 key2, T3 key3, T4 key4, V2 second) + where VResult : TResult, ICacheable, V2> + { + var key = new ByReference(key1, key2, key3, key4); + Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(T1 key1, T2 key2, T3 key3, T4 key4, V2 second, V3 third) + where VResult : TResult, ICacheable, V2, V3> + { + var key = new ByReference(key1, key2, key3, key4); + Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref VResult GetAndCheck(T1 first, T2 second, T3 third, T4 fourth) + where VResult : TResult, ICacheable> + { + var key = new ByReference(first, second, third, fourth); + ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key)); + if (cache.Dirty) + cache.Update(ref key); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref VResult GetAndCheck(T1 key1, T2 key2, T3 key3, T4 key4, V2 second) + where VResult : TResult, ICacheable, V2> + { + var key = new ByReference(key1, key2, key3, key4); + ref var cache = ref Unsafe.As(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(T1 key1, T2 key2, T3 key3, T4 key4, V2 second, + V3 third) + where VResult : TResult, ICacheable, V2, V3> + { + var key = new ByReference(key1, key2, key3, key4); + ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key)); + if (cache.Dirty) + cache.Update(ref key, second, third); + + return ref cache!; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ByReference(T1 first, T2 second, T3 third, T4 fourth) { First = first; Second = second; @@ -219,53 +859,43 @@ public struct ByReference : IEqu { try { - _ = FisheryLib.FunctionPointers.Equals.Default; - _ = FisheryLib.FunctionPointers.Equals.Default; - _ = FisheryLib.FunctionPointers.Equals.Default; - _ = FisheryLib.FunctionPointers.Equals.Default; - _ = FisheryLib.FunctionPointers.Equals>.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; - _ = FisheryLib.FunctionPointers.GetHashCode.Default; - _ = FisheryLib.FunctionPointers.GetHashCode>.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.Equals.Default; + _ = FisheryLib.FunctionPointers.Equals>.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.GetHashCode.Default; + _ = FisheryLib.FunctionPointers.GetHashCode>.Default; } catch (Exception ex) { - Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference).FullDescription()}\n{ex}"); + Log.Error( + $"Performance Fish encountered an exception while trying to initialize { + typeof(ByReference).FullName}\n{ex}"); } } - public T_first First; + public T1 First; - public T_second Second; + public T2 Second; - public T_third Third; + public T3 Third; - public T_fourth Fourth; + public T4 Fourth; private int _hashCode; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ByReference other) - => First.Equals(other.First) - && Second.Equals(other.Second) - && Third.Equals(other.Third) - && Fourth.Equals(other.Fourth); + public bool Equals(ByReference other) + => First.Equals(other.First) + && Second.Equals(other.Second) + && Third.Equals(other.Third) + && Fourth.Equals(other.Fourth); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => _hashCode; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - => obj is ByReference cache - && Equals(cache); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(ByReference left, ByReference right) - => left.Equals(right); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(ByReference left, ByReference right) - => !(left == right); -} \ No newline at end of file +} +#pragma warning restore CS9091 \ No newline at end of file diff --git a/Source/PerformanceFish/Cache/ByReferenceRefreshable.cs b/Source/PerformanceFish/Cache/ByReferenceRefreshable.cs deleted file mode 100644 index a6ebf6f..0000000 --- a/Source/PerformanceFish/Cache/ByReferenceRefreshable.cs +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (c) 2022 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.Cache; -public static class ByReferenceRefreshable - where T_first : notnull - where T_result : IIsRefreshable -{ - private static Dictionary _get = new(); - [ThreadStatic] - private static Dictionary? _getThreadStatic; - - public static Dictionary Get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew>(); - - public static Dictionary GetDirectly => _get; - - static ByReferenceRefreshable() => Utility.All.Add(_get); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T_result GetValue(T_first key) - { - ref var cache = ref Get.TryGetReferenceUnsafe(key); - - if (Unsafe.IsNullRef(ref cache)) - { - Get[key] = Reflection.New(); - cache = ref Get.GetReference(key)!; - goto RefreshCache; - } - - if (!cache.ShouldRefreshNow) - return ref cache!; - - RefreshCache: - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - - return ref cache!; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGetValue(T_first key, out T_result value) - => Get.TryGetValue(key, out value) - && !value.ShouldRefreshNow; - - public static T_result GetValueAndRefreshNow(T_first key) - { - var cache = Get.TryGetValue(key); - cache ??= Reflection.New(); - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - return cache; - } -} - -public struct ByReferenceRefreshable : IEquatable> - where T_first : notnull where T_second : notnull - where T_result : IIsRefreshable, T_result> -{ - internal ByReference _innerValue; - - private static Dictionary, T_result> _get = new(); - [ThreadStatic] - private static Dictionary, T_result>? _getThreadStatic; - - public static Dictionary, T_result> Get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew, T_result>>(); - - public static Dictionary, T_result> GetDirectly => _get; - - static ByReferenceRefreshable() => Utility.All.Add(_get); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ref T_result GetValue(ref ByReferenceRefreshable key) - { - // CS8347 without this crap - ref var cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key))); - - if (Unsafe.IsNullRef(ref cache)) - { - Get[key] = Reflection.New(); - cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!; - goto RefreshCache; - } - - if (!cache.ShouldRefreshNow) - return ref cache!; - - RefreshCache: - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - - return ref cache!; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGetValue(ByReferenceRefreshable key, out T_result value) - => Get.TryGetValue(key, out value) - && !value.ShouldRefreshNow; - - public static T_result GetValueAndRefreshNow(ByReferenceRefreshable key) - { - var cache = Get.TryGetValue(ref key); - cache ??= Reflection.New(); - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - return cache; - } - - public ByReferenceRefreshable(T_first first, T_second second) => _innerValue = new(first, second); - - public T_first First => _innerValue.First; - - public T_second Second => _innerValue.Second; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ByReferenceRefreshable other) - => _innerValue.Equals(other._innerValue); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => _innerValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - => obj is ByReferenceRefreshable cache - && Equals(cache); - - public static bool operator ==(ByReferenceRefreshable left, ByReferenceRefreshable right) - => left.Equals(right); - - public static bool operator !=(ByReferenceRefreshable left, ByReferenceRefreshable right) - => !(left == right); -} - -public struct ByReferenceRefreshable : IEquatable> - where T_first : notnull where T_second : notnull where T_third : notnull - where T_result : IIsRefreshable, T_result> -{ - private ByReference _innerValue; - - private static Dictionary, T_result> _get = new(); - [ThreadStatic] - private static Dictionary, T_result>? _getThreadStatic; - - public static Dictionary, T_result> Get - => /*UnityData.IsInMainThread ? _get - :*/ _getThreadStatic ??= Utility.AddNew, T_result>>(); - - public static Dictionary, T_result> GetDirectly => _get; - - static ByReferenceRefreshable() => Utility.All.Add(_get); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ref T_result GetValue(ref ByReferenceRefreshable key) - { - ref var cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key))); - - if (Unsafe.IsNullRef(ref cache)) - { - Get[key] = Activator.CreateInstance(); - cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!; - goto RefreshCache; - } - - if (!cache.ShouldRefreshNow) - return ref cache!; - - RefreshCache: - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - - return ref cache!; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGetValue(ByReferenceRefreshable key, out T_result value) - => Get.TryGetValue(key, out value) - && !value.ShouldRefreshNow; - - public static T_result GetValueAndRefreshNow(ByReferenceRefreshable key) - { - var cache = Get.TryGetValue(ref key); - cache ??= Activator.CreateInstance(); - cache.ShouldRefreshNow = false; - Get[key] = cache.SetNewValue(key); - return cache; - } - - public ByReferenceRefreshable(T_first first, T_second second, T_third third) - => _innerValue = new(first, second, third); - - public T_first First => _innerValue.First; - public T_second Second => _innerValue.Second; - public T_third Third => _innerValue.Third; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ByReferenceRefreshable other) - => _innerValue.Equals(other._innerValue); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => _innerValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - => obj is ByReferenceRefreshable cache - && Equals(cache); - - public static bool operator ==(ByReferenceRefreshable left, ByReferenceRefreshable right) - => left.Equals(right); - - public static bool operator !=(ByReferenceRefreshable left, ByReferenceRefreshable right) - => !(left == right); -} \ No newline at end of file diff --git a/Source/PerformanceFish/Cache/Database.cs b/Source/PerformanceFish/Cache/Database.cs new file mode 100644 index 0000000..b567a4f --- /dev/null +++ b/Source/PerformanceFish/Cache/Database.cs @@ -0,0 +1,53 @@ +// 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.Cache; + +#pragma warning disable CS9091 +public static class Database where TCache : ICacheKeyable where TValue : new() +{ + public static object SyncLock = new(); + + private static Dictionary _get = Utility.AddNew>(); + + [ThreadStatic] + private static Dictionary? _getThreadStatic; + + public static Dictionary Get + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _getThreadStatic ??= Utility.AddNew>(); + } + + public static Dictionary GetDirectly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _get; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TValue GetOrAddReference(T1 first, T2 second) + where VCache : IMemberCount, new() // new as generic constraint requires at least function pointers, + // and can't be inlined. Makes this slow, unfortunately + => ref Unsafe.As>(Get) + .GetOrAddReference(Reflection.New(first, second)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref TValue GetOrAddReference(in TCache key) + => ref Get.GetOrAddReference(ref Unsafe.AsRef(in key)); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe ref TValue GetExistingReference(T1 first, T2 second) + where VCache : IMemberCount, new() + { + var key = Reflection.New(first, second); + return ref Get.GetReference(ref Unsafe.As(ref key)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ref TValue GetExistingReference(in TCache key) + => ref Get.GetReference(ref Unsafe.AsRef(in key)); +} +#pragma warning restore CS9091 \ No newline at end of file diff --git a/Source/PerformanceFish/Cache/IIsRefreshable.cs b/Source/PerformanceFish/Cache/Extensions.cs similarity index 50% rename from Source/PerformanceFish/Cache/IIsRefreshable.cs rename to Source/PerformanceFish/Cache/Extensions.cs index 0191276..e81b06f 100644 --- a/Source/PerformanceFish/Cache/IIsRefreshable.cs +++ b/Source/PerformanceFish/Cache/Extensions.cs @@ -1,16 +1,11 @@ -// Copyright (c) 2022 bradson +// 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.Cache; -public interface IIsRefreshable where V : IIsRefreshable + +public static class Extensions { - public bool ShouldRefreshNow - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - set; - } - public V SetNewValue(T key); + // This is where I'd put extension methods for my cache structs, if I had any } \ No newline at end of file diff --git a/Source/PerformanceFish/Cache/ICacheable.cs b/Source/PerformanceFish/Cache/ICacheable.cs new file mode 100644 index 0000000..c2ac64d --- /dev/null +++ b/Source/PerformanceFish/Cache/ICacheable.cs @@ -0,0 +1,140 @@ +// 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 System.Threading.Tasks; + +namespace PerformanceFish.Cache; + +public interface ICacheKeyable +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetHashCode(); +} + +public interface IMemberCount +{ + public T1 First + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set; + } +} + +public interface IMemberCount : IMemberCount +{ + public T2 Second + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set; + } +} + +public interface IMemberCount : IMemberCount +{ + public T3 Third + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set; + } +} + +public interface IMemberCount : IMemberCount +{ + public T4 Fourth + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set; + } +} + +public interface IInitializable : IMemberCount +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Initialize(T key); +} + +public interface IInitializable : IMemberCount +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Initialize(T1 first, T2 second); +} + +public interface IInitializable : IMemberCount +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Initialize(T1 first, T2 second, T3 third); +} + +public interface IInitializable : IMemberCount +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Initialize(T1 first, T2 second, T3 third, T4 fourth); +} + +public interface IDirtyable +{ + public bool Dirty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } +} + +public interface ICacheable : IDirtyable +{ + public void Update(ref TKey key); +} + +public interface ICacheable : IDirtyable +{ + public void Update(ref TKey key, T2 second); +} + +public interface ICacheable : IDirtyable +{ + public void Update(ref TKey key, T2 second, T3 third); +} + +public interface IAsyncCacheable : IDirtyable +{ + public Task? Task + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set; + } + + public TResult? Result + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set; + } +} + +public interface IAsyncCacheable : IAsyncCacheable +{ + public TResult? MakeResultAsync(TKey key); +} + +public interface IAsyncCacheable : IAsyncCacheable +{ + public ValueTask MakeResultAsync(TKey key, T2 second); +} + +internal record Countdown(int Value) +{ + public volatile int Value = Value; +} \ No newline at end of file diff --git a/Source/PerformanceFish/Cache/Utility.cs b/Source/PerformanceFish/Cache/Utility.cs index 90f7a76..632f7ff 100644 --- a/Source/PerformanceFish/Cache/Utility.cs +++ b/Source/PerformanceFish/Cache/Utility.cs @@ -1,43 +1,314 @@ -// Copyright (c) 2022 bradson +// 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 System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection.Emit; +using JetBrains.Annotations; namespace PerformanceFish.Cache; + public static class Utility { + public static event Action? ThingDestroyed; // TODO + + private static volatile int _destroyedThingCounter, _lastGCTick, _gcRate = 1000; + private static int _currentGCProgress; + private static volatile bool _gcInProgress; + + private static ICollection[]? _cachesToProcessForGC; + + private static object _gcLock = new(); + + public static void NotifyThingDestroyed(Thing thing) + { + ThingDestroyed?.Invoke(thing); + + if (Interlocked.Increment(ref _destroyedThingCounter) < 100 + || Current.programStateInt != ProgramState.Playing + || TickHelper.TicksGame - _lastGCTick < _gcRate) + { + return; + } + + ContinueGC(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ContinueGC() + { + lock (_gcLock) + { + if (_gcInProgress) + return; + + _gcInProgress = true; + + if (_cachesToProcessForGC is null || _currentGCProgress >= _cachesToProcessForGC.Length) + { + _cachesToProcessForGC = All.ToArray(); + _gcRate = GenDate.TicksPerTwelfth / _cachesToProcessForGC.CountNonEmpty(); + _currentGCProgress = 0; + _destroyedThingCounter = 0; + } + + while (_cachesToProcessForGC[_currentGCProgress].Count == 0 + && _cachesToProcessForGC.Length - _currentGCProgress > 1) + { + _currentGCProgress++; + } + + if (_currentGCProgress < _cachesToProcessForGC.Length) + _cachesToProcessForGC[_currentGCProgress++].Clear(); + + _lastGCTick = TickHelper.TicksGame; + + _gcInProgress = false; + } + } + + private static int CountNonEmpty(this ICollection[] collection) + { + var count = 0; + for (var i = collection.Length; i-- > 0;) + { + if (collection.Length > 0) + count++; + } + + return count; + } + public static ConcurrentBag All { get; } = new(); public static void Clear() { foreach (var cache in All) + cache.Clear(); + } + + public static void LogCurrentCacheUtilization() + { + var allCaches = All.ToArray(); + Log.Message($"Total utilized caches: {allCaches.CountNonEmpty()}\n\n" + + $"Individual counts:\n{string.Join("\n", + allCaches.Select(static cache => cache.GetType()).Distinct().Select(type + => $"{type.FullDescription()} :: {allCaches.Where(cache => cache.GetType() == type) + .Select(static cache => cache.Count).Sum()}{TryCalculateSize( + type, allCaches)}"))}\n============================================================="); + } + + private static string TryCalculateSize(Type type, ICollection[] allCaches) + => (type.GetProperty(nameof(FishTable.SizeEstimate))?.GetMethod + ?? (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>) + ? AccessTools.Method(typeof(FisheryLib.CollectionExtensions), + nameof(FisheryLib.CollectionExtensions.GetSizeEstimate), generics: type.GetGenericArguments()) + : null)) is { } sizeGetter + ? $" ({(allCaches.Where(cache => cache.GetType() == type) + .Select(cache => (long)(uint)sizeGetter.Invoke(sizeGetter.IsStatic ? null : cache, + sizeGetter.IsStatic ? new object[] { cache } : null)).Sum() + is var sizeInBytes + && sizeInBytes > 1024L * 1024L ? $"{Math.Round(sizeInBytes / (1024d * 1024d), 2)} MiB" + : sizeInBytes > 1024L ? $"{Math.Round(sizeInBytes / 1024d, 2)} KiB" + : $"{sizeInBytes} B")})" + : ""; + + private static void Clear(this ICollection cache) + { + switch (cache) { - cache.GetType() - .GetMethods() - .Where(m - => m.Name == nameof(Array.Clear) - && m.GetParameters().Length == 0) - .First() - .Invoke(cache, null); + case IClearable clearable: + clearable.Clear(); + break; + case IDictionary dictionary: + dictionary.Clear(); + break; + case IList list: + list.Clear(); + break; + default: + ref var clearMethod = ref ClearMethods.GetOrAddReference(cache.GetType().TypeHandle.Value); + if (clearMethod.Action is null) + AssignClearMethod(cache, out clearMethod.Action); + clearMethod.Action(cache); + break; } } - public static T AddNew() where T : ICollection, new() + private static void AssignClearMethod(ICollection cache, out Action clearMethod) + => clearMethod = Unsafe.As>(Array.Find(cache.GetType().GetMethods(), + static m => m.Name == nameof(IList.Clear) && m.GetParameters().Length == 0) + .CreateDelegate(typeof(Action<>).MakeGenericType(cache.GetType()))); + + private static FishTable ClearMethods => _clearMethods ??= new(); + + [ThreadStatic] + private static FishTable? _clearMethods; + + private record struct ClearAction { - var newCollection = new T(); + public Action? Action; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static T AddNew() where T : class, ICollection, new() + { + var newCollection = Reflection.New(); + All.Add(newCollection); return newCollection; } - public static T AddNew(IEqualityComparer comparer) where T : ICollection, new() + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static FishTable AddNew(Action>? onEntryAdded = null, + Action>? onEntryRemoved = null) { - var newCollection = (T)Activator.CreateInstance(typeof(T), comparer); + var newCollection = new FishTable(); + + if (onEntryAdded != null) + newCollection.EntryAdded += onEntryAdded; + + if (onEntryRemoved != null) + newCollection.EntryRemoved += onEntryRemoved; + All.Add(newCollection); return newCollection; } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static T AddNewUnclearable() where T : class, ICollection, new() + { + var newCollection = Reflection.New(); + return newCollection; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static FishTable AddNewUnclearable( + Action>? onEntryAdded = null, + Action>? onEntryRemoved = null) + { + var newCollection = new FishTable(); + + if (onEntryAdded != null) + newCollection.EntryAdded += onEntryAdded; + + if (onEntryRemoved != null) + newCollection.EntryRemoved += onEntryRemoved; + + return newCollection; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static T AddNewWithRef(AccessTools.FieldRef fieldRef) where T : ICollection, new() + { + var collectionRef = Reflection.New, AccessTools.FieldRef>(fieldRef); + All.Add(collectionRef); + return Reflection.New(); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static T AddNew(IEqualityComparer comparer) where T : ICollection, new() + { + var newCollection = (T)Activator.CreateInstance(typeof(T), comparer); // different comparer subclasses can + All.Add(newCollection); // require different constructors. + return newCollection; // Reflection.New can't use arguments + } // for resolving method calls (yet) +} + +public delegate int IndexGetter(T item); + +public interface IClearable +{ + public void Clear(); +} + +public interface IFishBool +{ + public bool Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } } -public delegate int IndexGetter(T item); \ No newline at end of file +public struct FishBool : IFishBool +{ + private byte _value; + + public bool Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _value == 1; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => _value = value ? (byte)1 : (byte)0; + } + + public struct True : IFishBool + { + public bool Value => true; + } + + public struct False : IFishBool + { + public bool Value => false; + } +} + +[PublicAPI] +public class CollectionRef : IClearable, ICollection where T : ICollection +{ + private readonly AccessTools.FieldRef _fieldRef; + private static readonly ClearInvoker? _clearInvoker = CreateClearDelegate(); + + public CollectionRef(AccessTools.FieldRef fieldRef) => _fieldRef = fieldRef; + + public ref T Get => ref _fieldRef(); + + public void Create() => Get = Reflection.New(); + + public void Clear() + { + if (_clearInvoker is null) + ThrowForMissingClearMethod(); + + _clearInvoker(ref Get); + } + + [DoesNotReturn] + private static void ThrowForMissingClearMethod() + => throw new InvalidOperationException( + $"Tried to call Clear() on invalid collection of type {typeof(T).FullName}"); + + public IEnumerator GetEnumerator() => Get.GetEnumerator(); + + public void CopyTo(Array array, int index) => Get.CopyTo(array, index); + + public int Count => Get.Count; + + public object SyncRoot => Get.SyncRoot; + + public bool IsSynchronized => Get.IsSynchronized; + + private static ClearInvoker? CreateClearDelegate() + { + var clearMethod = AccessTools.Method(typeof(T), nameof(IList.Clear), Type.EmptyTypes); + if (clearMethod is null) + return null; + + var dm = new DynamicMethod($"ClearInvoker_{typeof(T).FullName}", null, + new[] { typeof(T).MakeByRefType() }, typeof(CollectionRef), true); + var il = dm.GetILGenerator(); + + il.Emit(FishTranspiler.Argument(0)); + il.Emit(FishTranspiler.Call(clearMethod)); + il.Emit(FishTranspiler.Return); + + return (ClearInvoker)dm.CreateDelegate(typeof(ClearInvoker)); + } + + private delegate void ClearInvoker(ref T collectionRef); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Collections/FishTable.cs b/Source/PerformanceFish/Collections/FishTable.cs deleted file mode 100644 index 7d40e2f..0000000 --- a/Source/PerformanceFish/Collections/FishTable.cs +++ /dev/null @@ -1,645 +0,0 @@ -// Copyright (c) 2022 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using RimWorld; -using UnityEngine; - -namespace PerformanceFish.Collections; - -#pragma warning disable CS8766, CS8767 -public class FishTable : IDictionary, IDictionary, IReadOnlyDictionary -{ - //private int[] _buckets; - - internal Entry[] _entries; - - private int _count; - - private int _version; - - //private unsafe delegate* _keyGetHashCode = FunctionPointers.GetHashCode.Default; - //private unsafe delegate* _keyEquals = FunctionPointers.Equals.Default; - - //private int _freeList; - - //private int _freeCount; - - //private KeyCollection _keys; - - //private ValueCollection _values; - - public ICollection Keys => throw new NotImplementedException(); - - public ICollection Values => throw new NotImplementedException(); - - public int Version - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _version; - } - - public int Count - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _count; - } - - public bool IsReadOnly => false; - - ICollection IDictionary.Keys => throw new NotImplementedException(); - - ICollection IDictionary.Values => throw new NotImplementedException(); - - bool IDictionary.IsFixedSize => false; - object ICollection.SyncRoot => this; // matching System.Collections.Generic.Dictionary - bool ICollection.IsSynchronized => false; - - IEnumerable IReadOnlyDictionary.Keys => throw new NotImplementedException(); - - IEnumerable IReadOnlyDictionary.Values => throw new NotImplementedException(); - - object? IDictionary.this[object key] - { - get - => key is TKey tkey - ? this[tkey] - : ThrowHelper.ThrowKeyNotFoundException(key); - - set - { - if (key is TKey tkey) - { - if (value is TValue tvalue) - this[tkey] = tvalue; - else if (value is null) - this[tkey] = default; - else - ThrowHelper.ThrowWrongValueTypeArgumentException(value); - } - else - { - ThrowHelper.ThrowWrongKeyTypeArgumentException(key); - } - } - } - - public TValue? this[TKey key] - { - get - { - var keyCode = (uint)HashCode.Get(key); - var entry = _entries[FastModulo(keyCode, (uint)_entries.Length)]; - - while (entry.HashCode != keyCode || !entry.Key.Equals(key)) - { - if (entry.Next != EntryReference.Empty && entry.Next != null) - entry = entry.Next.Entry; - else - ThrowHelper.ThrowKeyNotFoundException(key); - } - - if (entry.Next is null) - ThrowHelper.ThrowKeyNotFoundException(key); - - return entry.Value; - } - set - { - ref var reference = ref TryGetReferenceUnsafe(key); - if (!Unsafe.IsNullRef(ref reference)) - { - reference = value; - _version++; - return; - } - - Add(key, value!); - } - } - - public FishTable() : this(0) { } - - public FishTable(int minimumCapacity) - => _entries = new Entry[ - minimumCapacity <= 1 ? 1 // Minimum size to not throw on indexing into _entries. Length is not checked beforehand - : Mathf.NextPowerOfTwo(minimumCapacity)]; // System.Collections.Dictionary starts at 3. So wasteful. - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static uint FastModulo(uint first, uint second) - => first & (second - 1); - - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) - { - var keyCode = (uint)HashCode.Get(key); - var entry = _entries[FastModulo(keyCode, (uint)_entries.Length)]; - - while (entry.HashCode != keyCode || !entry.Key.Equals(key)) - { - if (entry.Next != EntryReference.Empty && entry.Next != null) - { - entry = entry.Next.Entry; - } - else - { - value = default; - return false; - } - } - - value = entry.Value; - return entry.Next != null; - } - - public TValue? TryGetValue(TKey key) - { - var keyCode = (uint)HashCode.Get(key); - var entry = _entries[FastModulo(keyCode, (uint)_entries.Length)]; - - while (entry.HashCode != keyCode || !entry.Key.Equals(key)) - { - if (entry.Next != EntryReference.Empty && entry.Next != null) - entry = entry.Next.Entry; - else - return default; - } - - return entry.Value; - } - - public ref TValue GetReference(TKey key) - { - var keyCode = (uint)HashCode.Get(key); - ref var entry = ref _entries[FastModulo(keyCode, (uint)_entries.Length)]; - - while (entry.HashCode != keyCode || !entry.Key.Equals(key)) - { - if (entry.Next != EntryReference.Empty && entry.Next != null) - entry = ref entry.Next.Entry; - else - ThrowHelper.ThrowKeyNotFoundException(key); - } - - if (entry.Next == null) - ThrowHelper.ThrowKeyNotFoundException(key); - - return ref entry.Value; - } - - /// - /// Returns a reference to the value field of an entry if the key exists and an Unsafe.NullRef{TValue} if not. - /// Must be checked with Unsafe.IsNullRef before assigning to a regular var or field without ref. - /// - /// The key - /// ref TValue or Unsafe.NullRef{TValue} - public ref TValue TryGetReferenceUnsafe(TKey key) - { - var keyCode = (uint)HashCode.Get(key); - ref var entry = ref _entries[FastModulo(keyCode, (uint)_entries.Length)]; - - while (entry.HashCode != keyCode || !entry.Key.Equals(key)) - { - if (entry.Next != EntryReference.Empty && entry.Next != null) - entry = ref entry.Next.Entry; - else - return ref Unsafe.NullRef()!; - } - - return ref entry.Next == null - ? ref Unsafe.NullRef() - : ref entry.Value; - } - - public bool ContainsKey(TKey key) - { - var keyCode = (uint)HashCode.Get(key); - var entry = _entries[FastModulo(keyCode, (uint)_entries.Length)]; - - while (entry.HashCode != keyCode || !entry.Key.Equals(key)) - { - if (entry.Next != EntryReference.Empty && entry.Next != null) - entry = entry.Next.Entry; - else - return false; - } - - return entry.Next != null; - } - - public void Add(TKey key, TValue value) - { - Guard.IsNotNull(key); - - if (_count == _entries.Length) // throwing DuplicateKeyExceptions happens later, so Expanding occurs based on expected - Expand(); // behaviour instead of matching actual results like in System.Collections.Generic. - - var keyCode = (uint)HashCode.Get(key); - ref var entry = ref _entries[FastModulo(keyCode, (uint)_entries.Length)]; - - while (entry.Next != null) - { - if (entry.HashCode == keyCode && entry.Key.Equals(key)) - ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key); - - if (entry.Next == EntryReference.Empty) - break; - - entry = ref entry.Next.Entry; - } - - if (entry.Next == null) // means the entry is empty - entry = new(keyCode, key, value); - else // if (entry.Next == _freeSlot) // occupied entry with an empty slot in entry.Next - entry.Next = new(keyCode, key, value); - // entry.Next != _freeSlot would be a threading issue, as this was just checked in the loop. Unhandled. - - _count++; - _version++; - } - - private void Expand() - { - var newLength = Mathf.NextPowerOfTwo(_entries.Length + 1); - var newArray = new Entry[newLength]; - - for (var i = 0; i < _entries.Length; i++) - { - var entry = _entries[i]; - if (entry.Next is null) - continue; - - BeforeAdding: - ref var newEntry = ref newArray[FastModulo(entry.HashCode, (uint)newLength)]; - - while (newEntry.Next != null) - { - if (newEntry.HashCode == entry.HashCode && newEntry.Key.Equals(entry.Key)) - ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(entry.Key); - - if (newEntry.Next == EntryReference.Empty) - break; - - newEntry = ref newEntry.Next.Entry; - } - - var nextEntry = entry.Next; - entry.Next = EntryReference.Empty; - - if (newEntry.Next == null) - newEntry = entry; - else - newEntry.Next = new(entry); - - if (nextEntry != EntryReference.Empty) - { - entry = nextEntry.Entry; - goto BeforeAdding; - } - } - - _entries = newArray; - } - - private ref Entry TryFindEntry(TKey key) - { - var keyCode = (uint)HashCode.Get(key); - ref var entry = ref _entries[FastModulo(keyCode, (uint)_entries.Length)]; - - while (entry.HashCode != keyCode || !entry.Key.Equals(key)) - { - if (entry.Next != EntryReference.Empty && entry.Next != null) - entry = ref entry.Next.Entry; - else - return ref Unsafe.NullRef(); - } - - return ref entry; - } - - private void RemoveEntry(ref Entry entry) - { - entry = entry.Next != EntryReference.Empty && entry.Next != null - ? entry.Next.Entry - : default; - - _count--; - _version++; - } - - public bool Remove(TKey key) - { - ref var entry = ref TryFindEntry(key); - if (Unsafe.IsNullRef(ref entry)) - return false; - - RemoveEntry(ref entry); - return true; - } - - public void Add(KeyValuePair item) => Add(item.Key, item.Value); - - public void Clear() - { - Array.Clear(_entries, 0, _entries.Length); - _count = 0; - _version++; - } - - public bool Contains(KeyValuePair item) - => TryGetValue(item.Key, out var value) - && value.Equals(item.Value); - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - var length = _entries.Length; - for (var i = 0; i < length; i++) - { - var entry = _entries[i]; - if (entry.Next == null) - continue; - - array[arrayIndex] = new(entry.Key, entry.Value); - arrayIndex++; - - while (entry.Next != EntryReference.Empty) - { - entry = entry.Next.Entry; - array[arrayIndex] = new(entry.Key, entry.Value); - arrayIndex++; - } - } - } - - public bool Remove(KeyValuePair item) - { - ref var entry = ref TryFindEntry(item.Key); - - if (Unsafe.IsNullRef(ref entry) - || !entry.Value.Equals(item.Value)) - { - return false; - } - - RemoveEntry(ref entry); - return true; - } - - public Enumerator GetEnumerator() - => new(this, Enumerator.KEY_VALUE_PAIR); - - IEnumerator> IEnumerable>.GetEnumerator() - => new Enumerator(this, Enumerator.KEY_VALUE_PAIR); - - IEnumerator IEnumerable.GetEnumerator() - => new Enumerator(this, Enumerator.KEY_VALUE_PAIR); - - bool IDictionary.Contains(object key) - { - Guard.IsNotNull(key); - - return key is TKey tkey - && ContainsKey(tkey); - } - - void IDictionary.Add(object key, object value) => throw new NotImplementedException(); - - IDictionaryEnumerator IDictionary.GetEnumerator() - => new Enumerator(this, Enumerator.DICT_ENTRY); - - void IDictionary.Remove(object key) - { - Guard.IsNotNull(key); - - if (key is TKey tkey) - Remove(tkey); - } - - void ICollection.CopyTo(Array array, int index) => throw new NotImplementedException(); - - internal struct Entry - { - public uint HashCode; - public TKey Key; - public EntryReference Next; - public TValue Value; - - internal Entry(uint hashCode, TKey key, TValue value) - { - HashCode = hashCode; - Key = key; - Value = value; - Next = EntryReference.Empty; - } - } - - internal class EntryReference - { - public static readonly EntryReference Empty = new(); - - public Entry Entry; - - public EntryReference(uint hashCode, TKey key, TValue value) => Entry = new(hashCode, key, value); - - public EntryReference(Entry entry) => Entry = entry; - - private EntryReference() { } - } - - public struct Enumerator : IEnumerator>, IDictionaryEnumerator - { - private readonly FishTable _dictionary; - private readonly int _version; - private int _index; - private EntryReference _next; - private KeyValuePair _current; - private readonly int _getEnumeratorRetType; // What should Enumerator.Current return? - - internal const int DICT_ENTRY = 1; - internal const int KEY_VALUE_PAIR = 2; - - internal Enumerator(FishTable dictionary, int getEnumeratorRetType) - { - _dictionary = dictionary; - _version = dictionary._version; - _index = 0; - _getEnumeratorRetType = getEnumeratorRetType; - _current = default; - _next = EntryReference.Empty; - } - - public bool MoveNext() - { - if (_version != _dictionary._version) - ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); - - if (_next != EntryReference.Empty) - { - _current = new(_next.Entry.Key, _next.Entry.Value); - _next = _next.Entry.Next; - return true; - } - - // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. - // dictionary.count+1 could be negative if dictionary.count is int.MaxValue - while ((uint)_index < (uint)_dictionary._entries.Length) - { - ref var entry = ref _dictionary._entries[_index++]; - - if (entry.Next != null) - { - _current = new KeyValuePair(entry.Key, entry.Value); - _next = entry.Next; - return true; - } - } - - _index = _dictionary._entries.Length + 1; - _current = default; - return false; - } - - public KeyValuePair Current => _current; - - public void Dispose() { } - - object? IEnumerator.Current - { - get - { - if (_index == 0 || (_index == _dictionary._entries.Length + 1)) - ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); - - return _getEnumeratorRetType == DICT_ENTRY - ? new DictionaryEntry(_current.Key, _current.Value) - : new KeyValuePair(_current.Key, _current.Value); - } - } - - void IEnumerator.Reset() - { - if (_version != _dictionary._version) - ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); - - _index = 0; - _current = default; - } - - DictionaryEntry IDictionaryEnumerator.Entry - { - get - { - if (_index == 0 || (_index == _dictionary._entries.Length + 1)) - ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); - - return new DictionaryEntry(_current.Key, _current.Value); - } - } - - object? IDictionaryEnumerator.Key - { - get - { - if (_index == 0 || (_index == _dictionary._entries.Length + 1)) - ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); - - return _current.Key; - } - } - - object? IDictionaryEnumerator.Value - { - get - { - if (_index == 0 || (_index == _dictionary._entries.Length + 1)) - ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); - - return _current.Value; - } - } - } - - private static class ThrowHelper - { - [DoesNotReturn] - internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion() - => throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); - - [DoesNotReturn] - internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen() - => throw new InvalidOperationException("Enumeration has either not started or has already finished."); - - [DoesNotReturn] - internal static void ThrowKeyNotFoundException(T key) - { - Guard.IsNotNull(key); - ThrowKeyNotFoundException((object)key); - } - - [DoesNotReturn] - internal static V ThrowKeyNotFoundException(T key) - { - Guard.IsNotNull(key); - ThrowKeyNotFoundException((object)key); - return default; - } - - [DoesNotReturn] - internal static void ThrowKeyNotFoundException(object key) - => throw new KeyNotFoundException( - string.Format("The given key '{0}' was not present in the dictionary.", key)); - - [DoesNotReturn] - internal static void ThrowWrongValueTypeArgumentException(object value) - => throw new ArgumentException( - string.Format("The value \"{0}\" is not of type \"{1}\" and cannot be used in this generic collection.", - value, typeof(TValue)), - nameof(value)); - - [DoesNotReturn] - internal static void ThrowWrongKeyTypeArgumentException(object? key) - { - Guard.IsNotNull(key); - throw new ArgumentException( - string.Format("The value \"{0}\" is not of type \"{1}\" and cannot be used in this generic collection.", - key, typeof(TKey)), - nameof(key)); - } - - [DoesNotReturn] - internal static void ThrowAddingDuplicateWithKeyArgumentException(T key) - { - Guard.IsNotNull(key); - ThrowAddingDuplicateWithKeyArgumentException((object)key); - } - - [DoesNotReturn] - internal static void ThrowAddingDuplicateWithKeyArgumentException(object key) - => throw new ArgumentException( - string.Format("An item with the same key has already been added. Key: {0}", key)); - } -} -public static class Extensions -{ - public static ref TValue TryGetReferenceUnsafe(this FishTable fishTable, ref TKey key) - where TKey : struct, IEquatable - { - var keyCode = (uint)HashCode.Get(ref key); - ref var entry = ref fishTable._entries[FishTable.FastModulo(keyCode, (uint)fishTable._entries.Length)]; - - while (entry.HashCode != keyCode || !entry.Key.Equals(ref key)) - { - if (entry.Next != FishTable.EntryReference.Empty && entry.Next != null) - entry = ref entry.Next.Entry; - else - return ref Unsafe.NullRef()!; - } - - return ref entry.Next == null - ? ref Unsafe.NullRef() - : ref entry.Value; - } -} -#pragma warning restore CS8766, CS8767 \ No newline at end of file diff --git a/Source/PerformanceFish/ContentFinderCaching.cs b/Source/PerformanceFish/ContentFinderCaching.cs new file mode 100644 index 0000000..51be435 --- /dev/null +++ b/Source/PerformanceFish/ContentFinderCaching.cs @@ -0,0 +1,53 @@ +// 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 PerformanceFish.Prepatching; + +namespace PerformanceFish; + +public class ContentFinderCaching : ClassWithFishPrepatches +{ + public class Get_Patch : FishPrepatch + { + public override string? Description { get; } + = "Caches texture lookups on the ContentFinder. This has a relatively large performance impact when used " + + "with mods like humanoid alien races"; + + public override MethodBase TargetMethodBase { get; } = methodof(ContentFinder.Get); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(string itemPath, bool reportFailure, ref T? __result, out bool __state) + { + ref var cache = ref Cache.ByReference>.GetOrAddReference(itemPath); + + if (!cache.Cached) + return __state = true; + + __result = cache.Value; + + return __state = reportFailure && __result is null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Postfix(string itemPath, T? __result, bool __state) + { + if (!__state) + return; + + UpdateCache(itemPath, __result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(string itemPath, T? __result) + { + ref var cache = ref Cache.ByReference>.GetExistingReference(itemPath); + + cache.Cached = true; + cache.Value = __result; + } + } + + public record struct CacheValue(bool Cached, T? Value); +} \ No newline at end of file diff --git a/Source/PerformanceFish/DDSLoader.cs b/Source/PerformanceFish/DDSLoader.cs new file mode 100644 index 0000000..08ee10e --- /dev/null +++ b/Source/PerformanceFish/DDSLoader.cs @@ -0,0 +1,189 @@ +// 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 System.IO; + +namespace PerformanceFish; + +//DDS importing from // https://github.com/sarbian/DDSLoader/blob/master/DatabaseLoaderTexture_DDS.cs +public static class DDSLoader +{ + private const uint + DDSD_MIPMAPCOUNT_BIT = 0x00020000, + DDPF_ALPHAPIXELS = 0x00000001, + DDPF_ALPHA = 0x00000002, + DDPF_FOURCC = 0x00000004, + DDPF_RGB = 0x00000040, + // DDPF_YUV = 0x00000200, + DDPF_LUMINANCE = 0x00020000; + // DDPF_NORMAL = 0x80000000; + + public static string? error; + + // DDS Texture loader inspired by + // http://answers.unity3d.com/questions/555984/can-you-load-dds-textures-during-runtime.html#answer-707772 + // http://msdn.microsoft.com/en-us/library/bb943992.aspx + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb205578(v=vs.85).aspx + // mipmapBias limits the number of mipmap when > 0 + public static Texture2D? LoadDDS(FileStream fileStream, out bool hasMipMaps) + { + hasMipMaps = false; + using var reader = new BinaryReader(fileStream); + + if (!FourCcEquals(reader.ReadBytes(4), "DDS ")) + { + error = "Invalid DDS file"; + return null; + } + + //this header byte should be 124 for DDS image files + if (reader.ReadUInt32() != 124u) + { + error = "Invalid header size"; + return null; + } + + var dwFlags = reader.ReadUInt32(); + var dwHeight = (int)reader.ReadUInt32(); + var dwWidth = (int)reader.ReadUInt32(); + + reader.ReadUInt64(); + var dwMipMapCount = reader.ReadUInt32(); + + if ((dwFlags & DDSD_MIPMAPCOUNT_BIT) != 0) + hasMipMaps = dwMipMapCount > 1u; + + // dwReserved1 + for (var i = 5; i-- > 0;) + reader.ReadUInt64(); + + reader.ReadUInt32(); + + // DDS_PIXELFORMAT + reader.ReadUInt32(); + var dds_pxlf_dwFlags = reader.ReadUInt32(); + var dds_pxlf_dwFourCC = reader.ReadBytes(4); + // var fourCC = Encoding.ASCII.GetString(dds_pxlf_dwFourCC); + var dds_pxlf_dwRGBBitCount = reader.ReadUInt32(); + var pixelSize = dds_pxlf_dwRGBBitCount / 8; + var dds_pxlf_dwRBitMask = reader.ReadUInt32(); + var dds_pxlf_dwGBitMask = reader.ReadUInt32(); + var dds_pxlf_dwBBitMask = reader.ReadUInt32(); + var dds_pxlf_dwABitMask = reader.ReadUInt32(); + + reader.ReadUInt64(); + reader.ReadUInt64(); + reader.ReadUInt32(); + + var rgb888 = dds_pxlf_dwRBitMask == 0x000000ff + && dds_pxlf_dwGBitMask == 0x0000ff00 + && dds_pxlf_dwBBitMask == 0x00ff0000; + + var bgr888 = dds_pxlf_dwRBitMask == 0x00ff0000 + && dds_pxlf_dwGBitMask == 0x0000ff00 + && dds_pxlf_dwBBitMask == 0x000000ff; + + var rgb565 = dds_pxlf_dwRBitMask == 0x0000F800 + && dds_pxlf_dwGBitMask == 0x000007E0 + && dds_pxlf_dwBBitMask == 0x0000001F; + + var argb4444 = dds_pxlf_dwABitMask == 0x0000f000 + && dds_pxlf_dwRBitMask == 0x00000f00 + && dds_pxlf_dwGBitMask == 0x000000f0 + && dds_pxlf_dwBBitMask == 0x0000000f; + + var rbga4444 = dds_pxlf_dwABitMask == 0x0000000f + && dds_pxlf_dwRBitMask == 0x0000f000 + && dds_pxlf_dwGBitMask == 0x000000f0 + && dds_pxlf_dwBBitMask == 0x00000f00; + + var textureFormat = TextureFormat.ARGB32; + var isCompressed = false; + + if ((dds_pxlf_dwFlags & DDPF_FOURCC) != 0) + { + // Texture dos not contain RGB data, check FourCC for format + isCompressed = true; + + if (FourCcEquals(dds_pxlf_dwFourCC, "DXT1")) + textureFormat = TextureFormat.DXT1; + else if (FourCcEquals(dds_pxlf_dwFourCC, "DXT5")) + textureFormat = TextureFormat.DXT5; + else if (FourCcEquals(dds_pxlf_dwFourCC, "DX10")) + textureFormat = TextureFormat.BC7; + } + else + { + var alphapixel = (dds_pxlf_dwFlags & DDPF_ALPHAPIXELS) != 0; + + switch ((dds_pxlf_dwFlags & DDPF_RGB) != 0) + { + case true when rgb888 || bgr888: + // RGB or RGBA format + textureFormat = alphapixel + ? TextureFormat.RGBA32 + : TextureFormat.RGB24; + break; + case true when rgb565: + // Nvidia texconv B5G6R5_UNORM + textureFormat = TextureFormat.RGB565; + break; + case true when alphapixel && argb4444: + // Nvidia texconv B4G4R4A4_UNORM + textureFormat = TextureFormat.ARGB4444; + break; + case true when alphapixel && rbga4444: + textureFormat = TextureFormat.RGBA4444; + break; + case false when (dds_pxlf_dwFlags & DDPF_ALPHA) != 0 != ((dds_pxlf_dwFlags & DDPF_LUMINANCE) != 0): + // A8 format or Luminance 8 + textureFormat = TextureFormat.Alpha8; + break; + default: + error = "Only BC7, DXT1, DXT5, A8, RGB24, BGR24, RGBA32, BGBR32, RGB565, ARGB4444 and " + + "RGBA4444 are supported"; + return null; + } + } + + var dataBias = (long)(textureFormat != TextureFormat.BC7 ? 128 : 148); + var dxtBytesLength = reader.BaseStream.Length - dataBias; + reader.BaseStream.Seek(dataBias, SeekOrigin.Begin); + var dxtBytes = reader.ReadBytes((int)dxtBytesLength); + + // Swap red and blue. + if (!isCompressed && bgr888) + { + for (uint i = 0; i < dxtBytes.Length; i += pixelSize) + { + var b = dxtBytes[i + 0]; + var r = dxtBytes[i + 2]; + + dxtBytes[i + 0] = r; + dxtBytes[i + 2] = b; + } + } + + // QualitySettings.masterTextureLimit = 0; + // Work around for a >Unity< Bug. + // if QualitySettings.masterTextureLimit != 0 (half or quarter texture rez) + // and dwWidth and dwHeight divided by 2 (or 4 for quarter rez) are not a multiple of 4 + // and we are creating a DXT5 or DXT1 texture + // Then you get an Unity error on the "new Texture" + + var quality = QualitySettings.masterTextureLimit; + + // If the bug conditions are present then switch to full quality + if (isCompressed && quality > 0 && (dwWidth >> quality) % 4 != 0 && (dwHeight >> quality) % 4 != 0) + QualitySettings.masterTextureLimit = 0; + + var texture = new Texture2D(dwWidth, dwHeight, textureFormat, hasMipMaps); + texture.LoadRawTextureData(dxtBytes); + return texture; + } + + private static bool FourCcEquals(IList bytes, string s) + => bytes[0] == s[0] && bytes[1] == s[1] && bytes[2] == s[2] && bytes[3] == s[3]; +} \ No newline at end of file diff --git a/Source/PerformanceFish/DebugLog.cs b/Source/PerformanceFish/DebugLog.cs index a832cf0..57ad9aa 100644 --- a/Source/PerformanceFish/DebugLog.cs +++ b/Source/PerformanceFish/DebugLog.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. diff --git a/Source/PerformanceFish/DefDatabasePatches.cs b/Source/PerformanceFish/DefDatabasePatches.cs new file mode 100644 index 0000000..6b9367d --- /dev/null +++ b/Source/PerformanceFish/DefDatabasePatches.cs @@ -0,0 +1,50 @@ +// 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/. + +// crashes to desktop somehow +/*using Mono.Cecil; +using Mono.Cecil.Cil; +using PerformanceFish.Prepatching; + +namespace PerformanceFish; + +public class DefDatabasePatches : ClassWithFishPrepatches +{ + public class GetNamedPatch : FishPrepatch + { + public override MethodBase TargetMethodBase { get; } = methodof(DefDatabase.GetNamed); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(GetNamed); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? GetNamed(string defName, bool errorOnFail = true) where T : Def + { + var value = DefDatabase.defsByName.TryGetValue(defName); + + if (errorOnFail && value is null) + LogMissingDef(defName); + + return value; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void LogMissingDef(string defName) where T : Def + => Log.Error($"Failed to find {typeof(T)} named {defName}. There are { + DefDatabase.defsList.Count} defs of this type loaded."); + } + + public class GetByShortHashPatch : FishPrepatch + { + public override MethodBase TargetMethodBase { get; } = methodof(DefDatabase.GetByShortHash); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(GetByShortHash); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? GetByShortHash(ushort shortHash) where T : Def + => DefDatabase.defsByShortHash.TryGetValue(shortHash); + } +}*/ \ No newline at end of file diff --git a/Source/PerformanceFish/DesignationManagerCaching.cs b/Source/PerformanceFish/DesignationManagerCaching.cs index 26e2aad..f90de42 100644 --- a/Source/PerformanceFish/DesignationManagerCaching.cs +++ b/Source/PerformanceFish/DesignationManagerCaching.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -7,8 +7,12 @@ //mostly integrated into vanilla with 1.4 using System.Linq; -using AllDesignationsOnCache = PerformanceFish.Cache.ByIntRefreshable>; -using DesignationsOfDefCache = PerformanceFish.Cache.ByIntRefreshable>; +using AllDesignationsOnCache = + PerformanceFish.Cache.ByIntRefreshable>; +using DesignationsOfDefCache = + PerformanceFish.Cache.ByIntRefreshable>; namespace PerformanceFish; @@ -17,8 +21,12 @@ public class DesignationManagerCaching : ClassWithFishPatches public class SpawnedDesignationsOfDef_Patch : FishPatch { public override string Description => "Designation caching"; - public override Expression TargetMethod => () => default(DesignationManager)!.SpawnedDesignationsOfDef(null); - public static bool Prefix(DesignationManager __instance, DesignationDef def, ref IEnumerable __result, out bool __state) + + public override Expression TargetMethod + => () => default(DesignationManager)!.SpawnedDesignationsOfDef(null); + + public static bool Prefix(DesignationManager __instance, DesignationDef def, + ref IEnumerable __result, out bool __state) { if (def.GetType() != typeof(DesignationDef)) // different types have different indexing. Can't use those { @@ -32,15 +40,18 @@ public class DesignationManagerCaching : ClassWithFishPatches __result = value.designations; return __state = false; } - public static void Postfix(DesignationManager __instance, DesignationDef def, bool __state, IEnumerable __result) + + public static void Postfix(DesignationManager __instance, DesignationDef def, bool __state, + IEnumerable __result) { if (__state) RefreshDesignationCache(DesignationsOfDefCache.Get, __instance, def, __result); } } - public static void RefreshDesignationCache(/*Cache.ByIndex>*/ - Dictionary>, DesignationCache> dict, DesignationManager instance, T key, IEnumerable result) + public static void RefreshDesignationCache( /*Cache.ByIndex>*/ + Dictionary>, DesignationCache> dict, + DesignationManager instance, T key, IEnumerable result) where T : notnull { if (dict.TryGetValue(new(instance, key), out var cache)) @@ -54,16 +65,24 @@ public class DesignationManagerCaching : ClassWithFishPatches public class AnySpawnedDesignationOfDef_Patch : FishPatch { public override string Description => "Designation caching"; - public override Expression TargetMethod => () => default(DesignationManager)!.AnySpawnedDesignationOfDef(null); - public static bool AnySpawnedDesignationOfDef(DesignationManager __instance, DesignationDef def) => __instance.SpawnedDesignationsOfDef(def).Any(); - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) => Reflection.GetCodeInstructions(AnySpawnedDesignationOfDef); + + public override Expression TargetMethod + => () => default(DesignationManager)!.AnySpawnedDesignationOfDef(null); + + public static bool AnySpawnedDesignationOfDef(DesignationManager __instance, DesignationDef def) + => __instance.SpawnedDesignationsOfDef(def).Any(); + + public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) + => Reflection.GetCodeInstructions(AnySpawnedDesignationOfDef); } public class AllDesignationsOn_Patch : FishPatch { public override string Description => "Designation caching"; public override Expression TargetMethod => () => default(DesignationManager)!.AllDesignationsOn(null); - public static bool Prefix(DesignationManager __instance, Thing t, ref IEnumerable __result, out bool __state) + + public static bool Prefix(DesignationManager __instance, Thing t, ref IEnumerable __result, + out bool __state) { if (!AllDesignationsOnCache.TryGetValue(new(__instance, t), out var value)) return __state = true; @@ -71,7 +90,9 @@ public class DesignationManagerCaching : ClassWithFishPatches __result = value.designations; return __state = false; } - public static void Postfix(DesignationManager __instance, Thing t, bool __state, IEnumerable __result) + + public static void Postfix(DesignationManager __instance, Thing t, bool __state, + IEnumerable __result) { if (__state) RefreshDesignationCache(AllDesignationsOnCache.Get, __instance, t, __result); @@ -82,15 +103,22 @@ public class DesignationManagerCaching : ClassWithFishPatches { public override string Description => "Designation caching"; public override Expression TargetMethod => () => default(DesignationManager)!.DesignationOn(null); - public static Designation DesignationOn(DesignationManager __instance, Thing t) => __instance.AllDesignationsOn(t).FirstOrDefault(); - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) => Reflection.GetCodeInstructions(DesignationOn); + + public static Designation DesignationOn(DesignationManager __instance, Thing t) + => __instance.AllDesignationsOn(t).FirstOrDefault(); + + public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) + => Reflection.GetCodeInstructions(DesignationOn); } public class DesignationOn_ByDef_Patch : FishPatch { public override string Description => "Designation caching"; public override Expression TargetMethod => () => default(DesignationManager)!.DesignationOn(null, null); - public static Designation? DesignationOn(DesignationManager __instance, Thing t, DesignationDef def) => FirstDesignationOfDef(__instance.AllDesignationsOn(t), def); + + public static Designation? DesignationOn(DesignationManager __instance, Thing t, DesignationDef def) + => FirstDesignationOfDef(__instance.AllDesignationsOn(t), def); + public static Designation? FirstDesignationOfDef(IEnumerable designations, DesignationDef def) { if (designations is not List designationList) @@ -102,8 +130,10 @@ public class DesignationManagerCaching : ClassWithFishPatches if (designation.def == def) return designation; } + return null; } + public static Designation? Fallback(IEnumerable designations, DesignationDef def) { foreach (var designation in designations) @@ -111,20 +141,28 @@ public class DesignationManagerCaching : ClassWithFishPatches if (designation.def == def) return designation; } + return null; } - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) => Reflection.GetCodeInstructions(DesignationOn); + + public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) + => Reflection.GetCodeInstructions(DesignationOn); } public class HasMapDesignationOn_Patch : FishPatch { public override string Description => "Designation caching"; public override Expression TargetMethod => () => default(DesignationManager)!.HasMapDesignationOn(null); - public static bool HasMapDesignationOn(DesignationManager __instance, Thing t) => __instance.AllDesignationsOn(t).Any(); - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) => Reflection.GetCodeInstructions(HasMapDesignationOn); + + public static bool HasMapDesignationOn(DesignationManager __instance, Thing t) + => __instance.AllDesignationsOn(t).Any(); + + public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) + => Reflection.GetCodeInstructions(HasMapDesignationOn); } - public struct DesignationCache : Cache.IIsRefreshable>, DesignationCache> + public record struct DesignationCache : Cache.IIsRefreshable< + Cache.ByIntRefreshable>, DesignationCache> where T : notnull { public IEnumerable designations; @@ -139,6 +177,7 @@ public class DesignationManagerCaching : ClassWithFishPatches _manager = manager; _managerListState = manager.allDesignations.Version(); } + public void UpdateList(IEnumerable designations) { var cachedList = (List)this.designations; @@ -147,12 +186,17 @@ public class DesignationManagerCaching : ClassWithFishPatches ShouldRefreshNow = false; _managerListState = _manager.allDesignations.Version(); } + public bool ShouldRefreshNow { - get => _nextRefreshTick < Current.Game.tickManager.TicksGame || _managerListState != _manager.allDesignations.Version(); + get + => _nextRefreshTick < Current.Game.tickManager.TicksGame + || _managerListState != _manager.allDesignations.Version(); set => _nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); } - public DesignationCache SetNewValue(Cache.ByIntRefreshable> key) => throw new NotImplementedException(); + + public DesignationCache SetNewValue(Cache.ByIntRefreshable> key) + => throw new NotImplementedException(); } } diff --git a/Source/PerformanceFish/Experimental/InlineArray256.cs b/Source/PerformanceFish/Experimental/InlineArray256.cs new file mode 100644 index 0000000..7570f60 --- /dev/null +++ b/Source/PerformanceFish/Experimental/InlineArray256.cs @@ -0,0 +1,31 @@ +// 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 +/// +/// References within fixed size buffers don't get tracked by the GC. This leads to memory corruption. Don't use +/// +[StructLayout(LayoutKind.Explicit)] +public unsafe struct InlineArray256 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 \ No newline at end of file diff --git a/Source/PerformanceFish/Experimental/UnalignedPointer.cs b/Source/PerformanceFish/Experimental/UnalignedPointer.cs new file mode 100644 index 0000000..ed99ac7 --- /dev/null +++ b/Source/PerformanceFish/Experimental/UnalignedPointer.cs @@ -0,0 +1,58 @@ +// 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; + +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public unsafe struct UnalignedPointer +{ + private fixed byte _data[6]; // 48bit + + // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] // 48bit + // private byte[] _data; + + public void* Value + { + get + { + fixed (void* value = _data) + { + return value; + } + } + } + + public UnalignedPointer(void* pointer) + { + fixed (UnalignedPointer* instance = &this) + { + instance->_data = (byte*)pointer; + } + } +} + +public unsafe struct TaggedPointer +{ + private void* _data; + + public void* Value + { + get + { + var value = _data; + return value; + } + } + + public ushort UpperBits => (ushort)_data; + + public byte LowerBits => (byte)_data; + + public TaggedPointer(void* pointer) => _data = pointer; +} +#endif \ No newline at end of file diff --git a/Source/PerformanceFish/Experimental/mono.cs b/Source/PerformanceFish/Experimental/mono.cs new file mode 100644 index 0000000..0e10b96 --- /dev/null +++ b/Source/PerformanceFish/Experimental/mono.cs @@ -0,0 +1,300 @@ +// 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; + +// ReSharper disable once InconsistentNaming +[StaticConstructorOnStartup] +public class JITPatch +{ + static JITPatch() + { + + } +} + +/* + * Handling System.Type objects: + * + * Fields defined as System.Type in managed code should be defined as MonoObject* + * in unmanaged structures, and the monotype_cast () function should be used for + * casting them to MonoReflectionType* to avoid crashes/security issues when + * encountering instances of user defined subclasses of System.Type. + */ + +/* This corresponds to System.Type */ +[MONO_RT_MANAGED_ATTR] +struct MonoReflectionType +{ + MonoObject @object; + MonoType* type; +} + +/* This corresponds to System.RuntimeType */ +unsafe struct MonoReflectionMonoType +{ + MonoReflectionType type; + MonoObject* type_info; +} + +/* + * The following structure must match the C# implementation in our corlib. + */ + +[MONO_RT_MANAGED_ATTR] +unsafe struct MonoReflectionMethod +{ + MonoObject @object; + MonoMethod* method; + MonoString* name; + MonoReflectionType* reftype; +} +/* Safely access System.Reflection.MonoMethod from native code */ + +[MONO_RT_MANAGED_ATTR] +unsafe struct MonoReflectionMethodBody +{ + MonoObject @object; + MonoArray* clauses; + MonoArray* locals; + MonoArray* il; + MonoBoolean init_locals; + uint local_var_sig_token; + uint max_stack; +} + +unsafe struct MonoMethodInfo +{ + MonoReflectionType* parent; + MonoReflectionType* ret; + uint attrs; + uint implattrs; + uint callconv; +} + +[MONO_RT_MANAGED_ATTR] +struct MonoString +{ + MonoObject @object; + int length; + ushort chars [MONO_ZERO_LEN_ARRAY]; +} + +[MONO_RT_MANAGED_ATTR] +unsafe struct MonoArray +{ + MonoObject obj; + /* bounds is NULL for szarrays */ + MonoArrayBounds* bounds; + /* total number of elements of the array */ + uint max_length; + /* we use double to ensure proper alignment on platforms that need it */ + double vector [MONO_ZERO_LEN_ARRAY]; +} + +struct MonoArrayBounds +{ + uint length; + int lower_bound; +} + +struct MonoBoolean +{ + byte Value; +} + +[MONO_RT_MANAGED_ATTR] +unsafe struct MonoObject +{ + MonoVTable* vtable; + MonoThreadsSync* synchronisation; +} + +unsafe struct MonoMethod { + ushort flags; /* method flags */ + ushort iflags; /* method implementation flags */ + uint token; + MonoClass* klass; /* To what class does this method belong */ + MonoMethodSignature* signature; + /* name is useful mostly for debugging */ + readonly char* name; + /* this is used by the inlining algorithm */ + uint inline_info:1; + uint inline_failure:1; + uint wrapper_type:5; + uint string_ctor:1; + uint save_lmf:1; + uint dynamic:1; /* created & destroyed during runtime */ + uint sre_method:1; /* created at runtime using Reflection.Emit */ + uint is_generic:1; /* whenever this is a generic method definition */ + uint is_inflated:1; /* whether we're a MonoMethodInflated */ + uint skip_visibility:1; /* whenever to skip JIT visibility checks */ + uint verification_success:1; /* whether this method has been verified successfully.*/ + int slot : 16; + + /* + * If is_generic is TRUE, the generic_container is stored in image->property_hash, + * using the key MONO_METHOD_PROP_GENERIC_CONTAINER. + */ +} + +unsafe struct MonoClass +{ + /* element class for arrays and enum basetype for enums */ + MonoClass* element_class; + /* used for subtype checks */ + MonoClass* cast_class; + + /* for fast subtype checks */ + MonoClass** supertypes; + ushort idepth; + + /* array dimension */ + byte rank; + + int instance_size; /* object instance size */ + + uint inited : 1; + + /* A class contains static and non static data. Static data can be + * of the same type as the class itself, but it does not influence + * the instance size of the class. To avoid cyclic calls to + * mono_class_init (from mono_class_instance_size ()) we first + * initialise all non static fields. After that we set size_inited + * to 1, because we know the instance size now. After that we + * initialise all static fields. + */ + + /* ALL BITFIELDS SHOULD BE WRITTEN WHILE HOLDING THE LOADER LOCK */ + uint size_inited : 1; + uint valuetype : 1; /* derives from System.ValueType */ + uint enumtype : 1; /* derives from System.Enum */ + uint blittable : 1; /* class is blittable */ + uint unicode : 1; /* class uses unicode char when marshalled */ + uint wastypebuilder : 1; /* class was created at runtime from a TypeBuilder */ + uint is_array_special_interface : 1; /* gtd or ginst of once of the magic interfaces that arrays implement */ + + /* next byte */ + byte min_align; + + /* next byte */ + uint packing_size : 4; + uint ghcimpl : 1; /* class has its own GetHashCode impl */ + uint has_finalize : 1; /* class has its own Finalize impl */ + uint marshalbyref : 1; /* class is a MarshalByRefObject */ + uint contextbound : 1; /* class is a ContextBoundObject */ + /* next byte */ + uint @delegate : 1; /* class is a Delegate */ + uint gc_descr_inited : 1; /* gc_descr is initialized */ + uint has_cctor : 1; /* class has a cctor */ + uint has_references : 1; /* it has GC-tracked references in the instance */ + uint has_static_refs : 1; /* it has static fields that are GC-tracked */ + uint no_special_static_fields : 1; /* has no thread/context static fields */ + /* directly or indirectly derives from ComImport attributed class. + * this means we need to create a proxy for instances of this class + * for COM Interop. set this flag on loading so all we need is a quick check + * during object creation rather than having to traverse supertypes + */ + uint is_com_object : 1; + uint nested_classes_inited : 1; /* Whenever nested_class is initialized */ + + /* next byte*/ + uint class_kind : 3; /* One of the values from MonoTypeKind */ + uint interfaces_inited : 1; /* interfaces is initialized */ + uint simd_type : 1; /* class is a simd intrinsic type */ + uint has_finalize_inited : 1; /* has_finalize is initialized */ + uint fields_inited : 1; /* setup_fields () has finished */ + uint has_failure : 1; /* See mono_class_get_exception_data () for a MonoErrorBoxed with the details */ + uint has_weak_fields : 1; /* class has weak reference fields */ + + MonoClass* parent; + MonoClass* nested_in; + + MonoImage* image; + const char* name; + const char* name_space; + + uint type_token; + int vtable_size; /* number of slots */ + + ushort interface_count; + uint interface_id; /* unique inderface id (for interfaces) */ + uint max_interface_id; + + ushort interface_offsets_count; + MonoClass** interfaces_packed; + ushort* interface_offsets_packed; +/* enabled only with small config for now: we might want to do it unconditionally */ + byte* interface_bitmap; + + MonoClass** interfaces; + + [StructLayout(LayoutKind.Explicit)] // union + struct sizes + { + [FieldOffset(0)] + int class_size; /* size of area for static fields */ + [FieldOffset(0)] + int element_size; /* for array types */ + [FieldOffset(0)] + int generic_param_token; /* for generic param types, both var and mvar */ + } + + /* + * Field information: Type and location from object base + */ + MonoClassField* fields; + + MonoMethod** methods; + + /* used as the type of the this argument and when passing the arg by value */ + MonoType this_arg; + MonoType byval_arg; + + MonoGCDescriptor gc_descr; + + MonoClassRuntimeInfo *runtime_info; + + /* Generic vtable. Initialized by a call to mono_class_setup_vtable () */ + MonoMethod** vtable; + + /* Infrequently used items. See class-accessors.c: InfrequentDataKind for what goes into here. */ + MonoPropertyBag infrequent_data; + + void* unity_user_data; +} + +/* the interface_offsets array is stored in memory before this struct */ +unsafe struct MonoVTable +{ + MonoClass* klass; + /* + * According to comments in gc_gcj.h, this should be the second word in + * the vtable. + */ + MonoGCDescriptor gc_descr; + MonoDomain* domain; /* each object/vtable belongs to exactly one domain */ + nint type; /* System.Type type for klass */ + byte* interface_bitmap; + uint max_interface_id; + byte rank; + /* Keep this a guint8, the jit depends on it */ + byte initialized; /* cctor has been run */ + uint remote : 1; /* class is remotely activated */ + uint init_failed : 1; /* cctor execution failed */ + uint has_static_fields : 1; /* pointer to the data stored at the end of the vtable array */ + uint gc_bits : MONO_VTABLE_AVAILABLE_GC_BITS; /* Those bits are reserved for the usaged of the GC */ + + uint imt_collisions_bitmap; + MonoRuntimeGenericContext* runtime_generic_context; + /* do not add any fields after vtable, the structure is dynamically extended */ + /* vtable contains function pointers to methods or their trampolines, at the + end there may be a slot containing the pointer to the static fields */ + nint vtable [MONO_ZERO_LEN_ARRAY]; +} +#endif \ No newline at end of file diff --git a/Source/PerformanceFish/FishDefOf.cs b/Source/PerformanceFish/FishDefOf.cs index 2989903..86de5e5 100644 --- a/Source/PerformanceFish/FishDefOf.cs +++ b/Source/PerformanceFish/FishDefOf.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. diff --git a/Source/PerformanceFish/FishPatch.cs b/Source/PerformanceFish/FishPatch.cs deleted file mode 100644 index c8c389d..0000000 --- a/Source/PerformanceFish/FishPatch.cs +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright (c) 2022 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 System.Collections.Concurrent; -using System.Diagnostics; -using System.Linq; - -namespace PerformanceFish; - -public abstract class FishPatch : SingletonFactory, IExposable, IHasDescription -{ - public virtual bool ShouldBenchmark => false; - - public virtual bool Enabled - { - get => _enabled; - set - { - _enabled = value; - if (value) - { - if (ShouldBePatched) - TryPatch(); - } - else - { - var shouldBePatched = ShouldBePatched; - TryUnpatch(); - ShouldBePatched = shouldBePatched; - } - } - } - private bool _enabled = true; - public virtual bool DefaultState => true; - public virtual int PrefixMethodPriority => TryGetPriority(PrefixMethodInfo); - public virtual int PostfixMethodPriority => TryGetPriority(PostfixMethodInfo); - public virtual int TranspilerMethodPriority => TryGetPriority(TranspilerMethodInfo); - public virtual int FinalizerMethodPriority => TryGetPriority(FinalizerMethodInfo); - public MethodInfo? HarmonyMethodInfo => HarmonyMethodInfos.FirstOrDefault(); - public List HarmonyMethodInfos { get; } = new(); - public MethodInfo? BenchmarkHarmonyMethodInfo => BenchmarkHarmonyMethodInfos.FirstOrDefault(); - public List BenchmarkHarmonyMethodInfos { get; } = new(); - private bool Patched { get; set; } - private bool ShouldBePatched { get; set; } - - public virtual string? Name => null; - public virtual string? Description => null; - - public virtual Delegate? TargetMethodGroup => null; - public virtual IEnumerable? TargetMethodGroups => null; - public virtual Expression? TargetMethod => null; - public virtual IEnumerable>? TargetMethods => null; - public virtual MethodBase TargetMethodInfo - => TargetMethod != null - ? SymbolExtensions.GetMethodInfo(TargetMethod) - : TargetMethodGroup?.Method - ?? throw new MissingMethodException(GetType().ToString()); - - public virtual IEnumerable TargetMethodInfos - => _targetMethodInfos - ??= (TargetMethods?.Select(m - => SymbolExtensions.GetMethodInfo(m)) - ?? TargetMethodGroups?.Select(m => m.Method) - ?? (TargetMethodInfo != null - ? new MethodBase[] { TargetMethodInfo }.AsEnumerable() - : throw new MissingMethodException(GetType().ToString()))).ToArray(); - - private MethodBase[]? _targetMethodInfos; - - public virtual MethodInfo ReversePatchMethodInfo => GetType().GetMethods(AccessTools.all) - .FirstOrDefault(m => m.Name.ToUpperInvariant() == "REVERSEPATCH" || (m.HasAttribute() && !m.HasAttribute())); - public virtual MethodInfo ReversePatchTranspilerMethodInfo => GetType().GetMethods(AccessTools.all) - .FirstOrDefault(m => m.Name.ToUpperInvariant() == "REVERSEPATCHTRANSPILER" || (m.HasAttribute() && m.HasAttribute())); - public virtual MethodInfo PrefixMethodInfo => GetType().GetMethods(AccessTools.all) - .FirstOrDefault(m => m.Name.ToUpperInvariant() == "PREFIX" || m.HasAttribute()); - public virtual MethodInfo PostfixMethodInfo => GetType().GetMethods(AccessTools.all) - .FirstOrDefault(m => m.Name.ToUpperInvariant() == "POSTFIX" || m.HasAttribute()); - public virtual MethodInfo TranspilerMethodInfo => GetType().GetMethods(AccessTools.all) - .FirstOrDefault(m => m.Name.ToUpperInvariant() == "TRANSPILER" || (m.HasAttribute() && !m.HasAttribute())); - public virtual MethodInfo FinalizerMethodInfo => GetType().GetMethods(AccessTools.all) - .FirstOrDefault(m => m.Name.ToUpperInvariant() == "FINALIZER" || m.HasAttribute()); - - internal bool IsBenchmarking => ShouldBenchmark && !FinishedBenchmarking; - internal bool FinishedBenchmarking = false; - - protected FishPatch() => _enabled = DefaultState; - - public virtual MethodInfo? TryPatch() - { - if (!TargetMethodInfos?.Any() ?? true) - { - Log.Error($"Tried to apply patches for {GetType().Name}, but it lacks a target method for patching."); - return null; - } - if (PrefixMethodInfo is null && PostfixMethodInfo is null && TranspilerMethodInfo is null && FinalizerMethodInfo is null && ReversePatchMethodInfo is null) - { - Log.Error($"Tried to apply patches for {GetType().Name}, but there are none. This is likely not intended."); - return null; - } - - ShouldBePatched = true; - if (Enabled && !Patched) - ApplyPatch(); - - return HarmonyMethodInfo; - } - - protected virtual void ApplyPatch() - { - Patched = true; - foreach (var method in TargetMethodInfos!) - { - DebugLog.Message($"Performance Fish is applying {GetType().Name} on {method.FullDescription()}"); - - try - { - if (IsBenchmarking) - { - Patched = false; - - if (AccessTools.EnumeratorMoveNext(method) is { } moveNext) - { - BenchmarkHarmonyMethodInfos.Add(PerformanceFishMod.Harmony.Patch(moveNext, - prefix: new(methodof(Benchmarking.Prefix), Priority.First + 1), - postfix: new(methodof(Benchmarking.Postfix), Priority.Last - 1))); - } - - BenchmarkHarmonyMethodInfos.Add(PerformanceFishMod.Harmony.Patch(method, - prefix: new(methodof(Benchmarking.Prefix), Priority.First + 1), - postfix: new(methodof(Benchmarking.Postfix), Priority.Last - 1))); - } - else - { - if (ReversePatchMethodInfo != null) - HarmonyMethodInfos.Add(Harmony.ReversePatch(method, new(ReversePatchMethodInfo), ReversePatchTranspilerMethodInfo)); - - if (PrefixMethodInfo != null || PostfixMethodInfo != null || TranspilerMethodInfo != null || FinalizerMethodInfo != null) - { - HarmonyMethodInfos.Add(PerformanceFishMod.Harmony.Patch(method, - prefix: PrefixMethodInfo != null ? new(PrefixMethodInfo, PrefixMethodPriority) : null, - postfix: PostfixMethodInfo != null ? new(PostfixMethodInfo, PostfixMethodPriority) : null, - transpiler: TranspilerMethodInfo != null ? new(TranspilerMethodInfo, TranspilerMethodPriority) : null, - finalizer: FinalizerMethodInfo != null ? new(FinalizerMethodInfo, FinalizerMethodPriority) : null)); - } - } - } - catch (Exception e) - { - Log.Error($"Performance Fish encountered an exception while trying to patch {method.FullDescription()} with {PrefixMethodInfo.FullDescription()}" + - $", {PostfixMethodInfo.FullDescription()}, {TranspilerMethodInfo.FullDescription()}, {FinalizerMethodInfo.FullDescription()}:\n{e}"); - } - } - } - - public virtual void TryUnpatch() - { - ShouldBePatched = false; - if (/*!Enabled &&*/ Patched) - ApplyUnpatching(); - } - - protected virtual void ApplyUnpatching() - { - Patched = false; - foreach (var method in TargetMethodInfos) - { - if (PrefixMethodInfo != null) - { - try - { - PerformanceFishMod.Harmony.Unpatch(method, PrefixMethodInfo); - } - catch (Exception e) - { - Log.Error($"Performance Fish encountered an exception when unpatching {PrefixMethodInfo.FullDescription()} from {method.FullDescription()}:\n{e}"); - } - } - if (PostfixMethodInfo != null) - { - try - { - PerformanceFishMod.Harmony.Unpatch(method, PostfixMethodInfo); - } - catch (Exception e) - { - Log.Error($"Performance Fish encountered an exception when unpatching {PostfixMethodInfo.FullDescription()} from {method.FullDescription()}:\n{e}"); - } - } - if (TranspilerMethodInfo != null) - { - try - { - PerformanceFishMod.Harmony.Unpatch(method, TranspilerMethodInfo); - } - catch (Exception e) - { - Log.Error($"Performance Fish encountered an exception when unpatching {TranspilerMethodInfo.FullDescription()} from {method.FullDescription()}:\n{e}"); - } - } - if (FinalizerMethodInfo != null) - { - try - { - PerformanceFishMod.Harmony.Unpatch(method, FinalizerMethodInfo); - } - catch (Exception e) - { - Log.Error($"Performance Fish encountered an exception when unpatching {FinalizerMethodInfo.FullDescription()} from {method.FullDescription()}:\n{e}"); - } - } - } - } - - private static int TryGetPriority(MethodInfo info) => info?.TryGetAttribute()?.info.priority ?? Priority.Normal; - - public virtual void ExposeData() - => Scribe_Values.Look(ref _enabled, "enabled", DefaultState); -} - -internal static class Benchmarking -{ - internal static int SwitchFrame = int.MaxValue; - internal static int EndFrame = int.MaxValue; - - public static void Prefix(MethodBase __originalMethod, out Results __state) - { - if (!Cache.ByReference.Get.TryGetValue(__originalMethod, out __state)) - Cache.ByReference.Get[__originalMethod] = __state = new(__originalMethod); - - __state.Stopwatch.Start(); - } - - public static void Postfix(Results __state) - { - __state.Stopwatch.Stop(); - __state.Count++; - - //Log.Message($"SwitchFrame is {SwitchFrame}, EndFrame is {EndFrame}, CurrentFrame is {Time.frameCount}"); - - //if (__state.Stopwatch.ElapsedMilliseconds > 100 || __state.Count > 100000) - if (__state.Switched ? GenTicks.TicksGame > EndFrame : GenTicks.TicksGame > SwitchFrame) - PrintResultsAndStop(__state); - } - - public static void PrintResultsAndStop(Results results) - { - var ticks = results.Stopwatch.ElapsedTicks; - Log.Message($"{results.Method.FullDescription()} took {ticks} ticks for {results.Count} calls. This equals {ticks * 1000L / results.Count} ticks per 1000 calls on average."); - - results.Count = 0; - results.Switched = true; - results.Stopwatch.Reset(); - - var fishPatch = PerformanceFishMod.AllPatchClasses - .SelectMany(patchClass - => patchClass.Patches) - .First(patch - => patch.TargetMethodInfos.Contains(results.Method)); - - if (fishPatch.FinishedBenchmarking) - { - foreach (var target in fishPatch.TargetMethodInfos) - { - if (AccessTools.EnumeratorMoveNext(target) is { } moveNext) - { - PerformanceFishMod.Harmony.Unpatch(moveNext, methodof(Prefix)); - PerformanceFishMod.Harmony.Unpatch(moveNext, methodof(Postfix)); - } - - PerformanceFishMod.Harmony.Unpatch(target, methodof(Prefix)); - PerformanceFishMod.Harmony.Unpatch(target, methodof(Postfix)); - } - } - else - { - fishPatch.FinishedBenchmarking = true; - fishPatch.TryPatch(); - } - } - - public class Results - { - public int Count; - public Stopwatch Stopwatch; - public MethodBase Method; - public bool Switched; - - public Results(MethodBase method) - { - Stopwatch = new(); - Method = method; - } - } -} - -public abstract class FirstPriorityFishPatch : FishPatch -{ - public override int PrefixMethodPriority => Priority.First; - public override int PostfixMethodPriority => Priority.First; - public override int TranspilerMethodPriority => Priority.First; - public override int FinalizerMethodPriority => Priority.First; -} - -public static class FishPatchExtensions -{ - public static void PatchAll(this IEnumerable patches) - { - foreach (var patch in patches) - patch.TryPatch(); - } - public static void UnpatchAll(this IEnumerable patches) - { - foreach (var patch in patches) - patch.TryUnpatch(); - } -} - -public class FishPatchHolder : IExposable, IEnumerable -{ - public ConcurrentDictionary All => _all; - public T Get() where T : FishPatch => (T)All[typeof(T)]; - public void Add(FishPatch patch) => All[patch.GetType()] = patch; - public void PatchAll() => All.Values.PatchAll(); - public void UnpatchAll() => All.Values.UnpatchAll(); - public FishPatch this[Type type] => All[type]; - - public FishPatchHolder(Type type) - { - _type = type; - AddPatchesRecursively(type); - } - - public void Scribe() => ExposeData(); // Exposable.Scribe(this, _type.FullName); // directly calling ExposeData prevents creating nested nodes in the config file. Looks cleaner imo. - - public void ExposeData() - { - foreach (var (type, patch) in All) - Exposable.Scribe(patch, type.FullName); - } - - private void AddPatchesRecursively(Type type) - { - if (typeof(FishPatch).IsAssignableFrom(type) && !All.ContainsKey(type)) - { - if (PerformanceFishMod.AllPatchClasses is { } allPatches) - RemoveDupes(allPatches, type); - - All.TryAdd(type, FishPatch.Get(type)); - } - - foreach (var nestedType in type.GetNestedTypes(AccessTools.all)) - AddPatchesRecursively(nestedType); - } - - private void RemoveDupes(IHasFishPatch[] patches, Type type) - { - var dupeClasses = patches - .Where(patchClass - => patchClass.GetType() != _type - && patchClass.Patches.All.ContainsKey(type)); - - if (!dupeClasses.Any()) - return; - - foreach (var patchClass in dupeClasses) - { - patchClass.Patches[type].Enabled = false; - patchClass.Patches.All.TryRemove(type, out _); - Log.Warning($"Performance Fish removed a duplicate patch from {patchClass.GetType().FullName}. This is likely caused by no longer valid mod configs"); - } - } - - public IEnumerator GetEnumerator() => All.Values.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => All.Values.GetEnumerator(); - - private ConcurrentDictionary _all = new(); - private Type _type; -} - -public interface IHasFishPatch -{ - public FishPatchHolder Patches { get; } - public bool RequiresLoadedGameForPatching { get; } -} - -public interface IHasDescription -{ - public string? Description { get; } -} - -public abstract class ClassWithFishPatches : SingletonFactory, IHasFishPatch -{ - public virtual FishPatchHolder Patches { get; } - public virtual bool RequiresLoadedGameForPatching => false; - - protected ClassWithFishPatches() => Patches = new(GetType()); -} \ No newline at end of file diff --git a/Source/PerformanceFish/FishSettings.cs b/Source/PerformanceFish/FishSettings.cs index c11110e..7547b74 100644 --- a/Source/PerformanceFish/FishSettings.cs +++ b/Source/PerformanceFish/FishSettings.cs @@ -1,51 +1,85 @@ -// Copyright (c) 2022 bradson +// 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 System.IO; +using System.Linq; +using JetBrains.Annotations; +using PerformanceFish.ModCompatibility; +using PerformanceFish.Planet; +using PerformanceFish.Prepatching; + namespace PerformanceFish; +[PublicAPI] public class FishSettings : ModSettings { + public static bool SettingsLoaded { get; private set; } + public override void ExposeData() { base.ExposeData(); - Scribe_Values.Look(ref _threadingEnabled, "ThreadingEnabled", false); + Scribe_Values.Look(ref _threadingEnabled, "ThreadingEnabled"); + Scribe_Values.Look(ref MothballEverything, "MothballEverything"); - foreach (var patchClass in PerformanceFishMod.AllPatchClasses) - patchClass.Patches.Scribe(); + if (PerformanceFishMod.AllPatchClasses is { } allPatchClasses) + { + foreach (var patchClass in allPatchClasses) + patchClass.Patches.Scribe(); + } + + if (PerformanceFishMod.AllPrepatchClasses is { } allPrepatchClasses) + { + foreach (var patchClass in allPrepatchClasses) + patchClass.Patches.Scribe(); + } + + SettingsLoaded = true; } public static void DoSettingsWindowContents(Rect inRect) { + var windowRect = Find.WindowStack.currentlyDrawnWindow.windowRect; + if (Widgets.ButtonText(new(windowRect.width - Window.CloseButSize.x - (Window.StandardMargin * 2f), + windowRect.height - Window.FooterRowHeight - Window.StandardMargin, Window.CloseButSize.x, + Window.CloseButSize.y), + "Reset all")) + { + ResetAll(); + } + Widgets.BeginScrollView(inRect, ref _scrollPosition, _scrollRect); var ls = new Listing_Standard(); ls.Begin(_scrollRect); var checkModEnabled = ModEnabled; - ls.CheckboxLabeled("Toggle all patches", ref checkModEnabled, "This enables or disables all patches of the mod with one click. For testing purposes"); + ls.CheckboxLabeled("Toggle all patches", ref checkModEnabled, + "This enables or disables all patches of the mod with one click. For testing purposes"); if (checkModEnabled != ModEnabled) { ModEnabled = checkModEnabled; - foreach (var patchClass in PerformanceFishMod.AllPatchClasses) - { - foreach (var patch in patchClass.Patches) - patch.Enabled = checkModEnabled; - - if (checkModEnabled && (!patchClass.RequiresLoadedGameForPatching || Current.ProgramState == ProgramState.Playing)) - patchClass.Patches.PatchAll(); - } + ToggleAllPatches(checkModEnabled); } + + ls.Gap(); + var curFontStyle = Text.CurFontStyle; + var curFontSize = curFontStyle.fontSize; + curFontStyle.fontSize = 20; + ls.Label("Harmony Patches"); + curFontStyle.fontSize = curFontSize; + ls.GapLine(2f); + ls.Gap(); - foreach (var patchClass in PerformanceFishMod.AllPatchClasses) + foreach (var patchClass in PerformanceFishMod.AllPatchClasses!) { try { ls.Label(patchClass.GetType().Name, tooltip: patchClass is IHasDescription classWithDescription - ? classWithDescription.Description - : null); + ? classWithDescription.Description + : null); ls.GapLine(2f); foreach (var patch in patchClass.Patches) @@ -62,10 +96,13 @@ public class FishSettings : ModSettings var label = patch.Name ?? patch.GetType().Name; if (label.EndsWith("_Patch")) label = label.Remove(label.Length - 6); + else if (label.EndsWith("Patch")) + label = label.Remove(label.Length - 5); ls.CheckboxLabeled(label, ref check, patch.Description); if (check != patch.Enabled) patch.Enabled = check; } + ls.Gap(); } catch (Exception ex) @@ -73,12 +110,83 @@ public class FishSettings : ModSettings Log.Error($"{ex}"); } } + + ls.Gap(); + curFontStyle.fontSize = 20; + ls.Label("Prepatches"); + curFontStyle.fontSize = curFontSize; + ls.GapLine(); + ls.Gap(); + + foreach (var patchClass in PerformanceFishMod.AllPrepatchClasses!) + { + try + { + ls.Label(patchClass.GetType().Name, + // ReSharper disable once SuspiciousTypeConversion.Global + tooltip: patchClass is IHasDescription classWithDescription + ? classWithDescription.Description + : null); - //ls.Gap(); - //var threadingEnabled = ThreadingEnabled; - //ls.CheckboxLabeled("Allow threading", ref threadingEnabled, "Currently affects the ListerHaulables tick, ingredient lookups in WorkGiver_DoBill and refreshing of the shared altar thoughtworker cache. " + - // "Experimental and disabled by default."); - //ThreadingEnabled = threadingEnabled; + ls.GapLine(2f); + foreach (var patch in patchClass.Patches) + { + if (ShouldSkipForScrollView(inRect.height, Text.LineHeight, ls.curY, _scrollPosition.y)) + { + ls.curY += Text.LineHeight; + continue; + } + + var check = patch.Enabled; + if (patch.Description == null) + Widgets.DrawHighlightIfMouseover(_scrollRect with { y = ls.curY, height = Text.LineHeight }); + var label = patch.Name ?? patch.GetType().Name; + if (label.EndsWith("_Patch")) + label = label.Remove(label.Length - 6); + else if (label.EndsWith("Patch")) + label = label.Remove(label.Length - 5); + ls.CheckboxLabeled(label, ref check, patch.Description); + if (check != patch.Enabled) + patch.Enabled = check; + } + + ls.Gap(); + } + catch (Exception ex) + { + Log.Error($"{ex}"); + } + } + + ls.Gap(); + ls.CheckboxLabeled("Mothball everything", ref MothballEverything, MothballEverythingDescription); + + ls.Gap(); + + if (ls.ButtonText("Log hediffs affected by mothball optimization")) + { + Log.Message($"Allowed defs for mothballing: {MothballOptimization.AllowedDefs.ToStringSafeEnumerable()}"); + Log.Message($"Blocking defs for mothballing: {DefDatabase.AllDefsListForReading.Where(static def + => !MothballOptimization.AllowedDefs.Contains(def)).ToStringSafeEnumerable()}"); + + Verse.Log.TryOpenLogWindow(); + } + + ls.Gap(); + + if (ls.ButtonText("Log patch count")) + { + PerformanceFishMod.LogPatchCount(); + Verse.Log.TryOpenLogWindow(); + } + + ls.Gap(); + + if (ls.ButtonText("Log cache utilization")) + { + Cache.Utility.LogCurrentCacheUtilization(); + Verse.Log.TryOpenLogWindow(); + } ls.Gap(); if (ls.ButtonText("Clear Cache")) @@ -87,14 +195,72 @@ public class FishSettings : ModSettings ls.End(); Widgets.EndScrollView(); - _scrollRect = _scrollRect with { height = ls.curY + 50f, width = inRect.width - GUI.skin.verticalScrollbar.fixedWidth - 5f }; + _scrollRect = _scrollRect with + { + height = ls.curY + 50f, width = inRect.width - GUI.skin.verticalScrollbar.fixedWidth - 5f + }; } - public static bool ShouldSkipForScrollView(float scrollViewSize, float entrySize, float entryPosition, float scrollPosition) + public static string MothballEverythingDescription + = MothballOptimization.BASIC_DESCRIPTION + " This setting removes all conditions, enabling every world " + + "pawn for mothballing. Note, health conditions do not progress on mothballed pawns. Requires the " + + "WorldPawnsDefPreventingMothball patch to apply"; + + private static void ToggleAllPatches(bool newState) => ToggleAllPatches(_ => newState, _ => newState); + + private static void ToggleAllPatches(Func fishPatchFunc, + Func fishPrepatchFunc) + { + foreach (var patchClass in PerformanceFishMod.AllPatchClasses!) + { + foreach (var patch in patchClass.Patches) + patch.Enabled = fishPatchFunc(patch); + } + + foreach (var patchClass in PerformanceFishMod.AllPrepatchClasses!) + { + foreach (var patch in patchClass.Patches) + patch.Enabled = fishPrepatchFunc(patch); + } + + Cache.Utility.Clear(); + } + + private static void ResetAll() + { + ToggleAllPatches(static patch => patch.DefaultState, static prepatch => prepatch.DefaultState); + TryDeleteModSettingsFile(); + } + + private static void TryDeleteModSettingsFile() + { + try + { + var settingsFile = new FileInfo(LoadedModManager.GetSettingsFilename( + PerformanceFishMod.Mod!.Content.FolderName, nameof(PerformanceFishMod))); + if (settingsFile.Exists) + settingsFile.Delete(); + } + catch (Exception e) + { + Log.Error($"Exception thrown while trying to delete mod settings:\n{e}"); + } + } + + public static bool ShouldSkipForScrollView(float scrollViewSize, float entrySize, float entryPosition, + float scrollPosition) => entryPosition + entrySize < scrollPosition || entryPosition > scrollPosition + scrollViewSize; - public static bool ModEnabled { get => _modEnabled; set => _modEnabled = value; } + public static bool ModEnabled + { + get => _modEnabled; + set => _modEnabled = value; + } + private static bool _modEnabled = true; + + public static bool MothballEverything; + public static bool ThreadingEnabled { get => false; //_threadingEnabled; @@ -107,8 +273,28 @@ public class FishSettings : ModSettings _threadingEnabled = value; } } - private static bool _threadingEnabled = false; + + private static bool _threadingEnabled; public static List ThreadingPatches { get; } = new(); private static Rect _scrollRect = new(0f, 0f, 500f, 9001f); private static Vector2 _scrollPosition; + + private static FishSettings TryLoadModSettings(bool fallBackAttempt = false) + { + try + { + return PerformanceFishMod.Settings = LoadedModManager.ReadModSettings( + ActiveMods.TryGetModContentPack(PerformanceFishMod.PACKAGE_ID)!.FolderName, nameof(PerformanceFishMod)); + } + catch (Exception ex) + { + Log.Error($"{PerformanceFishMod.NAME} encountered an exception while trying to load its settings. " + + $"Resetting now to avoid further issues.\n{ex}"); + TryDeleteModSettingsFile(); + + return !fallBackAttempt ? TryLoadModSettings(true) : null!; + } + } + + public static FishSettings Instance { get; private set; } = TryLoadModSettings(); } \ No newline at end of file diff --git a/Source/PerformanceFish/FunctionPointers/IndexGetter.cs b/Source/PerformanceFish/FunctionPointers/IndexGetter.cs index bbc33e0..8873ccd 100644 --- a/Source/PerformanceFish/FunctionPointers/IndexGetter.cs +++ b/Source/PerformanceFish/FunctionPointers/IndexGetter.cs @@ -1,9 +1,11 @@ -// Copyright (c) 2022 bradson +// 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 System.Linq; +using JetBrains.Annotations; +using RimWorld.Planet; namespace PerformanceFish.FunctionPointers; @@ -14,44 +16,48 @@ public static class IndexGetter where T : notnull public static class IndexGetter { + [UsedImplicitly(ImplicitUseTargetFlags.Members)] public static class Methods { public static int HediffSet(HediffSet set) => set.pawn.thingIDNumber; - public static int HediffDef(HediffDef def) => def.index; + public static int HediffDef(HediffDef def) => def.shortHash; public static int Bill(Bill bill) => bill.loadID; public static int Pawn(Pawn pawn) => pawn.thingIDNumber; public static int Thing(Thing thing) => thing.thingIDNumber; public static int CompAssignableToPawn_Bed(CompAssignableToPawn_Bed comp) => comp.parent.thingIDNumber; public static int Map(Map map) => map.uniqueID; public static int DesignationManager(DesignationManager manager) => manager.map.uniqueID; - public static int DesignationDef(DesignationDef def) => def.index; + 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.index; + 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; } internal static Cache.IndexGetter GetDefaultIndexGetter() where T : notnull => _indexFuncs.TryGetValue(typeof(T), out var value) - ? (Cache.IndexGetter)value - : Fallback(); + ? (Cache.IndexGetter)value + : Fallback(); internal static unsafe delegate* GetDefaultIndexFunctionPointer() where T : notnull - => (delegate*)(GetGetterMethods().FirstOrDefault(m => m.GetParameters()[0].ParameterType == typeof(T)) - ?? Fallback().Method).MethodHandle.GetFunctionPointer(); + => (delegate*)(_getterMethods.TryGetValue(typeof(T)) + ?? Fallback().Method).MethodHandle.GetFunctionPointer(); private static IEnumerable GetGetterMethods() - => typeof(Methods) - .GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly) - .Where(m - => m.ReturnType == typeof(int) - && m.GetParameters().Length == 1); + => typeof(Methods).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly) + .Where(static m => m.ReturnType == typeof(int) && m.GetParameters().Length == 1); private static Cache.IndexGetter Fallback() where T : notnull { var isDef = typeof(Def).IsAssignableFrom(typeof(T)); - var isThing = false; //typeof(Thing).IsAssignableFrom(typeof(T)) && !typeof(Mote).IsAssignableFrom(typeof(T)); // generally always safe in vanilla, but logging mods won't hurt either + var isThing = false; //typeof(Thing).IsAssignableFrom(typeof(T)) && !typeof(Mote).IsAssignableFrom(typeof(T)); + //// generally always safe in vanilla, but logging mods won't hurt either - var message = $"Performance Fish failed to find type {typeof(T).FullDescription()} in indexFuncs dictionary. Using fallback instead"; + var message + = $"Performance Fish failed to find type { + typeof(T).FullName} in indexFuncs dictionary. Using fallback instead"; if (isDef) Log.Error($"{message} to prevent further issues, but this likely won't work correctly."); @@ -60,18 +66,26 @@ public static class IndexGetter var type = typeof(T).BaseType; Delegate func; - while (!_indexFuncs.TryGetValue(type, out func)) - type = type.BaseType ?? throw new Exception($"Performance Fish failed to find a fallback method to handle type {typeof(T).FullDescription()} from mod {typeof(T).Assembly.GetName().Name}"); + while (!_indexFuncs.TryGetValue(type, out func!)) + { + type = type.BaseType + ?? throw new($"Performance Fish failed to find a fallback method to handle type { + typeof(T).FullName} from mod {typeof(T).Assembly.GetName().Name}"); + } if (!isThing) - Log.Message($"Found fallback of type {type.FullDescription()}.{(!isDef ? " This likely means we can safely continue from here." : "")}"); + { + Log.Message($"Found fallback of type {type.FullName}.{ + (!isDef ? " This likely means we can safely continue from here." : "")}"); + } return (Cache.IndexGetter)func; } + private static Dictionary _getterMethods + = GetGetterMethods().ToDictionary(static m => m.GetParameters()[0].ParameterType, static m => m); + private static Dictionary _indexFuncs - = GetGetterMethods() - .ToDictionary( - m => m.GetParameters()[0].ParameterType, - m => m.CreateDelegate(typeof(Cache.IndexGetter<>).MakeGenericType(m.GetParameters()[0].ParameterType))); + = _getterMethods.ToDictionary(static m => m.Key, + static m => m.Value.CreateDelegate(typeof(Cache.IndexGetter<>).MakeGenericType(m.Key))); } \ No newline at end of file diff --git a/Source/PerformanceFish/GenLocalDateCaching.cs b/Source/PerformanceFish/GenLocalDateCaching.cs index 68e3908..12676a5 100644 --- a/Source/PerformanceFish/GenLocalDateCaching.cs +++ b/Source/PerformanceFish/GenLocalDateCaching.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -6,18 +6,23 @@ using System.Linq; using System.Reflection.Emit; +// ReSharper disable PossibleMultipleEnumeration + namespace PerformanceFish; public class GenLocalDateCaching : ClassWithFishPatches { public class DayTickByThing_Patch : FishPatch { - public override string? Description => "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."; - public override Delegate? TargetMethodGroup => (Func)GenLocalDate.DayTick; + public override string Description { get; } + = "Caches results of GenLocalDate.DayTick for the first map. This is similar to Rim73's mind state " + + "optimization, but yields accurate results instead of a placeholder value to avoid issues."; + + public override Delegate TargetMethodGroup { get; } = (Func)GenLocalDate.DayTick; public override int TranspilerMethodPriority => Priority.First; public static CodeInstructions Transpiler(CodeInstructions codes, MethodBase method, ILGenerator generator) - //=> Reflection.GetCodeInstructions(Replacement); + //=> Reflection.GetCodeInstructions(Replacement); { var labelToStartOfMethod = generator.DefineLabel(); codes.First().labels.Add(labelToStartOfMethod); @@ -37,7 +42,6 @@ public class GenLocalDateCaching : ClassWithFishPatches foreach (var code in codes) yield return code; - } //[MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -58,21 +62,24 @@ public class GenLocalDateCaching : ClassWithFishPatches public class DayTickByMap_Patch : FishPatch { - public override string? Description => "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."; - public override Delegate? TargetMethodGroup => (Func)GenLocalDate.DayTick; + public override string Description { get; } + = "Caches results of GenLocalDate.DayTick for the first map. This is similar to Rim73's mind state " + + "optimization, but yields accurate results instead of a placeholder value to avoid issues."; + + public override Delegate TargetMethodGroup { get; } = (Func)GenLocalDate.DayTick; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Prefix(Map map, ref int __result, out bool __state) { if (map != SavedMap) { - if (SavedMap is null && Find.Maps?.Count > 0 && Find.Maps[0] == map) + if (SavedMap is null && Find.Maps is { Count: > 0 } maps && maps[0] == map) SavedMap = map; return __state = true; } - if (SavedTick != GenTicks.TicksGame) + if (SavedTick != TickHelper.TicksGame) return __state = true; __result = DayTick; @@ -85,7 +92,7 @@ public class GenLocalDateCaching : ClassWithFishPatches if (!__state || SavedMap != map) return; - SavedTick = GenTicks.TicksGame; + SavedTick = TickHelper.TicksGame; DayTick = __result; } } diff --git a/Source/PerformanceFish/GenTypesPatches.cs b/Source/PerformanceFish/GenTypesPatches.cs new file mode 100644 index 0000000..8a120c5 --- /dev/null +++ b/Source/PerformanceFish/GenTypesPatches.cs @@ -0,0 +1,136 @@ +// 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 System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; +using PerformanceFish.Prepatching; + +namespace PerformanceFish; + +public class GenTypesPatches : ClassWithFishPrepatches +{ + public class AllTypes : FishPrepatch + { + public override string? Description { get; } + = "Fix for thread safety. The vanilla method was rarely breaking and preventing the game from loading"; + + public static object Lock = new(); + + public override MethodBase TargetMethodBase { get; } + = AccessTools.PropertyGetter(typeof(GenTypes), nameof(GenTypes.AllTypes)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Prefix() => Monitor.Enter(Lock); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Postfix() => Monitor.Exit(Lock); // TODO: replace with finalizer + } + + public class AllTypesWithAttribute : FishPrepatch + { + public override string? Description { get; } + = "Fix for thread safety. The vanilla method was rarely breaking and preventing the game from loading"; + + public static object Lock = new(); + + public override MethodBase TargetMethodBase { get; } = methodof(GenTypes.AllTypesWithAttribute); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(ReplacementBody); + + public static List ReplacementBody() where TAttr : Attribute + { + lock (Lock) + { + var typesWithAttribute = GenTypes.cachedTypesWithAttribute.TryGetValue(typeof(TAttr)); + + if (typesWithAttribute is null) + { + GenTypes.cachedTypesWithAttribute.Add(typeof(TAttr), + typesWithAttribute = GenTypes.AllTypes.AsParallel() + .Where(Predicate()).ToList()); + } + + return typesWithAttribute; + } + } + + public static Func Predicate() where TAttr : Attribute + => static type => type.HasAttribute(); + } + + public class AllSubclasses : FishPrepatch + { + public override string? Description { get; } + = "Fix for thread safety. The vanilla method was rarely breaking and preventing the game from loading"; + + public static object Lock = new(); + + public override MethodBase TargetMethodBase { get; } = methodof(GenTypes.AllSubclasses); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(ReplacementBody); + + public static List ReplacementBody(Type baseType) + { + lock (Lock) + { + var subclasses = GenTypes.cachedSubclasses.TryGetValue(baseType); + + if (subclasses is null) + { + ((PredicateClass)Predicate.Target).BaseType = baseType; + GenTypes.cachedSubclasses.Add(baseType, + subclasses = GenTypes.AllTypes.AsParallel() + .Where(Predicate).ToList()); + } + + return subclasses; + } + } + + public static Func Predicate = new PredicateClass().Invoke; + + public class PredicateClass + { + public Type? BaseType; + public bool Invoke(Type type) => type.IsSubclassOf(BaseType!); + } + } + + public class AllSubclassesNonAbstract : FishPrepatch + { + public override string? Description { get; } + = "Fix for thread safety, paired with a small optimization for faster loading. The vanilla method was " + + "rarely breaking and preventing the game from loading"; + + public static object Lock = new(); + + public override MethodBase TargetMethodBase { get; } = methodof(GenTypes.AllSubclassesNonAbstract); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(ReplacementBody); + + public static List ReplacementBody(Type baseType) + { + lock (Lock) + { + var subclassesNonAbstract = GenTypes.cachedSubclassesNonAbstract.TryGetValue(baseType); + + if (subclassesNonAbstract is null) + { + GenTypes.cachedSubclassesNonAbstract.Add(baseType, + subclassesNonAbstract = baseType.AllSubclasses().AsParallel() + .Where(Predicate).ToList()); + } + + return subclassesNonAbstract; + } + } + + public static Func Predicate = static subClass => !subClass.IsAbstract; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/GeneTrackerOptimization.cs b/Source/PerformanceFish/GeneTrackerOptimization.cs new file mode 100644 index 0000000..722eb07 --- /dev/null +++ b/Source/PerformanceFish/GeneTrackerOptimization.cs @@ -0,0 +1,115 @@ +// 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 System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; +using PerformanceFish.Prepatching; + +namespace PerformanceFish; + +public class GeneTrackerOptimization : ClassWithFishPrepatches +{ + public class GeneTrackerTickPatch : FishPrepatch + { + public override string? Description { get; } + = "Every pawn has a gene tracker, which is responsible for ticking each of their genes. Normally it ticks " + + "all of them equally, including those don't change or affect anything through ticking, like skin colors " + + "or basic stat modifiers. This patch improves the gene tracker to determine genes that need ticking in " + + "advance, cache the list of them, and only tick those, skipping all the others."; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(Pawn_GeneTracker), nameof(Pawn_GeneTracker.GeneTrackerTick)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(GeneTrackerTick); + + public static void GeneTrackerTick(Pawn_GeneTracker instance) + { + if (!ModLister.BiotechInstalled) + return; + + var genesToTick = instance.GenesToTick(); + if (Dirty(instance, genesToTick)) + Update(instance, genesToTick); + + for (var i = genesToTick.Count - 1; i >= 0; i--) + { + if (genesToTick[i].Active) + genesToTick[i].Tick(); + } + + if (instance.pawn.Spawned + && instance.Xenotype != XenotypeDefOf.Baseliner + && instance.pawn.IsHashIntervalTick(300)) + { + LessonAutoActivator.TeachOpportunity(ConceptDefOf.GenesAndXenotypes, OpportunityType.Important); + } + } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static bool Dirty(List genes, List genesToTick) => genes._version != genesToTick._version; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Dirty(Pawn_GeneTracker instance, List genesToTick) + => GetListVersion(instance) != genesToTick._version; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(Pawn_GeneTracker instance, List genesToTick) + { + genesToTick.Clear(); + + AddTickableGenes(genesToTick, instance.xenogenes); + AddTickableGenes(genesToTick, instance.endogenes); + + genesToTick._version = GetListVersion(instance); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetListVersion(Pawn_GeneTracker instance) + // => HashCode.Combine(instance.xenogenes._version, instance.endogenes._version); + => (instance.xenogenes._version << 16) | (instance.endogenes._version & 0b1111_1111_1111_1111); + + private static void AddTickableGenes(List genesToTick, List genes) + { + 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); + } + } + + // [MethodImpl(MethodImplOptions.NoInlining)] + // public static void Update(List genes, List 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 SkippableTypes + = typeof(Gene).SubclassesWithNoMethodOverrideAndSelf(nameof(Gene.Tick)).ToHashSet(); + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/GetCompCaching.cs b/Source/PerformanceFish/GetCompCaching.cs new file mode 100644 index 0000000..0a01b50 --- /dev/null +++ b/Source/PerformanceFish/GetCompCaching.cs @@ -0,0 +1,472 @@ +// 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; +using RimWorld.Planet; + +namespace PerformanceFish; + +public class GetCompCaching : ClassWithFishPrepatches +{ + public class ThingCompPatch : FishPrepatch + { + public override string? Description { get; } + = "Optimizes GetComp 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.Method(typeof(ThingWithComps), nameof(ThingWithComps.GetComp)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(GetComp); + + public static T? GetComp(ThingWithComps thing) where T : ThingComp + { + if (thing.comps != null) + { + ref var cache = ref Cache.ByInt>.GetOrAddReference(thing.thingIDNumber); + return !cache.IsDirty(thing.comps) ? cache.Comp : UpdateCache(thing, ref cache); + } + return null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static T? UpdateCache(ThingWithComps thing, ref CacheValue cache) + where T : ThingComp + { + for (var i = 0; i < thing.comps.Count; i++) + { + if (thing.comps[i] is not T comp) + continue; + + cache.Update(thing, comp); + return comp; + } + + cache.Update(thing, null); + return null; + } + + public record struct CacheValue where T : ThingComp + { + private int _listVersion = -1; + public T? Comp; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsDirty(List comps) => comps._version != _listVersion; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Update(ThingWithComps thing, T? comp) + { + if (thing.thingIDNumber < 0) + return; + + _listVersion = thing.comps._version; + Comp = comp; + } + + public CacheValue() {} + } + } + + public class HediffCompPatch : FishPrepatch + { + public override string? Description { get; } + = "Optimizes GetComp 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; } + = methodof(HediffUtility.TryGetComp); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(TryGetComp); + + public static T? TryGetComp(Hediff hediff) where T : HediffComp + { + if (hediff is HediffWithComps { comps: { } } hediffWithComps) + { + ref var cache = ref Cache.ByInt>.GetOrAddReference(hediffWithComps.loadID); + return !cache.IsDirty(hediffWithComps.comps) ? cache.Comp : UpdateCache(hediffWithComps, ref cache); + } + return null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static T? UpdateCache(HediffWithComps hediffWithComps, ref CacheValue cache) + where T : HediffComp + { + for (var i = 0; i < hediffWithComps.comps.Count; i++) + { + if (hediffWithComps.comps[i] is not T comp) + continue; + + cache.Update(hediffWithComps, comp); + return comp; + } + + cache.Update(hediffWithComps, null); + return null; + } + + public record struct CacheValue where T : HediffComp + { + private int _listVersion = -1; + public T? Comp; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsDirty(List comps) => comps._version != _listVersion; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Update(HediffWithComps hediff, T? comp) + { + if (hediff.loadID < 0) + return; + + _listVersion = hediff.comps._version; + Comp = comp; + } + + public CacheValue() {} + } + } + + public class HediffCompPropsPatch : FishPrepatch + { + public override string? Description { get; } + = "Optimizes GetComp 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.Method(typeof(HediffDef), nameof(HediffDef.CompProps)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(CompProps); + + public static T? CompProps(HediffDef instance) where T : HediffCompProperties + { + if (instance.comps != null) + { + ref var cache = ref Cache.ByInt>.GetOrAddReference(instance.shortHash); + return !cache.IsDirty(instance.comps) ? cache.Comp : UpdateCache(instance, ref cache); + } + return null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static T? UpdateCache(HediffDef hediffDef, ref CacheValue cache) + where T : HediffCompProperties + { + for (var i = 0; i < hediffDef.comps.Count; i++) + { + if (hediffDef.comps[i] is not T comp) + continue; + + cache.Update(hediffDef, comp); + return comp; + } + + cache.Update(hediffDef, null); + return null; + } + + public record struct CacheValue where T : HediffCompProperties + { + private int _listVersion = -1; + public T? Comp; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsDirty(List comps) => comps._version != _listVersion; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Update(HediffDef hediffDef, T? comp) + { + if (hediffDef.shortHash == 0) + return; + + _listVersion = hediffDef.comps._version; + Comp = comp; + } + + public CacheValue() {} + } + } + + public class AbilityCompPatch : FishPrepatch + { + public override string? Description { get; } + = "Optimizes GetComp 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.Method(typeof(Ability), nameof(Ability.CompOfType)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(CompOfType); + + public static T? CompOfType(Ability ability) where T : AbilityComp + { + if (ability.comps != null) + { + ref var cache = ref Cache.ByInt>.GetOrAddReference(ability.Id); + return !cache.IsDirty(ability.comps) ? cache.Comp : UpdateCache(ability, ref cache); + } + return null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static T? UpdateCache(Ability ability, ref CacheValue cache) + where T : AbilityComp + { + for (var i = 0; i < ability.comps.Count; i++) + { + if (ability.comps[i] is not T comp) + continue; + + cache.Update(ability, comp); + return comp; + } + + cache.Update(ability, null); + return null; + } + + public record struct CacheValue where T : AbilityComp + { + private int _listVersion = -1; + public T? Comp; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsDirty(List comps) => comps._version != _listVersion; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Update(Ability ability, T? comp) + { + if (ability.Id < 0) + return; + + _listVersion = ability.comps._version; + Comp = comp; + } + + public CacheValue() {} + } + } + + public class WorldObjectCompPatch : FishPrepatch + { + public override string? Description { get; } + = "Optimizes GetComp 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.Method(typeof(WorldObject), nameof(WorldObject.GetComponent), Type.EmptyTypes); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(GetComponent); + + public static T? GetComponent(WorldObject worldObject) where T : WorldObjectComp + { + ref var cache = ref Cache.ByInt>.GetOrAddReference(worldObject.ID); + return !cache.IsDirty(worldObject.comps) ? cache.Comp : UpdateCache(worldObject, ref cache); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static T? UpdateCache(WorldObject worldObject, ref CacheValue cache) where T : WorldObjectComp + { + for (var i = 0; i < worldObject.comps.Count; i++) + { + if (worldObject.comps[i] is not T comp) + continue; + + cache.Update(worldObject, comp); + return comp; + } + + cache.Update(worldObject, null); + return null; + } + + public record struct CacheValue where T : WorldObjectComp + { + private int _listVersion = -1; + public T? Comp; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsDirty(List comps) => comps._version != _listVersion; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Update(WorldObject worldObject, T? comp) + { + if (worldObject.ID < 0) + return; + + _listVersion = worldObject.comps._version; + Comp = comp; + } + + public CacheValue() {} + } + } + + public class MapComponentPatch : FishPrepatch + { + public override string? Description { get; } + = "Optimizes GetComp 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.Method(typeof(Map), nameof(Map.GetComponent), Type.EmptyTypes); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(GetComponent); + + public static T? GetComponent(Map map) where T : MapComponent + { + ref var cache = ref Cache.ByMap>.GetReferenceFor(map); + return !cache.IsDirty(map.components) ? cache.Comp : UpdateCache(map, ref cache); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static T? UpdateCache(Map map, ref CacheValue cache) where T : MapComponent + { + for (var i = 0; i < map.components.Count; i++) + { + if (map.components[i] is not T comp) + continue; + + cache.Update(map, comp); + return comp; + } + + cache.Update(map, null); + return null; + } + + public record struct CacheValue where T : MapComponent + { + private int _listVersion = -1; + public T? Comp; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsDirty(List comps) => comps._version != _listVersion; + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Update(Map map, T? comp) + { + if (map.uniqueID < 0) + return; + + _listVersion = map.components._version; + Comp = comp; + } + + public CacheValue() {} + } + } + + public class WorldComponentPatch : FishPrepatch + { + public override string? Description { get; } + = "Optimizes GetComp 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.Method(typeof(World), nameof(World.GetComponent), Type.EmptyTypes); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(GetComponent); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? GetComponent(World world) where T : WorldComponent + => !Cache.IsDirty(world) ? Cache.Comp : UpdateCache(world); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static T? UpdateCache(World world) where T : WorldComponent + { + for (var i = 0; i < world.components.Count; ++i) + { + if (world.components[i] is not T comp) + continue; + + Cache.Update(world, comp); + return comp; + } + + Cache.Update(world, null); + return null; + } + + public static class Cache where T : WorldComponent + { + private static int _listVersion = -1; + private static World? _world; + public static T? Comp; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsDirty(World world) => world.components._version != _listVersion || _world != world; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(World world, T? comp) + { + _listVersion = world.components._version; + _world = world; + Comp = comp; + } + } + } + + public class GameComponentPatch : FishPrepatch + { + public override string? Description { get; } + = "Optimizes GetComp 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.Method(typeof(Game), nameof(Game.GetComponent), Type.EmptyTypes); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(GetComponent); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? GetComponent(Game game) where T : GameComponent + => !Cache.IsDirty(game) ? Cache.Comp : UpdateCache(game); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static T? UpdateCache(Game game) where T : GameComponent + { + for (var i = 0; i < game.components.Count; ++i) + { + if (game.components[i] is not T comp) + continue; + + Cache.Update(game, comp); + return comp; + } + + Cache.Update(game, null); + return null; + } + + public static class Cache where T : GameComponent + { + private static int _listVersion = -1; + private static Game? _game; + public static T? Comp; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsDirty(Game game) => game.components._version != _listVersion || _game != game; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Update(Game game, T? comp) + { + _listVersion = game.components._version; + _game = game; + Comp = comp; + } + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/HediffComp_PsychicHarmonizer_Bugfix.cs b/Source/PerformanceFish/HediffComp_PsychicHarmonizer_Bugfix.cs deleted file mode 100644 index bbecf3e..0000000 --- a/Source/PerformanceFish/HediffComp_PsychicHarmonizer_Bugfix.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2022 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 System.Linq; -using static System.Reflection.Emit.OpCodes; - -namespace PerformanceFish; - -public class HediffComp_PsychicHarmonizer_Bugfix : ClassWithFishPatches -{ - public class CompPostTick_Patch : FishPatch - { - public override string Description => "Fixes a bug in the psychic harmonizer tick method that was causing it to recalculate results on 149 out of 150 ticks instead of once every 150 ticks. Large impact."; - public override MethodBase TargetMethodInfo => AccessTools.Method(typeof(HediffComp_PsychicHarmonizer), nameof(HediffComp_PsychicHarmonizer.CompPostTick)); - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) - { - var codes = CodeInstructions.ToList(); - var pawn_int_IsHashIntervalTick = FishTranspiler.Call>(Gen.IsHashIntervalTick); - var success = false; - for (var i = 0; i < codes.Count; i++) - { - if (i + 2 < codes.Count && codes[i].opcode == Ldc_I4 && codes[i + 1] == pawn_int_IsHashIntervalTick && codes[i + 2].opcode == Brfalse_S) - { - //codes[i].operand = 128; - yield return codes[i]; - yield return codes[i + 1]; - codes[i + 2].opcode = Brtrue_S; - yield return codes[i + 2]; - success = true; - i += 2; - } - else - { - yield return codes[i]; - } - } - if (!success) - Log.Warning("Performance Fish failed to insert its Psychic Harmonizer Tick bugfix. This is probably harmless and likely just means someone else fixed it already"); - } - } -} diff --git a/Source/PerformanceFish/HediffSetCaching.cs b/Source/PerformanceFish/HediffSetCaching.cs deleted file mode 100644 index 5402217..0000000 --- a/Source/PerformanceFish/HediffSetCaching.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2022 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 PerformanceFish.Collections; -using HediffCache = PerformanceFish.Cache.ByIntRefreshable; -using VisibleHediffCache = PerformanceFish.Cache.ByIntRefreshable; - -namespace PerformanceFish; - -public class HediffSetCaching : ClassWithFishPatches -{ - public class HasHediff_Patch : FirstPriorityFishPatch - { - public override string Description => "Caches results of the HediffSet.HasHediff method. This scales with the number of hediffs pawns have and can make a decent difference in transhumanist playthroughs."; - public override Expression TargetMethod => () => default(HediffSet)!.HasHediff(null, default); - public static CodeInstructions Transpiler(CodeInstructions codes) - => Reflection.GetCodeInstructions(Replacement); - public static bool Replacement(HediffSet __instance, HediffDef def, bool mustBeVisible) => __instance.GetFirstHediffOfDef(def, mustBeVisible) != null; - } - - public class GetFirstHediffOfDef_Patch : FirstPriorityFishPatch - { - public override string Description => "Caches results of the HediffSet.GetFirstHediffOfDef method. This scales with the number of hediffs pawns have and can make a decent difference in transhumanist playthroughs."; - public override Expression TargetMethod => () => default(HediffSet)!.GetFirstHediffOfDef(null, default); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(HediffSet __instance, HediffDef def, bool mustBeVisible, ref Hediff? __result, out bool __state) - { - if (__instance.hediffs.Count < 5 || def is null || def.GetType() != typeof(HediffDef)) // different types have different indexing. Can't use those - { - __state = false; - return true; - } - - var key = new HediffCache() { First = __instance.pawn.thingIDNumber, Second = def.index }; - ref var cache = ref mustBeVisible ? ref Unsafe.As(ref VisibleHediffCache.Get.TryGetReferenceUnsafe(ref Unsafe.As(ref key))) - : ref HediffCache.Get.TryGetReferenceUnsafe(ref key); - - if (Unsafe.IsNullRef(ref cache) || cache.ShouldRefreshNow) - return __state = true; - - __result = cache.Hediff; - return __state = false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Postfix(HediffSet __instance, HediffDef def, bool mustBeVisible, Hediff? __result, bool __state) - { - if (!__state) - return; - - if (mustBeVisible) - VisibleHediffCache.Get[new(__instance, def)] = new(__instance, __result); - else - HediffCache.Get[new(__instance, def)] = new(__instance, __result); - } - } - - public class HediffSet_DirtyCache_Patch : FishPatch - { - public override string? Description => "Patched to trigger Psylink cache clearing for the psychic entropy optimization."; - public override MethodBase TargetMethodInfo => AccessTools.Method(typeof(HediffSet), nameof(HediffSet.DirtyCache)); - public static void Postfix(HediffSet __instance) - { - if (__instance.pawn.psychicEntropy is { } entropy) - entropy.psylinkCachedForTick = 0; - } - } - - public struct HediffCacheValue : Cache.IIsRefreshable - { - private int _nextRefreshTick; - private int _version; - private HediffSet _set; - public Hediff? Hediff; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public HediffCacheValue(HediffSet set, Hediff? hediff) - { - _set = set; - Hediff = hediff; - _version = set.hediffs._version; - _nextRefreshTick = Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); - } - public bool ShouldRefreshNow - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _nextRefreshTick < Current.Game.tickManager.ticksGameInt || _version != _set.hediffs._version; - set => _nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); - } - - public HediffCacheValue SetNewValue(HediffCache key) => throw new NotImplementedException(); - } - - public struct VisibleHediffCacheValue : Cache.IIsRefreshable - { - public HediffCacheValue Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public VisibleHediffCacheValue(HediffSet set, Hediff? hediff) => Value = new(set, hediff); - - public bool ShouldRefreshNow - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Value.ShouldRefreshNow; - set => Value.ShouldRefreshNow = value; - } - - public VisibleHediffCacheValue SetNewValue(VisibleHediffCache key) => throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/Source/PerformanceFish/Hediffs/HealthTrackerOptimization.cs b/Source/PerformanceFish/Hediffs/HealthTrackerOptimization.cs new file mode 100644 index 0000000..e01f046 --- /dev/null +++ b/Source/PerformanceFish/Hediffs/HealthTrackerOptimization.cs @@ -0,0 +1,37 @@ +// 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 PerformanceFish.Prepatching; + +namespace PerformanceFish.Hediffs; + +public class HealthTrackerOptimization : ClassWithFishPrepatches +{ + public static void Initialize() + { + var allHediffDefs = DefDatabase.AllDefsListForReading; + for (var i = allHediffDefs.Count; i-- > 0;) + { + var hediffDef = allHediffDefs[i]; + + if (hediffDef.stages is { Count: > 1}) + continue; + + SkippableHediffs.Add(hediffDef); + } + } + + public class HealthTickPatch : FishPrepatch + { + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(Pawn_HealthTracker), nameof(Pawn_HealthTracker.HealthTick)); + + + } + + public static HashSet SkippableHediffs = new(); +} +#endif \ No newline at end of file diff --git a/Source/PerformanceFish/Hediffs/HediffComp_PsychicHarmonizer_Bugfix.cs b/Source/PerformanceFish/Hediffs/HediffComp_PsychicHarmonizer_Bugfix.cs new file mode 100644 index 0000000..5c7d2d9 --- /dev/null +++ b/Source/PerformanceFish/Hediffs/HediffComp_PsychicHarmonizer_Bugfix.cs @@ -0,0 +1,60 @@ +// 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 System.Linq; +using static System.Reflection.Emit.OpCodes; + +namespace PerformanceFish.Hediffs; + +public class HediffComp_PsychicHarmonizer_Bugfix : ClassWithFishPatches +{ + public class CompPostTick_Patch : FishPatch + { + public override string Description { get; } + = "Fixes a bug in the psychic harmonizer tick method that was causing it to recalculate results on 149 " + + "out of 150 ticks instead of once every 150 ticks. Large impact."; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(HediffComp_PsychicHarmonizer), + nameof(HediffComp_PsychicHarmonizer.CompPostTick)); + + public static CodeInstructions Transpiler(CodeInstructions codeInstructions) + { + var codes = codeInstructions.ToList(); + var pawn_int_IsHashIntervalTick = FishTranspiler.Call>(Gen.IsHashIntervalTick); + var success = false; + for (var i = 0; i < codes.Count; i++) + { + if (i + 2 < codes.Count + && codes[i].opcode == Ldc_I4 + && codes[i + 1] == pawn_int_IsHashIntervalTick + && codes[i + 2].opcode == Brfalse_S) + { + //codes[i].operand = 128; + yield return codes[i]; + yield return codes[i + 1]; + + codes[i + 2].opcode = Brtrue_S; + yield return codes[i + 2]; + + success = true; + i += 2; + } + else + { + yield return codes[i]; + } + } + + if (!success) + { + Log.Warning("Performance Fish failed to insert its Psychic Harmonizer Tick bugfix. This is " + + "probably harmless and can happen when toggling patches from mod settings. It could indicate " + + "an actual failure to patch if settings were not toggled however, which would mean some other " + + "patch or a vanilla change either messes with this or implements the same thing"); + } + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Hediffs/HediffSetCaching.cs b/Source/PerformanceFish/Hediffs/HediffSetCaching.cs new file mode 100644 index 0000000..873ba2f --- /dev/null +++ b/Source/PerformanceFish/Hediffs/HediffSetCaching.cs @@ -0,0 +1,247 @@ +// 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; +using HediffCache + = PerformanceFish.Cache.ByInt; +using VisibleHediffCache + = PerformanceFish.Cache.ByInt; +using NotMissingPartsCache + = PerformanceFish.Cache.ByInt; + +namespace PerformanceFish.Hediffs; + +public class HediffSetPrecaching : ClassWithFishPrepatches +{ + public class GetFirstHediffOfDef : FishPrepatch + { + public override string Description { get; } + = "Caches results of the HediffSet.GetFirstHediffOfDef method. Impact scales with hediff count."; + + public override MethodBase TargetMethodBase { get; } + = SymbolExtensions.GetMethodInfo(static () + => default(HediffSet)!.GetFirstHediffOfDef(null, default)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(Replacement); + + public static Hediff? Replacement(HediffSet instance, HediffDef? def, bool mustBeVisible) + { + var canCache = true; + + if (instance.hediffs.Count >= 5 + && 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); + if (!cache.Dirty) + return cache.Hediff; + } + else + { + canCache = false; + } + + // original method, mostly left untouched in case of other mods patching in + Hediff? result; + var hediffs = instance.hediffs; + for (var i = 0; i < hediffs.Count; i++) + { + if (hediffs[i].def != def || (mustBeVisible && !hediffs[i].Visible)) + continue; + + result = hediffs[i]; + goto Result; + } + + result = null; + + Result: + if (canCache) + UpdateCache(instance, def!, mustBeVisible, result); + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref HediffSetCaching.HediffCacheValue GetCacheValue(HediffSet instance, HediffDef def, + bool mustBeVisible) + { + var key = new HediffCache { First = instance.pawn.thingIDNumber, Second = def.shortHash }; + ref var cache = ref mustBeVisible + ? ref Unsafe.As( + ref VisibleHediffCache.GetOrAddReference(Unsafe.As(ref key))) + : ref HediffCache.GetOrAddReference(key); + + return ref cache; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void UpdateCache(HediffSet __instance, HediffDef def, bool mustBeVisible, Hediff? __result) + { + if (mustBeVisible) + VisibleHediffCache.GetExistingReference(__instance, def).Update(__instance, __result); + else + HediffCache.GetExistingReference(__instance, def).Update(__instance, __result); + } + } + + public class HasHediff : FishPrepatch + { + public override string Description { get; } + = "Caches results of the HediffSet.HasHediff method. Impact scales with hediff count."; + + public override MethodBase TargetMethodBase { get; } + = SymbolExtensions.GetMethodInfo(static () + => default(HediffSet)!.HasHediff(null, default)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(Replacement); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Replacement(HediffSet __instance, HediffDef def, bool mustBeVisible) + => __instance.GetFirstHediffOfDef(def, mustBeVisible) != null; + } + + public class GetNotMissingParts : FishPrepatch + { + public override string? Description { get; } + = "Caches results of the HediffSet.GetNotMissingParts method. Impact scales with hediff count."; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(HediffSet), nameof(HediffSet.GetNotMissingParts)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(HediffSet __instance, BodyPartHeight height, BodyPartDepth depth, BodyPartTagDef? tag, + BodyPartRecord? partParent, ref IEnumerable __result, out bool __state) + { + if (height != BodyPartHeight.Undefined + || depth != BodyPartDepth.Undefined + || tag != null + || partParent != null) + { + __state = false; + return true; + } + + ref var cache = ref NotMissingPartsCache.GetOrAddReference(__instance.pawn.thingIDNumber); + + if (cache.Dirty) + return __state = true; + + __result = cache.Parts; + return __state = false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Postfix(HediffSet __instance, bool __state, ref IEnumerable __result) + { + if (!__state) + return; + + UpdateCache(__instance, ref __result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void UpdateCache(HediffSet __instance, ref IEnumerable __result) + => NotMissingPartsCache.GetExistingReference(__instance).Update(__instance, ref __result); + } +} + +public class HediffSetCaching : ClassWithFishPatches +{ + public class DirtyCache : FishPatch + { + public override string Description { get; } + = "Patched to trigger psylink cache clearing for the psychic entropy optimization."; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(HediffSet), nameof(HediffSet.DirtyCache)); + + public static void Postfix(HediffSet __instance) + { + if (__instance.pawn.psychicEntropy is { } entropy) + entropy.psylinkCachedForTick = 0; + } + } + + public record struct NotMissingPartsCacheValue + { + private int _version = -1; + private List _hediffsInSet = new(); + private int _nextRefreshTick; + public List Parts = new(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Update(HediffSet set, ref IEnumerable result) + { + _hediffsInSet = set.hediffs; + _version = set.hediffs._version; + _nextRefreshTick = TickHelper.Add(GenTicks.TickLongInterval, set.pawn.thingIDNumber); + + var previousPartsVersion = Parts._version; + Parts.Clear(); + Parts.AddRange(result); + Parts._version = previousPartsVersion; + + result = Parts; + } + + public bool Dirty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _version != _hediffsInSet._version || TickHelper.Past(_nextRefreshTick); + } + + public NotMissingPartsCacheValue() + { + } + } + + public record struct HediffCacheValue + { + private int _version = -1; + private List _hediffsInSet = new(); + private int _nextRefreshTick; + public Hediff? Hediff; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Update(HediffSet set, Hediff? hediff) + { + _hediffsInSet = set.hediffs; + Hediff = hediff; + _version = _hediffsInSet._version; + _nextRefreshTick = TickHelper.Add(GenTicks.TickLongInterval, set.pawn.thingIDNumber); + } + + public bool Dirty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _version != _hediffsInSet._version || TickHelper.Past(_nextRefreshTick); + } + + public HediffCacheValue() + { + } + } + + public record struct VisibleHediffCacheValue + { + public HediffCacheValue Value = new(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Update(HediffSet set, Hediff? hediff) => Value.Update(set, hediff); + + public VisibleHediffCacheValue() + { + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Hediffs/ImmunityHandlerCaching.cs b/Source/PerformanceFish/Hediffs/ImmunityHandlerCaching.cs new file mode 100644 index 0000000..dc24188 --- /dev/null +++ b/Source/PerformanceFish/Hediffs/ImmunityHandlerCaching.cs @@ -0,0 +1,146 @@ +// 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 ImmunityInfoCache + = PerformanceFish.Cache.ByInt; +using ImmunityRecordExistsCache + = PerformanceFish.Cache.ByInt; + +namespace PerformanceFish.Hediffs; + +public class ImmunityHandlerCaching : ClassWithFishPatches +{ + public class NeededImmunitiesNow : FirstPriorityFishPatch + { + public override string Description { get; } + = "Caches results of the ImmunityHandler.NeededImmunitiesNow method. Impact scales with hediff quantity."; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(ImmunityHandler), nameof(ImmunityHandler.NeededImmunitiesNow)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(ImmunityHandler __instance, ref List __result, + out bool __state) + { + ref var cache = ref ImmunityInfoCache.GetOrAddReference(__instance.pawn.thingIDNumber); + + if (cache.Dirty) + return __state = true; + + (__result = ImmunityHandler.tmpNeededImmunitiesNow).Clear(); + __result.AddRangeFast(cache.Infos); + + return __state = false; + } + + // ImmunityHandler.tmpNeededImmunitiesNow, which is normally used for the result, is static + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Postfix(ImmunityHandler __instance, List __result, + bool __state) + { + if (!__state) + return; + + UpdateCache(__instance, __result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(ImmunityHandler __instance, List __result) + => ImmunityInfoCache.GetExistingReference(__instance.pawn) + .Update(__instance.pawn.health.hediffSet, __result); + } + + public class ImmunityRecordExists : FishPatch + { + public override string? Description { get; } = "Required by the TryAddImmunityRecord optimization."; + + public override bool Enabled + { + set + { + if (base.Enabled == value) + return; + + Get().Enabled = base.Enabled = value; + } + } + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(ImmunityHandler), nameof(ImmunityHandler.ImmunityRecordExists)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Postfix(ImmunityHandler __instance, HediffDef def, bool __result) + { + ref var cache + = ref ImmunityRecordExistsCache.GetOrAddReference(__instance.pawn.thingIDNumber, def.shortHash); + cache.Value = __result; + cache.ImmunityListVersion = __instance.immunityList._version; + } + } + + public class TryAddImmunityRecord : FishPatch + { + public override string? Description { get; } + = "Optimization for this specific method to not run more checks than necessary."; + + public override bool Enabled + { + set + { + if (base.Enabled == value) + return; + + Get().Enabled = base.Enabled = value; + } + } + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(ImmunityHandler), nameof(ImmunityHandler.TryAddImmunityRecord)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(ImmunityHandler __instance, HediffDef def) + { + ref var cache + = ref ImmunityRecordExistsCache.GetOrAddReference(__instance.pawn.thingIDNumber, def.shortHash); + return !cache.Value || cache.ImmunityListVersion != __instance.immunityList._version; + } + } + + public record struct ImmunityInfoCacheValue + { + private int _version = -1; + private List _hediffsInSet = new(); + private int _nextRefreshTick; + public List Infos = new(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Update(HediffSet set, List infos) + { + Infos.ReplaceContentsWith(infos); + + _hediffsInSet = set.hediffs; + _version = _hediffsInSet._version; + _nextRefreshTick = TickHelper.Add(GenTicks.TickLongInterval, set.pawn.thingIDNumber); + } + + public bool Dirty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _version != _hediffsInSet._version || TickHelper.Past(_nextRefreshTick); + } + + public ImmunityInfoCacheValue() + { + } + } + + public record struct ImmunityRecordExistsCacheValue + { + public bool Value; + public int ImmunityListVersion; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Pawn_PsychicEntropyTrackerOptimization.cs b/Source/PerformanceFish/Hediffs/Pawn_PsychicEntropyTrackerOptimization.cs similarity index 53% rename from Source/PerformanceFish/Pawn_PsychicEntropyTrackerOptimization.cs rename to Source/PerformanceFish/Hediffs/Pawn_PsychicEntropyTrackerOptimization.cs index 483e6ff..dfe1d6f 100644 --- a/Source/PerformanceFish/Pawn_PsychicEntropyTrackerOptimization.cs +++ b/Source/PerformanceFish/Hediffs/Pawn_PsychicEntropyTrackerOptimization.cs @@ -1,27 +1,30 @@ -// Copyright (c) 2022 bradson +// 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; +namespace PerformanceFish.Hediffs; public class Pawn_PsychicEntropyTrackerOptimization : ClassWithFishPatches { public class Psylink_Patch : FirstPriorityFishPatch { - public override string? Description => "Caches/throttles the psylink method to only update every 32 ticks."; - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(Pawn_PsychicEntropyTracker), nameof(Pawn_PsychicEntropyTracker.Psylink)); + public override string Description { get; } = "Caches/throttles the psylink method to only update every 32 ticks."; - public static CodeInstructions Transpiler(CodeInstructions codes) - => Reflection.GetCodeInstructions(Psylink); + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(Pawn_PsychicEntropyTracker), + nameof(Pawn_PsychicEntropyTracker.Psylink)); + + public static CodeInstructions Transpiler(CodeInstructions codes) => Reflection.GetCodeInstructions(Psylink); public static Hediff_Psylink Psylink(Pawn_PsychicEntropyTracker __instance) { - if (__instance.psylinkCachedForTick <= Find.TickManager.TicksGame) + if (__instance.psylinkCachedForTick <= TickHelper.TicksGame) { __instance.psylinkCached = __instance.pawn.GetMainPsylinkSource(); - __instance.psylinkCachedForTick = Find.TickManager.TicksGame + 32; + __instance.psylinkCachedForTick = TickHelper.TicksGame + 32; } + return __instance.psylinkCached; } } diff --git a/Source/PerformanceFish/IdeoUtilityCaching.cs b/Source/PerformanceFish/IdeoUtilityCaching.cs index 7189ac1..109f81e 100644 --- a/Source/PerformanceFish/IdeoUtilityCaching.cs +++ b/Source/PerformanceFish/IdeoUtilityCaching.cs @@ -1,22 +1,26 @@ -// Copyright (c) 2022 bradson +// 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 PerformanceFish.Cache; -using StyleDominanceCache = PerformanceFish.Cache.ByIntRefreshable; +using PerformanceFish.Prepatching; +using StyleDominanceCache + = PerformanceFish.Cache.ByInt; namespace PerformanceFish; -public class IdeoUtilityCaching : ClassWithFishPatches + +public class IdeoUtilityCaching : ClassWithFishPrepatches { - public class GetStyleDominance_Patch : FishPatch + public class GetStyleDominance_Patch : FishPrepatch { - public override string? Description => "Caches IdeoUtility.GetStyleDominance to only update about once every 600 ticks or so. Only active when not using Performance Optimizer, as that mod includes a similar version too."; - public override bool Enabled => base.Enabled && !ModsConfig.IsActive("Taranchuk.PerformanceOptimizer"); - public override Delegate? TargetMethodGroup => IdeoUtility.GetStyleDominance; + public override string Description { get; } + = "Caches IdeoUtility.GetStyleDominance to only update about once every 900 ticks or so."; + + public override MethodBase TargetMethodBase { get; } = methodof(IdeoUtility.GetStyleDominance); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(Thing t, Ideo ideo, ref float __result, out bool __state) + public static bool Prefix(Thing? t, Ideo? ideo, ref float __result, out bool __state) { if (t is null || ideo is null || t.thingIDNumber < 0) { @@ -24,10 +28,9 @@ public class IdeoUtilityCaching : ClassWithFishPatches return true; } - var key = new StyleDominanceCache(t.thingIDNumber, ideo.id); - ref var cache = ref StyleDominanceCache.Get.TryGetReferenceUnsafe(ref key); + ref var cache = ref StyleDominanceCache.GetOrAddReference(new(t.thingIDNumber, ideo.id)); - if (Unsafe.IsNullRef(ref cache) || cache.ShouldRefreshNow) + if (cache.Dirty) return __state = true; __result = cache.Value; @@ -40,28 +43,30 @@ public class IdeoUtilityCaching : ClassWithFishPatches if (!__state) return; - StyleDominanceCache.Get[new(t, ideo)] = new(__result); + UpdateCache(t, ideo, __result); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Thing t, Ideo ideo, float __result) + => StyleDominanceCache.GetExistingReference(t, ideo).Update(__result, t); } - public struct StyleDominanceCacheValue : IIsRefreshable + public record struct StyleDominanceCacheValue { public float Value; private int _nextRefreshTick; - public StyleDominanceCacheValue(float value) + public void Update(float value, Thing thing) { Value = value; - _nextRefreshTick = Current.Game.tickManager.TicksGame + 512 + Math.Abs(Rand.Int % 256); // Pawn_StyleObserverTracker.StyleObservableTick runs every 900 ticks + _nextRefreshTick = TickHelper.Add(900 - 128, thing.thingIDNumber, 256); + // Pawn_StyleObserverTracker.StyleObservableTick runs every 900 ticks } - public bool ShouldRefreshNow + public bool Dirty { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _nextRefreshTick < Current.Game.tickManager.TicksGame; - set => _nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 512 + Math.Abs(Rand.Int % 256); + get => TickHelper.Past(_nextRefreshTick); } - - StyleDominanceCacheValue IIsRefreshable.SetNewValue(StyleDominanceCache key) => throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Source/PerformanceFish/JobSystem/DisabledWorkTypesOptimization.cs b/Source/PerformanceFish/JobSystem/DisabledWorkTypesOptimization.cs new file mode 100644 index 0000000..19ba46a --- /dev/null +++ b/Source/PerformanceFish/JobSystem/DisabledWorkTypesOptimization.cs @@ -0,0 +1,25 @@ +// 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.JobSystem; + +public class DisabledWorkTypesOptimization : ClassWithFishPatches +{ + public class PawnGetDisabledWorkTypes : FishPatch + { + public override string? Description { get; } + = "Essentially fixes a bug in Pawn.GetDisabledWorkTypes that causes the method to discard its cache and " + + "constantly keep recalculating. With the patch applied it'll return early when a valid cache exists."; + + public override MethodBase? TargetMethodInfo { get; } + = AccessTools.Method(typeof(Pawn), nameof(Pawn.GetDisabledWorkTypes)); + + [HarmonyPriority(Priority.VeryLow)] + public static bool Prefix(Pawn __instance, bool permanentOnly, out List __result) + => (__result = permanentOnly + ? __instance.cachedDisabledWorkTypesPermanent + : __instance.cachedDisabledWorkTypes) is null; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/JobSystem/GenClosestPatches.cs b/Source/PerformanceFish/JobSystem/GenClosestPatches.cs index 1b038f4..e0b909b 100644 --- a/Source/PerformanceFish/JobSystem/GenClosestPatches.cs +++ b/Source/PerformanceFish/JobSystem/GenClosestPatches.cs @@ -1,24 +1,28 @@ -// Copyright (c) 2022 bradson +// 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 System.Threading.Tasks; +#if disabledForNow +using System.Threading.Tasks; using Verse.AI; namespace PerformanceFish.JobSystem; public class GenClosestPatches : ClassWithFishPatches -{*/ -#if disabledForNow +{ public class ClosestThing_Global_Patch : FishPatch { - public override string Description => "Threading for job scanning. Disabled and unstable."; + public override string Description { get; } = "Threading for job scanning. Disabled and unstable."; public override bool DefaultState => false; public static bool ThreadingEnabled { get; set; } - public override Expression TargetMethod => () => GenClosest.ClosestThing_Global(default, null, default, null, null); + + public override Expression TargetMethod { get; } + = static () => GenClosest.ClosestThing_Global(default, null, default, null, null); + public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) => Reflection.MakeReplacementCall(ClosestThing_Global_Replacement); + private struct Variables { public IntVec3 center; @@ -27,18 +31,32 @@ public class GenClosestPatches : ClassWithFishPatches public float closestDistSquared; public Predicate? validator; } - public static Thing? ClosestThing_Global_Replacement(IntVec3 center, IEnumerable searchSet, float maxDistance = 99999f, Predicate? validator = null, Func? priorityGetter = null) + + public static Thing? ClosestThing_Global_Replacement(IntVec3 center, IEnumerable? searchSet, + float maxDistance = 99999f, Predicate? validator = null, Func? 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 }; + 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 (ThreadingEnabled + && validator != null + && JobGiver_Work_Predicate.GetScanner(predicate) is WorkGiver_DoBill + /*or WorkGiver_ConstructDeliverResources or WorkGiver_ConstructFinishFrames*/ + ) { if (searchSet is IList thingList) { @@ -47,15 +65,16 @@ public class GenClosestPatches : ClassWithFishPatches try { Parallel.For<(IList list, Variables vars)>(0, thingList.Count, - () => (thingList, variables), - (i, loop, listAndVars) => (listAndVars.list, ProcessThreaded(listAndVars.list[i], listAndVars.vars)), - _ => { }); + () => (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"); @@ -98,86 +117,112 @@ public class GenClosestPatches : ClassWithFishPatches } return chosen; + void Process(T t, Variables variables) where T : Thing { - if (t.Spawned) + if (!t.Spawned) + return; + + float num = (variables.center - t.Position).LengthHorizontalSquared; + if (!(num > variables.maxDistanceSquared) + && (variables.priorityGetter != null || num < closestDistSquared) + && (variables.validator == null || variables.validator(t))) { - float num = (variables.center - t.Position).LengthHorizontalSquared; - if (!(num > variables.maxDistanceSquared) - && (variables.priorityGetter != null || num < closestDistSquared) - && (variables.validator == null || variables.validator(t))) + var num2 = 0f; + if (variables.priorityGetter != null) { - var num2 = 0f; - if (variables.priorityGetter != null) - { - num2 = variables.priorityGetter(t); - if (num2 < bestPrio || (num2 == bestPrio && num >= closestDistSquared)) - return; - } - chosen = t; - closestDistSquared = variables.closestDistSquared = num; - bestPrio = num2; + 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, Variables variables) where T : Thing { - if (t.Spawned) + 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))) { - 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))) - { - lock (Lock) - { - var num2 = 0f; - if (variables.priorityGetter != null) - { - num2 = variables.priorityGetter(t); - if (num2 < bestPrio || (num2 == bestPrio && num >= closestDistSquared)) - return variables; - } - else - { - if (num >= closestDistSquared) - return variables; - } - chosen = t; - closestDistSquared = variables.closestDistSquared = num; - bestPrio = num2; - } - } + 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); + 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)); + = (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); + 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)); + = (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 : JobGiver_Work_PredicateAccess { public override WorkGiver_Scanner? GetScanner(object? obj) => obj is T t ? Scanner(t) : null; - public static AccessTools.FieldRef Scanner { get; } = AccessTools.FieldRefAccess("scanner"); + + public static AccessTools.FieldRef Scanner { get; } + = AccessTools.FieldRefAccess("scanner"); } + private abstract class GenClosest_PredicateAccess { public abstract Predicate? GetValidator(object? obj); } + private class GenClosest_PredicateAccessImplementation : GenClosest_PredicateAccess { public override Predicate? GetValidator(object? obj) => obj is T t ? Validator(t) : null; - public static AccessTools.FieldRef> Validator { get; } = AccessTools.FieldRefAccess>("validator"); + + public static AccessTools.FieldRef> Validator { get; } + = AccessTools.FieldRefAccess>("validator"); } } #endif @@ -187,8 +232,10 @@ public class GenClosestPatches : ClassWithFishPatches { protected CanReach_Patch() => FishSettings.ThreadingPatches.Add(this); public override bool DefaultState => false; - public override string Description => "Lock for threading. Only needed with threading enabled and off by default"; - public override Expression TargetMethod => () => default(Reachability)!.CanReach(default, null, default, default); + public override string Description { get; } + = "Lock for threading. Only needed with threading enabled and off by default"; + public override Expression TargetMethod { get; } + = static () => default(Reachability)!.CanReach(default, null, default, default); public static void Prefix() { if (FishSettings.ThreadingEnabled) diff --git a/Source/PerformanceFish/JobSystem/JobGiver_HaulOptimization.cs b/Source/PerformanceFish/JobSystem/JobGiver_HaulOptimization.cs index 91ef107..be476da 100644 --- a/Source/PerformanceFish/JobSystem/JobGiver_HaulOptimization.cs +++ b/Source/PerformanceFish/JobSystem/JobGiver_HaulOptimization.cs @@ -1,43 +1,54 @@ -// Copyright (c) 2022 bradson +// 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 PerformanceFish.ModCompatibility; + namespace PerformanceFish.JobSystem; public class JobGiver_HaulOptimization : ClassWithFishPatches { - public class PotentialWorkThingsGlobal_Patch : FishPatch - { - public override string Description => "Sorts haulables by distance before running expensive hauling calculations on them to avoid checks on far away items"; - public override MethodBase TargetMethodInfo => AccessTools.Method(typeof(JobGiver_Haul), nameof(JobGiver_Haul.TryGiveJob)); - public static CodeInstructions? Transpiler(CodeInstructions codes, MethodBase method) - { - var pawn_Map_listerHaulables_ThingsPotentiallyNeedingHauling = FishTranspiler.Call(() => default(ListerHaulables)!.ThingsPotentiallyNeedingHauling()); + public class PotentialWorkThingsGlobal_Patch : FishPatch + { + public override string Description { get; } + = "Sorts haulables by distance before running expensive hauling calculations on them to avoid checks on " + + "far away items"; - try - { - return codes.InsertAfter( - c => c == pawn_Map_listerHaulables_ThingsPotentiallyNeedingHauling, - new CodeInstruction[] - { - FishTranspiler.Argument(method, "pawn"), - FishTranspiler.Call(SortedThingsPotentiallyNeedingHauling) - }); - } - catch (Exception ex) - { - Log.Error($"{ex}"); - //Log.Error("Performance Fish failed to apply its JobGiver_Haul transpiler. This particular optimization will be doing nothing now"); - return null; - } - } - public static List SortedThingsPotentiallyNeedingHauling(List things, Pawn pawn) - { - _comparer.rootCell = pawn.Position; - things.Sort(_comparer); - return things; - } - private static WorkGiver_DoBillOptimization.ThingPositionComparer _comparer = new(); - } + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(JobGiver_Haul), nameof(JobGiver_Haul.TryGiveJob)); + + public static CodeInstructions? Transpiler(CodeInstructions codes, MethodBase method) + { + if (ActiveMods.Multiplayer) // PUAH has known issues with sorting and multiplayer + return codes; + + var pawn_Map_listerHaulables_ThingsPotentiallyNeedingHauling + = FishTranspiler.Call(static () => default(ListerHaulables)!.ThingsPotentiallyNeedingHauling()); + + try + { + return codes.InsertAfter(c => c == pawn_Map_listerHaulables_ThingsPotentiallyNeedingHauling, + new CodeInstruction[] + { + FishTranspiler.Argument(method, "pawn"), + FishTranspiler.Call(SortedThingsPotentiallyNeedingHauling) + }); + } + catch (Exception ex) + { + Log.Error($"{ex}"); + return null; + } + } + + public static List SortedThingsPotentiallyNeedingHauling(List things, Pawn pawn) + { + _comparer.rootCell = pawn.Position; + things.Sort(_comparer); + return things; + } + + private static WorkGiver_DoBillOptimization.ThingPositionComparer _comparer = new(); + } } \ No newline at end of file diff --git a/Source/PerformanceFish/JobSystem/JobGiver_WorkPatches.cs b/Source/PerformanceFish/JobSystem/JobGiver_WorkPatches.cs index b61b748..19f14e3 100644 --- a/Source/PerformanceFish/JobSystem/JobGiver_WorkPatches.cs +++ b/Source/PerformanceFish/JobSystem/JobGiver_WorkPatches.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. diff --git a/Source/PerformanceFish/JobSystem/WorkGiver_DoBillOptimization.cs b/Source/PerformanceFish/JobSystem/WorkGiver_DoBillOptimization.cs index a75348d..25dc8ee 100644 --- a/Source/PerformanceFish/JobSystem/WorkGiver_DoBillOptimization.cs +++ b/Source/PerformanceFish/JobSystem/WorkGiver_DoBillOptimization.cs @@ -1,40 +1,61 @@ -// Copyright (c) 2022 bradson +// 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 System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Emit; using Verse.AI; -using IngredientCacheList = PerformanceFish.Utility.KeyedList Defs, bool Found)>; -using RecipeIngredientCache = PerformanceFish.Cache.ByIndex; +using IngredientCacheList + = PerformanceFish.Utility.KeyedList Defs, + bool Found)>; +using RecipeIngredientCache + = PerformanceFish.Cache.ByIndex; namespace PerformanceFish.JobSystem; public class WorkGiver_DoBillOptimization : ClassWithFishPatches { - public static List BlackListedModExtensions => _blackListedModExtensions ??= new() { Reflection.Type("VFEAncients", "VFEAncients.RecipeExtension_Mend") }; - private static List? _blackListedModExtensions; + public static List BlackListedModExtensions + => _blackListedModExtensions ??= new() { Reflection.Type("VFEAncients", "VFEAncients.RecipeExtension_Mend") }; - public class TryFindBestIngredientsHelper_InnerDelegate_Patch : FishPatch + private static List? _blackListedModExtensions; + + public class TryFindBestIngredientsHelper_InnerDelegate_Patch : FirstPriorityFishPatch { - public override string Description => "Optimizes WorkGiver_DoBill to only check things that get accepted by a bill's filter for its ingredient lookup, instead of looping over everything and then checking filters afterwards. " + - "Also caches these filter acceptances. Large performance impact."; + public override string Description { get; } + = "Optimizes WorkGiver_DoBill to only check things that get accepted by a bill's filter for its " + + "ingredient lookup, instead of looping over everything and then checking filters afterwards. " + + "Also caches these filter acceptances. Large performance impact."; - public override MethodBase TargetMethodInfo - => AccessTools.FirstMethod(DelegateInstanceType, m - => m.GetParameters() is { Length: 1 } parms - && parms[0].ParameterType == typeof(Region) + public override bool Enabled + { + set + { + if (base.Enabled == value) + return; + + Get().Enabled + = Get().Enabled + = base.Enabled = value; + } + } + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.FirstMethod(DelegateInstanceType, static m + => m.GetParameters() is { Length: 1 } parameterInfos + && parameterInfos[0].ParameterType == typeof(Region) && m.ReturnType == typeof(bool) - && PatchProcessor.ReadMethodBody(m).Any(pair - => (pair.Key == OpCodes.Call || pair.Key == OpCodes.Callvirt) - && pair.Value is MethodInfo info + && MethodBodyReader.GetInstructions(null, m).Any(static instruction + => (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) + && instruction.operand is MethodInfo info && info == AccessTools.Method(typeof(ListerThings), nameof(ListerThings.ThingsMatching)))); - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions, MethodBase method) - => Reflection.MakeReplacementCall( - AccessTools.Method(typeof(TryFindBestIngredientsHelper_InnerDelegate_Patch), nameof(Replacement), generics: new[] { DelegateInstanceType })); + public static CodeInstructions Transpiler(CodeInstructions codeInstructions, MethodBase method) + => Reflection.MakeReplacementCall(AccessTools.Method( + typeof(TryFindBestIngredientsHelper_InnerDelegate_Patch), nameof(Replacement), + generics: new[] { DelegateInstanceType })); public static bool Replacement(T __instance, Region r) where T : class { @@ -54,46 +75,47 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches #if V1_4 || bill.billStack?.billGiver is Building_MechGestator #endif - || (BlackListedModExtensions.Exists(b => b != null) && IsBlackListed(bill))) // <-- Fuck these too + || (BlackListedModExtensions.Exists(static b => b != null) + && IsBlackListed(bill))) // <-- Fuck these too { list = GetDefaultList(r); } else { ref var cache = ref RecipeIngredientCache.Get.GetReference(bill); - if (cache.thingDefs is null || cache.ShouldRefreshNow) + if (cache.thingDefs is null || cache.Dirty) { cache.thingDefs = UpdateCache(bill, r, cache.thingDefs); - cache.ShouldRefreshNow = false; - RecipeIngredientCache.Get[bill] = cache; + cache.Update(bill); } + list = GetList(r.listerThings.listsByDef, cache.thingDefs); } } ActualLoop(list, r, instance.Pawn, instance.BaseValidator, instance.BillGiverIsPawn); - //list.Clear(); - instance.RegionsProcessed++; - if (WorkGiver_DoBill.newRelevantThings.Count > 0 && instance.RegionsProcessed > instance.AdjacentRegionsAvailable) - { - //WorkGiver_DoBill.relevantThings.AddRange(WorkGiver_DoBill.newRelevantThings); - InsertAtCorrectPosition(WorkGiver_DoBill.relevantThings, WorkGiver_DoBill.newRelevantThings, instance.Pawn); + if (WorkGiver_DoBill.newRelevantThings.Count <= 0 + || instance.RegionsProcessed <= instance.AdjacentRegionsAvailable) + return false; + + //WorkGiver_DoBill.relevantThings.AddRange(WorkGiver_DoBill.newRelevantThings); + InsertAtCorrectPosition(WorkGiver_DoBill.relevantThings, WorkGiver_DoBill.newRelevantThings, + instance.Pawn); + + WorkGiver_DoBill.newRelevantThings.Clear(); + if (!instance.FoundAllIngredientsAndChoose(WorkGiver_DoBill.relevantThings)) + return false; + + instance.FoundAll = true; + return true; - WorkGiver_DoBill.newRelevantThings.Clear(); - if (instance.FoundAllIngredientsAndChoose(WorkGiver_DoBill.relevantThings)) - { - instance.FoundAll = true; - return true; - } - } - return false; } private static bool HasNoFilters(Bill bill) => bill.ingredientFilter is null - || bill.recipe.fixedIngredientFilter is null; + || bill.recipe.fixedIngredientFilter is null; /*private static bool HasNoFilters(Bill bill) { @@ -113,13 +135,13 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches return false; }*/ - private static bool IsBlackListed(Bill bill) + private static bool IsBlackListed(Bill? bill) => bill is null - || (bill.recipe.modExtensions?.Exists(e - => BlackListedModExtensions.Contains(e.GetType())) - ?? false); + || bill.recipe.modExtensions.ExistsAndNotNull(static e + => BlackListedModExtensions.Contains(e.GetType())); - private static List GetDefaultList(Region r) => r.ListerThings.ThingsMatching(ThingRequest.ForGroup(ThingRequestGroup.HaulableEver)); + private static List GetDefaultList(Region r) + => r.ListerThings.ThingsMatching(ThingRequest.ForGroup(ThingRequestGroup.HaulableEver)); public static void InsertAtCorrectPosition(List list, List thingsToInsert, Pawn pawn) { @@ -139,18 +161,24 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches private static readonly ThingPositionComparer _insertAtCorrectPositionComparer = new(); - public static void ActualLoop(List list, Region r, Pawn pawn, Predicate baseValidator, bool billGiverIsPawn) + public static void ActualLoop(List list, Region r, Pawn pawn, Predicate baseValidator, + bool billGiverIsPawn) { var count = list.Count; for (var i = 0; i < count; i++) { var thing = list[i]; - if (!WorkGiver_DoBill.processedThings.Contains(thing) - && ReachabilityWithinRegion.ThingFromRegionListerReachable(thing, r, PathEndMode.ClosestTouch, pawn) && !(billGiverIsPawn && thing.def.IsMedicine) && baseValidator(thing)) + if (WorkGiver_DoBill.processedThings.Contains(thing) + || !ReachabilityWithinRegion.ThingFromRegionListerReachable(thing, r, PathEndMode.ClosestTouch, + pawn) + || (billGiverIsPawn && thing.def.IsMedicine) + || !baseValidator(thing)) { - WorkGiver_DoBill.newRelevantThings.Add(thing); - WorkGiver_DoBill.processedThings.Add(thing); + continue; } + + WorkGiver_DoBill.newRelevantThings.Add(thing); + WorkGiver_DoBill.processedThings.Add(thing); } } @@ -170,13 +198,14 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches var count = defs.Count; for (var j = 0; j < count; j++) { - if (listsByDef.TryGetValue(defs[j], out var listByDef) && listByDef.Any()) + if (!listsByDef.TryGetValue(defs[j], out var listByDef) + || !listByDef.Any() + || (i > 0 && !set.Add(defs[j]))) { - if (i > 0 && !set.Add(defs[j])) - continue; - - list.AddRange(listByDef); + continue; } + + list.AddRange(listByDef); } } @@ -190,7 +219,8 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches var listsByDef = r.Map.listerThings.listsByDef; var result = cacheList ?? new(3); - for (var i = 0; i < ingredients.Count; i++) // generally not larger than 3 in vanilla and anything that tries to be + for (var i = 0; i < ingredients.Count; + i++) // generally not larger than 3 in vanilla and anything that tries to be { if (result.TryGetValue(ingredients[i], out var ingredientList)) { @@ -203,15 +233,14 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches } var filter = ingredients[i].filter; - var allowedDefCount = filter.allowedDefs.Count; - if (allowedDefCount < 1) - continue; - - if (allowedDefCount == 1) + switch (filter.allowedDefs.Count) { - ingredientList.Defs.Add(filter.allowedDefs.First()); - continue; + case < 1: + continue; + case 1: + ingredientList.Defs.Add(filter.allowedDefs.First()); + continue; } var smallestFilter = bill.ingredientFilter; @@ -220,19 +249,25 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches OrderFiltersBySize(ref smallestFilter, ref mediumFilter, ref largestFilter); - var cachedSmallestList = AllowedDefsListCache.GetValue(smallestFilter).defs; + var cachedSmallestList = AllowedDefsListCache + .GetAndCheck(smallestFilter).Defs; + if (cachedSmallestList.Count != smallestFilter.allowedDefs.Count) { - // Log.Warning($"Cached list count: {cachedSmallestList.Count}, actual filter list count: {smallestFilter.allowedDefs.Count}. Forcing a refresh now to fix."); + // Log.Warning($"Cached list count: {cachedSmallestList.Count}, actual filter list count: { + // smallestFilter.allowedDefs.Count}. Forcing a refresh now to fix."); ThingFilterPatches.ForceSynchronizeCache(smallestFilter); - // Log.Warning($"New cached list count: {cachedSmallestList.Count}, actual filter list count: {smallestFilter.allowedDefs.Count}."); + // Log.Warning($"New cached list count: {cachedSmallestList.Count}, actual filter list count: { + // smallestFilter.allowedDefs.Count}."); // didn't trigger in 1.3, but now does. Fuck } + var smallestListCount = cachedSmallestList.Count; for (var k = 0; k < smallestListCount; k++) { var def = cachedSmallestList[k]; - if (listsByDef.TryGetValue(def, out var listByDef) && listByDef.Any() + if (listsByDef.TryGetValue(def, out var listByDef) + && listByDef.Any() && mediumFilter.Allows(def) && largestFilter.Allows(def)) { @@ -244,7 +279,8 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches return result; } - private static void OrderFiltersBySize(ref ThingFilter smallestFilter, ref ThingFilter mediumFilter, ref ThingFilter largestFilter) + private static void OrderFiltersBySize(ref ThingFilter smallestFilter, ref ThingFilter mediumFilter, + ref ThingFilter largestFilter) { if (smallestFilter.allowedDefs.Count > mediumFilter.allowedDefs.Count) (smallestFilter, mediumFilter) = (mediumFilter, smallestFilter); @@ -261,9 +297,10 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches } private static Type DelegateInstanceType { get; } - = AccessTools.FirstInner(typeof(WorkGiver_DoBill), t - => AccessTools.FirstMethod(t, m - => m.Name.Contains($"<{nameof(WorkGiver_DoBill.TryFindBestIngredientsHelper)}>")) != null + = AccessTools.FirstInner(typeof(WorkGiver_DoBill), static t + => AccessTools.FirstMethod(t, static m + => m.Name.Contains($"<{nameof(WorkGiver_DoBill.TryFindBestIngredientsHelper)}>")) + != null && AccessTools.Field(t, "pawn") != null && AccessTools.Field(t, "baseValidator") != null && AccessTools.Field(t, "billGiverIsPawn") != null @@ -272,25 +309,30 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches && AccessTools.Field(t, "foundAllIngredientsAndChoose") != null && AccessTools.Field(t, "foundAll") != null && AccessTools.Field(t, "thingValidator") != null) - ?? throw new("Performance Fish failed to find the target type for its WorkGiver_DoBill patch. This means that specific patch won't apply."); + ?? throw new("Performance Fish failed to find the target type for its WorkGiver_DoBill patch. " + + "This means that specific patch won't apply."); private static Type ThingValidatorInstanceType { get; } - = AccessTools.FirstInner(typeof(WorkGiver_DoBill), t - => AccessTools.FirstMethod(t, m - => m.Name.Contains($"<{nameof(WorkGiver_DoBill.TryFindBestBillIngredients)}>")) != null - && AccessTools.Field(t, "bill") != null + = AccessTools.FirstInner(typeof(WorkGiver_DoBill), static t + => AccessTools.FirstMethod(t, static m + => m.Name.Contains($"<{nameof(WorkGiver_DoBill.TryFindBestBillIngredients)}>")) + != null + && AccessTools.Field(t, "bill") != null //&& AccessTools.Field(t, "chosen") != null //&& AccessTools.Field(t, "billGiver") != null /*&& AccessTools.Field(t, "pawn") != null*/) - ?? throw new("Performance Fish failed to find the thingValidator type for its WorkGiver_DoBill patch. This likely flat out breaks the entire patch."); + ?? throw new("Performance Fish failed to find the thingValidator type for its WorkGiver_DoBill patch. " + + "This likely flat out breaks the entire patch."); - [SuppressMessage("Style", "IDE1006:Naming Styles")] + // ReSharper disable once InconsistentNaming private static DelegateInstance _delegateInstance { get; } - = (DelegateInstance)Activator.CreateInstance(typeof(DelegateInstanceImplementation<>).MakeGenericType(DelegateInstanceType)); + = (DelegateInstance)Activator.CreateInstance( + typeof(DelegateInstanceImplementation<>).MakeGenericType(DelegateInstanceType)); - [SuppressMessage("Style", "IDE1006:Naming Styles")] + // ReSharper disable once InconsistentNaming private static ThingValidatorInstance _thingValidatorInstance { get; } - = (ThingValidatorInstance)Activator.CreateInstance(typeof(ThingValidatorInstanceImplementation<>).MakeGenericType(ThingValidatorInstanceType)); + = (ThingValidatorInstance)Activator.CreateInstance( + typeof(ThingValidatorInstanceImplementation<>).MakeGenericType(ThingValidatorInstanceType)); private abstract class DelegateInstance { @@ -318,23 +360,75 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches return this; } - public override Pawn Pawn { get => _pawn(_instance); set => _pawn(_instance) = value; } - public override Predicate BaseValidator { get => _baseValidator(_instance); set => _baseValidator(_instance) = value; } - public override bool BillGiverIsPawn { get => _billGiverIsPawn(_instance); set => _billGiverIsPawn(_instance) = value; } - public override int RegionsProcessed { get => _regionsProcessed(_instance); set => _regionsProcessed(_instance) = value; } - public override int AdjacentRegionsAvailable { get => _adjacentRegionsAvailable(_instance); set => _adjacentRegionsAvailable(_instance) = value; } - public override Predicate> FoundAllIngredientsAndChoose { get => _foundAllIngredientsAndChoose(_instance); set => _foundAllIngredientsAndChoose(_instance) = value; } - public override bool FoundAll { get => _foundAll(_instance); set => _foundAll(_instance) = value; } - public override Predicate ThingValidator { get => _thingValidator(_instance); set => _thingValidator(_instance) = value; } + public override Pawn Pawn + { + get => _pawn(_instance); + set => _pawn(_instance) = value; + } + + public override Predicate BaseValidator + { + get => _baseValidator(_instance); + set => _baseValidator(_instance) = value; + } + + public override bool BillGiverIsPawn + { + get => _billGiverIsPawn(_instance); + set => _billGiverIsPawn(_instance) = value; + } + + public override int RegionsProcessed + { + get => _regionsProcessed(_instance); + set => _regionsProcessed(_instance) = value; + } + + public override int AdjacentRegionsAvailable + { + get => _adjacentRegionsAvailable(_instance); + set => _adjacentRegionsAvailable(_instance) = value; + } + + public override Predicate> FoundAllIngredientsAndChoose + { + get => _foundAllIngredientsAndChoose(_instance); + set => _foundAllIngredientsAndChoose(_instance) = value; + } + + public override bool FoundAll + { + get => _foundAll(_instance); + set => _foundAll(_instance) = value; + } + + public override Predicate ThingValidator + { + get => _thingValidator(_instance); + set => _thingValidator(_instance) = value; + } private static AccessTools.FieldRef _pawn = AccessTools.FieldRefAccess("pawn"); - private static AccessTools.FieldRef> _baseValidator = AccessTools.FieldRefAccess>("baseValidator"); - private static AccessTools.FieldRef _billGiverIsPawn = AccessTools.FieldRefAccess("billGiverIsPawn"); - private static AccessTools.FieldRef _regionsProcessed = AccessTools.FieldRefAccess("regionsProcessed"); - private static AccessTools.FieldRef _adjacentRegionsAvailable = AccessTools.FieldRefAccess("adjacentRegionsAvailable"); - private static AccessTools.FieldRef>> _foundAllIngredientsAndChoose = AccessTools.FieldRefAccess>>("foundAllIngredientsAndChoose"); + + private static AccessTools.FieldRef> _baseValidator + = AccessTools.FieldRefAccess>("baseValidator"); + + private static AccessTools.FieldRef _billGiverIsPawn + = AccessTools.FieldRefAccess("billGiverIsPawn"); + + private static AccessTools.FieldRef _regionsProcessed + = AccessTools.FieldRefAccess("regionsProcessed"); + + private static AccessTools.FieldRef _adjacentRegionsAvailable + = AccessTools.FieldRefAccess("adjacentRegionsAvailable"); + + private static AccessTools.FieldRef>> _foundAllIngredientsAndChoose + = AccessTools.FieldRefAccess>>("foundAllIngredientsAndChoose"); + private static AccessTools.FieldRef _foundAll = AccessTools.FieldRefAccess("foundAll"); - private static AccessTools.FieldRef> _thingValidator = AccessTools.FieldRefAccess>("thingValidator"); + + private static AccessTools.FieldRef> _thingValidator + = AccessTools.FieldRefAccess>("thingValidator"); } private abstract class ThingValidatorInstance @@ -364,7 +458,11 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches } } - public override Bill Bill { get => _bill(_instance); set => _bill(_instance) = value; } + public override Bill Bill + { + get => _bill(_instance); + set => _bill(_instance) = value; + } //public override Thing BillGiver { get => _billGiver(_instance); set => _billGiver(_instance) = value; } private static AccessTools.FieldRef _bill = AccessTools.FieldRefAccess("bill"); @@ -373,9 +471,20 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches public class TryFindBestIngredientsHelper_Patch : FishPatch { - public override string Description => "Part of the DoBill optimization"; + public override string Description { get; } = "Part of the DoBill optimization"; - public override Delegate TargetMethodGroup => WorkGiver_DoBill.TryFindBestIngredientsHelper; + public override bool Enabled + { + set + { + if (base.Enabled == value) + return; + + Get().Enabled = base.Enabled = value; + } + } + + public override Delegate TargetMethodGroup { get; } = WorkGiver_DoBill.TryFindBestIngredientsHelper; public static void Postfix(Predicate thingValidator) { @@ -399,24 +508,43 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches public class TryFindBestIngredientsInSet_NoMixHelper_Patch : FishPatch { - public override string Description => "Part of the DoBill optimization"; + public override string Description { get; } + = "Part of the DoBill optimization. Bills that need multiple ingredients, like for example steel and " + + "components for guns, normally scan their surroundings for all those ingredients and count them, until " + + "enough of both is found. This patch removes ingredients from the lookup when enough of them is found, " + + "allowing the patched method to skip counting them and go straight to the other missing ingredient. In " + + "case of steel and components that could mean not having to go over 100s of steel stacks at times. " + + "Prevents large spikes from these situations, does nothing for all other bills."; - public override Delegate TargetMethodGroup => WorkGiver_DoBill.TryFindBestIngredientsInSet_NoMixHelper; + public override bool Enabled + { + set + { + if (base.Enabled == value) + return; + + Get().Enabled = base.Enabled = value; + } + } + + public override Delegate TargetMethodGroup { get; } = WorkGiver_DoBill.TryFindBestIngredientsInSet_NoMixHelper; public static void Prefix(ref bool alreadySorted, Bill? bill) => alreadySorted = alreadySorted - || (bill?.billStack is { } stack + || (bill?.billStack is { } stack #if V1_4 - && stack.billGiver is not Building_MechGestator + // ReSharper disable once MergeIntoPattern + && stack.billGiver is not Building_MechGestator #endif - ); + ); - public static CodeInstructions? Transpiler(CodeInstructions codes, MethodBase method) + public static CodeInstructions Transpiler(CodeInstructions instructions, MethodBase method, + ILGenerator generator) { var i_variable = FishTranspiler.FirstLocalVariable(method, typeof(int)); + var new_i_label = generator.DefineLabel(); - return codes.ReplaceAt( - (codes, i) + return instructions.ReplaceAt((codes, i) => i + 3 < codes.Count && codes[i] == i_variable.Load() && codes[i + 1] == FishTranspiler.Constant(1) // i++; @@ -425,22 +553,33 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches code => new[] { + new(OpCodes.Br_S, new_i_label), // else FishTranspiler.FirstLocalVariable(method, typeof(IngredientCount)) .WithLabels(code.ExtractLabels()), FishTranspiler.FirstArgument(method, typeof(Bill)), FishTranspiler.Call(MarkIngredientCountAsFound), - code - } - ); + code.WithLabels(new_i_label) + }); + + // if (!flag) + // { + // if (missingIngredients == null) + // return false; + // missingIngredients.Add(ingredientCount); + // } + // else + // { + // MarkIngredientCountAsFound(ingredientCount, bill); + // } } - public static void MarkIngredientCountAsFound(IngredientCount ingredientCount, Bill bill) + public static void MarkIngredientCountAsFound(IngredientCount ingredientCount, Bill? bill) { if (bill?.billStack is not { } billStack #if V1_4 || billStack.billGiver is Building_MechGestator #endif - ) + ) { return; } @@ -451,7 +590,7 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches ref var cacheItem = ref cache._items[cache.IndexOfKey(ingredientCount)]; - if ((cache.Count == 1 && cacheItem.Value.Defs.Count == 1) + if ((cache.Count == 1 /*&& cacheItem.Value.Defs.Count == 1*/) || bill.recipe?.workerCounterClass != typeof(RecipeWorkerCounter)) { return; @@ -465,25 +604,24 @@ public class WorkGiver_DoBillOptimization : ClassWithFishPatches { public IntVec3 rootCell; - public int Compare(Thing x, Thing y) => (x.Position - rootCell).LengthHorizontalSquared.CompareTo((y.Position - rootCell).LengthHorizontalSquared); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Compare(Thing x, Thing y) + => (x.Position - rootCell).LengthHorizontalSquared.CompareTo( + (y.Position - rootCell).LengthHorizontalSquared); } - public struct RecipeIngredientCacheValue + public record struct RecipeIngredientCacheValue { - public IngredientCacheList thingDefs; - + public IngredientCacheList? thingDefs; private int _nextRefreshTick; - public RecipeIngredientCacheValue(IngredientCacheList thingDefs) - { - this.thingDefs = thingDefs; - _nextRefreshTick = Current.Game.tickManager.TicksGame + 384 + Math.Abs(Rand.Int % 256); - } + public void Update(Bill bill) + => _nextRefreshTick = TickHelper.Add(GenTicks.TickRareInterval * 2, bill.loadID); - public bool ShouldRefreshNow + public bool Dirty { - get => _nextRefreshTick < Current.Game.tickManager.TicksGame; - set => _nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 384 + Math.Abs(Rand.Int % 256); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => TickHelper.Past(_nextRefreshTick); } } } \ No newline at end of file diff --git a/Source/PerformanceFish/JobSystem/WorkGiver_HaulOptimization.cs b/Source/PerformanceFish/JobSystem/WorkGiver_HaulOptimization.cs index 500777a..8f095b9 100644 --- a/Source/PerformanceFish/JobSystem/WorkGiver_HaulOptimization.cs +++ b/Source/PerformanceFish/JobSystem/WorkGiver_HaulOptimization.cs @@ -1,29 +1,38 @@ -// Copyright (c) 2022 bradson +// 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 PerformanceFish.ModCompatibility; + namespace PerformanceFish.JobSystem; public class WorkGiver_HaulOptimization : ClassWithFishPatches { - public class PotentialWorkThingsGlobal_Patch : FishPatch - { - public override string Description => "Sorts haulables by distance before running expensive hauling calculations on them to avoid checks on far away items"; - public override MethodBase TargetMethodInfo => AccessTools.Method(typeof(WorkGiver_Haul), nameof(WorkGiver_Haul.PotentialWorkThingsGlobal)); - public static IEnumerable Postfix(IEnumerable __result, Pawn pawn) - { - if (pawn != null && __result is List list) - Sort(list, pawn); - return __result; - } - private static void Sort(List list, Pawn pawn) - { - _comparer.rootCell = pawn.Position; - list.Sort(_comparer); + public class PotentialWorkThingsGlobal_Patch : FishPatch + { + public override bool Enabled => base.Enabled && !ActiveMods.Multiplayer; - //Log.ErrorOnce(list.Select(t => t.Position.DistanceToSquared(pawn.Position)).ToStringSafeEnumerable(), 84736285); - } - private static WorkGiver_DoBillOptimization.ThingPositionComparer _comparer = new(); - } + public override string Description { get; } + = "Sorts haulables by distance before running expensive hauling calculations on them to avoid checks on " + + "far away items"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(WorkGiver_Haul), nameof(WorkGiver_Haul.PotentialWorkThingsGlobal)); + + public static IEnumerable Postfix(IEnumerable __result, Pawn? pawn) + { + if (pawn != null && __result is List list) + Sort(list, pawn); + return __result; + } + + private static void Sort(List list, Pawn pawn) + { + _comparer.rootCell = pawn.Position; + list.Sort(_comparer); + } + + private static WorkGiver_DoBillOptimization.ThingPositionComparer _comparer = new(); + } } \ No newline at end of file diff --git a/Source/PerformanceFish/JobSystem/WorkGiver_MergeOptimization.cs b/Source/PerformanceFish/JobSystem/WorkGiver_MergeOptimization.cs index 0cd6756..2b69252 100644 --- a/Source/PerformanceFish/JobSystem/WorkGiver_MergeOptimization.cs +++ b/Source/PerformanceFish/JobSystem/WorkGiver_MergeOptimization.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -11,18 +11,21 @@ public class WorkGiver_MergeOptimization : ClassWithFishPatches { public class JobOnThing_Patch : FishPatch { - public override string Description => "Removes mergeables from ListerMergeables if they no longer qualify as mergeable when trying to check for a merging job on them. Vanilla RimWorld just fails and keeps them in"; - public override MethodBase TargetMethodInfo => AccessTools.Method(typeof(WorkGiver_Merge), nameof(WorkGiver_Merge.JobOnThing)); + public override string Description { get; } + = "Removes mergeables from ListerMergeables if they no longer qualify as mergeable when trying to check " + + "for a merging job on them. Vanilla RimWorld just fails and keeps them in"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(WorkGiver_Merge), nameof(WorkGiver_Merge.JobOnThing)); public static bool Prefix(Thing t, ref Job? __result) { - if (t.GetSlotGroup() is null) - { - RemoveFromListerMergeables(t); - __result = null; - return false; - } - return true; + if (t.GetSlotGroup() is not null) + return true; + + RemoveFromListerMergeables(t); + __result = null; + return false; } private static void RemoveFromListerMergeables(Thing t) diff --git a/Source/PerformanceFish/Listers/Buildings.cs b/Source/PerformanceFish/Listers/Buildings.cs index 3f4f1d7..776e33e 100644 --- a/Source/PerformanceFish/Listers/Buildings.cs +++ b/Source/PerformanceFish/Listers/Buildings.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -13,12 +13,18 @@ public class Buildings : ClassWithFishPatches /// Get Things from the ListerThings dictionary with a rather quick lookup instead of looping over every single element in the lists of listerBuildings /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static List ThingsOfDefFast(ListerThings listerThings, ThingDef def) => listerThings.listsByDef.TryGetValue(def) ?? ListerThings.EmptyList; + public static List ThingsOfDefFast(ListerThings listerThings, ThingDef def) + => listerThings.listsByDef.TryGetValue(def) ?? ListerThings.EmptyList; public class ColonistsHaveBuilding_Patch : FishPatch { - public override string Description => "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much outside of a few prevented spikes here and there either"; - public override Expression TargetMethod => () => default(ListerBuildings)!.ColonistsHaveBuilding(default(ThingDef)); + public override string Description { get; } + = "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much " + + "outside of a few prevented spikes here and there either"; + + public override Expression TargetMethod { get; } + = static () => default(ListerBuildings)!.ColonistsHaveBuilding(default(ThingDef)); + public static bool Prefix(ListerBuildings __instance, ref bool __result, ThingDef def) { var lister = GetListerThings(__instance); @@ -32,35 +38,53 @@ public class Buildings : ClassWithFishPatches public class ColonistsHaveResearchBench_Patch : FishPatch { - public override string Description => "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much outside of a few prevented spikes here and there either"; - public override Expression TargetMethod => () => default(ListerBuildings)!.ColonistsHaveResearchBench(); + public override string Description { get; } + = "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much " + + "outside of a few prevented spikes here and there either"; + + public override Expression TargetMethod { get; } + = static () => default(ListerBuildings)!.ColonistsHaveResearchBench(); + public static bool Prefix(ListerBuildings __instance, ref bool __result) { var lister = GetListerThings(__instance); if (lister is null) return true; - foreach (var def in LazyInit.ResearchBenches) + var researchBenches = LazyInit.ResearchBenches; + for (var i = 0; i < researchBenches.Length; i++) { - if (ContainsDefForColonists(lister, def)) - { - __result = true; - break; - } + if (!ContainsDefForColonists(lister, researchBenches[i])) + continue; + + __result = true; + break; } + return false; } + public static class LazyInit { - public static ThingDef[] ResearchBenches = DefDatabase.AllDefsListForReading.Where(def => def.thingClass == typeof(Building_ResearchBench)).ToArray(); - static LazyInit() { } + public static ThingDef[] ResearchBenches = DefDatabase.AllDefsListForReading + .Where(static def => def.thingClass == typeof(Building_ResearchBench)).ToArray(); + + static LazyInit() + { + // beforefieldinit + } } } public class ColonistsHaveBuildingWithPowerOn_Patch : FishPatch { - public override string Description => "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much outside of a few prevented spikes here and there either"; - public override Expression TargetMethod => () => default(ListerBuildings)!.ColonistsHaveBuildingWithPowerOn(null); + public override string Description { get; } + = "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much " + + "outside of a few prevented spikes here and there either"; + + public override Expression TargetMethod { get; } + = static () => default(ListerBuildings)!.ColonistsHaveBuildingWithPowerOn(null); + public static bool Prefix(ListerBuildings __instance, ref bool __result, ThingDef def) { var lister = GetListerThings(__instance); @@ -71,21 +95,29 @@ public class Buildings : ClassWithFishPatches var playerFaction = Faction.OfPlayerSilentFail; for (var i = 0; i < things.Count; i++) { - if (things[i].Faction == playerFaction - && things[i].TryGetComp() is not { PowerOn: false }) + if (things[i].Faction != playerFaction + || things[i].TryGetComp() is { PowerOn: false }) { - __result = true; - break; + continue; } + + __result = true; + break; } + return false; } } public class AllBuildingsColonistOfDef_Patch : FishPatch { - public override string Description => "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much outside of a few prevented spikes here and there either"; - public override Expression TargetMethod => () => default(ListerBuildings)!.AllBuildingsColonistOfDef(null); + public override string Description { get; } + = "ListerBuildings optimization. Relatively large performance impact when having water power plants on the " + + "map."; + + public override Expression TargetMethod { get; } + = static () => default(ListerBuildings)!.AllBuildingsColonistOfDef(null); + public static bool Prefix(ListerBuildings __instance, ref IEnumerable __result, ThingDef def) { var lister = GetListerThings(__instance); @@ -95,6 +127,7 @@ public class Buildings : ClassWithFishPatches __result = AllBuildingsColonistOfDefReplacement(lister, def); return false; } + public static IEnumerable AllBuildingsColonistOfDefReplacement(ListerThings lister, ThingDef def) { var things = ThingsOfDefFast(lister, def); @@ -115,10 +148,20 @@ public class Buildings : ClassWithFishPatches } } + public record struct AllBuildingsColonistOfDefCacheValue + { + public List Buildings; + } + public class AllBuildingsNonColonistOfDef_Patch : FishPatch { - public override string Description => "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much outside of a few prevented spikes here and there either"; - public override Expression TargetMethod => () => default(ListerBuildings)!.AllBuildingsNonColonistOfDef(null); + public override string Description { get; } + = "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much " + + "outside of a few prevented spikes here and there either"; + + public override Expression TargetMethod { get; } + = static () => default(ListerBuildings)!.AllBuildingsNonColonistOfDef(null); + public static bool Prefix(ListerBuildings __instance, ref IEnumerable __result, ThingDef def) { var lister = GetListerThings(__instance); @@ -128,6 +171,7 @@ public class Buildings : ClassWithFishPatches __result = AllBuildingsNonColonistOfDefReplacement(lister, def); return false; } + public static IEnumerable AllBuildingsNonColonistOfDefReplacement(ListerThings lister, ThingDef def) { var things = ThingsOfDefFast(lister, def); @@ -149,84 +193,110 @@ public class Buildings : ClassWithFishPatches } #if harmonyIsFailingOnThisOne - public class AllBuildingsColonistOfClass_Patch : FishPatch + public class AllBuildingsColonistOfClass_Patch : FishPatch + { + public override string Description { get; } + = "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much " + + "outside of a few prevented spikes here and there either"; + + public override Expression TargetMethod { get; } + = static () => default(ListerBuildings)!.AllBuildingsColonistOfClass(); + + public static bool Prefix(ListerBuildings __instance, MethodBase __originalMethod, ref IEnumerable __result) { - public override string Description => "ListerBuildings optimization. Should be relatively riskfree, but doesn't impact performance much outside of a few prevented spikes here and there either"; - public override Expression TargetMethodExpression => () => default(ListerBuildings).AllBuildingsColonistOfClass(); - public static bool Prefix(ListerBuildings __instance, MethodBase __originalMethod, ref IEnumerable __result) + var lister = GetListerThings(__instance); + if (lister is null) + return true; + + var type = __originalMethod.GetGenericArguments()[0]; //<-- This always returns Building instead of the + // type it's actually meant to look for + DebugLog.Message($"AllBuildingsColonistOfClass running for type {type.Name}"); + if (!Replacements.TryGetValue(type, out var replacement)) { - var lister = GetListerThings(__instance); - if (lister is null) - return true; - - var type = __originalMethod.GetGenericArguments()[0]; //<-- This always returns Building instead of the type it's actually meant to look for - DebugLog.Message($"AllBuildingsColonistOfClass running for type {type.Name}"); - if (!Replacements.TryGetValue(type, out var replacement)) - Replacements.Add(type, (GenericReplacementInterface)Activator.CreateInstance(typeof(GenericReplacement<>).MakeGenericType(type))); - - __result = replacement.Replacement(lister); - return false; + Replacements.Add(type, + replacement = (GenericReplacementBase)Activator.CreateInstance( + typeof(GenericReplacement<>).MakeGenericType(type))); } - public static Dictionary Replacements { get; } = new(); - public interface GenericReplacementInterface + __result = replacement.Replacement(lister); + return false; + } + + public static Dictionary Replacements { get; } = new(); + + public abstract class GenericReplacementBase + { + public abstract IEnumerable Replacement(ListerThings lister); + } + + public class GenericReplacement : GenericReplacementBase where T : Building + { + public override IEnumerable Replacement(ListerThings lister) { - public IEnumerable Replacement(ListerThings lister); - } - public class GenericReplacement : GenericReplacementInterface where T : Building - { - public IEnumerable Replacement(ListerThings lister) + buildingsOfClass ??= DefDatabase.AllDefsListForReading + .Where(static def => def.thingClass == typeof(T)).ToArray(); + + foreach (var def in buildingsOfClass) { - buildingsOfClass ??= DefDatabase.AllDefsListForReading.Where(def => def.thingClass == typeof(T)).ToArray(); - foreach (var def in buildingsOfClass) + var things = ThingsOfDefFast(lister, def); + for (var i = 0; i < things.Count; i++) { - var things = ThingsOfDefFast(lister, def); - for (var i = 0; i < things.Count; i++) - { - if (things[i].Faction == Faction.OfPlayerSilentFail) - yield return things[i]; - } + if (things[i].Faction == Faction.OfPlayerSilentFail) + yield return things[i]; } } - - public ThingDef[] buildingsOfClass; } + + public ThingDef[]? buildingsOfClass; } + } #endif public class Map_Patch : FishPatch { - public override string Description => "Part of the ListerBuildings optimizations. Without this patch mods like Colony Manager throw errors during loading"; - public override MethodBase TargetMethodInfo => AccessTools.Constructor(typeof(Map)); + public override string Description { get; } + = "Part of the ListerBuildings optimizations. Without this patch mods like Colony Manager throw errors " + + "during loading"; + + public override MethodBase TargetMethodInfo { get; } = AccessTools.Constructor(typeof(Map)); public static void Postfix(Map __instance) => _tempMap = __instance; } - private static Map? _tempMap; //mapComps get constructed before maps are added to Game.Maps, so this makes the patches work there too + private static Map? _tempMap; + // mapComps get constructed before maps are added to Game.Maps, so this makes the patches work there too - public static void ThrowWarning(ThingDef def, Thing thing) => Log.Warning($"Tried to get building of def {def}, but its type is {thing.GetType()} instead of Building"); + public static void ThrowWarning(ThingDef def, Thing thing) + => Log.Warning($"Tried to get building of def {def}, but its type is {thing.GetType()} instead of Building"); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListerThings? GetListerThings(ListerBuildings listerBuildings) { - var maps = Current.Game.Maps; + var maps = Current.gameInt.Maps; for (var i = 0; i < maps.Count; i++) { if (maps[i].listerBuildings == listerBuildings) return maps[i].listerThings; } - return _tempMap!.listerBuildings == listerBuildings ? _tempMap!.listerThings : null; + return GetListerThingsUsingTempMapFallback(listerBuildings); } + [MethodImpl(MethodImplOptions.NoInlining)] + private static ListerThings? GetListerThingsUsingTempMapFallback(ListerBuildings listerBuildings) + => _tempMap!.listerBuildings == listerBuildings ? _tempMap.listerThings : null; + public static bool ContainsDefForColonists(ListerThings listerThings, ThingDef def) { if (!listerThings.listsByDef.TryGetValue(def, out var things)) return false; + var playerFaction = Faction.OfPlayerSilentFail; for (var i = 0; i < things.Count; i++) { if (things[i].Faction == playerFaction) return true; } + return false; } } \ No newline at end of file diff --git a/Source/PerformanceFish/Listers/Haulables.cs b/Source/PerformanceFish/Listers/Haulables.cs index 79c1dca..7460a54 100644 --- a/Source/PerformanceFish/Listers/Haulables.cs +++ b/Source/PerformanceFish/Listers/Haulables.cs @@ -1,13 +1,18 @@ -// Copyright (c) 2022 bradson +// 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/. -global using HaulablesCache = PerformanceFish.Cache.ByReferenceRefreshable; +global using HaulablesCache + = PerformanceFish.Cache.ByReference; using System.Collections.Concurrent; using System.Diagnostics; using System.Reflection.Emit; +using System.Runtime.InteropServices; using System.Threading.Tasks; +using PerformanceFish.Cache; +using PerformanceFish.ModCompatibility; namespace PerformanceFish.Listers; @@ -15,13 +20,18 @@ public class Haulables : ClassWithFishPatches { public class MapPreTick_Patch : FishPatch { - public override string Description => "Throttling of the ListerHaulablesTick. This marks whether items should be moved from one stockpile to another. " + - "When throttled these checks get spread out more evenly, helping against spikes that can happen with large deep storage containers."; + public override bool Enabled => base.Enabled && !ActiveMods.Multiplayer; + + public override string Description { get; } + = "Throttling of the ListerHaulablesTick. This marks whether items should be moved from one stockpile to " + + "another. When throttled these checks get spread out more evenly, helping against spikes that can " + + "happen with large deep storage containers."; - public override Expression TargetMethod => () => default(Map)!.MapPreTick(); + public override Expression TargetMethod { get; } = static () => default(Map)!.MapPreTick(); public static CodeInstructions Transpiler(CodeInstructions codes) - => codes.MethodReplacer(new ListerHaulables(null).ListerHaulablesTick, ThreadedOrThrottled_ListerHaulablesTick); + => codes.MethodReplacer(new ListerHaulables(null).ListerHaulablesTick, + ThreadedOrThrottled_ListerHaulablesTick); public static void ThreadedOrThrottled_ListerHaulablesTick(ListerHaulables instance) { @@ -42,7 +52,7 @@ public class Haulables : ClassWithFishPatches if (currentCellCheckCount is null) return; - if (_currentTask != null && !_currentTask.IsCompleted) + if (_currentTask is { IsCompleted: false }) { if (currentCellCheckCount.value > 1) currentCellCheckCount.value--; @@ -51,8 +61,8 @@ public class Haulables : ClassWithFishPatches Interlocked.Increment(ref _queue); _currentTask = _currentTask.ContinueWith(t => { - if (t.Exception is { } exception) - Log.Error(exception.ToString()); + if (t.Exception is { } taskException) + Log.Error(taskException.ToString()); lock (Lock) { @@ -67,7 +77,8 @@ public class Haulables : ClassWithFishPatches { if (_skippedTicks >= 9) { - Log.Warning("Skipped 10 or more ListerHaulables ticks. This often means there's some kind of hauling error happening"); + Log.Warning("Skipped 10 or more ListerHaulables ticks. This often means there's some " + + "kind of hauling error happening"); _skippedTicks = 0; } else @@ -75,6 +86,7 @@ public class Haulables : ClassWithFishPatches _skippedTicks++; } } + return; } else @@ -122,6 +134,7 @@ public class Haulables : ClassWithFishPatches { Log.Error(ex.ToString()); } + Stopwatch.Stop(); if (Stopwatch.ElapsedTicks > 4000L) { @@ -133,6 +146,7 @@ public class Haulables : ClassWithFishPatches if (cellCheckCount.value < 4) cellCheckCount.value++; } + Stopwatch.Reset(); } @@ -147,6 +161,7 @@ public class Haulables : ClassWithFishPatches if (groupCycleIndex >= int.MaxValue) groupCycleIndex = 0; } + _lastGroupCycleIndex = groupCycleIndex; var allGroupsListForReading = instance.map.haulDestinationManager.AllGroupsListForReading; @@ -155,9 +170,11 @@ public class Haulables : ClassWithFishPatches var num = groupCycleIndex % allGroupsListForReading.Count; var slotGroup = allGroupsListForReading[num]; - return slotGroup.CellsList.Count == 0 ? null + return slotGroup.CellsList.Count == 0 + ? null : ListerHaulablesTick_Patch.CellCheckCountsBySlotGroup.GetOrCreateValue(slotGroup); } + public static int MainThread { get; private set; } public static ConcurrentQueue ThingsToRemove { get; } = new(); public static ConcurrentQueue ThingsToAdd { get; } = new(); @@ -173,15 +190,21 @@ public class Haulables : ClassWithFishPatches public class ListerHaulablesTick_Patch : FishPatch { - public override string Description => "Part of the ListerHaulables throttling optimization"; - public override Expression TargetMethod => () => default(ListerHaulables)!.ListerHaulablesTick(); + public override bool Enabled => base.Enabled && !ActiveMods.Multiplayer; + + public override string Description { get; } = "Part of the ListerHaulables throttling optimization"; + + public override Expression TargetMethod { get; } + = static () => default(ListerHaulables)!.ListerHaulablesTick(); + public static CodeInstructions Transpiler(CodeInstructions codes) => codes //.MethodReplacer(GridsUtility.GetThingList, GetCopyOfThingList) - .Manipulator(c => c.LoadsConstant(4), c => - { - c.opcode = OpCodes.Call; - c.operand = typeof(ListerHaulablesTick_Patch).GetProperty(nameof(CurrentCellCheckCount)).GetMethod; - }); + .Manipulator(static c => c.LoadsConstant(4), static c => + { + c.opcode = OpCodes.Call; + c.operand = typeof(ListerHaulablesTick_Patch).GetProperty(nameof(CurrentCellCheckCount))!.GetMethod; + }); + /*public static List GetCopyOfThingList(IntVec3 c, Map map) { if (FishSettings.ThreadingEnabled) @@ -196,6 +219,7 @@ public class Haulables : ClassWithFishPatches }*/ public static int CurrentCellCheckCount { get; set; } = ListerHaulables.CellsPerTick; public static ConditionalWeakTable CellCheckCountsBySlotGroup { get; } = new(); + public class CellCheckCount { public int value = ListerHaulables.CellsPerTick; @@ -205,15 +229,14 @@ public class Haulables : ClassWithFishPatches public static void TryAdd(ListerHaulables instance, Thing t) { - var cache = GetAndCheckHaulablesCache(instance, t); - if (!cache.contains) - { - if (!instance.haulables.Contains(t)) - instance.haulables.Add(t); - cache.contains = true; - cache.ShouldRefreshNow = false; - HaulablesCache.Get[new(t, instance)] = cache; - } + ref var cache = ref HaulablesCache.GetAndCheck(t, instance); + if (cache.Contains) + return; + + if (!instance.haulables.Contains(t)) + instance.haulables.Add(t); + + cache.SetValue(true, t); } public static void ThreadSafeTryAdd(ListerHaulables instance, Thing t) @@ -224,21 +247,19 @@ public class Haulables : ClassWithFishPatches } else { - if (!GetAndCheckHaulablesCache(instance, t).contains) + if (!HaulablesCache.GetAndCheck(t, instance).Contains) MapPreTick_Patch.ThingsToAdd.Enqueue(t); } } public static void TryRemove(ListerHaulables instance, Thing t) { - var cache = GetAndCheckHaulablesCache(instance, t); - if (cache.contains) - { - instance.haulables.Remove(t); - cache.contains = false; - cache.ShouldRefreshNow = false; - HaulablesCache.Get[new(t, instance)] = cache; - } + ref var cache = ref HaulablesCache.GetAndCheck(t, instance); + if (!cache.Contains) + return; + + instance.haulables.Remove(t); + cache.SetValue(false, t); } public static void ThreadSafeTryRemove(ListerHaulables instance, Thing t) @@ -249,37 +270,45 @@ public class Haulables : ClassWithFishPatches } else { - if (GetAndCheckHaulablesCache(instance, t).contains) + if (HaulablesCache.GetAndCheck(t, instance).Contains) MapPreTick_Patch.ThingsToRemove.Enqueue(t); } } public class Check_Patch : FishPatch { - public override string Description => "Optimization for haulables checking"; - public override Expression TargetMethod => () => default(ListerHaulables)!.Check(null); + public override string Description { get; } = "Optimization for haulables checking"; + public override Expression TargetMethod { get; } = static () => default(ListerHaulables)!.Check(null); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Check(ListerHaulables instance, Thing t) { - //So the situation here is this: Vanilla RimWorld normally checks 4 storage cells per tick for valid haul destinations, usually with the intent of reaching higher priority storage. - //This check itself can get quite expensive, but 4 of them aren't at all enough to be worth bothering with. - //Now problems arise when LWM's deep storage comes into play: You put 5 stacks of items in one cell and suddenly 4 checks turn into 20. - //20 hauling checks in turn is a number large enough to cause performance issues in colonies with high wealth. + // So the situation here is this: Vanilla RimWorld normally checks 4 storage cells per tick for valid haul + // destinations, usually with the intent of reaching higher priority storage. This check itself can get + // quite expensive, but 4 of them aren't at all enough to be worth bothering with. Now problems arise when + // LWM's deep storage comes into play: You put 5 stacks of items in one cell and suddenly 4 checks turn + // into 20. 20 hauling checks in turn is a number large enough to cause performance issues in colonies with + // high wealth. + + if (!t.def.alwaysHaulable && !t.def.designateHaulable) + return; if (instance.ShouldBeHaulable(t)) ThreadSafeTryAdd(instance, t); else ThreadSafeTryRemove(instance, t); } - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) - => Reflection.MakeReplacementCall(Check); + + public static CodeInstructions Transpiler(CodeInstructions codeInstructions, ILGenerator generator) + => Reflection.GetCodeInstructions(Check, generator); } public class TryRemove_Patch : FishPatch { - public override string Description => "Part of the ListerHaulables optimization"; - public override Expression TargetMethod => () => default(ListerHaulables)!.TryRemove(null); + public override string Description { get; } = "Part of the ListerHaulables optimization"; + + public override Expression TargetMethod { get; } + = static () => default(ListerHaulables)!.TryRemove(null); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void TryRemove(ListerHaulables instance, Thing t) @@ -287,52 +316,62 @@ public class Haulables : ClassWithFishPatches if (t.def.category == ThingCategory.Item) ThreadSafeTryRemove(instance, t); } - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) - => Reflection.MakeReplacementCall(TryRemove); + + public static CodeInstructions Transpiler(CodeInstructions codeInstructions, ILGenerator generator) + => Reflection.GetCodeInstructions(TryRemove, generator); } public class CheckAdd_Patch : FishPatch { - public override string Description => "Part of the ListerHaulables optimization"; - public override Expression TargetMethod => () => default(ListerHaulables)!.CheckAdd(null); + public override string Description { get; } = "Part of the ListerHaulables optimization"; + + public override Expression TargetMethod { get; } + = static () => default(ListerHaulables)!.CheckAdd(null); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CheckAdd(ListerHaulables instance, Thing t) { - var cache = GetAndCheckHaulablesCache(instance, t); - - if (cache.contains || !instance.ShouldBeHaulable(t)) + if (!t.def.alwaysHaulable && !t.def.designateHaulable) return; + + if (HaulablesCache.GetAndCheck(t, instance).Contains + || !instance.ShouldBeHaulable(t)) + { + return; + } ThreadSafeTryAdd(instance, t); } - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) - => Reflection.MakeReplacementCall(CheckAdd); + + public static CodeInstructions Transpiler(CodeInstructions codeInstructions, ILGenerator generator) + => Reflection.GetCodeInstructions(CheckAdd, generator); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static FishHaulableInfo GetAndCheckHaulablesCache(ListerHaulables lister, Thing thing) + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public record struct HaulablesCacheValue : ICacheable { - var key = new HaulablesCache(thing, lister); - return HaulablesCache.GetValue(ref key); - } + private int _nextRefreshTick; + public bool Contains; - public struct FishHaulableInfo : Cache.IIsRefreshable - { - public int nextRefreshTick; - public bool contains; + public void Update(ref HaulablesCache key) + { + Contains = key.Second.haulables.Contains(key.First); + SetDirty(false, key.First.thingIDNumber); + } - public bool ShouldRefreshNow + public void SetValue(bool contains, Thing thing) + { + Contains = contains; + SetDirty(false, thing.thingIDNumber); + } + + public void SetDirty(bool value, int offset) + => _nextRefreshTick = value ? 0 : TickHelper.Add(3072, offset, 2048); + + public bool Dirty { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => nextRefreshTick < Current.Game.tickManager.TicksGame; - set => nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); - } - public FishHaulableInfo SetNewValue(HaulablesCache key) - { - contains = key.Second.haulables.Contains(key.First); - ShouldRefreshNow = false; - return this; + get => TickHelper.Past(_nextRefreshTick); } } } \ No newline at end of file diff --git a/Source/PerformanceFish/Listers/Mergeables.cs b/Source/PerformanceFish/Listers/Mergeables.cs index 76f4bf6..5aabf8f 100644 --- a/Source/PerformanceFish/Listers/Mergeables.cs +++ b/Source/PerformanceFish/Listers/Mergeables.cs @@ -1,9 +1,12 @@ -// Copyright (c) 2022 bradson +// 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/. -global using MergeablesCache = PerformanceFish.Cache.ByReferenceRefreshable; +global using MergeablesCache + = PerformanceFish.Cache.ByReference; +using PerformanceFish.Cache; namespace PerformanceFish.Listers; @@ -11,108 +14,111 @@ public class Mergeables : ClassWithFishPatches { public class Check_Patch : FishPatch { - public override string Description => "Optimization for mergeables checking"; - public override Expression TargetMethod => () => default(ListerMergeables)!.Check(null); + public override string Description { get; } = "Optimization for mergeables checking"; + public override Expression TargetMethod { get; } = static () => default(ListerMergeables)!.Check(null); + public static void Check(ListerMergeables instance, Thing t) { - var cache = GetAndCheckMergeablesCache(instance, t); + ref var cache = ref MergeablesCache.GetAndCheck(t, instance); - if (cache.contains) + if (cache.Contains) { - if (!instance.ShouldBeMergeable(t)) - { - instance.mergeables.Remove(t); - cache.contains = false; - cache.ShouldRefreshNow = false; - MergeablesCache.Get[new(t, instance)] = cache; - } + if (instance.ShouldBeMergeable(t)) + return; + + instance.mergeables.Remove(t); + + cache.SetValue(false, t); } else { - if (instance.ShouldBeMergeable(t)) - { - if (!instance.mergeables.Contains(t)) - instance.mergeables.Add(t); - cache.contains = true; - cache.ShouldRefreshNow = false; - MergeablesCache.Get[new(t, instance)] = cache; - } + if (!instance.ShouldBeMergeable(t)) + return; + + if (!instance.mergeables.Contains(t)) + instance.mergeables.Add(t); + + cache.SetValue(true, t); } } - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) + + public static CodeInstructions Transpiler(CodeInstructions codeInstructions) => Reflection.MakeReplacementCall(Check); } public class CheckAdd_Patch : FishPatch { - public override string Description => "Part of the ListerMergeables optimization"; - public override Expression TargetMethod => () => default(ListerMergeables)!.CheckAdd(null); + public override string Description { get; } = "Part of the ListerMergeables optimization"; + + public override Expression TargetMethod { get; } + = static () => default(ListerMergeables)!.CheckAdd(null); + public static void CheckAdd(ListerMergeables instance, Thing t) { - var cache = GetAndCheckMergeablesCache(instance, t); + ref var cache = ref MergeablesCache.GetAndCheck(t, instance); - if (cache.contains || !instance.ShouldBeMergeable(t)) + if (cache.Contains || !instance.ShouldBeMergeable(t)) return; if (!instance.mergeables.Contains(t)) instance.mergeables.Add(t); - cache.contains = true; - cache.ShouldRefreshNow = false; - - MergeablesCache.Get[new(t, instance)] = cache; + + cache.SetValue(true, t); } - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) + + public static CodeInstructions Transpiler(CodeInstructions codeInstructions) => Reflection.MakeReplacementCall(CheckAdd); } public class TryRemove_Patch : FishPatch { - public override string Description => "Part of the ListerMergeables optimization"; - public override Expression TargetMethod => () => default(ListerMergeables)!.TryRemove(null); + public override string Description { get; } = "Part of the ListerMergeables optimization"; + + public override Expression TargetMethod { get; } + = static () => default(ListerMergeables)!.TryRemove(null); + public static void TryRemove(ListerMergeables instance, Thing t) { if (t.def.category == ThingCategory.Item) { - var cache = GetAndCheckMergeablesCache(instance, t); + ref var cache = ref MergeablesCache.GetAndCheck(t, instance); - if (!cache.contains) + if (!cache.Contains) return; instance.mergeables.Remove(t); - cache.contains = false; - cache.ShouldRefreshNow = false; - - MergeablesCache.Get[new(t, instance)] = cache; + cache.SetValue(false, t); } } - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) + + public static CodeInstructions Transpiler(CodeInstructions codeInstructions) => Reflection.MakeReplacementCall(TryRemove); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static FishMergeableInfo GetAndCheckMergeablesCache(ListerMergeables lister, Thing thing) + public record struct MergeablesCacheValue : ICacheable { - var key = new MergeablesCache(thing, lister); - return MergeablesCache.GetValue(ref key); - } - - public struct FishMergeableInfo : Cache.IIsRefreshable - { - public int nextRefreshTick; - public bool contains; - - public bool ShouldRefreshNow + private int _nextRefreshTick; + public bool Contains; + + public void Update(ref MergeablesCache key) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => nextRefreshTick < Current.Game.tickManager.TicksGame; - set => nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); + Contains = key.Second.mergeables.Contains(key.First); + SetDirty(false, key.First.thingIDNumber); } - public FishMergeableInfo SetNewValue(MergeablesCache key) + public void SetValue(bool contains, Thing thing) { - contains = key.Second.mergeables.Contains(key.First); - ShouldRefreshNow = false; - return this; + Contains = contains; + SetDirty(false, thing.thingIDNumber); + } + + public void SetDirty(bool value, int offset) + => _nextRefreshTick = value ? 0 : TickHelper.Add(3072, offset, 2048); + + public bool Dirty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => TickHelper.Past(_nextRefreshTick); } } } \ No newline at end of file diff --git a/Source/PerformanceFish/Listers/Misc.cs b/Source/PerformanceFish/Listers/Misc.cs index a867699..05b28ba 100644 --- a/Source/PerformanceFish/Listers/Misc.cs +++ b/Source/PerformanceFish/Listers/Misc.cs @@ -1,19 +1,25 @@ -// Copyright (c) 2022 bradson +// 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.Listers; + public class Misc : ClassWithFishPatches { public class Building_Destroy_Patch : FishPatch { - public override string Description => "Used for haulables caching. Items don't become haulable after deconstructing stockpiles without this patch"; - public override Expression TargetMethod => () => default(Building)!.Destroy(default); + public override string Description { get; } + = "Used for haulables caching. Items don't become haulable after deconstructing stockpiles without this " + + "patch"; + + public override Expression TargetMethod { get; } = static () => default(Building)!.Destroy(default); + public static void Prefix(Building __instance) { if (__instance is not IHaulDestination) return; + HaulablesCache.Get.Clear(); MergeablesCache.Get.Clear(); } diff --git a/Source/PerformanceFish/Listers/StorageSettingsPatches.cs b/Source/PerformanceFish/Listers/StorageSettingsPatches.cs index 263d793..4606a03 100644 --- a/Source/PerformanceFish/Listers/StorageSettingsPatches.cs +++ b/Source/PerformanceFish/Listers/StorageSettingsPatches.cs @@ -1,9 +1,13 @@ -// Copyright (c) 2022 bradson +// 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 StorageSettingsCache = PerformanceFish.Cache.ByReferenceRefreshable; +using System.Runtime.InteropServices; +using PerformanceFish.Cache; +using StorageSettingsCache + = PerformanceFish.Cache.ByReference; namespace PerformanceFish.Listers; @@ -11,19 +15,20 @@ public class StorageSettingsPatches : ClassWithFishPatches { public class AllowedToAccept_Patch : FirstPriorityFishPatch { - public override string Description => "StorageSettings caching"; - public override Expression TargetMethod => () => default(StorageSettings)!.AllowedToAccept(default(Thing)); + public override string Description { get; } = "StorageSettings caching"; + + public override Expression TargetMethod { get; } + = static () => default(StorageSettings)!.AllowedToAccept(default(Thing)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe bool Prefix(StorageSettings __instance, Thing t, ref bool __result, out bool __state) + public static bool Prefix(StorageSettings __instance, Thing t, ref bool __result, out bool __state) { - var key = new StorageSettingsCache(t, __instance); - ref var cache = ref StorageSettingsCache.Get.TryGetReferenceUnsafe(ref key); + ref var cache = ref StorageSettingsCache.GetOrAddReference(new(t, __instance)); - if (Unsafe.IsNullRef(ref cache) || cache.ShouldRefreshNow) + if (cache.Dirty) return __state = true; - __result = cache.allowedToAccept; + __result = cache.AllowedToAccept; return __state = false; } @@ -33,14 +38,15 @@ public class StorageSettingsPatches : ClassWithFishPatches if (!__state) return; - StorageSettingsCache.Get[new(t, __instance)] = new() { ShouldRefreshNow = false, allowedToAccept = __result }; + StorageSettingsCache.Update(t, __instance, __result); } } public class TryNotifyChanged_Patch : FishPatch { - public override string Description => "StorageSettings cache resetting"; - public override Expression TargetMethod => () => default(StorageSettings)!.TryNotifyChanged(); + public override string Description { get; } = "StorageSettings cache resetting"; + public override Expression TargetMethod { get; } = static () => default(StorageSettings)!.TryNotifyChanged(); + public static void Postfix(StorageSettings __instance) { StorageSettingsCache.Get.Clear(); @@ -49,17 +55,22 @@ public class StorageSettingsPatches : ClassWithFishPatches } } - public struct FishStorageSettingsInfo : Cache.IIsRefreshable + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public record struct StorageSettingsCacheValue : ICacheable { - public int nextRefreshTick; - public bool allowedToAccept; + private int _nextRefreshTick; + public bool AllowedToAccept; - public bool ShouldRefreshNow + public bool Dirty { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => nextRefreshTick < Current.Game.tickManager.TicksGame; - set => nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); + get => TickHelper.Past(_nextRefreshTick); + } + + public void Update(ref StorageSettingsCache key, bool allowedToAccept) + { + AllowedToAccept = allowedToAccept; + _nextRefreshTick = TickHelper.Add(3072, key.First.thingIDNumber, 2048); } - public FishStorageSettingsInfo SetNewValue(StorageSettingsCache key) => throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Source/PerformanceFish/Listers/Things.cs b/Source/PerformanceFish/Listers/Things.cs index 419b0db..12ddc23 100644 --- a/Source/PerformanceFish/Listers/Things.cs +++ b/Source/PerformanceFish/Listers/Things.cs @@ -1,16 +1,71 @@ -// Copyright (c) 2022 bradson +// 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 disabled namespace PerformanceFish.Listers; public class Things : ClassWithFishPatches { + public class ThingsMatching_Patch : FirstPriorityFishPatch + { + public override string Description { get; } + = "Part of the ThingFilter.BestThingRequest optimization. Required by that particular patch and gets " + + "toggled alongside it"; + + public override bool Enabled + { + set + { + if (base.Enabled == value) + return; + + Get().Enabled = base.Enabled = value; + } + } + + public override Expression TargetMethod { get; } + = static () => new ListerThings(default).ThingsMatching(default); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(ListerThings __instance, ThingRequest req, ref List __result) + { + if ((object)req.singleDef is not HashSet hashSet) + return true; + + __result = ThingsOfDefsFor(__instance, hashSet); + return false; + } + } + + public static List ThingsOfDefsFor(ListerThings lister, HashSet set) + { + var list = GetThingsOfDefsListFor(set); + list.Clear(); + + foreach (var def in set) + { + if (lister.listsByDef.TryGetValue(def) is { } thingsOfSingleDef) + list.AddRangeFast(thingsOfSingleDef); + } + + return list; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static List GetThingsOfDefsListFor(IEnumerable thingDefs) + => (_thingsOfDefsDictionary ??= Cache.Utility.AddNew, List>>()) + .GetOrAdd(thingDefs); + + [ThreadStatic] + private static Dictionary, List>? _thingsOfDefsDictionary; + +#if disabled public class ThingRequest_IsUndefined_Patch : FishPatch { - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.IsUndefined)); + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.IsUndefined)); + [HarmonyPriority(Priority.Last)] public static void Postfix(ThingRequest __instance, ref bool __result) { @@ -19,13 +74,16 @@ public class Things : ClassWithFishPatches __result = NewResult(__instance); } + public static bool NewResult(ThingRequest __instance) => __instance.group == ThingRequestGroup.Undefined && !((FishCache)__instance.singleDef).IsSingleDef; } public class ThingRequest_CanBeFoundInRegion_Patch : FirstPriorityFishPatch { - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.CanBeFoundInRegion)); + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.CanBeFoundInRegion)); + public static bool Prefix(ThingRequest __instance, ref bool __result) { if (__instance.singleDef is not FishCache) @@ -34,48 +92,59 @@ public class Things : ClassWithFishPatches __result = NewResult(__instance); return false; } + public static bool NewResult(ThingRequest __instance) => ((FishCache)__instance.singleDef).IsSingleDef - || (__instance.group != ThingRequestGroup.Undefined && (__instance.group == ThingRequestGroup.Nothing || __instance.group.StoreInRegion())); + || (__instance.group != ThingRequestGroup.Undefined + && (__instance.group == ThingRequestGroup.Nothing || __instance.group.StoreInRegion())); } public class Add_Patch : FishPatch { - public override Expression TargetMethod => () => new ListerThings(default).Add(null); + public override Expression TargetMethod { get; } = static () => new ListerThings(default).Add(null); + public static void Postfix(ListerThings __instance, Thing t) { if (t.TryGetComp() is not { } style || style.SourcePrecept is null) return; - Cache.GetValue(__instance).things.Add(t); + + Cache.ByReference.GetOrAddReference(__instance).Things.Add(t); } } public class Remove_Patch : FishPatch { - public override Expression TargetMethod => () => new ListerThings(default).Remove(null); + public override Expression TargetMethod { get; } = static () => new ListerThings(default).Remove(null); + public static void Postfix(ListerThings __instance, Thing t) { if (t.TryGetComp() is not { } style || style.SourcePrecept is null) return; - Cache.GetValue(__instance).things.Remove(t); + + Cache.ByReference.GetOrAddReference(__instance).Things.Remove(t); } } public class Clear_Patch : FishPatch { - public override Expression TargetMethod => () => new ListerThings(default).Clear(); + public override Expression TargetMethod { get; } = static () => new ListerThings(default).Clear(); + public static void Postfix(ListerThings __instance, Thing t) { if (t.TryGetComp() is not { } style || style.SourcePrecept is null) return; - Cache.GetValue(__instance).things.Clear(); + + Cache.ByReference.GetOrAddReference(__instance).Things.Clear(); } } public class ThingsMatching_Patch : FirstPriorityFishPatch { - public override string Description => "Part of the work scanning optimization"; - public override Expression TargetMethod => () => new ListerThings(default).ThingsMatching(default); + public override string Description { get; } = "Part of the work scanning optimization"; + + public override Expression TargetMethod { get; } + = static () => new ListerThings(default).ThingsMatching(default); + public static bool Prefix(ListerThings __instance, ThingRequest req, ref List __result) { if (req.singleDef is not FishCache) @@ -84,6 +153,7 @@ public class Things : ClassWithFishPatches __result = GetCache(__instance, req); return false; } + public static List GetCache(ListerThings __instance, ThingRequest req) { var fishCache = (FishCache)req.singleDef; @@ -106,31 +176,34 @@ public class Things : ClassWithFishPatches : __instance.listsByGroup.TryGetItem((int)req.group, out var item) ? item : null) ?? ListerThings.EmptyList, CurrentPawn));*/ } - public static CompStyleablesInfo GetCompStyleablesCache(ListerThings lister) => Cache.GetValue(lister); + public static CompStyleablesInfo GetCompStyleablesCache(ListerThings lister) + => Cache.ByReference.GetOrAddReference(lister); - public struct CompStyleablesInfo : ICanRefresh + public record struct CompStyleablesInfo { - public List things; + public List Things; private int _nextRefreshTick; - public bool ShouldRefreshNow + public bool Dirty { get => _nextRefreshTick < Current.Game.tickManager.TicksGame; set => _nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); } + public CompStyleablesInfo SetNewValue(ListerThings key) { - if (things is null) - things = new(); + if (Things is null) + Things = new(); else - things.Clear(); + Things.Clear(); var allBuildings = key.listsByGroup[(int)ThingRequestGroup.BuildingArtificial]; var count = allBuildings.Count; for (var i = 0; i < count; i++) { if (allBuildings[i].TryGetComp() is { SourcePrecept: not null }) - things.Add(allBuildings[i]); + Things.Add(allBuildings[i]); } + return this; } } @@ -140,6 +213,7 @@ public class Things : ClassWithFishPatches public bool IsSingleDef => Def != null; public ThingDef Def { get; private set; } public List FilteredThings { get; set; } + public static FishCache ForDef(ThingDef def) { if (_dictOfDefs.TryGetValue(def, out var value)) @@ -148,6 +222,7 @@ public class Things : ClassWithFishPatches _dictOfDefs[def] = value = new(def.defName) { Def = def }; return value; } + public static FishCache ForGroup(ThingRequestGroup group) { if (_dictOfGroups.TryGetValue(group, out var value)) @@ -156,14 +231,16 @@ public class Things : ClassWithFishPatches _dictOfGroups[group] = value = new(group.ToString()); return value; } + public static void ClearAll() { _dictOfGroups.Clear(); _dictOfDefs.Clear(); } + private FishCache(string name) => defName = name; private static Dictionary _dictOfGroups { get; } = new(); private static Dictionary _dictOfDefs { get; } = new(); } -} -#endif \ No newline at end of file +#endif +} \ No newline at end of file diff --git a/Source/PerformanceFish/Log.cs b/Source/PerformanceFish/Log.cs index 1ca88f5..fd9a4d0 100644 --- a/Source/PerformanceFish/Log.cs +++ b/Source/PerformanceFish/Log.cs @@ -1,24 +1,205 @@ -// Copyright (c) 2022 bradson +// 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 System.Collections.Concurrent; +using System.Linq; +using System.Runtime.InteropServices; +using JetBrains.Annotations; +using PerformanceFish.Pools; +using PerformanceFish.Prepatching; + namespace PerformanceFish; public static class Log { - public static void Message(string message) => _message(message); - public static void Warning(string message) => _warning(message); - public static void Error(string message) => _error(message); + public static void Message(string message) => Config.Message(message); + public static void Message(PooledStringHandler message) => Config.Message(message.GetFormattedText()); + + public static void Warning(string message) => Config.Warning(message); + public static void Warning(PooledStringHandler message) => Config.Warning(message.GetFormattedText()); + + public static void Error(string message) => Config.Error(message); + public static void Error(PooledStringHandler message) => Config.Error(message.GetFormattedText()); + [PublicAPI] public static class Config { - public static Action Message { set => _message = value; } - public static Action Warning { set => _warning = value; } - public static Action Error { set => _error = value; } + public static Action Message { get; set; } = static text => + { + Verse.Log.logLock ??= new(); // in case this runs from the Log cctor, and in turn before it finishes setup + Verse.Log.Message(text); + HandleEarlyMessage(text, LogMessageType.Message); + }; + + public static Action Warning { get; set; } = static text => + { + Verse.Log.logLock ??= new(); + Verse.Log.Warning(text); + HandleEarlyMessage(text, LogMessageType.Warning); + }; + + public static Action Error { get; set; } = static text => + { + Verse.Log.logLock ??= new(); + Verse.Log.Error(text); + HandleEarlyMessage(text, LogMessageType.Error); + }; } - private static Action _message = Verse.Log.Message; - private static Action _warning = Verse.Log.Warning; - private static Action _error = Verse.Log.Error; + private static void HandleEarlyMessage(string text, LogMessageType messageType) + { + if (Ready) + return; + + FindAndEnqueueUnhandledMessage(text, messageType); + } + + private static void FindAndEnqueueUnhandledMessage(string text, LogMessageType messageType) + { + var foundMessage = false; + + var lastMessage = Verse.Log.messageQueue.lastMessage; + if (!TryEnqueueUnhandledMessage(text, messageType, lastMessage, ref foundMessage)) + { + foreach (var message in Verse.Log.messageQueue.messages) + { + if (TryEnqueueUnhandledMessage(text, messageType, message, ref foundMessage)) + break; + } + } + + if (!foundMessage) + { + var message = Verse.Log.messageQueue.messages.FirstOrDefault(message + => message.text.Contains(text) || text.Contains(message.text)); + if (message != null) + UnhandledMessages!.Enqueue(message); + } + } + + private static bool TryEnqueueUnhandledMessage(string text, LogMessageType messageType, LogMessage message, + ref bool foundMessage) + { + if (message.text != text || message.type != messageType) + return false; + + UnhandledMessages!.Enqueue(message); + foundMessage = true; + return true; + } + + public static UnhandledMessageQueue? UnhandledMessages { get; } + = SetupUnhandledMessageQueue(); + + private static UnhandledMessageQueue? SetupUnhandledMessageQueue() + { + if (Ready) + return null; + + ref var stash = ref FishStash.Get; + if (stash.UnhandledMessages is { } stashedMessages) + { + stash.MessageQueueGCHandle.Free(); + return stashedMessages; + } + + stash.UnhandledMessages = stashedMessages = new(); + stash.MessageQueueGCHandle = GCHandle.Alloc(stashedMessages, GCHandleType.Pinned); + return stash.UnhandledMessages; + } + + public static bool Ready { get; set; } = PerformanceFishMod.Mod != null; +} + +[StructLayout(LayoutKind.Sequential)] +public struct LogMessageData : IDisposable +{ + public IntPtr Text, + StackTrace; + public int Type, + Repeats; + + public LogMessage ToLogMessage() + => new((LogMessageType)Type, Marshal.PtrToStringUni(Text), Marshal.PtrToStringUni(StackTrace)) + { + repeats = Repeats + }; + + public LogMessageData(LogMessage logMessage) + { + Text = Marshal.StringToHGlobalUni(logMessage.text); + Type = (int)logMessage.type; + Repeats = logMessage.repeats; + StackTrace = Marshal.StringToHGlobalUni(logMessage.stackTrace); + } + + public void Dispose() + { + Marshal.FreeHGlobal(Text); + Marshal.FreeHGlobal(StackTrace); + } +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe class UnhandledMessageQueue : IDisposable +{ + public int Count { get; private set; } + private int _arrayLength; + private LogMessageData* _messages; + + public void Enqueue(LogMessageData message) + { + lock (this) + { + if (Count >= _arrayLength) + Expand(); + + _messages[Count++] = message; + } + } + + public void Enqueue(LogMessage message) => Enqueue(new LogMessageData(message)); + + public bool TryDequeue(out LogMessageData message) + { + lock (this) + { + if (--Count >= 0) + { + message = _messages[Count]; + _messages[Count] = default; + return true; + } + + message = default; + return false; + } + } + + private void Expand() + { + if (_arrayLength == 0) + { + _arrayLength = 1; + _messages = (LogMessageData*)Marshal.AllocHGlobal(sizeof(LogMessageData)); + } + else + { + _arrayLength <<= 1; + _messages = (LogMessageData*)Marshal.ReAllocHGlobal((IntPtr)_messages, (IntPtr)_arrayLength); + } + } + + // ReSharper disable once InconsistentlySynchronizedField + private void ReleaseUnmanagedResources() => Marshal.FreeHGlobal((IntPtr)_messages); + + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + ~UnhandledMessageQueue() => ReleaseUnmanagedResources(); } \ No newline at end of file diff --git a/Source/PerformanceFish/LogPatches.cs b/Source/PerformanceFish/LogPatches.cs new file mode 100644 index 0000000..ad708f3 --- /dev/null +++ b/Source/PerformanceFish/LogPatches.cs @@ -0,0 +1,84 @@ +// 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 System.Runtime.InteropServices; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Rocks; +using PerformanceFish.Prepatching; +using FieldAttributes = Mono.Cecil.FieldAttributes; + +namespace PerformanceFish; + +public class LogPatches : ClassWithFishPrepatches +{ + public const string PERFORMANCE_FISH_WELCOME_MESSAGE = "Performance Fish!!!"; + public const string PERFORMANCE_FISH_WELCOME_STACKTRACE = "UwU"; + + // This breaks my FishStash somehow // TODO: FIX + // public class LogVersionNumberPatch : FishPrepatch + // { + // public override MethodBase MethodBase { get; } = methodof(Root.CheckGlobalInit); + // + // public static void Prefix() => Log.Message("Performance Fish???"); + // } + + public class ClassPatch : FishClassPrepatch + { + public override string? Description { get; } + = "Important logging patch"; + + public override bool Enabled => true; + + public override Type Type => typeof(Verse.Log); + + public override void FreePatch(TypeDefinition typeDefinition) + { + var module = typeDefinition.Module; + + var fishStashHandleField = new FieldDefinition(nameof(FishStashHandle), + FieldAttributes.Private | FieldAttributes.Static, + typeDefinition.Module.ImportReference(typeof(FishStashHandle))); + + typeDefinition.Fields.Add(fishStashHandleField); + + var staticConstructorBody = typeDefinition.GetStaticConstructor().Body; + + staticConstructorBody.GetILProcessor().InsertRange(staticConstructorBody.Instructions.Count - 2, + (OpCodes.Ldc_I8, FishStash.StaticHandle), + (OpCodes.Newobj, + module.ImportReference(typeof(FishStashHandle).GetConstructor(new[] { typeof(long) }))), + (OpCodes.Stsfld, fishStashHandleField), + (OpCodes.Call, module.ImportReference(InsertEarlyMessages))); + } + + public static void InsertEarlyMessages() + { + var unhandledMessages = Log.UnhandledMessages; + if (unhandledMessages is null) + Debug.LogError("Message queue is null"); + + Log.Ready = true; + + Verse.Log.messageQueue.Enqueue(new(LogMessageType.Message, PERFORMANCE_FISH_WELCOME_MESSAGE, + PERFORMANCE_FISH_WELCOME_STACKTRACE)); + + while (unhandledMessages!.TryDequeue(out var message)) + { + try + { + if (Marshal.PtrToStringUni(message.Text) == PERFORMANCE_FISH_WELCOME_MESSAGE) + continue; + + Verse.Log.messageQueue.Enqueue(message.ToLogMessage()); + } + finally + { + message.Dispose(); + } + } + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/MapPawnsPatches.cs b/Source/PerformanceFish/MapPawnsPatches.cs index b5f25ef..f1ca999 100644 --- a/Source/PerformanceFish/MapPawnsPatches.cs +++ b/Source/PerformanceFish/MapPawnsPatches.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -12,6 +12,7 @@ public static class MapPawnsPatches List allPawnsUnspawned = instance.AllPawnsUnspawned; if (allPawnsUnspawned.Count == 0) return instance.pawnsSpawned; + instance.allPawnsResult.Clear(); instance.allPawnsResult.AddRange(instance.pawnsSpawned); instance.allPawnsResult.AddRange(allPawnsUnspawned); @@ -21,12 +22,14 @@ public static class MapPawnsPatches public static List AllPawnsUnspawned(MapPawns instance) { instance.allPawnsUnspawnedResult.Clear(); - ThingOwnerUtility.GetAllThingsRecursively(instance.map, ThingRequest.ForGroup(ThingRequestGroup.Pawn), instance.allPawnsUnspawnedResult, allowUnreal: true, null, alsoGetSpawnedThings: false); + ThingOwnerUtility.GetAllThingsRecursively(instance.map, ThingRequest.ForGroup(ThingRequestGroup.Pawn), + instance.allPawnsUnspawnedResult, allowUnreal: true, null, alsoGetSpawnedThings: false); for (int num = instance.allPawnsUnspawnedResult.Count - 1; num >= 0; num--) { if (instance.allPawnsUnspawnedResult[num].Dead) instance.allPawnsUnspawnedResult.RemoveAt(num); } + return instance.allPawnsUnspawnedResult; } @@ -37,11 +40,13 @@ public static class MapPawnsPatches Log.Error("Called PawnsInFaction with null faction."); return new List(); } + if (!instance.pawnsInFactionResult.TryGetValue(faction, out var value)) { value = new List(); instance.pawnsInFactionResult.Add(faction, value); } + value.Clear(); var allPawns = instance.AllPawns; for (var i = 0; i < allPawns.Count; i++) @@ -49,6 +54,7 @@ public static class MapPawnsPatches if (allPawns[i].Faction == faction) value.Add(allPawns[i]); } + return value; } @@ -59,13 +65,17 @@ public static class MapPawnsPatches value = new List(); instance.freeHumanlikesOfFactionResult.Add(faction, value); } + value.Clear(); List allPawns = instance.AllPawns; for (int i = 0; i < allPawns.Count; i++) { - if (allPawns[i].Faction == faction && (allPawns[i].HostFaction == null || allPawns[i].IsSlave) && allPawns[i].RaceProps.Humanlike) + if (allPawns[i].Faction == faction + && (allPawns[i].HostFaction == null || allPawns[i].IsSlave) + && allPawns[i].RaceProps.Humanlike) value.Add(allPawns[i]); } + return value; } diff --git a/Source/PerformanceFish/MassUtilityCaching.cs b/Source/PerformanceFish/MassUtilityCaching.cs index 87d2a4c..fd11929 100644 --- a/Source/PerformanceFish/MassUtilityCaching.cs +++ b/Source/PerformanceFish/MassUtilityCaching.cs @@ -1,10 +1,11 @@ -// Copyright (c) 2022 bradson +// 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 GearMassCache = PerformanceFish.Cache.ByIndex; -using InventoryMassCache = PerformanceFish.Cache.ByIndex; +using GearMassCache = PerformanceFish.Cache.ByInt; +using InventoryMassCache + = PerformanceFish.Cache.ByInt; namespace PerformanceFish; @@ -12,17 +13,17 @@ public class MassUtilityCaching : ClassWithFishPatches { public class GearMass_Patch : FirstPriorityFishPatch { - public override string Description => "Caches pawn gear mass. Hauling jobs check this quite a lot"; - public override Delegate TargetMethodGroup => MassUtility.GearMass; + public override string Description { get; } = "Caches pawn gear mass. Hauling jobs check this quite a lot"; + public override Delegate TargetMethodGroup { get; } = MassUtility.GearMass; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Prefix(Pawn p, ref float __result, out bool __state) { - ref var cache = ref GearMassCache.Get.GetReference(p); - if (cache.ShouldRefreshNow(p)) + ref var cache = ref GearMassCache.GetOrAddReference(p.thingIDNumber); + if (cache.IsDirty(p)) return __state = true; - __result = cache.mass; + __result = cache.Mass; return __state = false; } @@ -32,23 +33,27 @@ public class MassUtilityCaching : ClassWithFishPatches if (!__state) return; - GearMassCache.Get[p] = new(__result, p); + UpdateCache(p, __result); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Pawn p, float __result) + => GearMassCache.GetExistingReference(p).Update(__result, p); } public class InventoryMass_Patch : FirstPriorityFishPatch { - public override string Description => "Caches pawn inventory mass. Hauling jobs check this quite a lot"; - public override Delegate TargetMethodGroup => MassUtility.InventoryMass; + public override string Description { get; } = "Caches pawn inventory mass. Hauling jobs check this quite a lot"; + public override Delegate TargetMethodGroup { get; } = MassUtility.InventoryMass; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Prefix(Pawn p, ref float __result, out bool __state) { - ref var cache = ref InventoryMassCache.Get.GetReference(p); - if (cache.ShouldRefreshNow(p)) + ref var cache = ref InventoryMassCache.GetOrAddReference(p.thingIDNumber); + if (cache.IsDirty(p)) return __state = true; - __result = cache.mass; + __result = cache.Mass; return __state = false; } @@ -58,43 +63,64 @@ public class MassUtilityCaching : ClassWithFishPatches if (!__state) return; - InventoryMassCache.Get[p] = new(__result, p); + UpdateCache(p, __result); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Pawn p, float __result) + => InventoryMassCache.GetExistingReference(p).Update(__result, p); } - public struct GearMassCacheValue + public record struct GearMassCacheValue { - public float mass; + public float Mass; private int _nextRefreshTick; private int _equipmentListsState; - public GearMassCacheValue(float mass, Pawn p) + + public void Update(float mass, Pawn p) { - this.mass = mass; - _nextRefreshTick = Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); + Mass = mass; + _nextRefreshTick = TickHelper.Add(3072, p.thingIDNumber, 2048); _equipmentListsState = GetEquipmentListsState(p); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetEquipmentListsState(Pawn p) { - var result = p.apparel != null ? p.apparel.WornApparel.Version() : 0; + var result = p.apparel != null ? p.apparel.WornApparel._version : 0; if (p.equipment != null) - result += p.equipment.AllEquipmentListForReading.Version(); + result += p.equipment.AllEquipmentListForReading._version; return result; } - public bool ShouldRefreshNow(Pawn p) => _nextRefreshTick < Current.Game.tickManager.TicksGame || _equipmentListsState != GetEquipmentListsState(p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsDirty(Pawn p) + => TickHelper.Past(_nextRefreshTick) + || _equipmentListsState != GetEquipmentListsState(p); } - public struct InventoryMassCacheValue + public record struct InventoryMassCacheValue { - public float mass; + public float Mass; private int _nextRefreshTick; private int _inventoryListsState; - public InventoryMassCacheValue(float mass, Pawn p) + + public void Update(float mass, Pawn p) { - this.mass = mass; - _nextRefreshTick = Current.Game.tickManager.TicksGame + 256 + Math.Abs(Rand.Int % 256); - _inventoryListsState = GetInventoryListsState(p); //shorter refresh interval because of stack sizes not being considered by the listVersion check + Mass = mass; + _nextRefreshTick = TickHelper.Add(GenTicks.TickRareInterval, p.thingIDNumber); + _inventoryListsState + = GetInventoryListsState( + p); //shorter refresh interval because of stack sizes not being considered by the listVersion check } - private static int GetInventoryListsState(Pawn p) => p.inventory?.innerContainer is { } container ? container.innerList.Version() : 0; - public bool ShouldRefreshNow(Pawn p) => _nextRefreshTick < Current.Game.tickManager.TicksGame || _inventoryListsState != GetInventoryListsState(p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetInventoryListsState(Pawn p) + => p.inventory?.innerContainer is { } container ? container.innerList._version : 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsDirty(Pawn p) + => TickHelper.Past(_nextRefreshTick) + || _inventoryListsState != GetInventoryListsState(p); } } \ No newline at end of file diff --git a/Source/PerformanceFish/MeditationUtilityCaching.cs b/Source/PerformanceFish/MeditationUtilityCaching.cs new file mode 100644 index 0000000..fd738fd --- /dev/null +++ b/Source/PerformanceFish/MeditationUtilityCaching.cs @@ -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 PsyfocusGainCache + = PerformanceFish.Cache.ByInt; + +namespace PerformanceFish; + +// Suggested by https://github.com/notfood +public class MeditationUtilityCaching : ClassWithFishPatches +{ + public class PsyfocusGainPerTick : FirstPriorityFishPatch + { + public override string? Description { get; } = "Caches psyfocus gain per tick while the target remains " + + "constant. Impactful in psycasting focused playthroughs"; + + public override Delegate TargetMethodGroup { get; } = MeditationUtility.PsyfocusGainPerTick; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(Pawn pawn, Thing? focus, ref float __result, out bool __state) + { + ref var cache = ref PsyfocusGainCache.GetOrAddReference(pawn.thingIDNumber, focus?.thingIDNumber ?? 0); + + if (cache.Dirty) + return __state = true; + + __result = cache.PsyfocusGain; + return __state = false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Postfix(Pawn pawn, Thing? focus, float __result, bool __state) + { + if (!__state) + return; + + UpdateCache(pawn, focus, __result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Pawn pawn, Thing? focus, float __result) + => PsyfocusGainCache.GetExistingReference(pawn.thingIDNumber, focus?.thingIDNumber ?? 0) + .Update(__result, pawn); + } + + public record struct PsyfocusCacheValue + { + private int _nextRefreshTick; + public float PsyfocusGain; + + public void Update(float psyfocusGain, Pawn pawn) + { + PsyfocusGain = psyfocusGain; + _nextRefreshTick = TickHelper.Add(GenTicks.TickRareInterval, pawn.thingIDNumber); + } + + public bool Dirty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => TickHelper.Past(_nextRefreshTick); + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/MiscOptimizations.cs b/Source/PerformanceFish/MiscOptimizations.cs index 4f911fa..75a4d2d 100644 --- a/Source/PerformanceFish/MiscOptimizations.cs +++ b/Source/PerformanceFish/MiscOptimizations.cs @@ -1,55 +1,79 @@ -// Copyright (c) 2022 bradson +// 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 System.Linq; using System.Reflection.Emit; -using System.Runtime.CompilerServices; +using Mono.Cecil; +using Mono.Cecil.Cil; +using PerformanceFish.Prepatching; using static System.Reflection.Emit.OpCodes; namespace PerformanceFish; -public class MiscOptimizations : ClassWithFishPatches -{ - public class GenList_ListFullCopy_Patch : FishPatch - { - public override void TryUnpatch() { } // prevent unpatching - public override string Description => "Minor optimization. Requires a full restart to toggle off due to a bug in harmony"; - public override Delegate TargetMethodGroup => GenList.ListFullCopy; // harmony patches for all T types +public class MiscPrepatchedOptimizations : ClassWithFishPrepatches +{ + public class GenList_ListFullCopy : FishPrepatch + { + public override string Description { get; } + = "Optimization for a basic and frequently used list copying method. Replaces a for loop with an " + + "optimized IL instruction.";/*{(ActiveMods.GradientHair + ? " Disabled due to incompatibility with Gradient Hair" : "")}";*/ + + // public override bool Enabled => base.Enabled && !ActiveMods.GradientHair; + + public override MethodBase TargetMethodBase { get; } = methodof(GenList.ListFullCopy); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(ListFullCopy/*Utility.CollectionExtensions.Copy*/); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(IList source, ref IList __result) - => source is null || source.Count == 0 - || (__result = FisheryLib.CollectionExtensions.TryNew(source, source.GetType().GetGenericArguments()[0])) is null; + public static List ListFullCopy(List source) => source.Copy(); } - public class WindManager_WindManagerTick_Patch : FishPatch + public class GenCollection_FirstOrDefault : FishPrepatch { - public override string Description => "Throttles plant sway to update at most once per frame instead of every tick. Also fixes the plant sway setting to actually disable plant sway calculations when it should. " + - "By default it simply sets values to 0 but then still goes through everything, sacrificing tps for no reason"; - public override Expression TargetMethod => () => default(WindManager)!.WindManagerTick(); + public override string? Description { get; } + = "Minor optimization for a RimWorld method used for list lookups, by looping in a more efficient manner " + + "with fewer bounds checks"; - public static CodeInstructions? Transpiler(CodeInstructions codes) + public override MethodBase TargetMethodBase { get; } = methodof(GenCollection.FirstOrDefault); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(FirstOrDefault); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? FirstOrDefault(List list, Predicate predicate) => list.FirstOrDefaultFast(predicate); + } +} + +public class MiscOptimizations : ClassWithFishPatches +{ + public class WindManager_WindManagerTick : FishPatch + { + public override string Description { get; } + = "Throttles plant sway to update at most once per frame instead of every tick. Also fixes the plant sway " + + "setting to actually disable plant sway calculations when it should. By default it simply sets values to " + + "0 but then still goes through everything, sacrificing tps for no reason"; + + public override Expression TargetMethod { get; } = static () => default(WindManager)!.WindManagerTick(); + + public static CodeInstructions Transpiler(CodeInstructions instructions) { - var find_CurrentMap = FishTranspiler.CallPropertyGetter(typeof(Find), nameof(Find.CurrentMap)); + var find_CurrentMap = FishTranspiler.PropertyGetter(typeof(Find), nameof(Find.CurrentMap)); var this_map = FishTranspiler.Field(typeof(WindManager), nameof(WindManager.map)); - return codes.ReplaceAt( - (codes, i) + return instructions.ReplaceAt((codes, i) => i - 3 >= 0 && codes[i - 3] == find_CurrentMap && codes[i - 1] == this_map && codes[i].operand is Label, // goto return; - - code + static code => new[] { - code, - FishTranspiler.Call(ShouldSway), - FishTranspiler.IfFalse_Short((Label)code.operand) - } - ); + code, FishTranspiler.Call(ShouldSway), FishTranspiler.IfFalse_Short((Label)code.operand) + }); } public static bool ShouldSway() @@ -71,29 +95,101 @@ public class MiscOptimizations : ClassWithFishPatches private static bool _lastPrefValue; } - public class DrawBatch_Flush_Patch : FishPatch + public class CompRottable : FishPatch { - public override string Description => "Optimization of a fleck rendering method. Low - medium performance impact"; - public override Expression TargetMethod => () => default(DrawBatch)!.Flush(default); + public override string? Description { get; } + = "Throttles rotting on misconfigured defs to only recalculate every 256 ticks, instead of constantly."; - public static CodeInstructions? Transpiler(CodeInstructions CodeInstructions, ILGenerator generator) + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(RimWorld.CompRottable), nameof(RimWorld.CompRottable.CompTick)); + + public static CodeInstructions Transpiler(CodeInstructions codes, ILGenerator generator) + => Reflection.GetCodeInstructions(Replacement, generator); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Replacement(RimWorld.CompRottable instance) { - var codes = CodeInstructions.ToList(); + if ((TickHelper.TicksGame & 255) == (instance.parent.thingIDNumber & 255)) + instance.Tick(256); + } + } - var instance_tmpPropertyBlocks = FishTranspiler.Field(typeof(DrawBatch), nameof(DrawBatch.tmpPropertyBlocks)); - var instance_tmpPropertyBlocks_instance_propertyBlockCache_AddRange = FishTranspiler.Call, List>>(GenCollection.AddRange); - var instance_tmpPropertyBlocks_key_propertyBlock_Add = FishTranspiler.Call(new HashSet().Add); + public class RitualObligationTrigger_Date : FirstPriorityFishPatch + { + public override string? Description { get; } + = "Literally just reorders instructions in this method. They were nonsensical, discarding 90% of the " + + "computation."; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(RimWorld.RitualObligationTrigger_Date), + nameof(RimWorld.RitualObligationTrigger_Date.Tick)); + + public static CodeInstructions Transpiler(CodeInstructions codes, ILGenerator generator) + => Reflection.GetCodeInstructions(Replacement, generator); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Replacement(RimWorld.RitualObligationTrigger_Date instance) + { + if (instance.ritual.isAnytime) + return; + + if ((!instance.mustBePlayerIdeo || Faction.OfPlayerSilentFail.ideos.Has(instance.ritual.ideo)) + && instance.CurrentTickRelative() == instance.OccursOnTick()) + { + instance.ritual.AddObligation(new(instance.ritual)); + } + } + } + + public class ReportProbablyMissingAttributesFix : FishPatch + { + public override string? Description { get; } + = """Fixes the "probably needs a StaticConstructorOnStartup attribute" log warning to display full """ + + "type names to help identify what mod it's actually from."; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.FindIncludingInnerTypes(typeof(StaticConstructorOnStartupUtility), + static t => AccessTools.FirstMethod(t, static m + => PatchProcessor.ReadMethodBody(m).Any(static pair + => pair.Value is string text + && text.Contains("probably needs a StaticConstructorOnStartup attribute")))); + + public static CodeInstructions Transpiler(CodeInstructions codes) + => codes.MethodReplacer(AccessTools.PropertyGetter(typeof(MemberInfo), nameof(MemberInfo.Name)), + methodof(Reflection.FullDescription)); + } + + public class DrawBatch_Flush : FishPatch + { + public override string Description { get; } + = "Optimization of a fleck rendering method. Low - medium performance impact"; + + public override Expression TargetMethod { get; } = static () => default(DrawBatch)!.Flush(default); + + public static CodeInstructions? Transpiler(CodeInstructions codeInstructions, ILGenerator generator) + { + var codes = codeInstructions.ToList(); + + var instance_tmpPropertyBlocks + = FishTranspiler.Field(typeof(DrawBatch), nameof(DrawBatch.tmpPropertyBlocks)); + var instance_tmpPropertyBlocks_instance_propertyBlockCache_AddRange + = FishTranspiler.Call, List>>( + GenCollection.AddRange); + var instance_tmpPropertyBlocks_key_propertyBlock_Add + = FishTranspiler.Call(new HashSet().Add); var label_inFrontOf_instance_batches_Clear = generator.DefineLabel(); - var instance_batches_Clear = FishTranspiler.Call(new Dictionary>().Clear); + var instance_batches_Clear + = FishTranspiler.Call(new Dictionary>().Clear); codes[ - codes.FindIndex(c => c == instance_batches_Clear) - - 2 - ].labels.Add(label_inFrontOf_instance_batches_Clear); + codes.FindIndex(c => c == instance_batches_Clear) + - 2 + ].labels.Add(label_inFrontOf_instance_batches_Clear); var instance_myPropertyBlocks = FishTranspiler.Field(typeof(DrawBatch), nameof(DrawBatch.myPropertyBlocks)); - var instance_myPropertyBlocks_GetEnumerator = FishTranspiler.Call(new HashSet().GetEnumerator); + var instance_myPropertyBlocks_GetEnumerator + = FishTranspiler.Call(new HashSet().GetEnumerator); var addRangeRemoved = false; var addRemoved = false; @@ -124,6 +220,7 @@ public class MiscOptimizations : ClassWithFishPatches yield return FishTranspiler.Call(CheckFleckLeaks); yield return FishTranspiler.IfFalse(label_inFrontOf_instance_batches_Clear); yield return FishTranspiler.This; + checkFleckLeaksInserted = true; } else @@ -133,47 +230,49 @@ public class MiscOptimizations : ClassWithFishPatches } if (!addRangeRemoved || !addRemoved || !checkFleckLeaksInserted) - Log.Error($"Performance Fish failed to apply its DrawBatch.Flush() patch. Mod Settings has an option to disable this"); + { + Log.Error("Performance Fish failed to apply its DrawBatch.Flush() patch. Mod Settings has an " + + "option to disable this"); + } } public static bool CheckFleckLeaks(DrawBatch instance) { - if (instance.myPropertyBlocks.Count != instance.propertyBlockCache.Count) - { - instance.tmpPropertyBlocks.AddRange(instance.propertyBlockCache); - return true; - } - return false; + if (instance.myPropertyBlocks.Count == instance.propertyBlockCache.Count) + return false; + + instance.tmpPropertyBlocks.AddRange(instance.propertyBlockCache); + return true; + } #if whatItsGonnaLookLikeAfterTranspiling public static void Flush(DrawBatch __instance, bool draw = true) { - if (__instance.tmpPropertyBlock == null) - __instance.tmpPropertyBlock = new MaterialPropertyBlock(); + __instance.tmpPropertyBlock ??= new(); __instance.tmpPropertyBlocks.Clear(); -// __instance.tmpPropertyBlocks.AddRange(__instance.propertyBlockCache); // <--------------------------------------------------------- removed + // __instance.tmpPropertyBlocks.AddRange(__instance.propertyBlockCache); // <------------------------------- removed try { - foreach (var batch in __instance.batches) + foreach (var (batchKey, batchDatas) in __instance.batches) { - var key = batch.Key; try { - foreach (var item in batch.Value) + foreach (var batchData in batchDatas) { - var batchData = item; if (draw) { __instance.tmpPropertyBlock.Clear(); - if (key.propertyBlock != null) - key.propertyBlock.Write(__instance.tmpPropertyBlock); - if (key.renderInstanced) + batchKey.propertyBlock?.Write(__instance.tmpPropertyBlock); + if (batchKey.renderInstanced) { - key.material.enableInstancing = true; + batchKey.material.enableInstancing = true; if (batchData.hasAnyColors) __instance.tmpPropertyBlock.SetVectorArray("_Color", batchData.colors); - Graphics.DrawMeshInstanced(key.mesh, 0, key.material, item.matrices, item.ptr, __instance.tmpPropertyBlock, UnityEngine.Rendering.ShadowCastingMode.On, receiveShadows: true, key.layer); + Graphics.DrawMeshInstanced(batchKey.mesh, 0, batchKey.material, batchData.matrices, + batchData.ptr, + __instance.tmpPropertyBlock, UnityEngine.Rendering.ShadowCastingMode.On, + receiveShadows: true, batchKey.layer); } else { @@ -183,39 +282,46 @@ public class MiscOptimizations : ClassWithFishPatches var vector = batchData.colors[i]; if (batchData.hasAnyColors) __instance.tmpPropertyBlock.SetColor("_Color", vector); - Graphics.DrawMesh(key.mesh, matrix, key.material, key.layer, null, 0, __instance.tmpPropertyBlock); + Graphics.DrawMesh(batchKey.mesh, matrix, batchKey.material, batchKey.layer, + null, 0, + __instance.tmpPropertyBlock); } } } + batchData.Clear(); __instance.batchDataListCache.Add(batchData); } } finally { - if (key.propertyBlock != null && __instance.myPropertyBlocks.Contains(key.propertyBlock)) + if (batchKey.propertyBlock != null && __instance.myPropertyBlocks.Contains(batchKey.propertyBlock)) { -// __instance.tmpPropertyBlocks.Add(key.propertyBlock); // <---------------------------------------------------------- removed - key.propertyBlock.Clear(); - __instance.propertyBlockCache.Add(key.propertyBlock); + // __instance.tmpPropertyBlocks.Add(key.propertyBlock); // <-------------------------------- removed + batchKey.propertyBlock.Clear(); + __instance.propertyBlockCache.Add(batchKey.propertyBlock); } - __instance.batchListCache.Add(batch.Value); - batch.Value.Clear(); + + __instance.batchListCache.Add(batchDatas); + batchDatas.Clear(); } } } finally { - if (CheckFleckLeaks(__instance)) // <------------------------------------------------------------------------------------------ added + if (CheckFleckLeaks(__instance)) // <------------------------------------------------------------------- added { foreach (var myPropertyBlock in __instance.myPropertyBlocks) { if (!__instance.tmpPropertyBlocks.Contains(myPropertyBlock)) - Log.Warning("Property block from FleckDrawBatch leaked!" + ((myPropertyBlock.leakDebugString == null) ? null : ("Leak debug information: \n" + myPropertyBlock.leakDebugString))); + Log.Warning("Property block from FleckDrawBatch leaked!" + + ((myPropertyBlock.leakDebugString == null) + ? null + : ("Leak debug information: \n" + myPropertyBlock.leakDebugString))); } - var hashSet = __instance.myPropertyBlocks; - __instance.myPropertyBlocks = __instance.tmpPropertyBlocks; - __instance.tmpPropertyBlocks = hashSet; + + (__instance.myPropertyBlocks, __instance.tmpPropertyBlocks) + = (__instance.tmpPropertyBlocks, __instance.myPropertyBlocks); } __instance.batches.Clear(); diff --git a/Source/PerformanceFish/ModCompatibility/ActiveMods.cs b/Source/PerformanceFish/ModCompatibility/ActiveMods.cs new file mode 100644 index 0000000..f49b6d5 --- /dev/null +++ b/Source/PerformanceFish/ModCompatibility/ActiveMods.cs @@ -0,0 +1,108 @@ +// 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.ModCompatibility; + +public static class ActiveMods +{ + public static readonly bool + Fishery = Contains(PackageIDs.FISHERY), + // GradientHair = Contains(PackageIDs.GRADIENT_HAIR), + GraphicsSetter = Contains(PackageIDs.GRAPHICS_SETTINGS), + Harmony = Contains(PackageIDs.HARMONY), + Multiplayer = Contains(PackageIDs.MULTIPLAYER), + PerformanceOptimizer = Contains(PackageIDs.PERFORMANCE_OPTIMIZER), + Prepatcher = Contains(PackageIDs.PREPATCHER), + VanillaExpandedFramework = Contains(PackageIDs.VANILLA_EXPANDED_FRAMEWORK); + + // ModsConfig.IsActive does not ignore _steam postfixes, causing it to fail when local copies were made + public static bool Contains(string packageID) => TryGetModMetaData(packageID) != null; + + public static ModMetaData? TryGetModMetaData(string packageID) + => ModLister.GetActiveModWithIdentifier(packageID, true); + + public static ModContentPack? TryGetModContentPack(string packageID) + { + var allMods = LoadedModManager.RunningModsListForReading; + var count = allMods.Count; + for (var i = 0; i < count; i++) + { + if (allMods[i].PackageIdPlayerFacing.Equals(packageID, StringComparison.OrdinalIgnoreCase)) + return allMods[i]; + } + + return null; + } + + public static Assembly? TryGetAssembly(this ModContentPack modContentPack, string name) + { + var assemblies = modContentPack.assemblies.loadedAssemblies; + + for (var i = 0; i < assemblies.Count; i++) + { + if (assemblies[i].GetName().Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + return assemblies[i]; + } + + return null; + } + + public static void CheckRequirements() + { + if (!Fishery) + ThrowForMissingModDependency(nameof(Fishery)); + + if (!Harmony) + ThrowForMissingModDependency(nameof(Harmony)); + + VerifyLoadOrder(nameof(Fishery), PackageIDs.FISHERY, PackageIDs.PERFORMANCE_FISH, PackageIDs.BETTER_LOG); + VerifyLoadOrder(nameof(Harmony), PackageIDs.HARMONY, PackageIDs.PERFORMANCE_FISH); + + CheckHarmonyVersion(); + CheckFisheryVersion(); + } + + private static void VerifyLoadOrder(string dependencyName, string dependencyID, params string[] dependingModIDs) + { + var activeModsInLoadOrder = (List)ModsConfig.ActiveModsInLoadOrder; + if (activeModsInLoadOrder.FindIndex(mod => mod.PackageId == dependencyID) + > activeModsInLoadOrder.FindIndex(mod => dependingModIDs.Contains(mod.PackageId))) + { + ThrowForIncorrectLoadOrder(dependencyName); + } + } + + private static void CheckFisheryVersion() + { + if (FisheryLib.FisheryLib.VERSION > FisheryLib.FisheryLib.CurrentlyLoadedVersion) + ThrowForOutdatedModDependency(nameof(Fishery)); + } + + private static void CheckHarmonyVersion() + => CheckDependencyVersion(nameof(Harmony), typeof(Harmony), "0Harmony"); + + private static void CheckDependencyVersion(string dependencyName, Type dependencyType, + string dependencyAssemblyName) + { + if (typeof(PerformanceFishMod).Assembly.GetReferencedAssemblies().First(assembly + => assembly.FullName.StartsWith($"{dependencyAssemblyName}, Version", StringComparison.Ordinal)).Version + > dependencyType.Assembly.GetName().Version) + { + ThrowForOutdatedModDependency(dependencyName); + } + } + + private static void ThrowForIncorrectLoadOrder(string dependencyName) + => ThrowHelper.ThrowOperationCanceledException($"Incorrect load order detected. { + dependencyName} must be loaded before any mod depending on it."); + + private static void ThrowForOutdatedModDependency(string modName) + => ThrowHelper.ThrowOperationCanceledException($"Outdated {modName} mod detected. { + modName} must be updated for Performance Fish to work correctly"); + + private static void ThrowForMissingModDependency(string modName) + => ThrowHelper.ThrowOperationCanceledException( + $"{modName} mod is missing. Performance Fish requires this mod in order to function."); +} \ No newline at end of file diff --git a/Source/PerformanceFish/ModCompatibility/GraphicsSetter.cs b/Source/PerformanceFish/ModCompatibility/GraphicsSetter.cs new file mode 100644 index 0000000..5a6eab0 --- /dev/null +++ b/Source/PerformanceFish/ModCompatibility/GraphicsSetter.cs @@ -0,0 +1,26 @@ +// 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.ModCompatibility; + +public static class GraphicsSetter +{ + public static MethodBase? TargetMethod { get; } + = AccessTools.Method("GraphicSetter.Patches.TextureLoadingPatch+LoadPNGPatch:Prefix") + ?? AccessTools.Method("GraphicSetter.GraphicsPatches+LoadPNGPatch:Prefix"); + + public static ModMetaData? Mod { get; } = ActiveMods.TryGetModMetaData(PackageIDs.GRAPHICS_SETTINGS); + + public static MethodBase? TryPatch() + => TargetMethod != null + ? PerformanceFishMod.Harmony.Patch(TargetMethod, transpiler: new(methodof(Transpiler))) + : null; + + public static CodeInstructions Transpiler(CodeInstructions codes) + { + yield return FishTranspiler.Constant(1); + yield return FishTranspiler.Return; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/ModCompatibility/PackageIDs.cs b/Source/PerformanceFish/ModCompatibility/PackageIDs.cs new file mode 100644 index 0000000..ba653a8 --- /dev/null +++ b/Source/PerformanceFish/ModCompatibility/PackageIDs.cs @@ -0,0 +1,21 @@ +// 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.ModCompatibility; + +public static class PackageIDs +{ + public const string + BETTER_LOG = "bs.betterlog", + FISHERY = "bs.fishery", + // GRADIENT_HAIR = "automatic.gradienthair", + GRAPHICS_SETTINGS = "Telefonmast.GraphicsSettings", + HARMONY = "brrainz.harmony", + MULTIPLAYER = "rwmt.Multiplayer", + PERFORMANCE_FISH = "bs.performance", + PERFORMANCE_OPTIMIZER = "Taranchuk.PerformanceOptimizer", + PREPATCHER = "zetrith.prepatcher", + VANILLA_EXPANDED_FRAMEWORK = "OskarPotocki.VanillaFactionsExpanded.Core"; +} \ No newline at end of file diff --git a/Source/PerformanceFish/ModCompatibility/PerformanceOptimizer.cs b/Source/PerformanceFish/ModCompatibility/PerformanceOptimizer.cs new file mode 100644 index 0000000..02e1a90 --- /dev/null +++ b/Source/PerformanceFish/ModCompatibility/PerformanceOptimizer.cs @@ -0,0 +1,64 @@ +// 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.ModCompatibility; + +public static class PerformanceOptimizer +{ + private const string ASSEMBLY_NAME = "PerformanceOptimizer"; + + public static bool Active => ActiveMods.PerformanceOptimizer; + + public static ModContentPack? ModContentPack { get; } + = ActiveMods.TryGetModContentPack(PackageIDs.PERFORMANCE_OPTIMIZER); + + public static Type? OptimizationBaseType { get; } + + public static Type[]? BlockedPatches { get; } + + public static string[] BlockedPatchNames { get; } + = + { + "PerformanceOptimizer.Optimization_FasterGetCompReplacement", + "PerformanceOptimizer.Optimization_IdeoUtility_GetStyleDominance" + }; + + static PerformanceOptimizer() + { + if (ModContentPack is null) + return; + + var assembly = ModContentPack.TryGetAssembly(ASSEMBLY_NAME); + + if (assembly is null) + { + Log.Error("Performance Fish failed to find Performance Optimizer's assembly. " + + "This is likely to cause issues."); + return; + } + + OptimizationBaseType = assembly.GetType("PerformanceOptimizer.Optimization"); + + BlockedPatches = Array.ConvertAll(BlockedPatchNames, name => assembly.GetType(name)); + } + + public static bool TryPatch() + { + if (BlockedPatches.NullOrEmpty()) + return false; + + foreach (var patch in BlockedPatches.Concat(OptimizationBaseType)) + { + if (AccessTools.DeclaredPropertyGetter(patch, "IsEnabled") is { } isEnabledGetter) + PerformanceFishMod.Harmony.Patch(isEnabledGetter, new(methodof(IsEnabledPatch))); + } + + return true; + } + + public static bool IsEnabledPatch(object __instance, ref bool __result) + => !BlockedPatches!.Contains(__instance.GetType()) + || (__result = false); +} \ No newline at end of file diff --git a/Source/PerformanceFish/ModCompatibility/VanillaExpandedFixes.cs b/Source/PerformanceFish/ModCompatibility/VanillaExpandedFixes.cs new file mode 100644 index 0000000..1b5f225 --- /dev/null +++ b/Source/PerformanceFish/ModCompatibility/VanillaExpandedFixes.cs @@ -0,0 +1,46 @@ +// 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.ModCompatibility; + +public class VanillaExpandedFixes : ClassWithFishPatches +{ + public class ExpandableGraphicData : FishPatch + { + public override string? Description { get; } + = "Fixes the LoadAllFiles method in Vanilla Expanded Framework's ExpandableGraphicData to not scan through " + + "all texture folders of all mods for every graphic and instead just use cached files the game already " + + "loaded. Improves load times and prevents dds related errors."; + + public override bool Enabled => ActiveMods.VanillaExpandedFramework; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method("VFECore.ExpandableGraphicData:LoadAllFiles"); + + public static CodeInstructions Transpiler(CodeInstructions codes) + => Reflection.MakeReplacementCall(LoadAllFiles); + + public static List LoadAllFiles(object __instance, string? folderPath) + { + var paths = new List(); + var mods = LoadedModManager.RunningModsListForReading; + var count = mods.Count; + + for (var i = 0; i < count; i++) + { + var contentListTrie = mods[i].GetContentHolder().contentListTrie; + + var prefix = !folderPath.NullOrEmpty() && folderPath![^1] == '/' + ? folderPath + : folderPath + '/'; + + foreach (var path in contentListTrie.GetByPrefix(prefix)) + paths.Add(path); + } + + return paths; + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/OnAssembliesLoaded.cs b/Source/PerformanceFish/OnAssembliesLoaded.cs new file mode 100644 index 0000000..6772263 --- /dev/null +++ b/Source/PerformanceFish/OnAssembliesLoaded.cs @@ -0,0 +1,84 @@ +// 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 System.Threading.Tasks; +using PerformanceFish.ModCompatibility; +using PerformanceFish.Prepatching; +using PerformanceFish.System; + +namespace PerformanceFish; + +/// +/// Gets called before CreateModClasses(), after rimworld finishes loading assemblies +/// +public static class OnAssembliesLoaded +{ + public static bool Loaded { get; private set; } + + public static void Start() + { + Loaded = true; + + TryPatchGraphicsSetter(); + TryPatchPerformanceOptimizer(); + + TryInitialize(EqualityComparerOptimization.Optimization.Initialize); + + PerformanceFishMod.AllPatchClasses = PerformanceFishMod.InitializeAllPatchClasses(); + PerformanceFishMod.AllPrepatchClasses ??= PerformanceFishMod.InitializeAllPatchClasses(); + + TryInitialize(AnalyzerFixes.Patch, true); + + _ = FishSettings.Instance; + + Parallel.ForEach(PerformanceFishMod.AllPatchClasses, static patchClass => + { + try + { + if (!patchClass.RequiresLoadedGameForPatching) + patchClass.Patches.PatchAll(); + } + catch (Exception ex) + { + Log.Error($"{PerformanceFishMod.NAME} encountered an exception while trying to initialize { + patchClass.GetType().Name}:\n{ex}"); + } + }); + } + + private static void TryPatchGraphicsSetter() + { + if (ActiveMods.GraphicsSetter && GraphicsSetter.TryPatch() is null) + { + Log.Error($"Failed to apply compatibility patch for { + GraphicsSetter.Mod?.Name ?? "Graphics Settings+"}"); + } + } + + private static void TryPatchPerformanceOptimizer() + { + if (ActiveMods.PerformanceOptimizer && !PerformanceOptimizer.TryPatch()) + { + Log.Error($"Failed to apply compatibility patch for { + PerformanceOptimizer.ModContentPack?.Name ?? "Performance Optimizer"}"); + } + } + + private static async void TryInitialize(Action action, bool async = false) + { + try + { + if (async) + await Task.Run(action).ConfigureAwait(false); + else + action(); + } + catch (Exception ex) + { + Log.Error($"{PerformanceFishMod.NAME} encountered an exception while trying to initialize { + action.Method.FullDescription()}:\n{ex}"); + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/OnModuleLoaded.cs b/Source/PerformanceFish/OnModuleLoaded.cs new file mode 100644 index 0000000..b218785 --- /dev/null +++ b/Source/PerformanceFish/OnModuleLoaded.cs @@ -0,0 +1,22 @@ +// 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; + +/// +/// Prepatcher's FreePatch invoke, the Log patch and if neither of the two happened the Mod ctor invoke call this. +/// Generally runs twice with Prepatcher active as a result and doesn't really enable running anything earlier than +/// without this initializer. +/// +#pragma warning disable CA2255 +public static class OnModuleLoaded +{ + [ModuleInitializer] + public static void Start() + { + + } +} +#pragma warning restore CA2255 \ No newline at end of file diff --git a/Source/PerformanceFish/OnStartup.cs b/Source/PerformanceFish/OnStartup.cs new file mode 100644 index 0000000..b71df6b --- /dev/null +++ b/Source/PerformanceFish/OnStartup.cs @@ -0,0 +1,34 @@ +// 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 JetBrains.Annotations; + +namespace PerformanceFish; + +[StaticConstructorOnStartup] +[UsedImplicitly] +public static class OnStartup +{ + static OnStartup() + { + // Log.Message($"LoadItem took {TextureLoadingPatches.Stopwatch.ElapsedMilliSecondsAccurate()} ms"); + + // Log.Message($"Allowed defs for mothballing: {MothballOptimization.AllowedDefs.ToStringSafeEnumerable()}"); + // Log.Message($"Blocking defs for mothballing: {DefDatabase.AllDefsListForReading.Where(static def + // => !MothballOptimization.AllowedDefs.Contains(def)).ToStringSafeEnumerable()}"); + + Fields.Initialized = true; + } + + public static class State + { + public static bool Initialized => Fields.Initialized; + } + + private static class Fields + { + internal static bool Initialized; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Patching/ClassWithFishPatches.cs b/Source/PerformanceFish/Patching/ClassWithFishPatches.cs new file mode 100644 index 0000000..83f4908 --- /dev/null +++ b/Source/PerformanceFish/Patching/ClassWithFishPatches.cs @@ -0,0 +1,19 @@ +// 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/. + +extern alias nuget; +using nuget::JetBrains.Annotations; + +namespace PerformanceFish.Patching; + +[PublicAPI] +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.WithMembers)] +public abstract class ClassWithFishPatches : SingletonFactory, IHasFishPatch +{ + public virtual FishPatchHolder Patches { get; } + public virtual bool RequiresLoadedGameForPatching => false; + + protected ClassWithFishPatches() => Patches = new(GetType()); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Patching/FirstPriorityFishPatch.cs b/Source/PerformanceFish/Patching/FirstPriorityFishPatch.cs new file mode 100644 index 0000000..19f8372 --- /dev/null +++ b/Source/PerformanceFish/Patching/FirstPriorityFishPatch.cs @@ -0,0 +1,14 @@ +// 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.Patching; + +public abstract class FirstPriorityFishPatch : FishPatch +{ + public override int PrefixMethodPriority => Priority.First; + public override int PostfixMethodPriority => Priority.First; + public override int TranspilerMethodPriority => Priority.First; + public override int FinalizerMethodPriority => Priority.First; +} \ No newline at end of file diff --git a/Source/PerformanceFish/Patching/FishPatch.cs b/Source/PerformanceFish/Patching/FishPatch.cs new file mode 100644 index 0000000..540f6da --- /dev/null +++ b/Source/PerformanceFish/Patching/FishPatch.cs @@ -0,0 +1,333 @@ +// 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/. + +extern alias nuget; +using System.Diagnostics; +using System.Linq; +using nuget::JetBrains.Annotations; +using PerformanceFish.Cache; + +namespace PerformanceFish.Patching; + +[PublicAPI] +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.WithMembers)] +public abstract class FishPatch : SingletonFactory, IExposable, IHasDescription +{ + public virtual bool ShouldBenchmark => false; + + public virtual bool Enabled + { + get => _enabled; + set + { + if (value == _enabled) + return; + + _enabled = value; + if (value) + { + if (ShouldBePatched) + TryPatch(); + } + else + { + var shouldBePatched = ShouldBePatched; + TryUnpatch(); + ShouldBePatched = shouldBePatched; + } + } + } + + private bool _enabled; + + public virtual bool DefaultState + { + [Pure] get => true; + } + + public virtual int PrefixMethodPriority => TryGetPriority(PrefixMethodInfo); + public virtual int PostfixMethodPriority => TryGetPriority(PostfixMethodInfo); + public virtual int TranspilerMethodPriority => TryGetPriority(TranspilerMethodInfo); + public virtual int FinalizerMethodPriority => TryGetPriority(FinalizerMethodInfo); + public MethodInfo? HarmonyMethodInfo => HarmonyMethodInfos.FirstOrDefault(); + public List HarmonyMethodInfos { get; } = new(); + public MethodInfo? BenchmarkHarmonyMethodInfo => BenchmarkHarmonyMethodInfos.FirstOrDefault(); + public List BenchmarkHarmonyMethodInfos { get; } = new(); + private bool Patched { get; set; } + private bool ShouldBePatched { get; set; } + + public virtual string? Name => null; + public virtual string? Description => null; + + public virtual Delegate? TargetMethodGroup => null; + public virtual IEnumerable? TargetMethodGroups => null; + public virtual Expression? TargetMethod => null; + public virtual IEnumerable>? TargetMethods => null; + + public virtual MethodBase? TargetMethodInfo + => TargetMethod != null + ? SymbolExtensions.GetMethodInfo(TargetMethod) + : TargetMethodGroup?.Method; + + public virtual IEnumerable TargetMethodInfos + => _targetMethodInfos + ??= (TargetMethods?.Select(SymbolExtensions.GetMethodInfo) + ?? TargetMethodGroups?.Select(static m => m.Method))?.Cast().ToArray() + ?? (TargetMethodInfo != null! + ? new[] { TargetMethodInfo } + : Array.Empty()); + + private MethodBase[]? _targetMethodInfos; + + public virtual MethodInfo? ReversePatchMethodInfo + => TryGetMethod("REVERSEPATCH", + static m => m.HasAttribute() && !m.HasAttribute()); + + public virtual MethodInfo? ReversePatchTranspilerMethodInfo + => TryGetMethod("REVERSEPATCHTRANSPILER", + static m => m.HasAttribute() && m.HasAttribute()); + + public virtual MethodInfo? PrefixMethodInfo + => TryGetMethod("PREFIX", static m => m.HasAttribute()); + + public virtual MethodInfo? PostfixMethodInfo + => TryGetMethod("POSTFIX", static m => m.HasAttribute()); + + public virtual MethodInfo? TranspilerMethodInfo + => TryGetMethod("TRANSPILER", + static m => m.HasAttribute() && !m.HasAttribute()); + + public virtual MethodInfo? FinalizerMethodInfo + => TryGetMethod("FINALIZER", static m => m.HasAttribute()); + + internal Type ParentType => GetType().DeclaringType; + + internal IHasFishPatch? ParentClass + => PerformanceFishMod.AllPatchClasses?.FirstOrDefault(patchClass => patchClass.GetType() == ParentType); + + internal bool IsBenchmarking => ShouldBenchmark && !FinishedBenchmarking; + internal bool FinishedBenchmarking; + + // ReSharper disable once VirtualMemberCallInConstructor + protected FishPatch() => _enabled = DefaultState; + + public virtual MethodInfo? TryPatch() + { + if ((ParentClass?.RequiresLoadedGameForPatching ?? false) && Current.ProgramState != ProgramState.Playing) + return HarmonyMethodInfo; + + ShouldBePatched = true; + if (!Enabled || Patched) + return HarmonyMethodInfo; + + if (!TargetMethodInfos.Any()) + { + Log.Error($"Tried to apply patches for {GetType().Name}, but it lacks a target method for patching."); + return null; + } + + if (PrefixMethodInfo is null + && PostfixMethodInfo is null + && TranspilerMethodInfo is null + && FinalizerMethodInfo is null + && ReversePatchMethodInfo is null) + { + Log.Error($"Tried to apply patches for { + GetType().Name}, but there are none. This is likely not intended."); + return null; + } + + ApplyPatch(); + + return HarmonyMethodInfo; + } + + protected virtual void ApplyPatch() + { + Patched = true; + foreach (var method in TargetMethodInfos) + { + DebugLog.Message($"Performance Fish is applying {GetType().Name} on {method.FullDescription()}"); + + try + { + if (IsBenchmarking) + { + Patched = false; + + if (AccessTools.EnumeratorMoveNext(method) is { } moveNext) + { + BenchmarkHarmonyMethodInfos.Add(PerformanceFishMod.Harmony!.Patch(moveNext, + prefix: new(methodof(Benchmarking.Prefix), Priority.First + 1), + postfix: new(methodof(Benchmarking.Postfix), Priority.Last - 1))); + } + + BenchmarkHarmonyMethodInfos.Add(PerformanceFishMod.Harmony!.Patch(method, + prefix: new(methodof(Benchmarking.Prefix), Priority.First + 1), + postfix: new(methodof(Benchmarking.Postfix), Priority.Last - 1))); + } + else + { + if (ReversePatchMethodInfo != null) + { + HarmonyMethodInfos.Add(Harmony.ReversePatch(method, new(ReversePatchMethodInfo), + ReversePatchTranspilerMethodInfo)); + } + + if (PrefixMethodInfo != null + || PostfixMethodInfo != null + || TranspilerMethodInfo != null + || FinalizerMethodInfo != null) + { + HarmonyMethodInfos.Add(PerformanceFishMod.Harmony!.Patch(method, + prefix: TryMakeHarmonyMethod(PrefixMethodInfo, PrefixMethodPriority), + postfix: TryMakeHarmonyMethod(PostfixMethodInfo, PostfixMethodPriority), + transpiler: TryMakeHarmonyMethod(TranspilerMethodInfo, TranspilerMethodPriority), + finalizer: TryMakeHarmonyMethod(FinalizerMethodInfo, FinalizerMethodPriority))); + } + } + } + catch (Exception e) + { + Log.Error($"Performance Fish encountered an exception while trying to patch { + method.FullDescription()} with {PrefixMethodInfo.FullDescription()}, { + PostfixMethodInfo.FullDescription()}, {TranspilerMethodInfo.FullDescription()}, { + FinalizerMethodInfo.FullDescription()}:\n{e}"); + } + } + } + + private static HarmonyMethod? TryMakeHarmonyMethod(MethodInfo? method, int priority = -1, string[]? before = null, + string[]? after = null, bool? debug = null) + => method != null ? new(method, priority, before, after, debug) : null; + + public virtual void TryUnpatch() + { + ShouldBePatched = false; + if ( /*!Enabled &&*/ Patched) + ApplyUnpatching(); + } + + protected virtual void ApplyUnpatching() + { + Patched = false; + foreach (var method in TargetMethodInfos) + { + for (var i = 0; i < 4; i++) + { + TryUnpatchMethod(method, i switch + { + 0 => PrefixMethodInfo, + 1 => PostfixMethodInfo, + 2 => TranspilerMethodInfo, + _ => FinalizerMethodInfo + }); + } + } + } + + private static void TryUnpatchMethod(MethodBase targetMethod, MethodInfo? patchMethod) + { + if (patchMethod is null) + return; + + try + { + PerformanceFishMod.Harmony.Unpatch(targetMethod, patchMethod); + } + catch (Exception e) + { + Log.Error($"Performance Fish encountered an exception when unpatching { + patchMethod.FullDescription()} from {targetMethod.FullDescription()}:\n{e}"); + } + } + + private MethodInfo? TryGetMethod(string nameCaseInsensitive, Predicate? predicate = null) + => GetType().TryGetMethod(nameCaseInsensitive, predicate); + + private static int TryGetPriority(MethodInfo? info) + => info?.TryGetAttribute()?.info.priority ?? Priority.Normal; + + public virtual void ExposeData() => Scribe_Values.Look(ref _enabled, "enabled", DefaultState); + + public override string ToString() => GetType().FullName; +} + +internal static class Benchmarking +{ + internal static int SwitchFrame = int.MaxValue; + internal static int EndFrame = int.MaxValue; + + public static void Prefix(MethodBase __originalMethod, out Results __state) + { + __state = ByReference.Get.GetOrAdd(__originalMethod); + if (__state.Stopwatch is null) + ByReference.Get.GetReference(__originalMethod).Setup(__originalMethod); + + __state.Stopwatch!.Start(); + } + + public static void Postfix(Results __state) + { + __state.Stopwatch!.Stop(); + __state.Count++; + + //Log.Message($"SwitchFrame is {SwitchFrame}, EndFrame is {EndFrame}, CurrentFrame is {Time.frameCount}"); + + //if (__state.Stopwatch.ElapsedMilliseconds > 100 || __state.Count > 100000) + if (__state.Switched ? GenTicks.TicksGame > EndFrame : GenTicks.TicksGame > SwitchFrame) + PrintResultsAndStop(__state); + } + + public static void PrintResultsAndStop(Results results) + { + var stopwatch = results.Stopwatch!; + Log.Message($"{results.Method.FullDescription()} took {stopwatch.ElapsedMilliSecondsAccurate()} ms for { + results.Count} calls. This equals { + stopwatch.ElapsedNanoSeconds() / results.Count} ns per call on average."); + + results.Count = 0; + results.Switched = true; + stopwatch.Reset(); + + var fishPatch = PerformanceFishMod.AllPatchClasses! + .SelectMany(static patchClass => patchClass.Patches) + .First(patch => patch.TargetMethodInfos.Contains(results.Method)); + + if (fishPatch.FinishedBenchmarking) + { + foreach (var target in fishPatch.TargetMethodInfos) + { + if (AccessTools.EnumeratorMoveNext(target) is { } moveNext) + { + PerformanceFishMod.Harmony!.Unpatch(moveNext, methodof(Prefix)); + PerformanceFishMod.Harmony.Unpatch(moveNext, methodof(Postfix)); + } + + PerformanceFishMod.Harmony!.Unpatch(target, methodof(Prefix)); + PerformanceFishMod.Harmony.Unpatch(target, methodof(Postfix)); + } + } + else + { + fishPatch.FinishedBenchmarking = true; + fishPatch.TryPatch(); + } + } + + public class Results + { + public int Count; + public Stopwatch? Stopwatch; + public MethodBase? Method; + public bool Switched; + + public void Setup(MethodBase method) + { + Stopwatch = new(); + Method = method; + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Patching/FishPatchExtensions.cs b/Source/PerformanceFish/Patching/FishPatchExtensions.cs new file mode 100644 index 0000000..b8c00ef --- /dev/null +++ b/Source/PerformanceFish/Patching/FishPatchExtensions.cs @@ -0,0 +1,21 @@ +// 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.Patching; + +public static class FishPatchExtensions +{ + public static void PatchAll(this IEnumerable patches) + { + foreach (var patch in patches) + patch.TryPatch(); + } + + public static void UnpatchAll(this IEnumerable patches) + { + foreach (var patch in patches) + patch.TryUnpatch(); + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Patching/FishPatchHolder.cs b/Source/PerformanceFish/Patching/FishPatchHolder.cs new file mode 100644 index 0000000..9ea9029 --- /dev/null +++ b/Source/PerformanceFish/Patching/FishPatchHolder.cs @@ -0,0 +1,74 @@ +// 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/. + +extern alias nuget; +using System.Collections.Concurrent; +using nuget::JetBrains.Annotations; + +namespace PerformanceFish.Patching; + +[PublicAPI] +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] +public class FishPatchHolder : IExposable, IEnumerable +{ + public ConcurrentDictionary All { get; } = new(); + + public T Get() where T : FishPatch => (T)All[typeof(T)]; + public void Add(FishPatch patch) => All[patch.GetType()] = patch; + public void PatchAll() => All.Values.PatchAll(); + public void UnpatchAll() => All.Values.UnpatchAll(); + public FishPatch this[Type type] => All[type]; + + public FishPatchHolder(Type type) + { + _type = type; + AddPatchesRecursively(type); + } + + public void Scribe() => ExposeData(); // Exposable.Scribe(this, _type.FullName); + // directly calling ExposeData prevents creating nested nodes in the config file. Looks cleaner imo. + + public void ExposeData() + { + foreach (var (type, patch) in All) + Exposable.Scribe(patch, type.FullName ?? type.Name); + } + + private void AddPatchesRecursively(Type type) + { + if (typeof(FishPatch).IsAssignableFrom(type) && !All.ContainsKey(type)) + { + if (PerformanceFishMod.AllPatchClasses is { } allPatches) + RemoveDupes(allPatches, type); + + All.TryAdd(type, FishPatch.Get(type)); + } + + foreach (var nestedType in type.GetNestedTypes(AccessTools.all)) + AddPatchesRecursively(nestedType); + } + + private void RemoveDupes(IHasFishPatch[] patches, Type type) + { + foreach (var patchClass in patches) + { + if (patchClass.GetType() == _type + || !patchClass.Patches.All.ContainsKey(type)) + { + continue; + } + + patchClass.Patches[type].Enabled = false; + patchClass.Patches.All.TryRemove(type, out _); + Log.Warning($"Performance Fish removed a duplicate patch from {patchClass.GetType().FullName + }. This is likely caused by no longer valid mod configs"); + } + } + + public IEnumerator GetEnumerator() => All.Values.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => All.Values.GetEnumerator(); + + private Type _type; +} \ No newline at end of file diff --git a/Source/PerformanceFish/Patching/IHasDescription.cs b/Source/PerformanceFish/Patching/IHasDescription.cs new file mode 100644 index 0000000..abcd760 --- /dev/null +++ b/Source/PerformanceFish/Patching/IHasDescription.cs @@ -0,0 +1,15 @@ +// 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/. + +extern alias nuget; +using nuget::JetBrains.Annotations; + +namespace PerformanceFish.Patching; + +[PublicAPI] +public interface IHasDescription +{ + public string? Description { get; } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Patching/IHasFishPatch.cs b/Source/PerformanceFish/Patching/IHasFishPatch.cs new file mode 100644 index 0000000..92f617b --- /dev/null +++ b/Source/PerformanceFish/Patching/IHasFishPatch.cs @@ -0,0 +1,16 @@ +// 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/. + +extern alias nuget; +using nuget::JetBrains.Annotations; + +namespace PerformanceFish.Patching; + +[PublicAPI] +public interface IHasFishPatch +{ + public FishPatchHolder Patches { get; } + public bool RequiresLoadedGameForPatching { get; } +} \ No newline at end of file diff --git a/Source/PerformanceFish/PerformanceFish.csproj b/Source/PerformanceFish/PerformanceFish.csproj index 3d3a8b8..cdd2203 100644 --- a/Source/PerformanceFish/PerformanceFish.csproj +++ b/Source/PerformanceFish/PerformanceFish.csproj @@ -5,23 +5,36 @@ preview AnyCPU true - false bradson - Copyright (c) 2021 bradson + Copyright (c) 2023 bradson LICENSE True preview-all - none + true + portable prompt 5 enable True - v1.3;v1.4 + v1.4;v1.3 + 0.3.800.85 + 1701;1702;IL2121;CS8500;CS9082 + + + ..\..\1.4\Assemblies\ + + + + + ..\..\..\Fishery\1.4\Assemblies\1Fishery.dll + + + - ..\..\1.3\Assemblies\ + ..\..\1.3\Assemblies\ @@ -30,25 +43,21 @@ - - - ..\..\1.4\Assemblies\ - - - - - ..\..\..\Fishery\1.4\Assemblies\1Fishery.dll - - - + - - - + + + + + + + + + @@ -56,12 +65,23 @@ - - - + + + + + + + True - + + + + + nuget + + + \ No newline at end of file diff --git a/Source/PerformanceFish/PerformanceFishMod.cs b/Source/PerformanceFish/PerformanceFishMod.cs index d50b3a8..f61b181 100644 --- a/Source/PerformanceFish/PerformanceFishMod.cs +++ b/Source/PerformanceFish/PerformanceFishMod.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2022 bradson +// 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/. +// #define DEBUG + global using System; global using System.Collections; global using System.Collections.Generic; @@ -11,117 +13,164 @@ global using System.Reflection; global using System.Runtime.CompilerServices; global using System.Threading; global using FisheryLib; +global using FisheryLib.Cecil; +global using FisheryLib.Collections; global using FisheryLib.Utility.Diagnostics; global using HarmonyLib; global using PerformanceFish.Utility; +global using PerformanceFish.Patching; global using RimWorld; global using UnityEngine; global using Verse; global using static FisheryLib.Aliases; global using CodeInstructions = System.Collections.Generic.IEnumerable; +using System.Diagnostics; using System.Linq; using System.Security; -using PerformanceFish.System; +using System.Threading.Tasks; +using JetBrains.Annotations; +using PerformanceFish.ModCompatibility; +using PerformanceFish.Prepatching; [assembly: AllowPartiallyTrustedCallers] -[assembly: SecurityTransparent] [assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)] +[assembly: Debuggable(false, false)] + +[module: SkipLocalsInit] namespace PerformanceFish; +[PublicAPI] public class PerformanceFishMod : Mod { public const string NAME = "Performance Fish"; - public const decimal VERSION = 0.3M; + public const string PACKAGE_ID = PackageIDs.PERFORMANCE_FISH; + public const decimal VERSION = 0.4M; -#pragma warning disable IDE0079 // Visual Studio freaking out a bit over here -#pragma warning disable CS8618 - public static FishSettings Settings { get; private set; } - public static PerformanceFishMod Mod { get; private set; } - public static Harmony Harmony { get; private set; } - public static IHasFishPatch[] AllPatchClasses { get; private set; } -#pragma warning restore CS8618, IDE0079 + public static FishSettings? Settings { get; internal set; } + public static PerformanceFishMod? Mod { get; private set; } + public static Harmony Harmony { get; private set; } = new(PACKAGE_ID); + public static IHasFishPatch[]? AllPatchClasses { get; internal set; } + public static ClassWithFishPrepatches[]? AllPrepatchClasses { get; internal set; } public PerformanceFishMod(ModContentPack content) : base(content) + { + ActiveMods.CheckRequirements(); + InitializeMod(); + } + + private void InitializeMod() { DebugLog.Message($"Initializing {NAME} v{VERSION}"); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (Mod != null) - ThrowHelper.ThrowInvalidOperationException($"Initialized a second instance of the {NAME} mod. This should never happen. Cancelling now to avoid further issues."); + { + Verse.Log.TryOpenLogWindow(); + ThrowHelper.ThrowInvalidOperationException($"Initialized a second instance of the { + NAME} mod. This should never happen. Cancelling now to avoid further issues."); + } Mod = this; - TryInitialize(EqualityComparerOptimization.Initialize); - - Harmony = new(content.PackageIdPlayerFacing); - - AllPatchClasses = Assembly.GetExecutingAssembly().GetTypes() - .Where(t => typeof(IHasFishPatch).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract) - .Select(type - => { - try - { - return SingletonFactory.Get(type); - } - catch (Exception ex) - { - Log.Error($"Performance Fish encountered an exception while trying to initialize {type.Name}:\n{ex}"); - return null!; - } - }) - .Where(p => p != null) - .ToArray(); - - ReflectionCaching.Initialize(); - - try + if (!ActiveMods.Prepatcher) { - Settings = GetSettings(); - } - catch (Exception ex) - { - Log.Error($"Performance Fish encountered an expection while trying to load its settings\n{ex}"); - WriteSettings(); + Verse.Log.TryOpenLogWindow(); + Log.Error("Prepatcher mod is missing. Performance Fish will not be working correctly in this state."); } - TryInitialize(AnalyzerFixes.Patch); + modSettings = Settings; + modSettings!.Mod = this; - foreach (var patch in AllPatchClasses) + if (!OnAssembliesLoaded.Loaded) { + Verse.Log.TryOpenLogWindow(); + Log.Error("Performance Fish failed to initialize entirely. There are either critical dependencies " + + "missing or mods with hard incompatibilities present."); + } + +#if DEBUG + LogPatchCount(); +#endif + } + + public static void LogPatchCount() + { + var allFishPatches = AllPatchClasses!.SelectMany(static patchClass => patchClass.Patches.All.Values).ToList(); + var allFishPrepatches + = AllPrepatchClasses!.SelectMany(static patchClass => patchClass.Patches.All.Values).ToList(); + + var prefixCount = SumFor(allFishPatches, static fishPatch => fishPatch.PrefixMethodInfo); + var postfixCount = SumFor(allFishPatches, static fishPatch => fishPatch.PostfixMethodInfo); + var transpilerCount = SumFor(allFishPatches, static fishPatch => fishPatch.TranspilerMethodInfo); + var prepatcherPrefixCount = SumFor(allFishPrepatches, static fishPrepatch + => (fishPrepatch as FishPrepatch)?.PrefixMethodInfo); + var prepatcherPostfixCount = SumFor( + allFishPrepatches, static fishPrepatch => (fishPrepatch as FishPrepatch)?.PostfixMethodInfo); + var prepatcherTranspilerCount = SumFor(allFishPrepatches, static fishPrepatch + => fishPrepatch is FishPrepatch { PrefixMethodInfo: null, PostfixMethodInfo: null } + ? methodof(SumFor) : null); + var prepatcherClassPatchCount = SumFor(allFishPrepatches, static fishPrepatch + => fishPrepatch is FishClassPrepatch ? methodof(SumFor) : null); + + Log.Message($"Performance Fish applied {prefixCount} harmony prefixes, {postfixCount} harmony postfixes, { + transpilerCount} harmony transpilers, {prepatcherPrefixCount} prepatcher prefixes, { + prepatcherPostfixCount} prepatcher postfixes, {prepatcherTranspilerCount} prepatcher transpilers and { + prepatcherClassPatchCount} prepatcher class patch{ + (prepatcherClassPatchCount == 1 ? "" : "es")}. That's a total of {prefixCount + postfixCount + + transpilerCount + prepatcherPrefixCount + prepatcherPostfixCount + + prepatcherTranspilerCount + prepatcherClassPatchCount} patches. Amazing!\n\n" + + $"The following methods were patched:\n{string.Join("\n", allFishPatches.SelectMany(static fishPatch + => fishPatch.TargetMethodInfos).Concat(allFishPrepatches.Select(static fishPrepatch + => (fishPrepatch as FishPrepatch)?.TargetMethodBase)).Where(static method + => method != null).Select(static method => method.FullDescription()))}\n" + + $"==================================================================================="); + } + + private static int SumFor(List allFishPatches, Func methodInfoGetter) + => allFishPatches.Sum(fishPatch => methodInfoGetter(fishPatch) != null ? 1 : 0); + + internal static T[] InitializeAllPatchClasses() where T : notnull + { + var types = Assembly.GetExecutingAssembly().GetTypes(); + var list = new List(types.Length); + + Parallel.ForEach(types, type => + { + if (!type.IsAssignableTo(typeof(T)) + || type.IsInterface + || type.IsAbstract) + { + return; + } + try { - if (!patch.RequiresLoadedGameForPatching) - patch.Patches.PatchAll(); + var patchClass = SingletonFactory.Get(type); + + lock (((ICollection)list).SyncRoot) + list.Add(patchClass); } catch (Exception ex) { - Log.Error($"Performance Fish encountered an exception while trying to initialize {patch.GetType().Name}:\n{ex}"); + Log.Error($"{NAME} encountered an exception while trying to initialize {type.Name}:\n{ex}"); } - } - } - - private static void TryInitialize(Action action) - { - try - { - action(); - } - catch (Exception ex) - { - Log.Error($"Performance Fish encountered an exception while trying to initialize {action.Method.FullDescription()}:\n{ex}"); - } + }); + + return list.ToArray(); } public static T? Get() { - foreach (var patch in AllPatchClasses) + for (var i = AllPatchClasses!.Length - 1; i >= 0; i--) { - if (patch is T patchOfT) + if (AllPatchClasses[i] is T patchOfT) return patchOfT; } + return default; } - public override string SettingsCategory() => "Performance Fish"; + public override string SettingsCategory() => Mod!.Content.Name; public override void DoSettingsWindowContents(Rect inRect) => FishSettings.DoSettingsWindowContents(inRect); } \ No newline at end of file diff --git a/Source/PerformanceFish/Planet/MothballOptimization.cs b/Source/PerformanceFish/Planet/MothballOptimization.cs new file mode 100644 index 0000000..90939a2 --- /dev/null +++ b/Source/PerformanceFish/Planet/MothballOptimization.cs @@ -0,0 +1,106 @@ +// 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 System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; +using PerformanceFish.Prepatching; +using RimWorld.Planet; + +namespace PerformanceFish.Planet; + +public class MothballOptimization : ClassWithFishPrepatches +{ + public const string BASIC_DESCRIPTION + = "Mothballing means to \"take something out of operation but maintain it so that it can be used in the " + + "future.\" RimWorld has this system for world pawns along with a number of conditions which can block it " + + "from taking effect."; + + public class WorldPawnsDefPreventingMothball : FishPrepatch + { + public override string? Description { get; } + = BASIC_DESCRIPTION + " This patch essentially serves as a whitelist to allow mothballing a lot more " + + "pawns. Large performance impact, but not entirely side-effect free. Certain slowly progressing and not " + + "lethal conditions like addictions or pregnancy do not progress on mothballed pawns."; + + public static bool MothballEverything => FishSettings.MothballEverything; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(WorldPawns), nameof(WorldPawns.DefPreventingMothball)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + { + var instructions = ilProcessor.instructions; + for (var i = 0; i < instructions.Count; i++) + { + if (instructions[i].Operand is not MethodReference { Name: "IsPermanent" }) + continue; + + ilProcessor.InsertAt(i++, OpCodes.Dup); + i++; // bool IsPermanent(hediff) + + ilProcessor.InsertRange(i, + OpCodes.Ldarg_0, + OpCodes.Ldarg_1, + (OpCodes.Call, methodof(AllowMothball))); + + break; + } + } + + public static bool AllowMothball(Hediff hediff, bool isPermanent, WorldPawns worldPawns, Pawn pawn) + => MothballEverything + || isPermanent + || !hediff.Visible + || (/*IsLowPriority(worldPawns, pawn) + &&*/ AllowedDefs.Contains(hediff.def) + && hediff is + { + CurStage.lifeThreatening: false, SummaryHealthPercentImpact: < 0.01f, Bleeding: false + } + && (!HasBlockingComp(hediff) || !HasLifeThreateningStage(hediff.def))); + + private static bool HasBlockingComp(Hediff hediff) + => hediff.def.minSeverity <= 0f + && hediff is HediffWithComps hediffWithComps + && hediffWithComps.comps.ExistsAndNotNull(static comp + => comp is HediffComp_TendDuration + && comp.props is not HediffCompProperties_TendDuration { showTendQuality: false }); + + private static bool IsLowPriority(WorldPawns worldPawns, Pawn pawn) + => WorldPawns.lowPrioritySituations.Contains(worldPawns.GetSituation(pawn)); + } + + public static HashSet AllowedDefs => _allowedDefs ??= PrepareAllowedDefs(); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static HashSet PrepareAllowedDefs() + => DefDatabase.AllDefsListForReading.Where(static def + => def.lethalSeverity < 0f + // && !def.displayWound + && !CausesBleeding(def) + && !LosesSeverityPerDay(def) + && (!HasLifeThreateningStage(def) || !HasBlockingCompProperties(def))) + .ToHashSet(); + + private static bool HasBlockingCompProperties(HediffDef def) + => def.minSeverity <= 0f + && def.comps.ExistsAndNotNull(static props + => (props.compClass?.IsAssignableTo(typeof(HediffComp_TendDuration)) ?? false) + && props is not HediffCompProperties_TendDuration { showTendQuality: false }); + + private static bool LosesSeverityPerDay(HediffDef def) + => def.minSeverity <= 0f + && def.comps.ExistsAndNotNull(static props + => props is HediffCompProperties_SeverityPerDay severityProps + && (severityProps.severityPerDay <= -0.35f || severityProps.severityPerDayRange.max <= -0.35f)); + + private static bool HasLifeThreateningStage(HediffDef def) + => def.stages.ExistsAndNotNull(static stage => stage.lifeThreatening); + + private static bool CausesBleeding(HediffDef def) => def.injuryProps is { bleedRate: > 0.001f }; + + private static HashSet? _allowedDefs; +} \ No newline at end of file diff --git a/Source/PerformanceFish/Planet/WorldObjectsOptimization.cs b/Source/PerformanceFish/Planet/WorldObjectsOptimization.cs new file mode 100644 index 0000000..e2385ed --- /dev/null +++ b/Source/PerformanceFish/Planet/WorldObjectsOptimization.cs @@ -0,0 +1,114 @@ +// 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 System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; +using PerformanceFish.Prepatching; +using RimWorld.Planet; + +namespace PerformanceFish.Planet; + +public class WorldObjectsOptimization : ClassWithFishPrepatches +{ + public class WorldObjectsHolderTickPatch : FishPrepatch + { + public override string? Description { get; } + = "The world objects holder is responsible for ticking every world object. This includes settlements, " + + "caravans, outposts and any other object placed in the world, instead of maps. Normally it ticks " + + "everything equally, including many static objects that cannot possibly affect anything from a tick. " + + "This patch improves the world objects holder to determine objects that need ticking in advance, cache " + + "the list of them, and only tick those, skipping all the others."; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(WorldObjectsHolder), nameof(WorldObjectsHolder.WorldObjectsHolderTick)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(WorldObjectsHolderTick); + + public static void WorldObjectsHolderTick(WorldObjectsHolder instance) + { + if (CacheDirty(instance)) + UpdateCache(instance); + + var worldObjects = WorldObjectsHolder.tmpWorldObjects; + for (var i = worldObjects.Count; i-- > 0;) + worldObjects[i].Tick(); + } + + public static void UpdateCache(WorldObjectsHolder instance) + { + var staticWorldObjects = WorldObjectsHolder.tmpWorldObjects; + staticWorldObjects.Clear(); + + var instanceWorldObjects = instance.worldObjects; + + for (var i = instanceWorldObjects.Count; i-- > 0;) + { + var worldObject = instanceWorldObjects[i]; + + if (worldObject is not MapParent { HasMap: true } + && SkippableWorldObjects.Contains(worldObject.GetType()) + && (worldObject is not Settlement settlement || settlement.trader?.stock is null) + && CanSkipCompTick(worldObject)) + { + continue; + } + + staticWorldObjects.Add(worldObject); + } + + CachedWorldObjectsVersion = instanceWorldObjects._version; + CachedMapsVersion = Current.gameInt.maps._version; + } + + private static bool CanSkipCompTick(WorldObject worldObject) + { + var comps = worldObject.comps; + + for (var i = comps.Count; i-- > 0;) + { + var comp = comps[i]; + if (comp is EnterCooldownComp { Active: true }) + return false; + + if (!SkippableComps.Contains(comp.GetType())) + return false; + + comp.CompTick(); + } + + return true; + } + + public static int + CachedWorldObjectsVersion, + CachedMapsVersion; + + private static Type[] + _whitelistedTickingCompTypes + = + { + typeof(WorldObjectComp), + typeof(FormCaravanComp), + typeof(TimedDetectionRaids), + typeof(EnterCooldownComp) + }, + _whitelistedWorldObjectTypes = { typeof(WorldObject), typeof(MapParent), typeof(Settlement) }; + + public static HashSet + SkippableComps = MakeSubclassHashSet(typeof(WorldObjectComp), nameof(WorldObjectComp.CompTick), + _whitelistedTickingCompTypes), + SkippableWorldObjects = MakeSubclassHashSet(typeof(WorldObject), nameof(WorldObject.Tick), + _whitelistedWorldObjectTypes); + + private static HashSet MakeSubclassHashSet(Type type, string name, Type[] allowedDeclaringTypes) + => type.SubclassesWithNoMethodOverrideAndSelf(allowedDeclaringTypes, name).ToHashSet(); + + public static bool CacheDirty(WorldObjectsHolder instance) + => CachedWorldObjectsVersion != instance.worldObjects._version + || CachedMapsVersion != Current.gameInt.maps._version; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Planet/WorldPawnGCOptimization.cs b/Source/PerformanceFish/Planet/WorldPawnGCOptimization.cs new file mode 100644 index 0000000..4eb3caa --- /dev/null +++ b/Source/PerformanceFish/Planet/WorldPawnGCOptimization.cs @@ -0,0 +1,106 @@ +// 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; +using RimWorld.Planet; + +namespace PerformanceFish.Planet; + +public class WorldPawnGCOptimization : ClassWithFishPrepatches +{ + public class AddRelationshipsPatch : FishPrepatch + { + public override string? Description { get; } + = "The world pawn GC is responsible for deleting no longer relevant pawns. AddAllRelationships here tries " + + "to find every pawn another has any kind of relation to, no matter how distant and through how many " + + "other relatives, to block these pawns from getting deleted. This normally runs for every pawn the game " + + "considers critical, which includes colonists, faction leaders, spawned pawns and many other less " + + "important pawns, like someone who happened to have come up in someone's battle log, in a tale for art, " + + "in a quest etc. This patch prevents this relationship calculation for many of the less important pawns."; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(WorldPawnGC), nameof(WorldPawnGC.AddAllRelationships)); + + public static bool Prefix(WorldPawnGC __instance, Pawn pawn, Dictionary keptPawns) + => AcceptedCriticalPawnReasons.Contains(keptPawns[pawn]); + } + + public class WorldPawnGCTickPatch : FishPrepatch + { + public override string? Description { get; } + = "Normally the world pawn GC processes 1 pawn per tick, and speeds up if it fails to complete its " + + "processing before the game had to add or remove world pawns through other means. This patch changes the " + + "baseline to one pawn every 4 ticks to make the slowdown less noticeable."; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(WorldPawnGC), nameof(WorldPawnGC.WorldPawnGCTick)); + + public static bool Prefix() => TickHelper.MatchesModulo(4); + } + + public class CancelGCPassPatch : FishPrepatch + { + public override string? Description { get; } + = "When the game determines that it has to speed up its GC process because of world pawn additions or " + + "removals it normally aborts the process and entirely starts over. This patch makes it not do that, and " + + "instead just lets it continue at the faster rate."; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(WorldPawnGC), nameof(WorldPawnGC.CancelGCPass)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(ReplacementBody); + + public static void ReplacementBody(WorldPawnGC instance) + { + if (instance.activeGCProcess == null) + return; + + // instance.activeGCProcess = null; + instance.currentGCRate = Mathf.Min(instance.currentGCRate * 2, 16777216); + if (DebugViewSettings.logWorldPawnGC) + Log.Message("World pawn GC cancelled"); + } + } + + public class GetCriticalPawnReasonPatch : FishPrepatch + { + public override string? Description { get; } + = "Critical pawns are pawns the game's world pawn GC does not delete. Relationships other than parents of " + + "colonists are normally not included here, and instead get calculated within the AddRelationships " + + "method. The AddRelationships patch Performance Fish includes prevents many of the relationship " + + "calculations in that method however, so this here adds every direct relation to player-visible pawns " + + "as critical pawn reason, preventing these relatives from getting deleted."; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Method(typeof(WorldPawnGC), nameof(WorldPawnGC.GetCriticalPawnReason)); + + public static void Postfix(ref string? __result, Pawn pawn) + { + if (__result != null || pawn.relations is not { } relations) + return; + + foreach (var pawnWithDirectRelation in relations.pawnsWithDirectRelationsWithMe) + { + if (pawnWithDirectRelation is (not { RaceProps.Humanlike: true }) or + { + Faction: not { IsPlayer: true }, Spawned: false, + HostFaction: not { IsPlayer: true } + }) + { + continue; + } + + __result = "Relationship"; + return; + } + } + } + + public static HashSet AcceptedCriticalPawnReasons + = new(new[] { "Colonist", "Generating", "Kidnapped", "CaravanMember", "TransportPod", "Spawned" }); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Planet/WorldPawnsOptimization.cs b/Source/PerformanceFish/Planet/WorldPawnsOptimization.cs new file mode 100644 index 0000000..6dca958 --- /dev/null +++ b/Source/PerformanceFish/Planet/WorldPawnsOptimization.cs @@ -0,0 +1,111 @@ +// 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; +using RimWorld.Planet; + +namespace PerformanceFish.Planet; + +public class WorldPawnsOptimization : ClassWithFishPrepatches +{ + public class AllPawnsAlivePatch : FishPrepatch + { + public override string? Description { get; } + = "Minor optimization to reduce the amount of copying happening in the WorldPawns.AllPawnsAlive method"; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.PropertyGetter(typeof(WorldPawns), nameof(WorldPawns.AllPawnsAlive)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(ReplacementBody); + + public static List ReplacementBody(WorldPawns instance) + { + ref var cache = ref instance.Cache(); + + if (CacheDirty(ref cache, instance)) + { + instance.allPawnsAliveResult.Clear(); + instance.allPawnsAliveResult.AddRange(instance.pawnsAlive); + instance.allPawnsAliveResult.AddRange(instance.pawnsMothballed); + + UpdateCache(ref cache, instance); + } + + return instance.allPawnsAliveResult; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void UpdateCache(ref Cache cache, WorldPawns instance) + { + cache.PawnsAliveVersion = instance.pawnsAlive._version; + cache.PawnsMothballedVersion = instance.pawnsMothballed._version; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CacheDirty(ref Cache cache, WorldPawns instance) + => instance.pawnsAlive._version != cache.PawnsAliveVersion + || instance.pawnsMothballed._version != cache.PawnsMothballedVersion; + } + + public class AllPawnsAliveOrDeadPatch : FishPrepatch + { + public override string? Description { get; } + = "Minor optimization to reduce the amount of copying happening in the WorldPawns.AllPawnsAliveOrDead " + + "method"; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.PropertyGetter(typeof(WorldPawns), nameof(WorldPawns.AllPawnsAliveOrDead)); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(ReplacementBody); + + public static List ReplacementBody(WorldPawns instance) + { + ref var cache = ref instance.Cache(); + + var allPawnsAlive = instance.AllPawnsAlive; + var allPawnsDead = instance.AllPawnsDead; + + if (CacheDirty(ref cache, allPawnsAlive, allPawnsDead)) + { + instance.allPawnsAliveOrDeadResult.Clear(); + instance.allPawnsAliveOrDeadResult.AddRange(allPawnsAlive); + instance.allPawnsAliveOrDeadResult.AddRange(allPawnsDead); + + UpdateCache(ref cache, allPawnsAlive, allPawnsDead); + } + + return instance.allPawnsAliveOrDeadResult; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void UpdateCache(ref Cache cache, List allPawnsAlive, HashSet allPawnsDead) + { + cache.AllPawnsAliveVersion = allPawnsAlive._version; + cache.AllPawnsDeadVersion = allPawnsDead._version; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CacheDirty(ref Cache cache, List allPawnsAlive, HashSet allPawnsDead) + => allPawnsAlive._version != cache.AllPawnsAliveVersion + || allPawnsDead._version != cache.AllPawnsDeadVersion; + } + + public record struct Cache + { + public int + PawnsAliveVersion = -1, + PawnsMothballedVersion = -1, + AllPawnsAliveVersion = -1, + AllPawnsDeadVersion = -1; + + public Cache() + { + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Pools/FishPool.cs b/Source/PerformanceFish/Pools/FishPool.cs new file mode 100644 index 0000000..455858e --- /dev/null +++ b/Source/PerformanceFish/Pools/FishPool.cs @@ -0,0 +1,40 @@ +// 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.Pools; + +public static class FishPool where T : IFishPoolable +{ + private static T?[] _freeItems = new T[4]; + private static int _freeItemCount; + + public static T Get() + { + if (_freeItemCount == 0) + return Reflection.New(); + + var freeItem = _freeItems[--_freeItemCount]; + _freeItems[_freeItemCount] = default; + return freeItem!; + } + + public static void Return(T item) + { + item.Reset(); + + if (++_freeItemCount >= _freeItems.Length) + ExpandFreeItems(); + + _freeItems[_freeItemCount - 1] = item; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ExpandFreeItems() => Array.Resize(ref _freeItems, _freeItems.Length << 1); +} + +public interface IFishPoolable +{ + public void Reset(); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Pools/PooledIList.cs b/Source/PerformanceFish/Pools/PooledIList.cs new file mode 100644 index 0000000..6514823 --- /dev/null +++ b/Source/PerformanceFish/Pools/PooledIList.cs @@ -0,0 +1,48 @@ +// 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.Pools; + +public struct PooledIList : IDisposable where T : IList, new() +{ + public T List; + + private static T?[] _freeItems = new T[4]; + private static int _freeItemCount; + + public PooledIList() => List = Get(); + + public void Dispose() + { + if (List == null) + return; + + Return(List); + List = default!; + } + + public static T Get() + { + if (_freeItemCount == 0) + return Reflection.New(); + + var freeItem = _freeItems[--_freeItemCount]; + _freeItems[_freeItemCount] = default; + return freeItem!; + } + + public static void Return(T item) + { + item.Clear(); + + if (++_freeItemCount >= _freeItems.Length) + ExpandFreeItems(); + + _freeItems[_freeItemCount - 1] = item; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ExpandFreeItems() => Array.Resize(ref _freeItems, _freeItems.Length << 1); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Pools/PooledStringBuilder.cs b/Source/PerformanceFish/Pools/PooledStringBuilder.cs new file mode 100644 index 0000000..3a4a68e --- /dev/null +++ b/Source/PerformanceFish/Pools/PooledStringBuilder.cs @@ -0,0 +1,62 @@ +// 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 System.Security; +using System.Text; +using JetBrains.Annotations; + +namespace PerformanceFish.Pools; + +public struct PooledStringBuilder : IDisposable +{ + private FishPoolable _fishPoolable; + + public StringBuilder Builder => _fishPoolable.Builder; + + public StringBuilder Append(string? value) => Builder.Append(value); + public StringBuilder Append(char value) => Builder.Append(value); + public StringBuilder Append(char[] value) => Builder.Append(value); + public StringBuilder Append(PooledStringBuilderHandler value) => value.AppendToStringBuilder(Builder); + + public StringBuilder AppendLine(string? value) => Builder.AppendLine(value); + + public StringBuilder Insert(int index, string? value) => Builder.Insert(index, value); + public StringBuilder Insert(int index, char value) => Builder.Insert(index, value); + + public StringBuilder Remove(int startIndex, int length) => Builder.Remove(startIndex, length); + + public void Clear() => Builder.Clear(); + + public override string ToString() => Builder.ToString(); + + [SecuritySafeCritical] + public PooledStringBuilder() => _fishPoolable = FishPool.Get(); + + public PooledStringBuilder(int minimumSize) : this() + { + if (_fishPoolable.Builder.Capacity < minimumSize) + _fishPoolable.Builder.Capacity = minimumSize; + } + + [SecuritySafeCritical] + public void Dispose() + { + if (_fishPoolable.Builder == null!) + return; + + FishPool.Return(_fishPoolable); + _fishPoolable.Builder = null!; + } + + private struct FishPoolable : IFishPoolable + { + public StringBuilder Builder = new(); + + public void Reset() => Builder.Clear(); + + [UsedImplicitly] + public FishPoolable() {} + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Pools/PooledStringBuilderHandler.cs b/Source/PerformanceFish/Pools/PooledStringBuilderHandler.cs new file mode 100644 index 0000000..4fe3946 --- /dev/null +++ b/Source/PerformanceFish/Pools/PooledStringBuilderHandler.cs @@ -0,0 +1,39 @@ +// 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 System.Text; +using JetBrains.Annotations; + +namespace PerformanceFish.Pools; + +[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] +[InterpolatedStringHandler] +public struct PooledStringBuilderHandler : IDisposable +{ + private PooledIList> _chars; + + public PooledStringBuilderHandler(int literalLength, int formattedCount) + { + _chars = new(); + _chars.List.EnsureCapacity(literalLength + (formattedCount * 8)); + } + + public void AppendLiteral(string s) => _chars.List.Add(s); + + public void AppendFormatted(T t) + { + if (t != null) + _chars.List.Add(t.ToString()); + } + + public StringBuilder AppendToStringBuilder(StringBuilder destination) + { + destination.Append(_chars.List); + Dispose(); + return destination; + } + + public void Dispose() => _chars.Dispose(); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Pools/PooledStringHandler.cs b/Source/PerformanceFish/Pools/PooledStringHandler.cs new file mode 100644 index 0000000..a3c93a9 --- /dev/null +++ b/Source/PerformanceFish/Pools/PooledStringHandler.cs @@ -0,0 +1,37 @@ +// 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 JetBrains.Annotations; + +namespace PerformanceFish.Pools; + +[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] +[InterpolatedStringHandler] +public struct PooledStringHandler : IDisposable +{ + private PooledStringBuilder _builder; + + public PooledStringHandler(int literalLength, int formattedCount) + => _builder = new(literalLength + (formattedCount * 8)); + + public void AppendLiteral(string s) => _builder.Append(s); + + public void AppendFormatted(T t) + { + if (typeof(T) == typeof(PooledStringBuilderHandler)) + _builder.Append(Unsafe.As(ref t)); + else + _builder.Append(t?.ToString()); + } + + public string GetFormattedText() + { + var result = _builder.ToString(); + Dispose(); + return result; + } + + public void Dispose() => _builder.Dispose(); +} \ No newline at end of file diff --git a/Source/PerformanceFish/PrepatcherFields.cs b/Source/PerformanceFish/PrepatcherFields.cs new file mode 100644 index 0000000..0512653 --- /dev/null +++ b/Source/PerformanceFish/PrepatcherFields.cs @@ -0,0 +1,36 @@ +// 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 PerformanceFish.Planet; +using Prepatcher; +using RimWorld.Planet; + +namespace PerformanceFish; + +public static class PrepatcherFields +{ + [PrepatcherField] + [ValueInitializer(nameof(CreateGeneList))] + public static extern List GenesToTick(this Pawn_GeneTracker geneTracker); + + // [PrepatcherField] + // [ValueInitializer(nameof(CreateGeneList))] + // public static extern List EndogenesToTick(this Pawn_GeneTracker geneTracker); + + [PrepatcherField] + [ValueInitializer(nameof(CreateWorldPawnsCache))] + public static extern ref WorldPawnsOptimization.Cache Cache(this WorldPawns worldPawns); + +#if false + [PrepatcherField] + [ValueInitializer(nameof(CreateHediffList))] + public static extern List HediffsToTick(this Pawn_HealthTracker healthTracker); + + public static List CreateHediffList() => new(); +#endif + + public static List CreateGeneList() => new(); + public static WorldPawnsOptimization.Cache CreateWorldPawnsCache() => new(); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Prepatching/ClassWithFishPrepatches.cs b/Source/PerformanceFish/Prepatching/ClassWithFishPrepatches.cs new file mode 100644 index 0000000..36f44e2 --- /dev/null +++ b/Source/PerformanceFish/Prepatching/ClassWithFishPrepatches.cs @@ -0,0 +1,19 @@ +// 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/. + +extern alias nuget; +using nuget::JetBrains.Annotations; + +namespace PerformanceFish.Prepatching; + +[PublicAPI] +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.WithMembers)] +public abstract class ClassWithFishPrepatches : SingletonFactory +{ + public virtual FishPrepatchHolder Patches { get; } + public virtual bool RequiresLoadedGameForPatching => false; + + protected ClassWithFishPrepatches() => Patches = new(GetType()); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Prepatching/FishClassPrepatch.cs b/Source/PerformanceFish/Prepatching/FishClassPrepatch.cs new file mode 100644 index 0000000..31c26e4 --- /dev/null +++ b/Source/PerformanceFish/Prepatching/FishClassPrepatch.cs @@ -0,0 +1,17 @@ +// 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; + +namespace PerformanceFish.Prepatching; + +public abstract class FishClassPrepatch : FishPrepatchBase +{ + public abstract Type Type { get; } + + public virtual void FreePatch(TypeDefinition typeDefinition) + { + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Prepatching/FishPrepatch.cs b/Source/PerformanceFish/Prepatching/FishPrepatch.cs new file mode 100644 index 0000000..1421a07 --- /dev/null +++ b/Source/PerformanceFish/Prepatching/FishPrepatch.cs @@ -0,0 +1,27 @@ +// 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; + +namespace PerformanceFish.Prepatching; + +public abstract class FishPrepatch : FishPrepatchBase +{ + public abstract MethodBase TargetMethodBase { get; } + + public virtual MethodInfo? PrefixMethodInfo + => TryGetMethod("PREFIX", static m => m.HasAttribute()); + + public virtual MethodInfo? PostfixMethodInfo + => TryGetMethod("POSTFIX", static m => m.HasAttribute()); + + public virtual void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + { + } + + private MethodInfo? TryGetMethod(string nameCaseInsensitive, Predicate? predicate = null) + => GetType().TryGetMethod(nameCaseInsensitive, predicate); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Prepatching/FishPrepatchBase.cs b/Source/PerformanceFish/Prepatching/FishPrepatchBase.cs new file mode 100644 index 0000000..4d989fc --- /dev/null +++ b/Source/PerformanceFish/Prepatching/FishPrepatchBase.cs @@ -0,0 +1,35 @@ +// 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/. + +extern alias nuget; +using nuget::JetBrains.Annotations; + +namespace PerformanceFish.Prepatching; + +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors | ImplicitUseTargetFlags.WithMembers)] +public abstract class FishPrepatchBase : SingletonFactory, IExposable, IHasDescription +{ + private bool _enabled; + + public virtual string? Description => null; + + public virtual string? Name => null; + + public virtual bool Enabled + { + get => _enabled; + set => _enabled = value; + } + + public virtual bool DefaultState + { + [Pure] get => true; + } + + // ReSharper disable once VirtualMemberCallInConstructor + protected FishPrepatchBase() => _enabled = DefaultState; + + public virtual void ExposeData() => Scribe_Values.Look(ref _enabled, "enabled", DefaultState); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Prepatching/FishPrepatchHolder.cs b/Source/PerformanceFish/Prepatching/FishPrepatchHolder.cs new file mode 100644 index 0000000..64bcc8e --- /dev/null +++ b/Source/PerformanceFish/Prepatching/FishPrepatchHolder.cs @@ -0,0 +1,438 @@ +// 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/. + +extern alias nuget; +using System.Collections.Concurrent; +using System.Diagnostics; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Rocks; +using Mono.Collections.Generic; +using MonoMod.Utils; +using nuget::JetBrains.Annotations; + +namespace PerformanceFish.Prepatching; + +[PublicAPI] +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] +public class FishPrepatchHolder : IExposable, IEnumerable +{ + public ConcurrentDictionary All { get; } = new(); + + public T Get() where T : FishPrepatchBase => (T)All[typeof(T)]; + public void Add(FishPrepatchBase patch) => All[patch.GetType()] = patch; + public void PatchAll(ModuleDefinition module) + { + var patches = All.Values; + foreach (var patch in patches) + { + if (!patch.Enabled) + continue; + + try + { + switch (patch) + { + case FishPrepatch methodPatch: + ApplyMethodPatch(module, methodPatch); + break; + case FishClassPrepatch classPatch: + ApplyClassPatch(module, classPatch); + break; + } + } + catch (Exception ex) + { + Log.Error($"Exception while applying prepatches for {PerformanceFishMod.NAME}:\n{ex}"); + } + } + } + + private static void ApplyMethodPatch(ModuleDefinition module, FishPrepatch patch) + { + // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract + var patchMethod = patch.TargetMethodBase + ?? ThrowHelper.ThrowInvalidOperationException( + $"Patch {patch.GetType().FullName} has no target method."); + + if (patchMethod.IsGenericMethod && patchMethod is MethodInfo info) + patchMethod = info.GetGenericMethodDefinition(); + + var patchType = patchMethod.ReflectedType + ?? ThrowHelper.ThrowInvalidOperationException( + $"Method {patchMethod.FullDescription()} has no declaring type."); + + if (patchMethod.ReflectedType != patchMethod.DeclaringType) + { + ThrowHelper.ThrowInvalidOperationException($"Tried to apply patch on method { + patchMethod.Name} for type {patchMethod.ReflectedType}, but it is only declared in { + patchMethod.DeclaringType}."); + } + + if (patchType.IsGenericType) + { + patchType = patchType.GetGenericTypeDefinition(); + patchMethod = patchMethod is MethodInfo + ? AccessTools.DeclaredMethod(patchType, patchMethod.Name, patchMethod.GetParameters().GetTypes()) + : AccessTools.Constructor(patchType, patchMethod.GetParameters().GetTypes(), patchMethod.IsStatic); + } + + DebugLog.Message($"Applying prepatch {patch.GetType().FullName} on method { + patchMethod.FullDescription()}"); + + var targetType = module.GetTypeDefinition(patchType) ?? ThrowForFailureToFindType(patchType); + var targetMethod = targetType.GetMethod(patchMethod.Name, patchMethod.GetParameters().GetTypes()) + ?? ThrowForFailureToFindMethod(targetType, patchMethod); + + // PrepatchManager.AddSecurityAttributes(targetMethod.SecurityDeclarations, module, targetMethod.FullName); + + var ilProcessor = targetMethod.Body.GetILProcessor(); + + try + { + patch.Transpiler(ilProcessor, module); + } + catch (Exception e) + { + Log.Error($"Exception while applying transpiler for {patch.GetType().FullName}\n{e}\n{ + new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}"); + } + + var patchVariables = new PatchVariables(); + + if (patch.PrefixMethodInfo is { } prefixMethod) + { + try + { + ApplyPrefix(prefixMethod, ilProcessor, module, ref patchVariables); + } + catch (Exception e) + { + Log.Error($"Exception while applying prefix for {patch.GetType().FullName}\n{e}\n{ + new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}"); + } + } + + if (patch.PostfixMethodInfo is { } postfixMethod) + { + try + { + ApplyPostfix(postfixMethod, ilProcessor, module, ref patchVariables); + } + catch (Exception e) + { + Log.Error($"Exception while applying postfix for {patch.GetType().FullName}\n{e}\n{ + new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}"); + } + } + + targetMethod.Body.OptimizeMacros(); + } + + private static void ApplyPrefix(MethodInfo prefixMethodInfo, ILProcessor ilProcessor, ModuleDefinition module, + ref PatchVariables patchVariables) + { + if (prefixMethodInfo.ReturnType != typeof(bool) && prefixMethodInfo.ReturnType != typeof(void)) + { + ThrowHelper.ThrowArgumentException($"Prefix has invalid return type: { + prefixMethodInfo.ReturnType.FullName}. Must be void or bool"); + } + + var instructions = ilProcessor.instructions; + var targetMethod = ilProcessor.GetMethod(); + + var genericParameters = GetGenericParameters(targetMethod); + var prefixParameters = prefixMethodInfo.GetParameters(); + + var resultParameter = prefixParameters.TryGetNamed("__result"); + VerifyResultParameter(module, resultParameter, targetMethod, true); + + var stateParameter = prefixParameters.TryGetNamed("__state"); + VerifyStateParameter(stateParameter); + + // var skipOriginalFlag = ilProcessor.DeclareLocal(typeof(bool)); + var resultVariable = GetResultVariable(ilProcessor, ref patchVariables, resultParameter, targetMethod); + var stateVariable = GetStateVariable(ilProcessor, ref patchVariables, stateParameter); + var startOfOriginal = instructions[0]; + + var index = 0; + foreach (var parameter in prefixParameters) + { + ilProcessor.InsertAt(index++, GetInstructionForParameter(module, parameter, resultParameter, resultVariable, + stateParameter, stateVariable, targetMethod)); + } + + if (genericParameters != null) + { + ilProcessor.InsertAt(index++, OpCodes.Call, + module.ImportMethodWithGenericArguments(prefixMethodInfo, genericParameters)); + } + else + { + ilProcessor.InsertAt(index++, OpCodes.Call, prefixMethodInfo); + } + + if (prefixMethodInfo.ReturnType == typeof(bool)) + { + ilProcessor.InsertAt(index++, (OpCodes.Brtrue_S, startOfOriginal)); + + if (resultVariable != null) + { + ilProcessor.InsertAt(index++, (OpCodes.Ldloc_S, resultVariable)); + } + else if (targetMethod.ReturnType.FullName != module.TypeSystem.Void.FullName) + { + Log.Error($"Prefix {prefixMethodInfo.FullDescription() + } with return type of bool must assign to a __result parameter"); + } + + ilProcessor.InsertAt(index, OpCodes.Ret); + } + } + + private static VariableDefinition? GetStateVariable(ILProcessor ilProcessor, ref PatchVariables patchVariables, + ParameterInfo? stateParameter) + => stateParameter != null + ? patchVariables.State ??= ilProcessor.DeclareLocal(GetUnderlyingType(stateParameter.ParameterType)) + : null; + + private static VariableDefinition? GetResultVariable(ILProcessor ilProcessor, ref PatchVariables patchVariables, + ParameterInfo? resultParameter, MethodDefinition targetMethod) + => resultParameter != null + ? patchVariables.Result ??= ilProcessor.DeclareLocal(targetMethod.ReturnType) + : null; + + private static void VerifyStateParameter(ParameterInfo? stateParameter) + { + if (stateParameter is { ParameterType.IsByRef: false }) + ThrowHelper.ThrowArgumentException("__state parameter must be declared with ref or out keyword."); + } + + private static Collection? GetGenericParameters(MethodDefinition targetMethod) + => targetMethod.HasGenericParameters ? targetMethod.GenericParameters + : targetMethod.DeclaringType.HasGenericParameters ? targetMethod.DeclaringType.GenericParameters + : null; + + private static Type GetUnderlyingType(Type type) => type.IsByRef ? type.GetElementType()! : type; + + private static void ApplyPostfix(MethodInfo postfixMethodInfo, ILProcessor ilProcessor, ModuleDefinition module, + ref PatchVariables patchVariables) + { + var targetMethod = ilProcessor.GetMethod(); + + if (postfixMethodInfo.ReturnType != typeof(void) + && !postfixMethodInfo.ReturnType.Is(targetMethod.ReturnType)) + { + ThrowHelper.ThrowArgumentException($"Postfix has invalid return type: { + postfixMethodInfo.ReturnType.FullName}. Must be void or {targetMethod.ReturnType}"); + } + + var instructions = ilProcessor.instructions; + var genericParameters = GetGenericParameters(targetMethod); + var postfixParameters = postfixMethodInfo.GetParameters(); + + var resultParameter = postfixParameters.TryGetNamed("__result"); + VerifyResultParameter(module, resultParameter, targetMethod, false); + + var stateParameter = postfixParameters.TryGetNamed("__state"); + // Verify? It would make sense to match against prefix state + + // var skipOriginalFlag = ilProcessor.DeclareLocal(typeof(bool)); + var resultVariable = GetResultVariable(ilProcessor, ref patchVariables, resultParameter, targetMethod); + var stateVariable = GetStateVariable(ilProcessor, ref patchVariables, stateParameter); + + var postfixInstructions = new Collection(); + + if (resultVariable != null) + postfixInstructions.Add((OpCodes.Stloc_S, resultVariable)); + + foreach (var parameter in postfixParameters) + { + postfixInstructions.Add(GetInstructionForParameter(module, parameter, resultParameter, resultVariable, + stateParameter, stateVariable, targetMethod)); + } + + if (genericParameters != null) + { + postfixInstructions.Add((OpCodes.Call, + module.ImportMethodWithGenericArguments(postfixMethodInfo, genericParameters))); + } + else + { + postfixInstructions.Add((OpCodes.Call, postfixMethodInfo)); + } + + if (postfixMethodInfo.ReturnType == typeof(void) && resultVariable != null) + postfixInstructions.Add((OpCodes.Ldloc_S, resultVariable)); + + // TODO: use label instead of inserting multiple times + for (var i = 0; i < instructions.Count; i++) + { + if (instructions[i].OpCode != OpCodes.Ret) + continue; + + ilProcessor.InsertRange(i, postfixInstructions); + + i += postfixInstructions.Count; + } + } + + private static ITuple GetInstructionForParameter(ModuleDefinition module, ParameterInfo parameter, + ParameterInfo? resultParameter, VariableDefinition? resultVariable, ParameterInfo? stateParameter, + VariableDefinition? stateVariable, MethodDefinition targetMethod) + => parameter == resultParameter + ? (parameter.ParameterType.IsByRef ? OpCodes.Ldloca_S : OpCodes.Ldloc_S, resultVariable) + : parameter == stateParameter + ? (parameter.ParameterType.IsByRef ? OpCodes.Ldloca_S : OpCodes.Ldloc_S, stateVariable) + : GetMatchingTargetParameter(targetMethod, parameter, module); + + private static void VerifyResultParameter(ModuleDefinition module, ParameterInfo? resultParameter, + MethodDefinition targetMethod, bool requiresByRef) + { + if (resultParameter is null) + return; + + if (targetMethod.ReturnType.IsGeneric() ? !GetUnderlyingType(resultParameter.ParameterType).IsGeneric() + : !GetUnderlyingType(resultParameter.ParameterType).Is(targetMethod.ReturnType)) + { + // var firstType = targetMethod.ReturnType; + // var secondType = GetUnderlyingType(resultParameter.ParameterType); + // Log.Message($"Type FullName: {firstType.FullName + // }: ContainsGenericParameter: {firstType.ContainsGenericParameter + // }, HasGenericParameters :{firstType.HasGenericParameters + // }, IsGenericInstance: {firstType.IsGenericInstance + // }, Type FullName: {secondType.FullName + // } ContainsGenericParameters: {secondType.ContainsGenericParameters + // }, IsGenericType {secondType.IsGenericType + // }, IsConstructedGenericType: {secondType.IsConstructedGenericType}"); + + ThrowHelper.ThrowArgumentException($"__result parameter has invalid type: { + resultParameter.ParameterType.FullName}. Must match the target's return type: { + targetMethod.ReturnType.FullName}"); + } + if (requiresByRef && !resultParameter.ParameterType.IsByRef) + { + ThrowHelper.ThrowArgumentException( + "__result parameter must be declared with ref or out keyword."); + } + } + + private record struct PatchVariables + { + public VariableDefinition? Result, State; + } + + private static ITuple GetMatchingTargetParameter(MethodDefinition targetMethod, + ParameterInfo parameter, ModuleDefinition module) + { + var targetParameters = targetMethod.Parameters; + var targetParameter = parameter.Name == "__instance" + ? targetMethod.Body.ThisParameter + : targetParameters.TryGetNamed(parameter.Name); + if (targetParameter is null) + { + return ThrowHelper.ThrowArgumentException($"No parameter named { + parameter.Name} found in target method"); + } + + var targetParameterType = targetParameter.ParameterType; + var patchParameterType = parameter.ParameterType; + + return GetUnderlyingType(targetParameter).Name != GetUnderlyingType(patchParameterType).Name + ? ThrowHelper.ThrowArgumentException($"Parameter has invalid type: { + patchParameterType.FullName}. Must match the target method parameter's type: { + targetParameterType.FullName}") + : (patchParameterType.IsByRef && !targetParameter.ParameterType.IsByReference + ? OpCodes.Ldarga_S + : OpCodes.Ldarg_S, targetParameter); + } + + private static TypeReference GetUnderlyingType(ParameterInfo prefixParameter, ModuleDefinition module) + => module.ImportReference(GetUnderlyingType(prefixParameter.ParameterType)); + + private static TypeReference GetUnderlyingType(ParameterDefinition prefixParameter) + => prefixParameter.ParameterType.IsByReference ? prefixParameter.ParameterType.GetElementType()! + : prefixParameter.ParameterType; + + private static TypeDefinition ThrowForFailureToFindType(Type patchType) + => ThrowHelper.ThrowInvalidOperationException( + $"Failed to find type with namespace {patchType.Namespace} and name {patchType.Name}"); + + private static MethodDefinition ThrowForFailureToFindMethod(TypeDefinition? type, MethodBase patchMethod) + => ThrowHelper.ThrowInvalidOperationException( + $"Failed to find method with type {type} and name {patchMethod.Name}"); + + private static void ApplyClassPatch(ModuleDefinition module, FishClassPrepatch patch) + { + // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract + var patchType = patch.Type + ?? ThrowHelper.ThrowInvalidOperationException( + $"Patch {patch.GetType().FullName} has no target type."); + + if (patchType.IsGenericType) + patchType = patchType.GetGenericTypeDefinition(); + + DebugLog.Message($"Applying prepatch {patch.GetType().FullName} on type { + patchType.FullName}"); + + var targetType = module.GetType(patchType.Namespace, patchType.Name) ?? ThrowForFailureToFindType(patchType); + + // PrepatchManager.AddSecurityAttributes(targetType.SecurityDeclarations, module); + + patch.FreePatch(targetType); + } + + public FishPrepatchBase this[Type type] => All[type]; + + public FishPrepatchHolder(Type type) + { + _type = type; + AddPatchesRecursively(type); + } + + public void Scribe() => ExposeData(); // Exposable.Scribe(this, _type.FullName); + // directly calling ExposeData prevents creating nested nodes in the config file. Looks cleaner imo. + + public void ExposeData() + { + foreach (var (type, patch) in All) + Exposable.Scribe(patch, type.FullName ?? type.Name); + } + + private void AddPatchesRecursively(Type type) + { + if (typeof(FishPrepatchBase).IsAssignableFrom(type) && !All.ContainsKey(type)) + { + if (PerformanceFishMod.AllPrepatchClasses is { } allPatches) + RemoveDupes(allPatches, type); + + All.TryAdd(type, FishPrepatchBase.Get(type)); + } + + foreach (var nestedType in type.GetNestedTypes(AccessTools.all)) + AddPatchesRecursively(nestedType); + } + + private void RemoveDupes(ClassWithFishPrepatches[] patches, Type type) + { + foreach (var patchClass in patches) + { + if (patchClass.GetType() == _type + || !patchClass.Patches.All.ContainsKey(type)) + { + continue; + } + + patchClass.Patches.All.TryRemove(type, out _); + Log.Warning($"Performance Fish removed a duplicate patch from { + patchClass.GetType().FullName}. This is likely caused by no longer valid mod configs"); + } + } + + public IEnumerator GetEnumerator() => All.Values.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => All.Values.GetEnumerator(); + + private Type _type; +} \ No newline at end of file diff --git a/Source/PerformanceFish/Prepatching/FishStash.cs b/Source/PerformanceFish/Prepatching/FishStash.cs new file mode 100644 index 0000000..b7146f9 --- /dev/null +++ b/Source/PerformanceFish/Prepatching/FishStash.cs @@ -0,0 +1,63 @@ +// 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 System.Runtime.InteropServices; + +namespace PerformanceFish.Prepatching; + +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] +public struct FishStash +{ + private const string ANCHOR_STRING_VALUE = "F1shSt4sh69"; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)] + private string? _anchorString; + public UnhandledMessageQueue? UnhandledMessages; + public GCHandle MessageQueueGCHandle; + + public static ref FishStash Get + => ref StaticHandle != default + ? ref FromHandle(StaticHandle) + : ref GetFishStashFromInternalHandle(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ref FishStash GetFishStashFromInternalHandle() + { + StaticHandle = InternalFishStashHandle?.Handle + ?? ThrowHelper.ThrowInvalidOperationException("Failed to fetch internal FishStashHandle."); + + ref var stash = ref InternalFishStashHandle.Stash; + if (stash._anchorString != ANCHOR_STRING_VALUE) + ThrowHelper.ThrowInvalidDataException($"FishStash started with invalid anchor: {stash._anchorString}"); + + return ref stash; + } + + internal static FishStashHandle? InternalFishStashHandle { get; } + = typeof(Verse.Log).GetField(nameof(FishStashHandle), BindingFlags.NonPublic | BindingFlags.Static) + ?.GetValue(null) as FishStashHandle; + + internal static nint StaticHandle { get; private set; } + + internal static unsafe ref FishStash FromHandle(IntPtr handle) => ref Unsafe.AsRef(handle.ToPointer()); + + private static unsafe void Create() + { + if (StaticHandle != default) + ThrowHelper.ThrowInvalidOperationException("Tried creating duplicate FishStash."); + + var handle = Marshal.AllocHGlobal(sizeof(FishStash)); + Unsafe.InitBlockUnaligned(handle.ToPointer(), 0, (uint)sizeof(FishStash)); + ((FishStash*)handle)->_anchorString = ANCHOR_STRING_VALUE; + + StaticHandle = handle; + } + + static FishStash() + { + if (!Log.Ready && InternalFishStashHandle is null) + Create(); + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Prepatching/FishStashHandle.cs b/Source/PerformanceFish/Prepatching/FishStashHandle.cs new file mode 100644 index 0000000..1ee32a4 --- /dev/null +++ b/Source/PerformanceFish/Prepatching/FishStashHandle.cs @@ -0,0 +1,41 @@ +// 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 System.Runtime.InteropServices; + +namespace PerformanceFish.Prepatching; + +public class FishStashHandle : IDisposable +{ + private static readonly List _allHandles = new(); + + public ref FishStash Stash => ref FishStash.FromHandle(Handle); + + public nint Handle { get; } + + public FishStashHandle(long handle) + { + if (_allHandles.Contains((nint)handle)) + ThrowHelper.ThrowInvalidOperationException("Tried creating duplicate FishStashHandle."); + else + _allHandles.Add((nint)handle); + + Handle = (nint)handle; + } + + private void ReleaseUnmanagedResources() + { + Marshal.FreeHGlobal(Handle); + _allHandles.Remove(Handle); + } + + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + ~FishStashHandle() => ReleaseUnmanagedResources(); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Prepatching/FreePatchEntry.cs b/Source/PerformanceFish/Prepatching/FreePatchEntry.cs new file mode 100644 index 0000000..4a177d2 --- /dev/null +++ b/Source/PerformanceFish/Prepatching/FreePatchEntry.cs @@ -0,0 +1,22 @@ +// 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 JetBrains.Annotations; +using Mono.Cecil; +using PerformanceFish.ModCompatibility; +using Prepatcher; + +namespace PerformanceFish.Prepatching; + +public static class FreePatchEntry +{ + [FreePatch] + [UsedImplicitly] + public static void Start(ModuleDefinition module) + { + ActiveMods.CheckRequirements(); + PrepatchManager.Start(module); + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Prepatching/PrepatchManager.cs b/Source/PerformanceFish/Prepatching/PrepatchManager.cs new file mode 100644 index 0000000..b9540bf --- /dev/null +++ b/Source/PerformanceFish/Prepatching/PrepatchManager.cs @@ -0,0 +1,376 @@ +// 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/. + +extern alias nuget; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Security; +using System.Threading.Tasks; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Collections.Generic; +using nuget::JetBrains.Annotations; +using PerformanceFish.ModCompatibility; +using CollectionExtensions = PerformanceFish.Utility.CollectionExtensions; +using MethodBody = Mono.Cecil.Cil.MethodBody; + +namespace PerformanceFish.Prepatching; + +public static class PrepatchManager +{ + public static bool PatchingFinished { get; private set; } + + [UsedImplicitly] + public static void Start(ModuleDefinition module) + { + if (PatchingFinished) + { + ThrowHelper.ThrowInvalidOperationException("Tried running free patches a second time. This should " + + "never happen. Cancelling now to avoid further issues."); + } + + Log.Message(LogPatches.PERFORMANCE_FISH_WELCOME_MESSAGE); + if (!ActiveMods.Prepatcher) + Log.Warning("Prepatches running without prepatcher active. This should normally not happen."); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + // if (Type.GetType("Prepatcher.Process.FreePatcher, PrepatcherImpl") is { } freePatcherType + // && AccessTools.Method(freePatcherType, "AddSkipVerificationAttribute") is { } addSkipVerificationMethod + // /*Type.GetType("HugsLib.Patches.MainMenuDrawer_Quickstart_Patch, HugsLib") is { } freePatcherType + // && AccessTools.Method(freePatcherType, "QuicktestButtonUsesQuickstarter") is { } addSkipVerificationMethod*/) + // { + // var rimworldAssembly = typeof(Root).Assembly; + // var harmonyAssembly = typeof(Harmony).Assembly; + // var prepatcherAssembly = freePatcherType.Assembly; + // var fishAssembly = typeof(PerformanceFishMod).Assembly; + // var monoModAssembly = typeof(MonoMod.Utils.Cil.ILGeneratorShim).Assembly; + // + // SetReflectionOnly(rimworldAssembly, false, out var previousRimworldValue); + // SetReflectionOnly(harmonyAssembly, false, out var previousHarmonyValue); + // SetReflectionOnly(prepatcherAssembly, false, out var previousPrepatcherValue); + // SetReflectionOnly(monoModAssembly, false, out var previousMonoModValue); + // SetReflectionOnly(fishAssembly, false, out var previousFishValue); + // + // PerformanceFishMod.Harmony.Patch(addSkipVerificationMethod, + // prefix: new(methodof(FreePatcherSkipVerificationPatch))); + // + // SetReflectionOnly(rimworldAssembly, previousRimworldValue, out _); + // SetReflectionOnly(harmonyAssembly, previousHarmonyValue, out _); + // SetReflectionOnly(prepatcherAssembly, previousPrepatcherValue, out _); + // SetReflectionOnly(fishAssembly, previousMonoModValue, out _); + // SetReflectionOnly(fishAssembly, previousFishValue, out _); + // } + + AddAttributes(module); + + ModifyAllTypes(module); + + var allPrepatchClasses + = PerformanceFishMod.AllPrepatchClasses + = PerformanceFishMod.InitializeAllPatchClasses(); + + _ = FishSettings.Instance; + + foreach (var prepatchClass in allPrepatchClasses) + prepatchClass.Patches.PatchAll(module); + + PatchingFinished = true; + stopwatch.Stop(); + Verse.Log.Message($"Performance Fish finished applying prepatches in { + stopwatch.ElapsedSecondsAccurate():N7} seconds"); + } + + // private static readonly FieldInfo? _monoAssemblyField = AccessTools.Field(typeof(Assembly), "_mono_assembly"); + // + // internal static unsafe void SetReflectionOnly(Assembly asm, bool value, out bool previousValue) + // { + // var refOnlyField = (int*)((IntPtr)_monoAssemblyField!.GetValue(asm) + 0x74); + // previousValue = *refOnlyField == 1 || (*refOnlyField == 0 ? false : throw new()); + // *refOnlyField = value ? 1 : 0; + // } + // + // public static bool FreePatcherSkipVerificationPatch() => false; + + private static void ModifyAllTypes(ModuleDefinition module) + { + var types = module.Types; + + Parallel.For(0, types.Count, // about 20% faster than sequential for in testing + i => ModifyAllMethodsIn(types[i])); + } + + private static void ModifyAllMethodsIn(TypeDefinition type) + { + if (type.Methods is not { } methods) + return; + + for (var i = methods.Count; i-- > 0;) + { + if (methods[i].Body is { } body) + ModifyMethodBody(body); + } + + if (type.NestedTypes is not { } nestedTypes) + return; + + for (var i = nestedTypes.Count; i-- > 0;) + ModifyAllMethodsIn(nestedTypes[i]); + } + + private static void ModifyMethodBody(MethodBody body) + { + ApplySkipLocalsInit(body); + ModifyInstructions(body.Method, body.Instructions); + } + + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + private enum LinqMethod + { + None, + First, + FirstOrDefault, + Where, + Any, + Select, + Count, + Contains, + Sum + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + [SuppressMessage("ReSharper", "UnusedMember.Local")] + private static class LinqStrings + { + public const string + First = "First", + FirstOrDefault = "FirstOrDefault", + Where = "Where", + Any = "Any", + Select = "Select", + Count = "Count", + Contains = "Contains", + Sum = "Sum", + DeclaringType = "System.Linq.Enumerable"; + } + + private static void ModifyInstructions(MethodDefinition method, Collection instructions) + { + for (var i = 0; i < instructions.Count; i++) + { + var instruction = instructions[i]; + + if (instruction.OpCode != OpCodes.Call) + continue; + + if (i - 4 < 0) + continue; + + if (!(i + 1 < instructions.Count + && instruction.Operand is MethodReference + { + Name: LinqStrings.Where, DeclaringType.FullName: LinqStrings.DeclaringType + } + && instructions[i + 1].Operand is MethodReference + { + Name: LinqStrings.FirstOrDefault or LinqStrings.First, + DeclaringType.FullName: LinqStrings.DeclaringType + }) + && instruction.Operand is not MethodReference + { + Name: LinqStrings.FirstOrDefault or LinqStrings.First, + DeclaringType.FullName: LinqStrings.DeclaringType + }) + { + continue; + } + + var linqTarget = LinqMethod.FirstOrDefault; + + if (((MethodReference)instruction.Operand).Name == LinqStrings.First + || (i + 1 < instructions.Count + && ((MethodReference)instruction.Operand).Name == LinqStrings.Where + && ((MethodReference)instructions[i + 1].Operand).Name == LinqStrings.First)) + { + linqTarget = LinqMethod.First; + } + + if (!(instructions[i - 1].OpCode == OpCodes.Newobj && instructions[i - 2].OpCode == OpCodes.Ldftn) + && !(instructions[i - 1].OpCode == OpCodes.Stsfld && instructions[i - 2].OpCode == OpCodes.Dup + && instructions[i - 3].OpCode == OpCodes.Newobj && instructions[i - 4].OpCode == OpCodes.Ldftn)) + continue; + + var inputMethodInstruction = instructions[i - 4]; + var inputMethod = inputMethodInstruction.OpCode == OpCodes.Call + ? inputMethodInstruction.Operand as MethodReference + : null; + + if (inputMethod is null && i - 6 < 0) + continue; + + if ((inputMethod ??= (inputMethodInstruction = instructions[i - 6]).Operand as MethodReference) is null) + continue; + + var module = method.Module; + + if (inputMethod!.Name == "get_AllDefs") + { + inputMethodInstruction.Operand + = inputMethod + = module.ImportReference((inputMethod.DeclaringType.ContainsGenericParameter + ? typeof(DefDatabase<>).GetProperty(nameof(DefDatabase.AllDefsListForReading)) + : typeof(DefDatabase<>) + .MakeGenericType(AccessTools.TypeByName(((GenericInstanceType)inputMethod.DeclaringType) + .GenericArguments[0].FullName)) + .GetProperty(nameof(DefDatabase.AllDefsListForReading)))!.GetMethod, inputMethod); + } + + var inputReturnType = inputMethod.ReturnType; + if (inputReturnType.Name == "List`1") + { + var containsGenericParameter = inputReturnType.ContainsGenericParameter + && (inputReturnType.TryGetGenericArguments() + ?.Any(a => a.TryGetGenericParameterType(inputMethod) is null) ?? true); + + var listFindMethod = linqTarget == LinqMethod.FirstOrDefault + ? containsGenericParameter + ? typeof(List<>).GetMethod(nameof(List.Find)) + : typeof(List<>) + .MakeGenericType(AccessTools.TypeByName(inputReturnType + .TryGetGenericArguments(inputMethod)![0].FullName)).GetMethod(nameof(List.Find)) + : methodof((Func, Predicate, object>)CollectionExtensions.First) + .GetGenericMethodDefinition(); + + if (linqTarget == LinqMethod.First && !containsGenericParameter) + { + listFindMethod = listFindMethod!.MakeGenericMethod(AccessTools.TypeByName(inputReturnType + .TryGetGenericArguments(inputMethod)![0].FullName)); + } + + var newOperand = containsGenericParameter + ? module.ImportReference(listFindMethod, method) + : module.ImportReference(listFindMethod); + + // Log.Message($"Performance Fish successfully optimized { + // ((MethodReference)instruction.Operand).FullName} within method { + // method.FullName} by applying new operand {newOperand.FullName}"); + + var instructionsToRemove = 0; + if (instruction.Operand is MethodReference { Name: LinqStrings.Where }) + instructionsToRemove++; + + instruction.Operand = newOperand; + + while (instructionsToRemove-- > 0) + instructions.RemoveAt(i + 1); + } + } + } + + private static void ApplySkipLocalsInit(MethodBody body) => body.InitLocals = false; + + private static void AddAttributes(ModuleDefinition module) + { + AddModuleAttributes(module); + AddAssemblyAttributes(module); + // AddSecurityAttributes(module.Assembly.SecurityDeclarations, module); + } + + private static void AddAssemblyAttributes(ModuleDefinition module) + { + var rimworldCustomAttributes = module.Assembly.CustomAttributes; + + if (TryAddAttribute(module, rimworldCustomAttributes, typeof(IgnoresAccessChecksToAttribute), + typeof(string)) is { } ignoresAccessChecksToAttribute) + { + ignoresAccessChecksToAttribute.ConstructorArguments.Add(new(module.TypeSystem.String, "mscorlib")); + } + + TryAddAttribute(module, rimworldCustomAttributes, typeof(AllowPartiallyTrustedCallersAttribute)); + + TryAddAttribute(module, rimworldCustomAttributes, typeof(SecurityTransparentAttribute)); + + if (TryAddAttribute(module, rimworldCustomAttributes, typeof(SecurityRulesAttribute), + typeof(SecurityRuleSet)) is { } securityRulesAttribute) + { + securityRulesAttribute.ConstructorArguments.Add(new(module.ImportReference(typeof(SecurityRuleSet)), + SecurityRuleSet.Level2)); + + securityRulesAttribute.Properties.Add(new(nameof(SecurityRulesAttribute.SkipVerificationInFullTrust), + new(module.TypeSystem.Boolean, true))); + } + + var debuggableAttribute = rimworldCustomAttributes.First(static a + => a.AttributeType.Name == nameof(DebuggableAttribute)); + + debuggableAttribute.Constructor + = module.ImportReference(typeof(DebuggableAttribute) + .GetConstructor(new[] { typeof(bool), typeof(bool) })); + + debuggableAttribute.ConstructorArguments.Clear(); + debuggableAttribute.ConstructorArguments.Add(new(module.TypeSystem.Boolean, false)); + debuggableAttribute.ConstructorArguments.Add(new(module.TypeSystem.Boolean, false)); + } + + private static void AddModuleAttributes(ModuleDefinition module) + => TryAddAttribute(module, module.CustomAttributes, typeof(UnverifiableCodeAttribute)); + + // internal static void AddSecurityAttributes(Collection securityDeclarations, + // ModuleDefinition module, [CallerArgumentExpression(nameof(securityDeclarations))] string? targetName = null) + // { + // if (securityDeclarations.Any(static s => s.SecurityAttributes + // .Any(static a => a.AttributeType.Name == nameof(SecurityPermissionAttribute) + // && a.Properties.Any(static p => p.Name == nameof(SecurityPermissionAttribute.SkipVerification))))) + // { + // return; + // } + // + // if (targetName != null) + // Log.Message($"Added skipVerification attribute to {targetName}"); + // + // securityDeclarations.Add(new(Mono.Cecil.SecurityAction.RequestMinimum) + // { + // SecurityAttributes = + // { + // new SecurityAttribute(module.ImportReference(typeof(SecurityPermissionAttribute))) + // { + // Properties = + // { + // new(nameof(SecurityPermissionAttribute.SkipVerification), + // new(module.TypeSystem.Boolean, true)) + // } + // } + // } + // }); + // } + + public static CustomAttribute? TryAddAttribute(ModuleDefinition module, IList attributes, + Type attributeType, params Type[] parameters) + { + if (ContainsAttribute(attributes, attributeType.Name)) + return null; + + var attribute = ImportAttribute(module, attributeType, parameters); + attributes.Add(attribute); + return attribute; + } + + private static CustomAttribute ImportAttribute(ModuleDefinition module, Type attributeType, + params Type[] parameters) + => new(module.ImportReference(AccessTools.Constructor(attributeType, parameters))); + + private static bool ContainsAttribute(IList attributes, string name) + { + for (var i = attributes.Count; i-- > 0;) + { + if (attributes[i].AttributeType.Name == name) + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/RecipeDefCaching.cs b/Source/PerformanceFish/RecipeDefCaching.cs index 83f68f2..fd0e184 100644 --- a/Source/PerformanceFish/RecipeDefCaching.cs +++ b/Source/PerformanceFish/RecipeDefCaching.cs @@ -1,34 +1,41 @@ -// Copyright (c) 2022 bradson +// 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 RecipeUsersCache = PerformanceFish.Cache.ByIndex; +using RecipeUsersCache + = PerformanceFish.Cache.ByInt; namespace PerformanceFish; public class RecipeDefCaching : ClassWithFishPatches { public override bool RequiresLoadedGameForPatching => true; + public class AllRecipeUsers_Patch : FirstPriorityFishPatch { - public override string Description => "Caches results of the AllRecipeUsers method. Becomes impactful with lots of unfinished things around, as those check everything for potentially accepting them"; - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(RecipeDef), nameof(RecipeDef.AllRecipeUsers)); + public override string Description { get; } + = "Caches results of the AllRecipeUsers method. Becomes impactful with lots of unfinished things around, " + + "as those check everything for potentially accepting them"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(RecipeDef), nameof(RecipeDef.AllRecipeUsers)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(RecipeDef __instance, ref IEnumerable __result, out bool __state) + public static bool Prefix(RecipeDef? __instance, ref IEnumerable __result, out bool __state) { - if (__instance is null || __instance.GetType() != typeof(RecipeDef)) + if (__instance is null) { __state = false; return true; } - ref var cache = ref RecipeUsersCache.Get.GetReference(__instance); - if (cache.ShouldRefreshNow) + ref var cache = ref RecipeUsersCache.GetOrAddReference(__instance.shortHash); + if (cache.Dirty) return __state = true; - __result = cache.recipeUsers; + __result = cache.RecipeUsers; return __state = false; } @@ -38,43 +45,58 @@ public class RecipeDefCaching : ClassWithFishPatches if (!__state) return; - var cache = RecipeUsersCache.Get[__instance]; - - if (cache.recipeUsers is List list) - { - list.Clear(); - list.AddRange(__result); - } - else - { - cache.recipeUsers = new List(__result); - } - cache.ShouldRefreshNow = false; - - RecipeUsersCache.Get[__instance] = cache; + UpdateCache(__instance, __result); } - public struct RecipeUsersCacheValue : Cache.IIsRefreshable - { - public IEnumerable recipeUsers; - private int _nextRefreshFrame; + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(RecipeDef __instance, IEnumerable __result) + => RecipeUsersCache.GetExistingReference(__instance).Update(__instance, __result); - public bool ShouldRefreshNow + public record struct RecipeUsersCacheValue + { + private int _defDatabaseVersion; + private int _defRecipeUsersVersion; + private RecipeDef _def; + public List RecipeUsers; + + public void Update(RecipeDef instance, IEnumerable result) { - get => Time.frameCount > _nextRefreshFrame; - set => _nextRefreshFrame = value ? 0 : Time.frameCount + 4096 + Math.Abs(Rand.Int % 2048); + if (RecipeUsers is { } list) + { + list.Clear(); + list.AddRange(result); + } + else + { + RecipeUsers = new(result); + } + + _def = instance; + _defDatabaseVersion = DefDatabase.defsList._version; + _defRecipeUsersVersion = instance.recipeUsers?._version ?? 0; + } + + public bool Dirty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + => _defDatabaseVersion != DefDatabase.defsList._version + || _defRecipeUsersVersion != (_def.recipeUsers?._version ?? 0); } - public RecipeUsersCacheValue SetNewValue(RecipeDef key) => throw new NotImplementedException(); } } /*public class PotentiallyMissingIngredients_Patch : FirstPriorityFishPatch { - public override Expression TargetMethod => () => new RecipeDef().PotentiallyMissingIngredients(null, null); + public override Expression TargetMethod { get; } + = () => new RecipeDef().PotentiallyMissingIngredients(null, null); + public static bool Prefix(RecipeDef __instance, Pawn billDoer, Map map, ref IEnumerable __result) { - if (!Cache.TryGetValue(__instance, billDoer, map, out var cache)) + if (!Cache.ByReference.TryGetValue(__instance, billDoer, map, + out var cache)) return true; + if (__instance.ingredients.Count != 0) { Log.Warning("RecipeDef ingredients mismatched its cache in Performance Fish, forcing a refresh"); @@ -84,16 +106,26 @@ public class RecipeDefCaching : ClassWithFishPatches __result = cache.ingredientThingDefs; return false; } - public static void Postfix(RecipeDef __instance, Pawn billDoer, Map map, IEnumerable __result, bool __runOriginal) + + public static void Postfix(RecipeDef __instance, Pawn billDoer, Map map, IEnumerable __result, + bool __runOriginal) { if (!__runOriginal) return; - Cache.Get[new(__instance, billDoer, map)] - = new() { ingredientThingDefs = __result.ToArray(), firstIngredientsItemCount = __instance.ingredients.FirstOrDefault()?.count ?? 0, ShouldRefreshNow = false }; + + Cache.ByReference.Get[new(__instance, billDoer, map)] + = new() + { + ingredientThingDefs = __result.ToArray(), + firstIngredientsItemCount = __instance.ingredients.FirstOrDefault()?.count ?? 0, + ShouldRefreshNow = false + }; } - public static MissingIngredientsCache GetCache(RecipeDef def, Pawn billDoer, Map map) => Cache.GetValue(def, billDoer, map); - public struct MissingIngredientsCache : ICanRefresh, MissingIngredientsCache> + public static MissingIngredientsCache GetCache(RecipeDef def, Pawn billDoer, Map map) + => Cache.ByReference.GetValue(def, billDoer, map); + + public record struct MissingIngredientsCache { public ThingDef[] ingredientThingDefs; private int _nextRefreshFrame; @@ -104,7 +136,9 @@ public class RecipeDefCaching : ClassWithFishPatches get => Time.frameCount > _nextRefreshFrame; set => _nextRefreshFrame = value ? 0 : Time.frameCount + 2048 + Math.Abs(Rand.Int % 1024); } - public MissingIngredientsCache SetNewValue(Cache key) => throw new NotImplementedException(); + + public MissingIngredientsCache SetNewValue(Cache key) + => throw new NotImplementedException(); } }*/ } \ No newline at end of file diff --git a/Source/PerformanceFish/Rendering/GenDrawPatches.cs b/Source/PerformanceFish/Rendering/GenDrawPatches.cs index 1df4233..a4699e9 100644 --- a/Source/PerformanceFish/Rendering/GenDrawPatches.cs +++ b/Source/PerformanceFish/Rendering/GenDrawPatches.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -8,7 +8,8 @@ public class GenDrawPatches : ClassWithFishPatches { public class DrawMeshNowOrLater_Patch : FishPatch { - public override Delegate TargetMethodGroup => (Action)GenDraw.DrawMeshNowOrLater; + public override Delegate TargetMethodGroup { get; } + = (Action)GenDraw.DrawMeshNowOrLater; public static void Replacement(Mesh mesh, Vector3 loc, Quaternion quat, Material mat, bool drawNow) { @@ -27,7 +28,8 @@ public class GenDrawPatches : ClassWithFishPatches public class DrawMeshNowOrLaterWithMatrix_Patch : FishPatch { - public override Delegate TargetMethodGroup => (Action)GenDraw.DrawMeshNowOrLater; + public override Delegate TargetMethodGroup { get; } + = (Action)GenDraw.DrawMeshNowOrLater; public static void Replacement(Mesh mesh, Matrix4x4 matrix, Material mat, bool drawNow) { diff --git a/Source/PerformanceFish/Rendering/GraphicPatches.cs b/Source/PerformanceFish/Rendering/GraphicPatches.cs index cf46f84..31e7d6e 100644 --- a/Source/PerformanceFish/Rendering/GraphicPatches.cs +++ b/Source/PerformanceFish/Rendering/GraphicPatches.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -8,7 +8,8 @@ public class GraphicPatches : ClassWithFishPatches { public class DrawMeshInt_Patch : FishPatch { - public override MethodBase TargetMethodInfo => AccessTools.DeclaredMethod(typeof(Graphic), nameof(Graphic.DrawMeshInt)); + public override MethodBase TargetMethodInfo { get; } + = AccessTools.DeclaredMethod(typeof(Graphic), nameof(Graphic.DrawMeshInt)); public static void Replacement(Graphic __instance, Mesh mesh, Vector3 loc, Quaternion quat, Material mat) { @@ -18,9 +19,11 @@ public class GraphicPatches : ClassWithFishPatches public class Shadow_DrawWorker_Patch : FishPatch { - public override MethodBase TargetMethodInfo => AccessTools.DeclaredMethod(typeof(Graphic_Shadow), nameof(Graphic_Shadow.DrawWorker)); + 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) + 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 diff --git a/Source/PerformanceFish/Rendering/PawnCacheRendererPatches.cs b/Source/PerformanceFish/Rendering/PawnCacheRendererPatches.cs index 5ee3d82..010e664 100644 --- a/Source/PerformanceFish/Rendering/PawnCacheRendererPatches.cs +++ b/Source/PerformanceFish/Rendering/PawnCacheRendererPatches.cs @@ -1,16 +1,20 @@ -// Copyright (c) 2022 bradson +// 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 class PawnCacheRendererPatches : ClassWithFishPatches // The PawnCacheRenderer's Camera.Render() call has a considerable performance impact despite only being used for rendering of single pawns. These are failed attempts at lowering its impact. + +public class PawnCacheRendererPatches : ClassWithFishPatches + // The PawnCacheRenderer's Camera.Render() call has a considerable performance impact despite only being used for + // rendering of single pawns. These are failed attempts at lowering its impact. { private static bool _optimizeCameraEnabled; public class OnPostRender_Patch : FishPatch { - public override MethodBase TargetMethodInfo => AccessTools.DeclaredMethod(typeof(PawnCacheRenderer), nameof(PawnCacheRenderer.OnPostRender)); + public override MethodBase TargetMethodInfo + => AccessTools.DeclaredMethod(typeof(PawnCacheRenderer), nameof(PawnCacheRenderer.OnPostRender)); public static CodeInstructions Transpiler(CodeInstructions codes) => Reflection.MakeReplacementCall(OnPostRender); @@ -18,7 +22,10 @@ public class PawnCacheRendererPatches : ClassWithFishPatches // The PawnCacheR public static void OnPostRender(PawnCacheRenderer __instance) { Find.PawnCacheCamera.cullingMask = DefaultCullingMask; - __instance.pawn.Drawer.renderer.RenderCache(__instance.rotation, __instance.angle, __instance.positionOffset, __instance.renderHead, __instance.renderBody, __instance.portrait, __instance.renderHeadgear, __instance.renderClothes, __instance.overrideApparelColor, __instance.overrideHairColor, __instance.stylingStation); + __instance.pawn.Drawer.renderer.RenderCache(__instance.rotation, __instance.angle, + __instance.positionOffset, __instance.renderHead, __instance.renderBody, __instance.portrait, + __instance.renderHeadgear, __instance.renderClothes, __instance.overrideApparelColor, + __instance.overrideHairColor, __instance.stylingStation); Find.PawnCacheCamera.cullingMask = 16; // random } @@ -28,7 +35,8 @@ public class PawnCacheRendererPatches : ClassWithFishPatches // The PawnCacheR public class RenderPawn_Patch : FishPatch { - public override MethodBase TargetMethodInfo => AccessTools.DeclaredMethod(typeof(PawnCacheRenderer), nameof(PawnCacheRenderer.RenderPawn)); + public override MethodBase TargetMethodInfo + => AccessTools.DeclaredMethod(typeof(PawnCacheRenderer), nameof(PawnCacheRenderer.RenderPawn)); public static void Prefix(ref Vector3 cameraOffset, ref Vector3 positionOffset) { @@ -58,8 +66,12 @@ public class PawnCacheRendererPatches : ClassWithFishPatches // The PawnCacheR cacheCamera.renderingPath = _optimizeCameraEnabled ? RenderingPath.VertexLit : DefaultCacheCameraRenderingPath; } - private static RenderingPath DefaultCameraRenderingPath => _defaultCameraRenderingPath ??= Find.Camera.renderingPath; - private static RenderingPath DefaultCacheCameraRenderingPath => _defaultCacheCameraRenderingPath ??= Find.PawnCacheCamera.renderingPath; + private static RenderingPath DefaultCameraRenderingPath + => _defaultCameraRenderingPath ??= Find.Camera.renderingPath; + + private static RenderingPath DefaultCacheCameraRenderingPath + => _defaultCacheCameraRenderingPath ??= Find.PawnCacheCamera.renderingPath; + private static RenderingPath? _defaultCameraRenderingPath; private static RenderingPath? _defaultCacheCameraRenderingPath; }*/ \ No newline at end of file diff --git a/Source/PerformanceFish/RoomOptimizations.cs b/Source/PerformanceFish/RoomOptimizations.cs index 10d4131..ba1440d 100644 --- a/Source/PerformanceFish/RoomOptimizations.cs +++ b/Source/PerformanceFish/RoomOptimizations.cs @@ -1,30 +1,44 @@ -// Copyright (c) 2022 bradson +// 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 System.Linq; using System.Reflection.Emit; -using RoomOwnersCache = PerformanceFish.Cache.ByIndex; +using RoomOwnersCache + = PerformanceFish.Cache.ByIndex; +using RoomRoleCache + = PerformanceFish.Cache.ByIndex; +using ContainedAndAdjacentThingsCache + = PerformanceFish.Cache.ByIndex; namespace PerformanceFish; + public class RoomOptimizations : ClassWithFishPatches, IHasDescription { - public string Description => "Room stats related optimizations"; + public string Description { get; } = "Room stats related optimizations"; + public class Role_Patch : FirstPriorityFishPatch { - public override string Description => "Throttles room stats and role updating to happen at most once every 512 or so ticks per room, instead of every time anything at all changes or moves"; - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(Room), nameof(Room.Role)); - public static CodeInstructions Transpiler(CodeInstructions codes) + public override string Description { get; } + = "Throttles room stats and role updating to happen at most once every 512 or so ticks per room, " + + "instead of every time anything at all changes or moves"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(Room), nameof(Room.Role)); + + public static CodeInstructions Transpiler(CodeInstructions instructions) { var this_statsAndRoleDirty = FishTranspiler.Field(typeof(Room), nameof(Room.statsAndRoleDirty)); try { - return codes.ReplaceAt((codes, i) - => i - 1 > 0 && codes[i - 1] == this_statsAndRoleDirty - && codes[i].opcode == OpCodes.Brfalse_S && codes[i].operand is Label, - code => new[] + return instructions.ReplaceAt((codes, i) + => i - 1 > 0 + && codes[i - 1] == this_statsAndRoleDirty + && codes[i].opcode == OpCodes.Brfalse_S + && codes[i].operand is Label, static code => new[] { code, FishTranspiler.This, @@ -34,93 +48,133 @@ public class RoomOptimizations : ClassWithFishPatches, IHasDescription } catch (Exception ex) { - Log.Error($"Performance Fish failed to patch Room.Role. This just means that specific patch will be doing nothing.\n{ex}"); + Log.Error("Performance Fish failed to patch Room.Role. This just means that specific patch " + + $"will be doing nothing.\n{ex}"); return null!; } } + public static bool ShouldUpdate(Room __instance) { - if (__instance.role is null || !RoomThrottleInfo.TryGetValue(__instance, out var value) || value < Current.Game.tickManager.TicksGame) + if (__instance.role is not null + || !RoomRoleCache.Get[__instance.ID].Dirty) { - RoomThrottleInfo[__instance] = Current.Game.tickManager.TicksGame + 384 + Math.Abs(Rand.Int % 256); - return true; + return false; } - return false; + + UpdateCache(__instance); + return true; + + } + + private static void UpdateCache(Room __instance) + => RoomRoleCache.Get.GetReference(__instance).SetDirty(__instance); + + public record struct CacheValue + { + private int _nextUpdateTick; + public bool Dirty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => TickHelper.Past(_nextUpdateTick); + } + public void SetDirty(Room room) + => _nextUpdateTick = TickHelper.Add(384, room.ID, 256); } - public static Dictionary RoomThrottleInfo { get; } = new(); - static Role_Patch() => Cache.Utility.All.Add(RoomThrottleInfo); } public class Owners_Patch : FirstPriorityFishPatch { - public override string Description => "Caches room owner data and throttles them to only update at most once every ~512 or so ticks. RimWorld calculates owners by checking ownership of all items contained in a room, which can get quite expensive"; - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(Room), nameof(Room.Owners)); + public override string Description { get; } + = "Caches room owner data and throttles them to only update at most once every ~512 or so ticks. " + + "RimWorld calculates owners by checking ownership of all items contained in a room, which can get " + + "quite expensive"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(Room), nameof(Room.Owners)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Prefix(Room __instance, ref IEnumerable __result, out bool __state) { - ref var cache = ref RoomOwnersCache.Get.GetReference(__instance); + ref var cache = ref RoomOwnersCache.Get.GetReference(__instance.ID); - if (cache.ShouldRefreshNow) + if (cache.Dirty) { - if (__instance.TouchesMapEdge || __instance.IsHuge || (__instance.statsAndRoleDirty && !__instance.ContainedBeds.Any())) - { - cache.ShouldRefreshNow = false; - cache.owners = Array.Empty(); - RoomOwnersCache.Get[__instance] = cache; - __result = cache.owners; - return __state = false; - } - - return __state = true; + if (CanCache(__instance)) + return __state = true; + else + cache.Update(__instance, null); } - __result = cache.owners; + __result = cache.Owners; return __state = false; } + private static bool CanCache(Room __instance) + => __instance is { TouchesMapEdge: false, IsHuge: false } + && (!__instance.statsAndRoleDirty || __instance.ContainedBeds.Any()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Postfix(Room __instance, bool __state, ref IEnumerable __result) { if (!__state) return; - if (RoomOwnersCache.Get.TryGetValue(__instance, out var cache) && cache.owners is List list) - { - list.Clear(); - foreach (var pawn in __result) - list.Add(pawn); - } - else - { - cache.owners = new List(__result); - } - - cache.ShouldRefreshNow = false; - RoomOwnersCache.Get[__instance] = cache; - __result = cache.owners; + UpdateCache(__instance, ref __result); } - public struct FishRoomOwnersInfo + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Room __instance, ref IEnumerable __result) { - public int nextRefreshTick; - public IEnumerable owners; + ref var cache = ref RoomOwnersCache.Get.GetReference(__instance); + + cache.Update(__instance, __result); + + __result = cache.Owners; + } - public bool ShouldRefreshNow + public record struct CacheValue + { + private int _nextRefreshTick; + public List Owners; + + public void Update(Room room, IEnumerable? result) + { + if (result is null) + { + (Owners ??= new(0)).Clear(); + } + else + { + if (Owners != null) + { + Owners.Clear(); + Owners.AddRange(result); + } + else + { + Owners = new(result); + } + } + + _nextRefreshTick = TickHelper.Add(384, room.ID, 256); + } + + public bool Dirty { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => nextRefreshTick < Current.Game.tickManager.TicksGame; - set => nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 384 + Math.Abs(Rand.Int % 256); + get => TickHelper.Past(_nextRefreshTick); } } } - public class Regions_Patch : FishPatch + public class Regions_Patch : FirstPriorityFishPatch { - public override string Description => "Minor optimization"; - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(Room), nameof(Room.Regions)); + 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) + public static CodeInstructions Transpiler(CodeInstructions codeInstructions) => Reflection.MakeReplacementCall(Regions_Replacement); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -128,25 +182,27 @@ public class RoomOptimizations : ClassWithFishPatches, IHasDescription { var tmpRegions = instance.tmpRegions; var districts = instance.districts; - lock (_lock) - { - tmpRegions.Clear(); - for (var i = 0; i < districts.Count; i++) - tmpRegions.AddRange(districts[i].Regions); - } + + tmpRegions.Clear(); + for (var i = 0; i < districts.Count; i++) + tmpRegions.AddRangeFast(districts[i].Regions); + return tmpRegions; } - - private static readonly object _lock = new(); } - public class ContainedBeds_Patch : FishPatch + public class ContainedBeds_Patch : FirstPriorityFishPatch { - public override string Description => "So RimWorld has a list of beds and a list of all things. For some reason ludeon chose to have ContainedBeds retrieve its beds by looping through all things. This fixes that. Helps a lot against spikes"; - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(Room), nameof(Room.ContainedBeds)); + public override string Description { get; } + = "So RimWorld has a list of beds and a list of all things. For some reason Ludeon chose to have " + + "ContainedBeds retrieve its beds by looping through all things. This fixes that. Helps a lot " + + "against spikes"; - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) - => Reflection.MakeReplacementCall(ContainedBeds_Replacement); + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(Room), nameof(Room.ContainedBeds)); + + public static CodeInstructions Transpiler(CodeInstructions codeInstructions, ILGenerator generator) + => Reflection.GetCodeInstructions(ContainedBeds_Replacement, generator); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable ContainedBeds_Replacement(Room instance) @@ -157,92 +213,99 @@ public class RoomOptimizations : ClassWithFishPatches, IHasDescription var beds = regions[i].ListerThings.listsByGroup[(int)ThingRequestGroup.Bed]; if (beds is null) continue; + for (var j = 0; j < beds.Count; j++) yield return (Building_Bed)beds[j]; } } } - public class ContainedAndAdjacentThings_Patch : FishPatch + public class ContainedAndAdjacentThings_Patch : FirstPriorityFishPatch { - public override string Description => "Caching of room content info. Variable impact"; - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(Room), nameof(Room.ContainedAndAdjacentThings)); + public override string Description { get; } = "Caching of room content info. Variable impact"; - public static CodeInstructions Transpiler(CodeInstructions CodeInstructions) + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(Room), nameof(Room.ContainedAndAdjacentThings)); + + public static CodeInstructions Transpiler(CodeInstructions codeInstructions) => Reflection.MakeReplacementCall(ContainedAndAdjacentThings_Replacement); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List ContainedAndAdjacentThings_Replacement(Room instance) { - lock (_lock) + instance.uniqueContainedThings.Clear(); + + var regions = instance.Regions; + + ref var cache = ref ContainedAndAdjacentThingsCache.Get.GetReference(instance.ID); + + if (cache.ListVersions != null + && cache.ListVersions.Count == regions.Count + && !cache.Dirty) { - var uniqueContainedThings = instance.uniqueContainedThings; - uniqueContainedThings.Clear(); - - var regions = instance.Regions; - - if (CachedThingLists.TryGetValue(instance, out var cache) && cache.listVersions.Count == regions.Count && !cache.ShouldRefreshNow) + var cacheIsAccurate = true; + for (var i = 0; i < regions.Count; i++) { - var cacheIsAccurate = true; - for (var i = 0; i < regions.Count; i++) + var allThings = regions[i].listerThings.AllThings; + if (allThings == null) { - var allThings = regions[i].listerThings.AllThings; - if (allThings == null) - { - if (cache.listVersions[i] != -1) - cacheIsAccurate = false; - } - else if (cache.listVersions[i] != allThings.Version()) - { + if (cache.ListVersions[i] != -1) cacheIsAccurate = false; - } } - if (cacheIsAccurate) + else if (cache.ListVersions[i] != allThings._version) { - uniqueContainedThings.AddRange(cache.things); - return uniqueContainedThings; + cacheIsAccurate = false; } } - return RefreshContainedAndAdjacentThings(instance, regions, cache); + if (cacheIsAccurate) + { + instance.uniqueContainedThings.AddRangeFast(cache.Things); + return instance.uniqueContainedThings; + } } + + return RefreshContainedAndAdjacentThings(instance, regions, ref cache); } - private static List RefreshContainedAndAdjacentThings(Room instance, List regions, FishThingsInfo cache) + private static List RefreshContainedAndAdjacentThings(Room instance, List regions, + ref CacheValue cache) { var uniqueContainedThings = instance.uniqueContainedThings; var uniqueContainedThingsSet = instance.uniqueContainedThingsSet; uniqueContainedThingsSet.Clear(); - (cache.listVersions ??= new()).Clear(); + (cache.ListVersions ??= new()).Clear(); for (var i = 0; i < regions.Count; i++) { var allThings = regions[i].ListerThings.AllThings; if (allThings == null) { - cache.listVersions.Add(-1); + cache.ListVersions.Add(-1); continue; } + var count = allThings.Count; for (var j = 0; j < count; j++) uniqueContainedThingsSet.Add(allThings[j]); - - cache.listVersions.Add(allThings.Version()); + + cache.ListVersions.Add(allThings.Version()); } + uniqueContainedThings.AddRange(uniqueContainedThingsSet); - if (cache.things is List list) + if (cache.Things is { } list) list.ReplaceContentsWith(uniqueContainedThings); else - cache.things = new List(uniqueContainedThings); - cache.ShouldRefreshNow = false; - - CachedThingLists[instance] = cache; + cache.Things = new(uniqueContainedThings); + + cache.SetDirty(false, instance); uniqueContainedThingsSet.Clear(); return uniqueContainedThings; } - /*private static List RefreshContainedAndAdjacentThingsAlternative(Room instance, List regions, FishThingsInfo cache) + /*private static List RefreshContainedAndAdjacentThingsAlternative(Room instance, List regions, + FishThingsInfo cache) { var uniqueContainedThings = instance.uniqueContainedThings; (cache.listVersions ??= new()).Clear(); @@ -261,6 +324,7 @@ public class RoomOptimizations : ClassWithFishPatches, IHasDescription cache.listVersions.Add(-1); continue; } + var count = allThings.Count; for (var j = 0; j < count; j++) { @@ -270,7 +334,7 @@ public class RoomOptimizations : ClassWithFishPatches, IHasDescription adjacentThings.Add(allThings[j]); } - cache.listVersions.Add(Utility.getListVersion(allThings)); + cache.listVersions.Add(allThings._version); } for (var i = 0; i < adjacentThings.Count; i++) @@ -290,31 +354,30 @@ public class RoomOptimizations : ClassWithFishPatches, IHasDescription return uniqueContainedThings; }*/ - private static readonly object _lock = new(); - - public static Dictionary CachedThingLists { get; } = new(); - static ContainedAndAdjacentThings_Patch() => Cache.Utility.All.Add(CachedThingLists); - - public struct FishThingsInfo + public record struct CacheValue { - public int nextRefreshTick; - public List listVersions; - public IEnumerable things; + private int _nextRefreshTick; + public List? ListVersions; + public List Things; + + public void SetDirty(bool value, Room room) + => _nextRefreshTick = value ? 0 : TickHelper.Add(3072, room.ID, 2048); - public bool ShouldRefreshNow + public bool Dirty { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => nextRefreshTick < Current.Game.tickManager.TicksGame; - set => nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); + get => TickHelper.Past(_nextRefreshTick); } } } - public class RoomAt_Patch : FishPatch + public class RoomAt_Patch : FirstPriorityFishPatch { - public override string? Description => "Minor optimization through inlining ond cleaning up of duplicate calls in the method. About 1/4 faster, which isn't much, but it likely won't break anything either."; + public override string? Description { get; } + = "Minor optimization through inlining ond cleaning up of duplicate calls in the method. About 1/4 " + + "faster, which isn't much, but it likely won't break anything either."; - public override Delegate? TargetMethodGroup => RegionAndRoomQuery.RoomAt; + public override Delegate TargetMethodGroup { get; } = RegionAndRoomQuery.RoomAt; public static CodeInstructions Transpiler(CodeInstructions codes) => Reflection.MakeReplacementCall(RoomAt); @@ -323,17 +386,23 @@ public class RoomOptimizations : ClassWithFishPatches, IHasDescription public static Room? RoomAt(IntVec3 c, Map map, RegionType allowedRegionTypes = RegionType.Set_All) { var mapSize = map.info.Size; - if ((uint)c.x >= mapSize.x || (uint)c.z >= mapSize.z) + if ((uint)c.x >= (uint)mapSize.x || (uint)c.z >= (uint)mapSize.z) return null; var regionAndRoomUpdater = map.regionAndRoomUpdater; if (regionAndRoomUpdater.Enabled) + { regionAndRoomUpdater.TryRebuildDirtyRegionsAndRooms(); + } else if (regionAndRoomUpdater.AnythingToRebuild) - Log.Warning($"Trying to get valid region at {c} but RegionAndRoomUpdater is disabled. The result may be incorrect."); + { + 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 * mapSize.x) + c.x]; - return region is null || !region.valid || (region.type & allowedRegionTypes) == 0 ? null + return region is null || !region.valid || (region.type & allowedRegionTypes) == 0 + ? null : region.District?.Room; } } diff --git a/Source/PerformanceFish/StoreUtilityPatches.cs b/Source/PerformanceFish/StoreUtilityPatches.cs index 87e8def..7a6d8ab 100644 --- a/Source/PerformanceFish/StoreUtilityPatches.cs +++ b/Source/PerformanceFish/StoreUtilityPatches.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -9,8 +9,13 @@ public class StoreUtilityPatches : ClassWithFishPatches { public class IsInValidBestStorage_Patch : FishPatch { - public override string Description => "Patches IsInValidBestStorage to immediately remove things from haulables when they're found to be in the best storage. Default often has them checked several more times before removal happens"; - public override Delegate TargetMethodGroup => StoreUtility.IsInValidBestStorage; + public override string Description { get; } + = "Patches IsInValidBestStorage to immediately remove things from haulables when they're found to be in " + + "the best storage. Default often has them checked several more times before removal happens"; + + public override Delegate TargetMethodGroup { get; } = StoreUtility.IsInValidBestStorage; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Postfix(Thing t, bool __result) { if (__result) @@ -19,7 +24,8 @@ public class StoreUtilityPatches : ClassWithFishPatches } /*else if (!t.IsInValidStorage()) { - if (!StoreUtility.TryFindBestBetterStorageFor(t, null, t.Map, StoragePriority.Unstored, Faction.OfPlayerSilentFail, out IntVec3 _, out IHaulDestination _, needAccurateResult: false)) + if (!StoreUtility.TryFindBestBetterStorageFor(t, null, t.Map, StoragePriority.Unstored, + Faction.OfPlayerSilentFail, out IntVec3 _, out IHaulDestination _, needAccurateResult: false)) { } @@ -32,19 +38,25 @@ public class StoreUtilityPatches : ClassWithFishPatches { protected IsGoodStoreCell_Patch() => FishSettings.ThreadingPatches.Add(this); public override bool DefaultState => false; - public override string Description => "Lock for threading. Only needed with threading enabled and off by default"; - public override Delegate TargetMethodGroup => StoreUtility.IsGoodStoreCell; + + public override string Description { get; } + = "Lock for threading. Only needed with threading enabled and off by default"; + + public override Delegate TargetMethodGroup { get; } = StoreUtility.IsGoodStoreCell; + public static void Prefix() { if (FishSettings.ThreadingEnabled) Monitor.Enter(Lock); } + public static Exception Finalizer(Exception __exception) { if (FishSettings.ThreadingEnabled) Monitor.Exit(Lock); return __exception; } + private static object Lock { get; } = new(); } @@ -52,19 +64,25 @@ public class StoreUtilityPatches : ClassWithFishPatches { protected GetStatValue_Patch() => FishSettings.ThreadingPatches.Add(this); public override bool DefaultState => false; - public override string Description => "Lock for threading. Only needed with threading enabled and off by default"; - public override Delegate TargetMethodGroup => StatExtension.GetStatValue; + + public override string Description { get; } + = "Lock for threading. Only needed with threading enabled and off by default"; + + public override Delegate TargetMethodGroup { get; } = StatExtension.GetStatValue; + public static void Prefix() { if (FishSettings.ThreadingEnabled) Monitor.Enter(Lock); } + public static Exception Finalizer(Exception __exception) { if (FishSettings.ThreadingEnabled) Monitor.Exit(Lock); return __exception; } + private static object Lock { get; } = new(); } #endif diff --git a/Source/PerformanceFish/System/EqualityComparerOptimization.cs b/Source/PerformanceFish/System/EqualityComparerOptimization.cs index 7fb18fd..5f0391b 100644 --- a/Source/PerformanceFish/System/EqualityComparerOptimization.cs +++ b/Source/PerformanceFish/System/EqualityComparerOptimization.cs @@ -1,41 +1,76 @@ -// Copyright (c) 2022 bradson +// 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.System; -public class EqualityComparerOptimization : ClassWithFishPatches, IHasDescription -{ - public string Description => "Specialized EqualityComparers for slightly faster collection lookups on ValueTypes. No settings for this one. Can reduce memory usage by preventing unnecessary boxing into object."; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; - public static void Initialize() +namespace PerformanceFish.System; + +public class EqualityComparerOptimization : ClassWithFishPatches +{ + public class Optimization : FishPatch { - foreach (var type in AccessTools.AllTypes()) + public override string? Description { get; } + = "Specialized EqualityComparers for slightly faster collection lookups on ValueTypes. Can reduce memory " + + "usage by preventing unnecessary boxing into object and helps with performance of a few UI methods. " + + "Changes require a restart."; + + public override MethodInfo? TryPatch() => null; + + public static void Initialize() { - if (type.IsValueType - && type != typeof(void) - && typeof(IEquatable<>).MakeGenericType(type).IsAssignableFrom(type) - && type != typeof(int) - && type != typeof(ushort) - && !type.IsGenericTypeDefinition) + if (!Get().Enabled) + return; + + var types = AccessTools.AllTypes().ToList(); + + Parallel.ForEach(Partitioner.Create(0, types.Count), (range, _) => { - SetValueForDefaultComparer(type, Activator.CreateInstance(typeof(EquatableValueTypeEqualityComparer<>).MakeGenericType(type))); - } + for (var i = range.Item1; i < range.Item2; i++) + { + var type = types[i]; + object? equalityComparer + = type == typeof(int) ? new IntEqualityComparer() + : type == typeof(ushort) ? new UshortEqualityComparer() + : type == typeof(string) ? new StringEqualityComparer() + : null; + + if (equalityComparer != null) + { + SetValueForDefaultComparer(type, equalityComparer); + return; + } + + if (!type.IsValueType + || type == typeof(void) + || type.IsGenericTypeDefinition + || type.IsEnum + || type == typeof(byte)) + { + return; + } + + SetValueForDefaultComparer(type, + Activator.CreateInstance( + type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) + ? (type.GetGenericArguments()[0] is var genericArgument + && genericArgument.IsAssignableTo(typeof(IEquatable<>), type) + ? typeof(EquatableNullableEqualityComparer<>) + : typeof(NullableEqualityComparer<>)).MakeGenericType(genericArgument) + : (type.IsAssignableTo(typeof(IEquatable<>), type) + ? typeof(EquatableValueTypeEqualityComparer<>) + : typeof(ValueTypeEqualityComparer<>)).MakeGenericType(type))); + } + }); } - SetValueForDefaultComparer(typeof(int), new IntEqualityComparer()); - SetValueForDefaultComparer(typeof(ushort), new UshortEqualityComparer()); - SetValueForDefaultComparer(typeof(string), new StringEqualityComparer()); - - // below aren't IEquatable, so why don't we just go fix that - SetValueForDefaultComparer(typeof(IntPtr), new IntPtrEqualityComparer()); - SetValueForDefaultComparer(typeof(RuntimeFieldHandle), new RuntimeFieldHandleEqualityComparer()); - SetValueForDefaultComparer(typeof(RuntimeTypeHandle), new RuntimeTypeHandleEqualityComparer()); - SetValueForDefaultComparer(typeof(RuntimeMethodHandle), new RuntimeMethodHandleEqualityComparer()); + private static void SetValueForDefaultComparer(Type type, object value) + => typeof(EqualityComparer<>).MakeGenericType(type).GetField("defaultComparer", AccessTools.allDeclared)! + .SetValue(null, value); } - - private static void SetValueForDefaultComparer(Type type, object value) - => typeof(EqualityComparer<>).MakeGenericType(type).GetField("defaultComparer", AccessTools.allDeclared).SetValue(null, value); } [Serializable] @@ -43,13 +78,55 @@ public class EquatableValueTypeEqualityComparer : EqualityComparer where T { [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(T x, T y) => x.Equals(y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(T obj) => obj.GetHashCode(); + public override int GetHashCode(T obj) => HashCode.Get(obj); /// /// Equals method for the comparer itself. /// public override bool Equals(object obj) => obj is EquatableValueTypeEqualityComparer; + + public override int GetHashCode() => GetType().Name.GetHashCode(); +} + +[Serializable] +public class EquatableNullableEqualityComparer : EqualityComparer where T : struct, IEquatable +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(T? x, T? y) + => x.HasValue + ? y.HasValue && x.Value.Equals(y.Value) + : !y.HasValue; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode(T? obj) => !obj.HasValue ? 0 : HashCode.Get(obj); + + /// + /// Equals method for the comparer itself. + /// + public override bool Equals(object obj) => obj is EquatableNullableEqualityComparer; + + public override int GetHashCode() => GetType().Name.GetHashCode(); +} + +[Serializable] +public class NullableEqualityComparer : EqualityComparer where T : struct +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(T? x, T? y) + => x.HasValue + ? y.HasValue && x.Value.Equals(y.Value) + : !y.HasValue; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode(T? obj) => !obj.HasValue ? 0 : HashCode.Get(obj); + + /// + /// Equals method for the comparer itself. + /// + public override bool Equals(object obj) => obj is NullableEqualityComparer; + public override int GetHashCode() => GetType().Name.GetHashCode(); } @@ -57,7 +134,9 @@ public class EquatableValueTypeEqualityComparer : EqualityComparer where T public class StringEqualityComparer : EqualityComparer { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(string x, string y) => string.Equals(x, y); // already does a nullcheck, so this skips the extra check that'd otherwise happen + 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 + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode(string obj) => obj.GetHashCode(); @@ -65,66 +144,7 @@ public class StringEqualityComparer : EqualityComparer /// Equals method for the comparer itself. /// public override bool Equals(object obj) => obj is StringEqualityComparer; - public override int GetHashCode() => GetType().Name.GetHashCode(); -} -[Serializable] -public class IntPtrEqualityComparer : EqualityComparer -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(IntPtr x, IntPtr y) => x == y; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(IntPtr obj) => obj.GetHashCode(); - - /// - /// Equals method for the comparer itself. - /// - public override bool Equals(object obj) => obj is IntPtrEqualityComparer; - public override int GetHashCode() => GetType().Name.GetHashCode(); -} - -[Serializable] -public class RuntimeFieldHandleEqualityComparer : EqualityComparer -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(RuntimeFieldHandle x, RuntimeFieldHandle y) => x.Value == y.Value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(RuntimeFieldHandle obj) => obj.GetHashCode(); - - /// - /// Equals method for the comparer itself. - /// - public override bool Equals(object obj) => obj is RuntimeFieldHandleEqualityComparer; - public override int GetHashCode() => GetType().Name.GetHashCode(); -} - -[Serializable] -public class RuntimeTypeHandleEqualityComparer : EqualityComparer -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(RuntimeTypeHandle x, RuntimeTypeHandle y) => x.Value == y.Value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(RuntimeTypeHandle obj) => obj.GetHashCode(); - - /// - /// Equals method for the comparer itself. - /// - public override bool Equals(object obj) => obj is RuntimeTypeHandleEqualityComparer; - public override int GetHashCode() => GetType().Name.GetHashCode(); -} - -[Serializable] -public class RuntimeMethodHandleEqualityComparer : EqualityComparer -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(RuntimeMethodHandle x, RuntimeMethodHandle y) => x.Value == y.Value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(RuntimeMethodHandle obj) => obj.GetHashCode(); - - /// - /// Equals method for the comparer itself. - /// - public override bool Equals(object obj) => obj is RuntimeMethodHandleEqualityComparer; public override int GetHashCode() => GetType().Name.GetHashCode(); } @@ -133,6 +153,7 @@ public class IntEqualityComparer : EqualityComparer { [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(int x, int y) => x == y; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode(int obj) => obj; @@ -140,6 +161,7 @@ public class IntEqualityComparer : EqualityComparer /// Equals method for the comparer itself. /// public override bool Equals(object obj) => obj is IntEqualityComparer; + public override int GetHashCode() => GetType().Name.GetHashCode(); } @@ -148,6 +170,7 @@ public class UshortEqualityComparer : EqualityComparer { [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(ushort x, ushort y) => x == y; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode(ushort obj) => obj; @@ -155,16 +178,18 @@ public class UshortEqualityComparer : EqualityComparer /// Equals method for the comparer itself. /// public override bool Equals(object obj) => obj is UshortEqualityComparer; + public override int GetHashCode() => GetType().Name.GetHashCode(); } [Serializable] public class ReferenceEqualityComparer : EqualityComparer { - public static new readonly ReferenceEqualityComparer Default = new(); + public new static readonly ReferenceEqualityComparer Default = new(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(T x, T y) => ReferenceEquals(x, y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj); @@ -172,5 +197,25 @@ public class ReferenceEqualityComparer : EqualityComparer /// Equals method for the comparer itself. /// public override bool Equals(object obj) => obj is ReferenceEqualityComparer; + + public override int GetHashCode() => GetType().Name.GetHashCode(); +} + +[Serializable] +public class ValueTypeEqualityComparer : EqualityComparer where T : struct +{ + public new static readonly ValueTypeEqualityComparer Default = new(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(T x, T y) => x.Equals(y); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode(T obj) => HashCode.Get(obj); + + /// + /// Equals method for the comparer itself. + /// + public override bool Equals(object obj) => obj is ReferenceEqualityComparer; + public override int GetHashCode() => GetType().Name.GetHashCode(); } \ No newline at end of file diff --git a/Source/PerformanceFish/System/ReflectionCaching.cs b/Source/PerformanceFish/System/ReflectionCaching.cs index aec088e..614f60a 100644 --- a/Source/PerformanceFish/System/ReflectionCaching.cs +++ b/Source/PerformanceFish/System/ReflectionCaching.cs @@ -1,25 +1,64 @@ -// Copyright (c) 2022 bradson +// 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 System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection.Emit; - -using ConstructorInfoCache = PerformanceFish.Cache.ByReference, System.Reflection.ConstructorInfo>; -using FieldGetters = PerformanceFish.Cache.ByReference; -using FieldInfoCache = PerformanceFish.Cache.ByReference; -using FieldSetters = PerformanceFish.Cache.ByReference; -using MethodInfoCache = PerformanceFish.Cache.ByReference; -using MethodInfoWithTypesAndFlagsCache = PerformanceFish.Cache.ByReference, System.Reflection.MethodInfo>; -using MethodInfoWithTypesCache = PerformanceFish.Cache.ByReference, System.Reflection.MethodInfo>; -using MethodInvokers = PerformanceFish.Cache.ByReference; -using PropertyInfoCache = PerformanceFish.Cache.ByReference; +using System.Runtime.InteropServices; +using System.Security; +using System.Threading.Tasks; +using PerformanceFish.Cache; +using ConstructorInfoCache + = PerformanceFish.Cache.ByReference, + PerformanceFish.System.ReflectionCaching.TypePatches.ConstructorInfoCacheValue>; +using FieldGetters + = PerformanceFish.Cache.ByReferenceUnclearable; +using FieldInfoCache + = PerformanceFish.Cache.ByReference; +using FieldSetters + = PerformanceFish.Cache.ByReferenceUnclearable; +using MethodInfoCache + = PerformanceFish.Cache.ByReference; +using MethodInfoWithTypesAndFlagsCache + = PerformanceFish.Cache.ByReference, + PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>; +using MethodInfoWithTypesCache + = PerformanceFish.Cache.ByReference, + PerformanceFish.System.ReflectionCaching.TypePatches.MethodInfoCacheValue>; +using MethodInvokers + = PerformanceFish.Cache.ByReferenceUnclearable; +using PropertyInfoCache + = PerformanceFish.Cache.ByReference; +using CustomAttributeCache + = PerformanceFish.Cache.ByReference; +using TypeFullNameCache + = PerformanceFish.Cache.ByReferenceUnclearable; +using ActivatorCache + = PerformanceFish.Cache.ByReferenceUnclearable; +using ActivatorWithArgumentsCache + = PerformanceFish.Cache.ByReference, + PerformanceFish.System.ReflectionCaching.ActivatorPatches.ActivatorCacheValue>; namespace PerformanceFish.System; + public class ReflectionCaching : ClassWithFishPatches { - public static void Initialize() // necessary to prevent recursion between patches and function pointer creation + static ReflectionCaching() // necessary to prevent recursion between patches and function pointer creation { try { @@ -35,11 +74,12 @@ public class ReflectionCaching : ClassWithFishPatches } catch (Exception ex) { - Log.Error($"Performance Fish encountered an exception while trying to initialize its ReflectionCaching:\n{ex}"); + Log.Error($"Performance Fish encountered an exception while trying to initialize its ReflectionCaching:\n{ + ex}"); } } - public struct StateAndFlags + public record struct StateAndFlags { public bool State; public BindingFlags Flags; @@ -49,16 +89,20 @@ public class ReflectionCaching : ClassWithFishPatches { public class GetField_Patch : FishPatch { - public override string Description => "Caches GetField lookups"; - public override MethodBase TargetMethodInfo - => Reflection.MethodInfo("mscorlib", "System.RuntimeType", nameof(Type.GetField), new[] { typeof(string), typeof(BindingFlags) }); + public override string Description { get; } = "Caches GetField lookups"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(RuntimeType), nameof(RuntimeType.GetField), + new[] { typeof(string), typeof(BindingFlags) })!; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(Type __instance, string name, BindingFlags bindingAttr, ref FieldInfo? __result, out StateAndFlags __state) + public static bool Prefix(Type __instance, string name, BindingFlags bindingAttr, out FieldInfo? __result, + out StateAndFlags __state) { - var key = new FieldInfoCache(__instance.TypeHandle, bindingAttr, ToLowerIfNeededForBindingFlags(name, bindingAttr)); + var key = new FieldInfoCache(__instance.TypeHandle, bindingAttr, + ToLowerIfNeededForBindingFlags(name, bindingAttr)); - __result = FieldInfoCache.Get.TryGetValue(ref key); + __result = FieldInfoCache.Get.GetOrAdd(ref key).Info; if (__result is null) { @@ -73,72 +117,100 @@ public class ReflectionCaching : ClassWithFishPatches } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Postfix(Type __instance, string name, FieldInfo __result, StateAndFlags __state) + public static void Postfix(Type __instance, string name, FieldInfo? __result, StateAndFlags __state) { if (!__state.State || __result is null) return; - FieldInfoCache.Get[new(__instance.TypeHandle, __state.Flags, ToLowerIfNeededForBindingFlags(name, __state.Flags))] = __result; + UpdateCache(__instance, name, __result, __state); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Type __instance, string name, FieldInfo __result, StateAndFlags __state) + => FieldInfoCache.GetExistingReference(__instance.TypeHandle, __state.Flags, + ToLowerIfNeededForBindingFlags(name, __state.Flags)).Info + = __result; } public class GetMethodWithFlags_Patch : FishPatch { - public override string Description => "Caches GetMethod lookups"; - public override Expression TargetMethod => () => default(Type)!.GetMethod(null, default(BindingFlags)); + public override string Description { get; } = "Caches GetMethod lookups"; + + public override Expression TargetMethod { get; } + = static () => default(Type)!.GetMethod(null!, default(BindingFlags)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(Type __instance, string name, BindingFlags bindingAttr, ref MethodInfo? __result, out bool __state) + public static bool Prefix(Type __instance, string name, BindingFlags bindingAttr, out MethodInfo? __result, + out bool __state) { - var key = new MethodInfoCache(__instance.TypeHandle, bindingAttr, ToLowerIfNeededForBindingFlags(name, bindingAttr)); + var key = new MethodInfoCache(__instance.TypeHandle, bindingAttr, + ToLowerIfNeededForBindingFlags(name, bindingAttr)); - __result = MethodInfoCache.Get.TryGetValue(ref key); + __result = MethodInfoCache.Get.GetOrAdd(ref key).Info; return __state = __result is null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Postfix(Type __instance, string name, BindingFlags bindingAttr, MethodInfo __result, bool __state) + public static void Postfix(Type __instance, string name, BindingFlags bindingAttr, MethodInfo? __result, + bool __state) { if (!__state || __result is null) return; - MethodInfoCache.Get[new(__instance.TypeHandle, bindingAttr, ToLowerIfNeededForBindingFlags(name, bindingAttr))] = __result; + UpdateCache(__instance, name, bindingAttr, __result); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Type __instance, string name, BindingFlags bindingAttr, MethodInfo __result) + => MethodInfoCache.GetExistingReference(__instance.TypeHandle, bindingAttr, + ToLowerIfNeededForBindingFlags(name, bindingAttr)).Info + = __result; } public class GetMethodWithTypes_Patch : FishPatch { - public override string Description => "Caches GetMethod lookups"; - public override Expression TargetMethod => () => default(Type)!.GetMethod(null, null); + public override string Description { get; } = "Caches GetMethod lookups"; + + public override Expression TargetMethod { get; } + = static () => default(Type)!.GetMethod(null!, null!); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(Type __instance, string name, Type[] types, ref MethodInfo? __result, out bool __state) + public static bool Prefix(Type __instance, string name, Type[] types, out MethodInfo? __result, + out bool __state) { var key = new MethodInfoWithTypesCache(__instance.TypeHandle, name, new(types)); - __result = MethodInfoWithTypesCache.Get.TryGetValue(ref key); + __result = MethodInfoWithTypesCache.Get.GetOrAdd(ref key).Info; return __state = __result is null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Postfix(Type __instance, string name, Type[] types, MethodInfo __result, bool __state) + public static void Postfix(Type __instance, string name, Type[] types, MethodInfo? __result, bool __state) { if (!__state || __result is null) return; - MethodInfoWithTypesCache.Get[new(__instance.TypeHandle, name, new(types))] = __result; + UpdateCache(__instance, name, types, __result); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Type __instance, string name, Type[] types, MethodInfo __result) + => MethodInfoWithTypesCache.GetExistingReference(__instance.TypeHandle, name, new(types)).Info + = __result; } public class GetMethodWithFlagsAndTypes_Patch : FishPatch { - public override string Description => "Caches GetMethod lookups"; - public override Expression TargetMethod => () => default(Type)!.GetMethod(null, default, null, null, null); + public override string Description { get; } = "Caches GetMethod lookups"; + + public override Expression TargetMethod { get; } + = static () => default(Type)!.GetMethod(null!, default, null, null!, null); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(Type __instance, string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers, ref MethodInfo? __result, out bool __state) + public static bool Prefix(Type __instance, string name, BindingFlags bindingAttr, Binder? binder, + Type[] types, ParameterModifier[] modifiers, ref MethodInfo? __result, out bool __state) { if (binder != null || modifiers is { Length: > 0 }) { @@ -146,30 +218,42 @@ public class ReflectionCaching : ClassWithFishPatches return true; } - var key = new MethodInfoWithTypesAndFlagsCache(__instance.TypeHandle, bindingAttr, ToLowerIfNeededForBindingFlags(name, bindingAttr), new(types)); + var key = new MethodInfoWithTypesAndFlagsCache(__instance.TypeHandle, bindingAttr, + ToLowerIfNeededForBindingFlags(name, bindingAttr), new(types)); - __result = MethodInfoWithTypesAndFlagsCache.Get.TryGetValue(ref key); + __result = MethodInfoWithTypesAndFlagsCache.Get.GetOrAdd(ref key).Info; return __state = __result is null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Postfix(Type __instance, string name, Type[] types, BindingFlags bindingAttr, MethodInfo __result, bool __state) + public static void Postfix(Type __instance, string name, Type[] types, BindingFlags bindingAttr, + MethodInfo? __result, bool __state) { if (!__state || __result is null) return; - MethodInfoWithTypesAndFlagsCache.Get[new(__instance.TypeHandle, bindingAttr, ToLowerIfNeededForBindingFlags(name, bindingAttr), new(types))] = __result; + UpdateCache(__instance, name, types, bindingAttr, __result); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Type __instance, string name, Type[] types, BindingFlags bindingAttr, + MethodInfo __result) + => MethodInfoWithTypesAndFlagsCache.GetExistingReference(__instance.TypeHandle, bindingAttr, + ToLowerIfNeededForBindingFlags(name, bindingAttr), + new(types)).Info = __result; } public class GetConstructor_Patch : FishPatch { - public override string Description => "Caches GetConstructor lookups"; - public override Expression TargetMethod => () => default(Type)!.GetConstructor(default, null, null, null); + public override string Description { get; } = "Caches GetConstructor lookups"; + + public override Expression TargetMethod { get; } + = static () => default(Type)!.GetConstructor(default, null, null!, null); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(Type __instance, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers, ref ConstructorInfo? __result, out bool __state) + public static bool Prefix(Type __instance, BindingFlags bindingAttr, Binder? binder, Type[] types, + ParameterModifier[] modifiers, ref ConstructorInfo? __result, out bool __state) { if (binder != null || modifiers is { Length: > 0 }) { @@ -179,97 +263,418 @@ public class ReflectionCaching : ClassWithFishPatches var key = new ConstructorInfoCache(__instance.TypeHandle, bindingAttr, new(types)); - __result = ConstructorInfoCache.Get.TryGetValue(ref key); + __result = ConstructorInfoCache.Get.GetOrAdd(ref key).Info; return __state = __result is null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Postfix(Type __instance, BindingFlags bindingAttr, Type[] types, ConstructorInfo __result, bool __state) + public static void Postfix(Type __instance, BindingFlags bindingAttr, Type[] types, + ConstructorInfo? __result, bool __state) { if (!__state || __result is null) return; - ConstructorInfoCache.Get[new(__instance.TypeHandle, bindingAttr, new(types))] = __result; + UpdateCache(__instance, bindingAttr, types, __result); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Type __instance, BindingFlags bindingAttr, Type[] types, + ConstructorInfo __result) + => ConstructorInfoCache.GetExistingReference(__instance.TypeHandle, bindingAttr, new(types)).Info + = __result; } public class GetProperty_Patch : FishPatch { - public override string Description => "Caches GetProperty lookups"; - public override Expression TargetMethod => () => default(Type)!.GetProperty(null, default(BindingFlags)); + public override string Description { get; } = "Caches GetProperty lookups"; + + public override Expression TargetMethod { get; } + = static () => default(Type)!.GetProperty(null!, default(BindingFlags)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(Type __instance, string name, BindingFlags bindingAttr, ref PropertyInfo? __result, out bool __state) + public static bool Prefix(Type __instance, string name, BindingFlags bindingAttr, + out PropertyInfo? __result, out bool __state) { - var key = new PropertyInfoCache(__instance.TypeHandle, bindingAttr, ToLowerIfNeededForBindingFlags(name, bindingAttr)); + var key = new PropertyInfoCache(__instance.TypeHandle, bindingAttr, + ToLowerIfNeededForBindingFlags(name, bindingAttr)); - __result = PropertyInfoCache.Get.TryGetValue(ref key); + __result = PropertyInfoCache.Get.GetOrAdd(ref key).Info; return __state = __result is null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Postfix(Type __instance, string name, BindingFlags bindingAttr, PropertyInfo __result, bool __state) + public static void Postfix(Type __instance, string name, BindingFlags bindingAttr, PropertyInfo? __result, + bool __state) { if (!__state || __result is null) return; - PropertyInfoCache.Get[new(__instance.TypeHandle, bindingAttr, ToLowerIfNeededForBindingFlags(name, bindingAttr))] = __result; + UpdateCache(__instance, name, bindingAttr, __result); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(Type __instance, string name, BindingFlags bindingAttr, + PropertyInfo __result) + => PropertyInfoCache.GetExistingReference(__instance.TypeHandle, bindingAttr, + ToLowerIfNeededForBindingFlags(name, bindingAttr)).Info + = __result; + } + + public class GetFullName : FishPatch + { + public override string? Description { get; } + = "Normally averages at over 1000ns per call. Caching makes this about 30 times faster, including " + + "Harmony overhead. Type.Name is similarly slow too, but unfortunately can't be patched as it's an " + + "internal call. This has a relatively large load time impact."; + // https://github.com/Unity-Technologies/mono/blob/unity-2019.4-mbe/mono/metadata/icall.c#L2737-L2754 + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(RuntimeType), nameof(RuntimeType.FullName)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(RuntimeType __instance, ref string? __result, out bool __state) + { + ref var cache = ref TypeFullNameCache.GetOrAddReference(__instance.TypeHandle); + + if (!cache.Cached && !TryGetFromCentralCache(__instance, ref cache)) + return __state = true; + + __result = cache.Name; + return __state = false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool TryGetFromCentralCache(RuntimeType instance, + ref TypeFullNameCacheValue cache) + { + lock (_lock) + { + ref var centralCache + = ref TypeFullNameCache.GetDirectly.GetOrAddReference(instance.TypeHandle); + cache.Name = centralCache.Name; + // cache.Cached = centralCache.Cached; + } + + return cache.Cached; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Postfix(RuntimeType __instance, string? __result, bool __state) + { + if (!__state) + return; + + UpdateCache(__instance, __result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(RuntimeType __instance, string? __result) + { + ref var cache = ref TypeFullNameCache.GetExistingReference(__instance.TypeHandle); + cache.Name = __result; + // cache.Cached = 1; + + lock (_lock) + { + ref var centralCache + = ref TypeFullNameCache.GetDirectly.GetReference(__instance.TypeHandle); + centralCache.Name = __result; + // centralCache.Cached = 1; + } + } + + private static object _lock = new(); } // probably never actually needed [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string ToLowerIfNeededForBindingFlags(string name, BindingFlags bindingAttr) => (bindingAttr & BindingFlags.IgnoreCase) != 0 ? name.ToLowerInvariant() : name; + private static string ToLowerIfNeededForBindingFlags(string name, BindingFlags bindingAttr) + => (bindingAttr & BindingFlags.IgnoreCase) != 0 ? name.ToLowerInvariant() : name; + + public record struct FieldInfoCacheValue + { + public FieldInfo? Info; + } + + public record struct MethodInfoCacheValue + { + public MethodInfo? Info; + } + + public record struct ConstructorInfoCacheValue + { + public ConstructorInfo? Info; + } + + public record struct PropertyInfoCacheValue + { + public PropertyInfo? Info; + } + + public record struct TypeFullNameCacheValue + { + private object? _value; + + public TypeFullNameCacheValue() => _value = typeof(void); + + public string? Name + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Unsafe.As(_value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => _value = value; + } + + public bool Cached + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _value is not MemberInfo; + } + } } - public struct ByValueComparableArray : IEquatable> + public class MonoCustomAttrs + { + public class GetCustomAttributes : FishPatch + { + public override string? Description { get; } + = "Caches attributes for reflection lookups. Decent load time improvement."; + + public override Delegate? TargetMethodGroup { get; } + = (Func)global::System.MonoCustomAttrs + .GetCustomAttributes; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Prefix(ICustomAttributeProvider obj, Type attributeType, bool inherit, + out object[]? __result, out CustomAttributeState __state) + { + var key = new CustomAttributeCache(obj, (__state.AttributeType = attributeType).TypeHandle, + __state.Inherit = inherit); + + ref var cache = ref CustomAttributeCache.GetOrAddReference(in key); + if (cache.Attributes is null) + TryGetFromCentralCache(ref key, ref cache); + + return __state.State = (__result = cache.Attributes) is null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void TryGetFromCentralCache(ref CustomAttributeCache key, + ref CustomAttributeCacheValue cache) + { + lock (_lock) + cache.Attributes = CustomAttributeCache.GetDirectly.GetOrAdd(ref key).Attributes; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Postfix(ICustomAttributeProvider obj, object[]? __result, + in CustomAttributeState __state) + { + if (!__state.State || __result is null) + return; + + UpdateCache(obj, __state, __result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(ICustomAttributeProvider obj, in CustomAttributeState __state, + object[]? __result) + { + var key = new CustomAttributeCache(obj, __state.AttributeType.TypeHandle, __state.Inherit); + + CustomAttributeCache.GetExistingReference(in key).Attributes = __result; + + lock (_lock) + CustomAttributeCache.GetDirectly.GetReference(ref key).Attributes = __result; + } + + private static object _lock = new(); + } + + public record struct CustomAttributeCacheValue + { + public object[]? Attributes; + } + + public record struct CustomAttributeState + { + public bool State, Inherit; + public Type AttributeType; + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public record struct ByValueComparableArray where T : class { + public byte Length; + private int _hashCode; public T[] Array; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ByValueComparableArray(T[] array) { Guard.IsNotNull(array); + Guard.IsLessThanOrEqualTo(array.Length, byte.MaxValue); Array = array; + Length = (byte)array.Length; + _hashCode = HashCode.Combine((int)Length, Length > 0 && Array[0] is { } item ? item.GetHashCode() : 0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ByValueComparableArray other) { - var length = Array.Length; - if (length != other.Array.Length) + if (Length != other.Length) return false; - for (var i = 0; i < length; i++) + for (var i = 0; i < Length; i++) { if (!Array[i].Equals(other.Array[i])) return false; } + return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) => obj is ByValueComparableArray array && Equals(array); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(Array.Length, Array.Length > 0 && Array[0] is { } item ? item.GetHashCode() : 0); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(ByValueComparableArray left, ByValueComparableArray right) => left.Equals(right); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(ByValueComparableArray left, ByValueComparableArray right) => !(left == right); + public override int GetHashCode() + => _hashCode; } -#if eee == false + public class ActivatorPatches + { + public class CreateInstance_Type : FishPatch + { + public override string? Description { get; } + = "Optimizes the parameterless Activator.CreateInstance method by invoking through specialized cached " + + "delegates"; + + public override Delegate? TargetMethodGroup { get; } = (Func)Activator.CreateInstance; + + public static bool Prefix(Type type, ref object __result) + { + ref var cache = ref ActivatorCache.GetOrAddReference(type.TypeHandle); + + if (cache.Dirty + && !ActivatorCache.UpdateAsyncCache>(ref cache, + type.TypeHandle, type)) + { + return true; + } + + __result = cache.Result!(); + return false; + } + } + + // public class CreateInstance_Type_Args : FishPatch + // { + // public override Delegate? TargetMethodGroup { get; } + // = (Func)Activator.CreateInstance; + // } + + public record struct ActivatorCacheValue : IAsyncCacheable> + { + public Func? Result { get; set; } + public Task>? Task { get; set; } + public bool Dirty => Result == null; + + public ValueTask?> MakeResultAsync(RuntimeTypeHandle key, Type type) + => new(Reflection.CreateConstructorDelegate>(type)); + } + + // public record struct ActivatorWithArgumentsCacheValue : IAsyncCacheable + // { + // public MethodBasePatches.MethodInvoker[]? Result { get; set; } + // public Task? Task { get; set; } + // public bool Dirty => Result == null; + // + // public async ValueTask MakeResultAsync( + // ActivatorWithArgumentsCache key, Type type) + // { + // var constructors = type.GetConstructors(); + // var methodInvokerTasks = new List>(); + // + // foreach (var constructor in constructors) + // { + // var parameters = constructor.GetParameters(); + // + // if (parameters.Length != key.Second.Length) + // continue; + // + // var parametersAreAssignable = true; + // for (var i = 0; i < parameters.Length; i++) + // { + // if (parameters[i].ParameterType.IsAssignableFrom(key.Second.Array[i])) + // parametersAreAssignable = false; + // } + // + // if (!parametersAreAssignable) // TODO: find the best constructor + // continue; + // + // methodInvokerTasks.Add(MethodInvokers + // .RequestFromCacheAsync(constructor.MethodHandle, constructor)); + // } + // + // var methodInvokers = new MethodBasePatches.MethodInvoker[methodInvokerTasks.Count]; + // for (var i = 0; i < methodInvokerTasks.Count; i++) + // methodInvokers[i] = await methodInvokerTasks[i].ConfigureAwait(false); + // + // return methodInvokers; + // } + // } + } + + // public static class Test // TODO: Toggle this when actually testing + // { + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static bool GetValue(FieldInfo __instance, object? obj, ref object? __result) + // { + // ref var cache = ref FieldGetters.GetOrAddReference(__instance.FieldHandle); + // + // if (cache.Dirty) + // cache.Result = FieldInfoPatches.MakeGetterDelegate(__instance); + // + // __result = cache.Result!(obj); + // return false; + // } + // + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static bool SetValue(FieldInfo __instance, object? obj, object? value) + // { + // ref var cache = ref FieldSetters.GetOrAddReference(__instance.FieldHandle); + // + // if (cache.Dirty) + // cache.Result = FieldInfoPatches.MakeSetterDelegate(__instance); + // + // cache.Result!(obj, value); + // return false; + // } + // + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static bool Invoke(MethodBase __instance, object? obj, object[]? parameters, ref object? __result) + // { + // ref var cache = ref MethodInvokers.GetOrAddReference(__instance.MethodHandle); + // + // if (cache.Dirty) + // cache.Result = MethodBasePatches.MakeInvokeDelegate(__instance); + // + // __result = cache.Result!(obj, parameters); + // return false; + // } + // } + public class FieldInfoPatches { public class GetValue_Patch : FishPatch { - public override string Description => "Optimizes the GetValue method by invoking it through specialized cached delegates"; + public override string Description { get; } + = "Optimizes the GetValue method by invoking it through specialized cached delegates"; //public GetValue_Patch() //{ @@ -280,120 +685,64 @@ public class ReflectionCaching : ClassWithFishPatches // Log.Message(types.ToStringSafeEnumerable()); //} - public override MethodBase TargetMethodInfo => Reflection.MethodInfo("mscorlib", "System.Reflection.MonoField", nameof(FieldInfo.GetValue)); + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(MonoField), nameof(MonoField.GetValue))!; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(FieldInfo __instance, object obj, ref object __result) + public static bool Prefix(FieldInfo __instance, object? obj, ref object? __result) { - ref var cache = ref FieldGetters.Get.TryGetReferenceUnsafe(__instance.FieldHandle); + ref var cache = ref FieldGetters.GetOrAddReference(__instance.FieldHandle); - if (!Unsafe.IsNullRef(ref cache)) + if (cache.Dirty + && !FieldGetters.UpdateAsyncCache(ref cache, + __instance.FieldHandle, __instance)) { - if (cache.func != null || UpdateGetterCache(ref cache, __instance)) - { - __result = cache.func!(obj); - return false; - } return true; } - else - { - return AddCache(__instance); - } - } - private static bool AddCache(FieldInfo __instance) - { - FieldGetters.Get.Add(__instance.FieldHandle, new()); - return true; + __result = cache.Result!(obj); + return false; } } public class SetValue_Patch : FishPatch { - public override string Description => "Optimizes the SetValue method by invoking it through specialized cached delegates"; + public override string Description { get; } + = "Optimizes the SetValue method by invoking it through specialized cached delegates"; - public override Expression TargetMethod => () => default(FieldInfo)!.SetValue(null, null); + public override Expression TargetMethod { get; } + = static () => default(FieldInfo)!.SetValue(null, null); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(FieldInfo __instance, object obj, object value) + public static bool Prefix(FieldInfo __instance, object? obj, object? value) { - ref var cache = ref FieldSetters.Get.TryGetReferenceUnsafe(__instance.FieldHandle); + ref var cache = ref FieldSetters.GetOrAddReference(__instance.FieldHandle); - if (!Unsafe.IsNullRef(ref cache)) + if (cache.Dirty + && !FieldSetters.UpdateAsyncCache(ref cache, + __instance.FieldHandle, __instance)) { - if (cache.action != null || UpdateSetterCache(ref cache, __instance)) - { - cache.action!(obj, value); - return false; - } return true; } - else - { - return AddCache(__instance); - } - } - private static bool AddCache(FieldInfo __instance) - { - FieldSetters.Get.Add(__instance.FieldHandle, new()); - return true; + cache.Result!(obj, value); + return false; } } - public static bool UpdateGetterCache(ref FieldInfoGetterCache cache, FieldInfo instance) - { - cache.callCount++; - - if (cache.callCount == 300) - cache.func = MakeGetterDelegate(instance); - else if (cache.callCount > 300) - return false; - - FieldGetters.Get[instance.FieldHandle] = cache; - - return cache.func != null; - } - - public static bool UpdateSetterCache(ref FieldInfoSetterCache cache, FieldInfo instance) - { - cache.callCount++; - - if (cache.callCount == 300) - cache.action = MakeSetterDelegate(instance); - else if (cache.callCount > 300) - return false; - - FieldSetters.Get[instance.FieldHandle] = cache; - - return cache.action != null; - } - - private static FieldGetter? MakeGetterDelegate(FieldInfo info) + public static FieldGetter? MakeGetterDelegate(FieldInfo info) { try { - var dm = new DynamicMethod($"FieldGetter_{info.Name}", typeof(object), new[] { typeof(object) }, info.DeclaringType, true); + var dm = new DynamicMethod($"FieldGetter_{info.Name}", typeof(object), new[] { typeof(object) }, + GetOwnerType(info), true); var il = dm.GetILGenerator(); - if (!info.IsStatic) - { - il.Emit(FishTranspiler.This); + TryEmitFieldInstanceArgument(il, info); - il.Emit(info.DeclaringType.IsValueType - ? FishTranspiler.Unbox(info.DeclaringType) - : FishTranspiler.Cast(info.DeclaringType)); - } - - il.Emit( - /*info.DeclaringType.IsEnum - && info.DeclaringType.GetEnumUnderlyingType() is var enumType - ? enumType == typeof(long) || enumType == typeof(ulong) - ? FishTranspiler.Constant((long)info.GetValue(info.DeclaringType)) - : FishTranspiler.Constant((int)info.GetValue(info.DeclaringType)) - :*/ info.IsLiteral && !info.IsInitOnly ? FishTranspiler.Constant(info.GetRawConstantValue()) - : FishTranspiler.Field(info)); + il.Emit(info is { IsLiteral: true, IsInitOnly: false } + ? FishTranspiler.Constant(info.GetRawConstantValue()) + : FishTranspiler.Field(info)); if (info.FieldType.IsValueType) il.Emit(FishTranspiler.Box(info.FieldType)); @@ -404,39 +753,41 @@ public class ReflectionCaching : ClassWithFishPatches } catch (Exception ex) { - Log.Error($"PerformanceFish failed to generate an optimized delegate for {GetFieldDescription(info)}. Reverting to default behaviour instead.\n{ex}"); + Log.Error($"PerformanceFish failed to generate an optimized delegate for { + info.FullDescription()}. Reverting to default behaviour instead.\n{ex}\n{ + new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}"); return null; } } - private static FieldSetter? MakeSetterDelegate(FieldInfo info) + public static FieldSetter? MakeSetterDelegate(FieldInfo info) { try { - var dm = new DynamicMethod($"FieldSetter_{info.Name}", typeof(void), new[] { typeof(object), typeof(object) }, info.DeclaringType, true); + var dm = new DynamicMethod($"FieldSetter_{info.Name}", typeof(void), + new[] { typeof(object), typeof(object) }, GetOwnerType(info), true); var il = dm.GetILGenerator(); - if (!info.IsStatic) - { - il.Emit(FishTranspiler.This); - - il.Emit(info.DeclaringType.IsValueType - ? FishTranspiler.Unbox(info.DeclaringType) - : FishTranspiler.Cast(info.DeclaringType)); - } + TryEmitFieldInstanceArgument(il, info); il.Emit(FishTranspiler.Argument(1)); if (info.FieldType.IsValueType) { - //il.Emit(FishTranspiler.UnboxAny(info.FieldType)); - il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), info.FieldType.IsNullable() ? nameof(UnboxNullableSafely) : nameof(UnboxSafely), + il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), + info.FieldType.IsNullable() ? nameof(UnboxNullableSafely) : nameof(UnboxSafely), new[] { typeof(object) }, - new[] { info.FieldType.IsNullable() ? Nullable.GetUnderlyingType(info.FieldType) : info.FieldType })); + new[] + { + info.FieldType.IsNullable() // TODO: verify that this is actually correct + ? Nullable.GetUnderlyingType(info.FieldType) + : info.FieldType + })); } else { - il.Emit(FishTranspiler.Cast(info.FieldType)); + il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastOrConvert), + generics: new[] { info.FieldType })); } il.Emit(FishTranspiler.StoreField(info)); @@ -447,99 +798,118 @@ public class ReflectionCaching : ClassWithFishPatches } catch (Exception ex) { - Log.Error($"PerformanceFish failed to generate an optimized delegate for {GetFieldDescription(info)}. Reverting to default behaviour instead.\n{ex}"); + Log.Error($"PerformanceFish failed to generate an optimized delegate for { + info.FullDescription()}. Reverting to default behaviour instead.\n{ex}\n{ + new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}"); return null; } } - public struct FieldInfoGetterCache + private static void TryEmitFieldInstanceArgument(ILGenerator il, FieldInfo info) { - public FieldGetter? func; - public int callCount; + if (!info.IsStatic) + EmitInstanceArgument(il, info); } - public struct FieldInfoSetterCache + public record struct FieldInfoGetterCache : IAsyncCacheable { - public FieldSetter? action; - public int callCount; + public FieldGetter? Result { get; set; } + public Task? Task { get; set; } + + public bool Dirty => Result == null; + + public ValueTask MakeResultAsync(RuntimeFieldHandle key, FieldInfo second) + => new(MakeGetterDelegate(second)); } - public delegate object FieldGetter(object obj); + public record struct FieldInfoSetterCache : IAsyncCacheable + { + public FieldSetter? Result { get; set; } + public Task? Task { get; set; } - public delegate void FieldSetter(object obj, object value); + public bool Dirty => Result == null; + + public ValueTask MakeResultAsync(RuntimeFieldHandle key, FieldInfo second) + => new(MakeSetterDelegate(second)); + + } + + public delegate object? FieldGetter(object? obj); + + public delegate void FieldSetter(object? obj, object? value); } - private static string GetFieldDescription(FieldInfo info) => $"{(info.IsPublic ? "public" : "private")} {(info.IsLiteral && !info.IsInitOnly ? "const " : info.IsStatic ? "static " : "")} {info.FieldType.FullDescription()} {info.DeclaringType.FullDescription()}:{info.Name}"; - public class MethodBasePatches { public class Invoke_Patch : FishPatch { - public override string Description => "Optimizes the Invoke method by invoking it through specialized cached delegates"; + public override bool DefaultState => false; // TODO: fix - public override Expression TargetMethod => () => default(MethodBase)!.Invoke(null, null); + public override string Description { get; } + = "Optimizes the Invoke method by compiling specialized cached delegates. Disabled by default due to " + + "issues with various mods, like RIMMSqol and Performance Optimizer"; + + public override Expression TargetMethod { get; } + = static () => default(MethodBase)!.Invoke(null, null); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(MethodBase __instance, object obj, object[] parameters, ref object __result) + public static bool Prefix(MethodBase __instance, object? obj, object[]? parameters, ref object? __result) { - if (__instance is not MethodInfo method) - return true; + ref var cache = ref MethodInvokers.GetOrAddReference(__instance.MethodHandle); - ref var cache = ref MethodInvokers.Get.TryGetReferenceUnsafe(method.MethodHandle); + // if (cache.Dirty) // TODO: remove + // { + // if (OnStartup.State.Initialized) + // { + // Log.Message($"Called Invoke for {__instance.FullDescription()} with {obj.ToStringSafe()}, { + // parameters?.Length.ToStringSafe() ?? "null"} parameters"); + // } + // + // if (!MethodInvokers.UpdateAsyncCache(ref cache, + // __instance.MethodHandle, __instance)) + // { + // return true; + // } + // } - if (!Unsafe.IsNullRef(ref cache)) + if (cache.Dirty + && !MethodInvokers.UpdateAsyncCache(ref cache, + __instance.MethodHandle, __instance)) { - if (cache.func != null || UpdateCache(ref cache, method)) - { - __result = cache.func!(obj, parameters); - return false; - } return true; } - else - { - return AddCache(method); - } - } - private static bool AddCache(MethodInfo method) - { - MethodInvokers.Get.Add(method.MethodHandle, new()); - return true; - } - } - - public static bool UpdateCache(ref MethodInvokerCache cache, MethodInfo instance) - { - cache.callCount++; - if (cache.callCount == 300) - cache.func = MakeInvokeDelegate(instance); - else if (cache.callCount > 300) + __result = cache.Result!(obj, parameters); return false; - - MethodInvokers.Get[instance.MethodHandle] = cache; - - return cache.func != null; + } } - // HarmonyLib.MethodInvoker can't handle arrays with null values when used with ByRef value types. That aside this is near identical. - public static MethodInvoker? MakeInvokeDelegate(MethodInfo info) + // Similar to HarmonyLib.MethodInvoker, but correctly handles a couple of edge cases Harmony would throw on. + public static MethodInvoker? MakeInvokeDelegate(MethodBase methodBase) { + if (methodBase.DeclaringType is null && methodBase.Name.StartsWith("PadMethod")) + return null; // padding methods generated by harmony for linux + try { - //DebugLog.Message($"Making delegate for {(info.IsStatic ? "static" : "")} {info.DeclaringType} {info} with arguments {info.GetParameters().ToStringSafeEnumerable()}"); + var methodInfo = methodBase as MethodInfo; + if (methodInfo is { ReturnType.IsByRef: true }) + return null; // how to box a ref return? + + var constructorInfo = methodBase as ConstructorInfo; - var dm = new DynamicMethod($"MethodInvoker_{info.Name}", typeof(object), new[] { typeof(object), typeof(object[]) }, info.DeclaringType, true); + if (methodInfo is null && constructorInfo is null) + return null; + + var dm = new DynamicMethod($"MethodInvoker_{methodBase.Name}", typeof(object), + new[] { typeof(object), typeof(object[]) }, + GetOwnerType(methodBase), true); var il = dm.GetILGenerator(); - var parameters = info.GetParameters(); + var parameters = methodBase.GetParameters(); - if (!info.IsStatic) - { - il.Emit(FishTranspiler.This); - if (info.DeclaringType.IsValueType) - il.Emit(FishTranspiler.UnboxAny(info.DeclaringType)); - } + if (methodInfo != null && !methodBase.IsStatic) + EmitInstanceArgument(il, methodBase); for (var i = 0; i < parameters.Length; i++) { @@ -549,37 +919,15 @@ public class ReflectionCaching : ClassWithFishPatches var parameterType = parameters[i].ParameterType; if (parameterType.IsByRef) - { - parameterType = parameterType.GetElementType(); - - il.Emit(FishTranspiler.LoadElementAddress()); - - il.Emit(parameterType.IsValueType - ? FishTranspiler.Call(typeof(ReflectionCaching), nameof(UnboxRefSafely), - new[] { typeof(object).MakeByRefType() }, - new[] { parameterType.IsNullable() ? Nullable.GetUnderlyingType(parameterType) : parameterType }) - : FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastRefSafely), - new[] { typeof(object).MakeByRefType() }, - new[] { typeof(object), parameterType })); - } + EmitInvokeByRefParameterLoad(il, parameterType); else - { - il.Emit(FishTranspiler.LoadElement()); - - il.Emit(parameterType.IsValueType - ? FishTranspiler.Call(typeof(ReflectionCaching), parameterType.IsNullable() ? nameof(UnboxNullableSafely) : nameof(UnboxSafely), - new[] { typeof(object) }, - new[] { parameterType.IsNullable() ? Nullable.GetUnderlyingType(parameterType) : parameterType }) - : FishTranspiler.As(parameterType)); - } + EmitInvokeParameterLoad(il, parameterType); } - il.Emit(FishTranspiler.Call(info)); - - if (info.ReturnType == typeof(void)) - il.Emit(FishTranspiler.Null); - else if (info.ReturnType.IsValueType) - il.Emit(FishTranspiler.Box(info.ReturnType)); + if (methodInfo != null) + EmitInvokeMethodCall(methodBase, il, methodInfo); + else if (constructorInfo != null) + il.Emit(FishTranspiler.New(constructorInfo)); il.Emit(FishTranspiler.Return); @@ -587,52 +935,173 @@ public class ReflectionCaching : ClassWithFishPatches } catch (Exception ex) { - Log.Error($"PerformanceFish failed to generate an optimized delegate for {info.FullDescription()}. Reverting to default behaviour instead.\n{ex}"); + Log.Warning($"PerformanceFish failed to generate an optimized delegate for { + Reflection.FullDescription(methodBase)}. Reverting to default behaviour instead.\n{ex}\n{ + new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}"); return null; } } - public struct MethodInvokerCache + private static void EmitInvokeMethodCall(MethodBase methodBase, ILGenerator il, MethodInfo methodInfo) { - public MethodInvoker? func; - public int callCount; + il.Emit(FishTranspiler.Call(methodBase)); + + if (!methodInfo.ReturnType.IsValueType) + return; + + il.Emit(methodInfo.ReturnType == typeof(void) + ? FishTranspiler.Null + : FishTranspiler.Box(methodInfo.ReturnType)); } - public delegate object MethodInvoker(object obj, object[] parameters); + private static void EmitInvokeParameterLoad(ILGenerator il, Type parameterType) + { + il.Emit(FishTranspiler.LoadElement()); + + il.Emit(parameterType.IsValueType + ? FishTranspiler.Call(typeof(ReflectionCaching), + parameterType.IsNullable() ? nameof(UnboxNullableSafely) : nameof(UnboxSafely), + new[] { typeof(object) }, + new[] + { + parameterType.IsNullable() + ? Nullable.GetUnderlyingType(parameterType) + : parameterType + }) + : FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastOrConvert), + generics: new[] { parameterType })); + } + + private static void EmitInvokeByRefParameterLoad(ILGenerator il, Type parameterType) + { + parameterType = parameterType.GetElementType()!; + + il.Emit(FishTranspiler.LoadElementAddress()); + + if (parameterType.IsNullable()) + { + il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(VerifyNullableBoxObject), + generics: new[] { Nullable.GetUnderlyingType(parameterType) })); + + il.Emit(FishTranspiler.UnboxAddress(parameterType)); + } + else + { + il.Emit(parameterType.IsValueType + ? FishTranspiler.Call(typeof(ReflectionCaching), nameof(UnboxRefSafely), + // new[] { typeof(object).MakeByRefType() }, + generics: new[] + { + /*parameterType.IsNullable() + ? Nullable.GetUnderlyingType(parameterType) + :*/ parameterType + }) + : FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastRefSafely), + // new[] { typeof(object).MakeByRefType() }, + generics: new[] { parameterType })); + } + } + + public record struct MethodInvokerCache : IAsyncCacheable + { + public MethodInvoker? Result { get; set; } + public Task? Task { get; set; } + public bool Dirty => Result == null; + + public ValueTask MakeResultAsync(RuntimeMethodHandle key, MethodBase method) + => new(MakeInvokeDelegate(method)); + } + + public delegate object? MethodInvoker(object? obj, object[]? parameters); } -#endif + + private static void EmitInstanceArgument(ILGenerator il, MemberInfo info) + { + var declaringType = info.DeclaringType!; + + if (declaringType.IsValueType) + { + il.Emit(FishTranspiler.ArgumentAddress(0)); + il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(UnboxRefSafely), + generics: new[] { declaringType })); + } + else + { + il.Emit(FishTranspiler.This); + il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastOrConvert), + generics: new[] { declaringType })); + } + } + + private static Type GetOwnerType(MemberInfo info) + => (info.DeclaringType ?? info.ReflectedType ?? typeof(void)) is var type + // ReSharper disable once MergeIntoPattern + && type.IsInterface ? typeof(InterfaceCache) + : type.IsArray ? typeof(ArrayCache) + : type; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref TTo CastRefSafely(ref TFrom from) - => ref from is TTo or null - ? ref Unsafe.As(ref from) + [SecuritySafeCritical] + public static ref TTo CastRefSafely(ref object? from) + => ref from is TTo or null + ? ref Unsafe.As(ref from) : ref ThrowInvalidCastException(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref TTo UnboxRefSafely(ref object from) where TTo : struct + [SecuritySafeCritical] + public static ref TTo UnboxRefSafely(ref object? from) where TTo : struct => ref from is TTo - ? ref Unsafe.Unbox(from) - : ref Unsafe.Unbox(from - = from is null - ? default - : FisheryLib.Convert.To((IConvertible)from)); - + ? ref Unsafe.Unbox(from) + : ref Unsafe.Unbox(from + = from is null + ? default + : FisheryLib.Convert.Type(from)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TTo UnboxSafely(object from) where TTo : struct - => from is TTo to - ? to - // : from is IConvertible - : FisheryLib.Convert.To((IConvertible)from); - + public static object? VerifyNullableBoxObject(ref object? from) where TTo : struct + => from is TTo + ? from + : from = from is null + ? default + : FisheryLib.Convert.Type(from); + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TTo? UnboxNullableSafely(object from) where TTo : struct - => from is TTo to - ? to - : from is null - ? null - // : from is IConvertible - : FisheryLib.Convert.To((IConvertible)from); + [SecuritySafeCritical] + public static TTo? CastOrConvert(object? from) + => from is TTo or null + ? Unsafe.As(ref from) + : FisheryLib.Convert.Type(from); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TTo UnboxSafely(object? from) where TTo : struct + => from switch + { + TTo to => to, + null => default, + _ => FisheryLib.Convert.Type(from) + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TTo? UnboxNullableSafely(object? from) where TTo : struct + => from switch + { + TTo to => to, + null => null, + _ => FisheryLib.Convert.Type(from) + }; [DoesNotReturn] private static ref T ThrowInvalidCastException() => throw new InvalidCastException(); + + public static class InterfaceCache + { + // populated with dynamic methods by MethodBasePatches.MakeInvokeDelegate, MakeGetterDelegate and + // MakeSetterDelegate + } + + public static class ArrayCache + { + // populated with dynamic methods by MethodBasePatches.MakeInvokeDelegate, MakeGetterDelegate and + // MakeSetterDelegate + } } \ No newline at end of file diff --git a/Source/PerformanceFish/TextureLoadingPatches.cs b/Source/PerformanceFish/TextureLoadingPatches.cs new file mode 100644 index 0000000..8ff4532 --- /dev/null +++ b/Source/PerformanceFish/TextureLoadingPatches.cs @@ -0,0 +1,194 @@ +// 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 System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; +using PerformanceFish.Pools; +using PerformanceFish.Prepatching; +using RimWorld.IO; + +namespace PerformanceFish; + +public class TextureLoadingPatches : ClassWithFishPrepatches +{ + public const string DDS_EXTENSION = ".dds"; + + public class ModContentLoader_StaticConstructor : FishPrepatch + { + public override string? Description { get; } + = "Adds .dds to the accepted extensions for texture loading. Those files load faster than png, render at " + + "better quality and help prevent memory issues"; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.Constructor(typeof(ModContentLoader<>), searchForStatic: true); + + public static void Postfix() where T : class + => ModContentLoader.AcceptableExtensionsTexture + = ModContentLoader.AcceptableExtensionsTexture.Add(DDS_EXTENSION); + } + + public class ModContentLoader_LoadItem : FishPrepatch + { + public override string? Description { get; } + = "Adds .dds to the accepted extensions for texture loading. Those files load faster than png, render at " + + "better quality and help prevent memory issues"; + + public override MethodBase TargetMethodBase { get; } = methodof(ModContentLoader.LoadItem); + + public static bool Prefix(VirtualFile file, ref LoadedContentItem? __result) where T : class + { + if (typeof(T) != typeof(Texture2D) + || GetFileInfo(file).Extension.Equals(DDS_EXTENSION, StringComparison.OrdinalIgnoreCase) + || !File.Exists(Path.ChangeExtension(file.FullPath, DDS_EXTENSION))) + { + return true; + } + + __result = null; + return false; + } + } + + public class ModContentLoader_LoadTexture : FishPrepatch + { + public override string? Description { get; } + = "Adds .dds to the accepted extensions for texture loading. Those files load faster than png, render at " + + "better quality and help prevent memory issues"; + + public override MethodBase TargetMethodBase { get; } = methodof(ModContentLoader.LoadTexture); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + => ilProcessor.ReplaceBodyWith(LoadTexture); + + public static Texture2D? LoadTexture(VirtualFile file) + { + if (!file.Exists) + return null; + + Texture2D? texture2D; + + var fileInfo = GetFileInfo(file); + var isDds = fileInfo.Extension.Equals(DDS_EXTENSION, StringComparison.OrdinalIgnoreCase); + var hasStoredMipMaps = false; + + if (isDds) + { + texture2D = DDSLoader.LoadDDS(fileInfo.OpenRead(), out hasStoredMipMaps); + + if (!DDSLoader.error.NullOrEmpty()) + LogDDSLoadingError(file); + + if (texture2D is null) + { + isDds = false; + if ((texture2D = LoadTextureUsingFallbackFormat(file)) is null) + return null; + } + } + else + { + var data = file.ReadAllBytes(); + texture2D = new(2, 2, TextureFormat.Alpha8, true); + texture2D.LoadImage(data); + } + + if (!isDds && Prefs.TextureCompression) + texture2D.Compress(true); + + texture2D.name = Path.GetFileNameWithoutExtension(file.Name); + texture2D.filterMode = FilterMode.Trilinear; + texture2D.anisoLevel = 0; + texture2D.mipMapBias = -0.7f; + texture2D.Apply(!hasStoredMipMaps, true); + return texture2D; + } + + public static Texture2D? LoadTextureUsingFallbackFormat(VirtualFile file) + { + if (ReadBytesForFallbackFormat(file) is not { } data) + { + Log.Error($"Could not load texture at {file.FullPath}."); + return null; + } + + var texture2D = new Texture2D(2, 2, TextureFormat.Alpha8, true); + texture2D.LoadImage(data); + return texture2D; + } + + private static byte[]? ReadBytesForFallbackFormat(VirtualFile file) + { + byte[]? data = null; + foreach (var acceptableExtension in ModContentLoader.AcceptableExtensionsTexture) + { + if (acceptableExtension == DDS_EXTENSION) + continue; + + var fallbackPath = Path.ChangeExtension(file.FullPath, acceptableExtension); + if (!File.Exists(fallbackPath)) + continue; + + Log.Warning($"Could not load .dds at {file.FullPath}. Loading as {acceptableExtension} instead."); + data = File.ReadAllBytes(fallbackPath); + break; + } + + return data; + } + + public static void LogDDSLoadingError(VirtualFile file) + => Log.Warning($"DDS error at '{file.FullPath}': {DDSLoader.error}"); + } + + public static FileInfo GetFileInfo(VirtualFile file) => ((FilesystemFile)file).fileInfo; + + public class GetAllFilesForModPatch : FishPrepatch + { + public override string? Description { get; } + = "Adds .dds to the accepted extensions for texture loading. Those files load faster than png, render at " + + "better quality and help prevent memory issues"; + + public override MethodBase TargetMethodBase { get; } = methodof(ModContentPack.GetAllFilesForMod); + + public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module) + { + var index = ilProcessor.instructions.FirstIndexOf(static i + => i.OpCode == OpCodes.Newobj + && i.Operand is MethodReference method + && method.DeclaringType.Name == "Dictionary`2"); + + ilProcessor.instructions[index].Operand = module.ImportReference(AccessTools.Constructor( + typeof(Dictionary), new[] { typeof(IEqualityComparer) })); + ilProcessor.InsertAt(index, OpCodes.Call, AccessTools.PropertyGetter(typeof(StringComparer), + nameof(StringComparer.OrdinalIgnoreCase))); + } + + public static void Postfix(Dictionary __result) + { + using var keysToRemove = new PooledIList>(); + using var sb = new PooledStringBuilder(); + + foreach (var pair in __result) + { + if (pair.Key.EndsWith(DDS_EXTENSION, StringComparison.OrdinalIgnoreCase)) + continue; + + sb.Append(pair.Key); + var extensionLength = pair.Value.Extension.Length; + sb.Remove(pair.Key.Length - extensionLength, extensionLength); + sb.Append(DDS_EXTENSION); + + if (__result.ContainsKey(sb.ToString())) + keysToRemove.List.Add(pair.Key); + + sb.Clear(); + } + + foreach (var key in keysToRemove.List) + __result.Remove(key); + } + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/ThingCompCaching.cs b/Source/PerformanceFish/ThingCompCaching.cs index 9d9e322..1c4685b 100644 --- a/Source/PerformanceFish/ThingCompCaching.cs +++ b/Source/PerformanceFish/ThingCompCaching.cs @@ -1,9 +1,11 @@ -// Copyright (c) 2022 bradson +// 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 IdeoForbidsCache = PerformanceFish.Cache.ByIntRefreshable; +using IdeoForbidsCache + = PerformanceFish.Cache.ByInt; namespace PerformanceFish; @@ -11,11 +13,13 @@ public class ThingCompCaching : ClassWithFishPatches { public class CompAssignableToPawn_Bed_Patch : FishPatch { - public override string Description => "Caches results of CompAssignableToPawn_Bed.IdeoligionForbids"; - public override MethodBase TargetMethodInfo => AccessTools.Method(typeof(CompAssignableToPawn_Bed), nameof(CompAssignableToPawn_Bed.IdeoligionForbids)); + public override string Description { get; } = "Caches results of CompAssignableToPawn_Bed.IdeoligionForbids"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(CompAssignableToPawn_Bed), nameof(CompAssignableToPawn_Bed.IdeoligionForbids)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Prefix(CompAssignableToPawn_Bed __instance, Pawn pawn, ref bool __result, out bool __state) + public static bool Prefix(CompAssignableToPawn_Bed __instance, Pawn? pawn, ref bool __result, out bool __state) { if (pawn is null) { @@ -23,10 +27,10 @@ public class ThingCompCaching : ClassWithFishPatches return true; } - var key = new IdeoForbidsCache(__instance.parent.thingIDNumber, pawn.thingIDNumber); - ref var cache = ref IdeoForbidsCache.Get.TryGetReferenceUnsafe(ref key); + ref var cache + = ref IdeoForbidsCache.GetOrAddReference(new(__instance.parent.thingIDNumber, pawn.thingIDNumber)); - if (Unsafe.IsNullRef(ref cache) || cache.ShouldRefreshNow) + if (cache.Dirty) return __state = true; __result = cache.Result; @@ -39,11 +43,15 @@ public class ThingCompCaching : ClassWithFishPatches if (!__state) return; - IdeoForbidsCache.Get[new(__instance, pawn)] = new(__instance, __result, pawn); + UpdateCache(__instance, pawn, __result); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UpdateCache(CompAssignableToPawn_Bed __instance, Pawn pawn, bool __result) + => IdeoForbidsCache.GetExistingReference(__instance, pawn).Update(__instance, pawn, __result); } - public struct IdeoForbidsCacheValue : Cache.IIsRefreshable + public record struct IdeoForbidsCacheValue { public bool Result; private int _nextRefreshTick; @@ -51,26 +59,24 @@ public class ThingCompCaching : ClassWithFishPatches private int _directRelationsVersion; private List _assignedPawns; private List _directRelations; - - public IdeoForbidsCacheValue(CompAssignableToPawn_Bed comp, bool result, Pawn pawn) + + public void Update(CompAssignableToPawn_Bed comp, Pawn pawn, bool result) { _directRelations = pawn.relations.DirectRelations; _assignedPawns = comp.assignedPawns; - _directRelationsVersion = _directRelations.Version(); + _directRelationsVersion = _directRelations._version; Result = result; - _assignedPawnsVersion = _assignedPawns.Version(); - _nextRefreshTick = Current.Game.tickManager.TicksGame + 1024 + Math.Abs(Rand.Int % 512); + _assignedPawnsVersion = _assignedPawns._version; + _nextRefreshTick = TickHelper.Add(1024, pawn.thingIDNumber); } - public bool ShouldRefreshNow + + public bool Dirty { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get - => _nextRefreshTick < Current.Game.tickManager.TicksGame - || _assignedPawnsVersion != _assignedPawns.Version() - || _directRelationsVersion != _directRelations.Version(); - - set => _nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 2048 + Math.Abs(Rand.Int % 1024); + => TickHelper.Past(_nextRefreshTick) + || _assignedPawnsVersion != _assignedPawns._version + || _directRelationsVersion != _directRelations._version; } - - public IdeoForbidsCacheValue SetNewValue(IdeoForbidsCache key) => throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Source/PerformanceFish/ThingFilterPatches.cs b/Source/PerformanceFish/ThingFilterPatches.cs index c38cd07..af4553b 100644 --- a/Source/PerformanceFish/ThingFilterPatches.cs +++ b/Source/PerformanceFish/ThingFilterPatches.cs @@ -1,63 +1,85 @@ -// Copyright (c) 2022 bradson +// 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/. -global using AllowedDefsListCache = PerformanceFish.Cache.ByReferenceRefreshable; +global using AllowedDefsListCache + = PerformanceFish.Cache.ByReference; +using PerformanceFish.Cache; namespace PerformanceFish; + public class ThingFilterPatches : ClassWithFishPatches { public override bool RequiresLoadedGameForPatching => true; /*public class Allows_Patch : FishPatch { - public override Expression TargetMethod => () => new ThingFilter().Allows(null as Thing); + public override Expression TargetMethod { get; } = static () => new ThingFilter().Allows(null as Thing); + public static bool Prefix(ThingFilter __instance, ref bool __result, Thing t, out bool __state) { - if (!Cache.ByReferenceRefreshable.TryGetValue(t, __instance, out var cache)) + if (!Cache.ByReference.Get.TryGetValue(new(t, __instance), + out var cache) + || cache.Dirty) return __state = true; __result = cache.allows; return __state = false; } + public static void Postfix(ThingFilter __instance, bool __result, Thing t, bool __state) { if (!__state) return; - ThingFilterAllowsCache[new(t, __instance)] = new() { ShouldRefreshNow = false, allows = __result }; + ThingFilterAllowsCache[new(t, __instance)] = new() { Dirty = false, allows = __result }; } - public static Dictionary, FishThingFilterInfo> ThingFilterAllowsCache => Cache.ByReferenceRefreshable.Get; - public struct FishThingFilterInfo : ICanRefresh, FishThingFilterInfo> + + public static Dictionary, FishThingFilterInfo> + ThingFilterAllowsCache + => Cache.ByReference.Get; + + public record struct FishThingFilterInfo { public int nextRefreshTick; public bool allows; - public bool ShouldRefreshNow + public bool Dirty { get => nextRefreshTick < Current.Game.tickManager.TicksGame; - set => nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); + set + => nextRefreshTick + = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); } - - public FishThingFilterInfo SetNewValue(Cache key) => throw new NotImplementedException(); } }*/ public class AllowedThingDefs_Patch : FishPatch { - public override string Description => "Small optimization. Returns a list instead of hashset for better performance when enumerating"; - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(ThingFilter), nameof(ThingFilter.AllowedThingDefs)); - public static IEnumerable Replacement(ThingFilter instance) => AllowedDefsListCache.GetValue(instance).defs; - public static CodeInstructions Transpiler(CodeInstructions codes) => Reflection.GetCodeInstructions(Replacement); + public override string Description { get; } + = "Small optimization. Returns a list instead of hashset for better performance when enumerating"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(ThingFilter), nameof(ThingFilter.AllowedThingDefs)); + + public static IEnumerable Replacement(ThingFilter instance) + => AllowedDefsListCache.GetAndCheck(instance).Defs; + + public static CodeInstructions Transpiler(CodeInstructions codes) + => Reflection.GetCodeInstructions(Replacement); } public class AllStorableThingDefs_Patch : FishPatch { - public override string Description => "Small optimization"; - public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(ThingFilter), nameof(ThingFilter.AllStorableThingDefs)); + public override string Description { get; } = "Small optimization"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(ThingFilter), nameof(ThingFilter.AllStorableThingDefs)); + public static IEnumerable Replacement() { - var allDefs = DefDatabase.AllDefsListForReading; + var allDefs = DefDatabase.defsList; var count = allDefs.Count; for (var i = 0; i < count; i++) { @@ -65,99 +87,152 @@ public class ThingFilterPatches : ClassWithFishPatches yield return allDefs[i]; } } + public static CodeInstructions Transpiler(CodeInstructions codes) => Reflection.MakeReplacementCall(Replacement); } public class CopyAllowancesFrom_Patch : FirstPriorityFishPatch { - public override string Description => "Used by the WorkGiver_DoBill patch to allow for faster refreshing of its cache"; - public override Expression TargetMethod => () => default(ThingFilter)!.CopyAllowancesFrom(null); + public override string Description { get; } + = "Used by the WorkGiver_DoBill patch to allow for faster refreshing of its cache"; + + public override Expression TargetMethod { get; } + = static () => default(ThingFilter)!.CopyAllowancesFrom(null); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Prefix(ThingFilter __instance) => AllowedDefsListCache.GetValue(__instance).defs.Clear(); + public static void Prefix(ThingFilter __instance) + => AllowedDefsListCache.GetAndCheck(__instance).Defs.Clear(); } public class SetAllow_Patch : FishPatch { - public override string Description => "Used by the WorkGiver_DoBill patch to allow for faster refreshing of its cache"; - public override Expression TargetMethod => () => default(ThingFilter)!.SetAllow(default(ThingDef), default); + public override string Description { get; } + = "Used by the WorkGiver_DoBill patch to allow for faster refreshing of its cache"; + + public override Expression TargetMethod { get; } + = static () => default(ThingFilter)!.SetAllow(default(ThingDef), default); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Prefix(ThingFilter __instance, ThingDef thingDef, bool allow) + public static void Prefix(ThingFilter __instance, ThingDef? thingDef, bool allow) { - if (__instance.allowedDefs is null) + if (__instance.allowedDefs is null || thingDef is null) return; - ref var cache = ref AllowedDefsListCache.GetValue(__instance); + ref var cache + = ref AllowedDefsListCache.GetAndCheck(__instance, thingDef.shortHash); if (allow) { - if (!__instance.allowedDefs.Contains(thingDef)) - { - var index = ~Array.BinarySearch(cache.defs._items, 0, cache.defs.Count, thingDef, Comparer); - //var index = ~cache.defs.BinarySearch(thingDef, Comparer); - if (index < 0) - index = ~index; - cache.defs.Insert(index, thingDef); - } + if (__instance.allowedDefs.Contains(thingDef)) + return; + + var index = ~Array.BinarySearch(cache.Defs._items, 0, cache.Defs.Count, thingDef, Comparer); + if (index < 0) + index = ~index; + cache.Defs.Insert(index, thingDef); } else { if (__instance.allowedDefs.Contains(thingDef)) - cache.defs.Remove(thingDef); + cache.Defs.Remove(thingDef); } } private static Comparer Comparer { get; set; } = Comparer.Create(ComparerMethod); - private static int ComparerMethod(ThingDef a, ThingDef b) - => a.BaseMarketValue.CompareTo(b.BaseMarketValue); + private static int ComparerMethod(ThingDef a, ThingDef b) => a.BaseMarketValue.CompareTo(b.BaseMarketValue); } public class SetDisallowAll_Patch : FishPatch { - public override string Description => "Used by the WorkGiver_DoBill patch to allow for faster refreshing of its cache"; - public override Expression TargetMethod => () => default(ThingFilter)!.SetDisallowAll(null, null); + public override string Description { get; } + = "Used by the WorkGiver_DoBill patch to allow for faster refreshing of its cache"; + + public override Expression TargetMethod { get; } + = static () => default(ThingFilter)!.SetDisallowAll(null, null); + [HarmonyPriority(Priority.Last)] public static void Postfix(ThingFilter __instance) => ForceSynchronizeCache(__instance); } public class SetAllowAll_Patch : FishPatch { - public override string Description => "Used by the WorkGiver_DoBill patch to allow for faster refreshing of its cache"; - public override Expression TargetMethod => () => default(ThingFilter)!.SetAllowAll(null, default); + public override string Description { get; } + = "Used by the WorkGiver_DoBill patch to allow for faster refreshing of its cache"; + + public override Expression TargetMethod { get; } + = static () => default(ThingFilter)!.SetAllowAll(null, default); + [HarmonyPriority(Priority.Last)] public static void Postfix(ThingFilter __instance) => ForceSynchronizeCache(__instance); } - public static void ForceSynchronizeCache(ThingFilter filter) => AllowedDefsListCache.GetValueAndRefreshNow(filter); - - public struct AllowedDefsListCacheValue : Cache.IIsRefreshable + public class BestThingRequest_Patch : FishPatch { - public List defs; - private int _nextRefreshTick; - public bool ShouldRefreshNow + public override string? Description { get; } + = "RimWorld is normally only capable of creating ThingRequests either for predefined groups or for single " + + "defs. This patch makes ThingFilters return requests for multiple defs, improving performance for " + + "cases where the fallback group would have to be something like all haulables. Refueling benefits a " + + "lot from this. Requires the patch on ListerThings.ThingsMatching to function."; + + public override bool Enabled + { + set + { + if (base.Enabled == value) + return; + + Get().Enabled = base.Enabled = value; + } + } + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.PropertyGetter(typeof(ThingFilter), nameof(ThingFilter.BestThingRequest)); + + public static void Postfix(ThingFilter __instance, ref ThingRequest __result) + { + if (__result.group is not (ThingRequestGroup.HaulableEver or ThingRequestGroup.Everything) + || __instance.allowedDefs.Count > 16) + { + return; + } + + __result.group = ThingRequestGroup.Undefined; + __result.singleDef = Unsafe.As(__instance.allowedDefs); + } + } + + public static void ForceSynchronizeCache(ThingFilter filter) + => AllowedDefsListCache.GetOrAddReference(filter).Update(ref filter); + + public record struct AllowedDefsListCacheValue : ICacheable, ICacheable + { + public List Defs = new(); + private int _nextRefreshTick = -2; + + public bool Dirty { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _nextRefreshTick < Current.Game.tickManager.TicksGame; - set => _nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 16384 + Math.Abs(Rand.Int % 4096); + get => TickHelper.Past(_nextRefreshTick); } - public AllowedDefsListCacheValue SetNewValue(ThingFilter key) + + public void SetDirty(bool value, int offset) + => _nextRefreshTick = value ? 0 : TickHelper.Add(16384, offset, 4096); + + public void Update(ref ThingFilter key) => Update(ref key, GenTicks.TickLongInterval); + + public void Update(ref ThingFilter key, int tickOffset) { var allowedDefs = key.allowedDefs; allowedDefs.Remove(null); // apparently happens - if (defs is null) - defs = new(allowedDefs); - else - defs.ReplaceContentsWith(allowedDefs); + Defs.ReplaceContentsWith(allowedDefs); - SortDefs(defs); // why, actually? I don't remember - defs.Version()++; - - return this; + SortDefs(Defs); // why, actually? I don't remember + + SetDirty(false, tickOffset + Defs.Count + (key.disallowedSpecialFilters?.Count ?? 0)); } private static void SortDefs(List defs) @@ -174,7 +249,11 @@ public class ThingFilterPatches : ClassWithFishPatches defs._version++; } - private static float[] GetTempArrayForSorting(int defCount) => _tempArrayForSorting.Length >= defCount ? _tempArrayForSorting : _tempArrayForSorting = new float[defCount]; + private static float[] GetTempArrayForSorting(int defCount) + => _tempArrayForSorting.Length >= defCount + ? _tempArrayForSorting + : _tempArrayForSorting = new float[defCount]; + private static float[] _tempArrayForSorting = Array.Empty(); private static float GetMarketValueSafely(ThingDef def) @@ -189,25 +268,46 @@ public class ThingFilterPatches : ClassWithFishPatches } } - private static float LogAndFixMarketValue(ThingDef def, Exception ex) + private static float LogAndFixMarketValue(ThingDef def, Exception exception) { - Log.Warning($"Exception thrown while calculating market value for def {def.defName} from mod {def.modContentPack?.Name ?? "null"}. Attempting to fix this now so it doesn't happen again.\n{ex}"); - var recipe = StatWorker_MarketValue.CalculableRecipe(def); //<-- this is slow, but gets cached by perf optimizer. Might be worth replicating + Guard.IsNotNull(def); + Log.Warning($"Exception thrown while calculating market value for def {def.defName} from mod { + def.modContentPack?.Name ?? "null"}. Attempting to fix this now so it doesn't happen again.\n{ + exception}"); + + RecipeDef? recipe; + try + { + recipe = StatWorker_MarketValue.CalculableRecipe(def); + // this is slow, but gets cached by perf optimizer. Might be worth replicating + } + catch (Exception ex) + { + Log.Error($"Exception thrown while calculating recipe for def {def.defName} from mod { + def.modContentPack?.Name ?? "null"}.\n{ex}"); + recipe = null; + } if (recipe?.ingredients is { } ingredients) - ingredients.Remove(null); + ingredients.RemoveAll(static ingredientCount => ingredientCount is null); else if (def.CostList is { } costList) - costList.Remove(null); + costList.RemoveAll(static thingDefCountClass => thingDefCountClass?.thingDef is null); try { return def.BaseMarketValue; } - catch (Exception ex2) + catch (Exception ex) { - Log.Error($"Exception thrown while calculating market value for def {def.defName} from mod {def.modContentPack?.Name ?? "null"} again. Fix failed, F.\n{ex2}"); + Log.Error($"Exception thrown while calculating market value for def {def.defName} from mod { + def.modContentPack?.Name ?? "null"} again. Fix failed, F.\n{ex}"); + return 0f; } } + + public AllowedDefsListCacheValue() + { + } } } \ No newline at end of file diff --git a/Source/PerformanceFish/ThingOwnerUtilityCaching.cs b/Source/PerformanceFish/ThingOwnerUtilityCaching.cs index d9a3254..58d8cac 100644 --- a/Source/PerformanceFish/ThingOwnerUtilityCaching.cs +++ b/Source/PerformanceFish/ThingOwnerUtilityCaching.cs @@ -1,25 +1,110 @@ -// Copyright (c) 2022 bradson +// 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 ThingRequestCache = PerformanceFish.Cache.ByReferenceRefreshable; +using PerformanceFish.Prepatching; +/*using ThingRequestCache + = PerformanceFish.Cache.ByReference;*/ namespace PerformanceFish; -public class ThingOwnerUtilityCaching : ClassWithFishPatches -{ - public class GetAllThingsRecursively_Patch : FishPatch - { - public override string Description => "Caching of information about mostly unspawned pawns and things. Relatively large performance impact"; - public override Delegate TargetMethodGroup => ThingOwnerUtility.GetAllThingsRecursively; - public static bool Prefix(Map map, ThingRequest request, IList outThings, bool allowUnreal, Predicate passCheck, bool alsoGetSpawnedThings, out bool __state) +public class ThingOwnerUtilityPreCaching : ClassWithFishPrepatches +{ + public class GetAllThingsRecursively_Patch : FishPrepatch + { + public override string Description { get; } + = "Caching of information about mostly unspawned pawns and things. Relatively large performance impact"; + + public override MethodBase TargetMethodBase { get; } + = methodof(ThingOwnerUtility.GetAllThingsRecursively); + + public static bool Prefix(Map map, ThingRequest request, List? outThings, bool allowUnreal, + Predicate? passCheck, bool alsoGetSpawnedThings, out bool __state) + where T : Thing { - if (request.singleDef != null || request.group == 0 || !allowUnreal || passCheck != null || alsoGetSpawnedThings || outThings == null) + if (request.singleDef != null + || request.group == 0 + || !allowUnreal + || passCheck != null + || alsoGetSpawnedThings + || outThings == null) { __state = false; return true; } + + var listsByGroup = map.listerThings.listsByGroup[(int)request.group]; + if (listsByGroup is null) + { + __state = false; + return true; + } + + var key = new Cache.ByInt>(map.uniqueID, (byte)request.group); + ref var cache = ref Cache.ByInt>.Get.GetOrAddReference(ref key); + + if (cache.Dirty + || cache.ListsByGroupCount != listsByGroup.Count + || cache.ThingHolderCount != ThingHolderCount(map)) + { + return __state = true; + } + + outThings.ReplaceContentsWith(cache.Things!); + return __state = false; + } + + public static void Postfix(Map map, ThingRequest request, List outThings, bool __state) + where T : Thing + { + if (!__state) + return; + + ref var cache = ref Cache.ByInt>.Get.GetReference(new(map.uniqueID, + (byte)request.group)); + + if (cache.Things is null) + cache.Things = outThings.Copy(); + else + cache.Things.ReplaceContentsWith(outThings); + + cache.ListsByGroupCount = map.listerThings.listsByGroup[(int)request.group]?.Count ?? -1; + cache.ThingHolderCount = ThingHolderCount(map); + cache.SetDirty(false, map.uniqueID + (int)request.group + cache.ListsByGroupCount + cache.ThingHolderCount); + } + } + + public static int ThingHolderCount(Map map) + => (map.passingShipManager?.passingShips.Count ?? 0) + + map.components.Count + + (map.listerThings.listsByGroup[(int)ThingRequestGroup.ThingHolder]?.Count ?? 0); +} + +/*public class ThingOwnerUtilityCaching : ClassWithFishPatches +{ + public class GetAllThingsRecursively_Patch : FishPatch + { + public override string Description { get; } + = "Caching of information about mostly unspawned pawns and things. Relatively large performance impact"; + + public override Delegate TargetMethodGroup { get; } = ThingOwnerUtility.GetAllThingsRecursively; + + public static bool Prefix(Map map, ThingRequest request, IList? outThings, bool allowUnreal, + Predicate? passCheck, bool alsoGetSpawnedThings, out bool __state) + { + if (request.singleDef != null + || request.group == 0 + || !allowUnreal + || passCheck != null + || alsoGetSpawnedThings + || outThings == null) + { + __state = false; + return true; + } + var listByGroup = map.listerThings.listsByGroup[(int)request.group]; if (listByGroup is null) { @@ -28,18 +113,20 @@ public class ThingOwnerUtilityCaching : ClassWithFishPatches } var type = outThings.GetType().GetGenericArguments()[0]; - var cache = GetAndCheckCache(map, request.group, type); - if (cache.count == listByGroup.Count) - { - if (ThingHolderCount(map) != cache.thingHolderCount) - return __state = true; + + var key = new ThingRequestCache(map.uniqueID, (byte)request.group, type.TypeHandle); + ref var cache = ref ThingRequestCache.Get.GetOrAddReference(ref key); - outThings.Clear(); - outThings.TryAddRange(cache.things, type); - return __state = false; + if (cache.Dirty + || cache.count != listByGroup.Count + || cache.thingHolderCount != ThingHolderCount(map)) + { + return __state = true; } - return __state = true; + outThings.Clear(); + outThings.TryAddRange(cache.things!, type); + return __state = false; } public static void Postfix(Map map, ThingRequest request, IList outThings, bool __state) @@ -48,7 +135,9 @@ public class ThingOwnerUtilityCaching : ClassWithFishPatches return; var type = outThings.GetType().GetGenericArguments()[0]; - var cache = ThingRequestCache.Get[new(map.uniqueID, (byte)request.group, type.TypeHandle)]; + ref var cache + = ref ThingRequestCache.Get.GetReference(new(map.uniqueID, (byte)request.group, type.TypeHandle)); + if (cache.things is null) { cache.things = FisheryLib.CollectionExtensions.TryNew(outThings, type); @@ -58,116 +147,41 @@ public class ThingOwnerUtilityCaching : ClassWithFishPatches cache.things.Clear(); cache.things.TryAddRange(outThings, type); } - cache.ShouldRefreshNow = false; + cache.count = map.listerThings.listsByGroup[(int)request.group]?.Count ?? -1; cache.thingHolderCount = ThingHolderCount(map); - - ThingRequestCache.Get[new(map.uniqueID, (byte)request.group, type.TypeHandle)] = cache; - } - - public static ref ThingRequestInfo GetAndCheckCache(Map map, ThingRequestGroup group, Type type) - { - var key = new ThingRequestCache(map.uniqueID, (byte)group, type.TypeHandle); - ref var cache = ref ThingRequestCache.Get.TryGetReferenceUnsafe(ref key); - - if (Unsafe.IsNullRef(ref cache)) - { - ThingRequestCache.Get[key] = new(); - cache = ref ThingRequestCache.Get.TryGetReferenceUnsafe(ref key); - } - - if (cache.ShouldRefreshNow) - { - cache.count = map.listerThings.listsByGroup[(int)group]?.Count ?? -1; - cache.thingHolderCount = ThingHolderCount(map); - cache.ShouldRefreshNow = false; - ThingRequestCache.Get[new(map.uniqueID, (byte)group, type.TypeHandle)] = cache; - cache.count = -1; - } - - unsafe - { - return ref Unsafe.AsRef(Unsafe.AsPointer(ref cache)); - } + cache.SetDirty(false, map.uniqueID + (int)request.group + cache.count + cache.thingHolderCount); } } - public static int ThingHolderCount(Map map) => (map.passingShipManager?.passingShips.Count ?? 0) + map.components.Count + (map.listerThings.listsByGroup[(int)ThingRequestGroup.ThingHolder]?.Count ?? 0); + public static int ThingHolderCount(Map map) + => (map.passingShipManager?.passingShips.Count ?? 0) + + map.components.Count + + (map.listerThings.listsByGroup[(int)ThingRequestGroup.ThingHolder]?.Count ?? 0); +}*/ - /*public static void GetAllThingsRecursively(IThingHolder holder, List outThings) - { - outThings.Clear(); - - ThingOwnerUtility.tmpStack.Clear(); - ThingOwnerUtility.tmpStack.Push(holder); - while (ThingOwnerUtility.tmpStack.Count != 0) - { - IThingHolder thingHolder = ThingOwnerUtility.tmpStack.Pop(); - ThingOwner directlyHeldThings = thingHolder.GetDirectlyHeldThings(); - if (directlyHeldThings != null) - { - outThings.AddRange(directlyHeldThings); - } - - ThingOwnerUtility.tmpHolders.Clear(); - thingHolder.GetChildHolders(ThingOwnerUtility.tmpHolders); - for (int i = 0; i < ThingOwnerUtility.tmpHolders.Count; i++) - { - ThingOwnerUtility.tmpStack.Push(ThingOwnerUtility.tmpHolders[i]); - } - } - - ThingOwnerUtility.tmpStack.Clear(); - ThingOwnerUtility.tmpHolders.Clear(); - } - - public static void AppendThingHoldersFromThings(List outThingsHolders, IList container) - { - if (container == null) - { - return; - } - - int i = 0; - for (int count = container.Count; i < count; i++) - { - IThingHolder thingHolder = container[i] as IThingHolder; - if (thingHolder != null) - { - outThingsHolders.Add(thingHolder); - } - - ThingWithComps thingWithComps = container[i] as ThingWithComps; - if (thingWithComps == null) - { - continue; - } - - List allComps = thingWithComps.AllComps; - for (int j = 0; j < allComps.Count; j++) - { - IThingHolder thingHolder2 = allComps[j] as IThingHolder; - if (thingHolder2 != null) - { - outThingsHolders.Add(thingHolder2); - } - } - } - }*/ -} - -public struct ThingRequestInfo : Cache.IIsRefreshable +/*public record struct ThingRequestInfo { - public int nextRefreshTick; + private int _nextRefreshTick; public int count; public int thingHolderCount; - public IList things; + public IList? things; - public bool ShouldRefreshNow - { - get => nextRefreshTick < Current.Game.tickManager.TicksGame; - set => nextRefreshTick = value ? 0 : Current.Game.tickManager.TicksGame + 3072 + Math.Abs(Rand.Int % 2048); - } + public void SetDirty(bool value, int offset) + => _nextRefreshTick = value ? 0 : TickHelper.Add(3072, offset, 2048); - public ThingRequestInfo SetNewValue(ThingRequestCache key) => throw new NotImplementedException(); + public bool Dirty => _nextRefreshTick < Current.Game.tickManager.TicksGame; +}*/ + +public record struct ThingRequestInfo where T : Thing +{ + private int _nextUpdateTick; + public int ListsByGroupCount; + public int ThingHolderCount; + public List? Things; + + public void SetDirty(bool value, int offset) + => _nextUpdateTick = value ? 0 : TickHelper.Add(3072, offset, 2048); + + public bool Dirty => TickHelper.Past(_nextUpdateTick); } \ No newline at end of file diff --git a/Source/PerformanceFish/ThoughtWorker_Precept_AltarSharingOptimization.cs b/Source/PerformanceFish/ThoughtWorker_Precept_AltarSharingOptimization.cs index 83f7557..561b92c 100644 --- a/Source/PerformanceFish/ThoughtWorker_Precept_AltarSharingOptimization.cs +++ b/Source/PerformanceFish/ThoughtWorker_Precept_AltarSharingOptimization.cs @@ -1,10 +1,11 @@ -// Copyright (c) 2022 bradson +// 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 System.Threading.Tasks; -using SharedAltarCache = PerformanceFish.Cache.ByIndex; +using SharedAltarCache + = PerformanceFish.Cache.ByIndex; namespace PerformanceFish; @@ -12,92 +13,43 @@ public class ThoughtWorker_Precept_AltarSharingOptimization : ClassWithFishPatch { public class SharedAltar_Patch : FirstPriorityFishPatch { - public override string Description => "Caches results of ideology's shared altar thoughtworker, throttles it to wait a minimum of 128 ticks between refreshes and makes refreshing happen async on a different thread. Quite impactful on performance"; - public override Expression TargetMethod => () => default(ThoughtWorker_Precept_AltarSharing)!.SharedAltar(null); - public static void AsyncThoughtWorker(ThoughtWorker_Precept_AltarSharing __instance, Pawn pawn) - { - var allThings = pawn.Map.listerThings.listsByGroup[(int)ThingRequestGroup.BuildingArtificial].ToArray(); - Task.Run(() => - { - var newThing = SharedAltarParallel(pawn, allThings); - lock (Lock) - SharedAltarCache.GetDirectly[pawn.Ideo] = new(pawn, newThing); - }); - } - public static bool Prefix(ThoughtWorker_Precept_AltarSharing __instance, Pawn pawn, ref Thing? __result, out bool __state) + public override string Description { get; } + = "Caches results of ideology's shared altar thoughtworker and throttles it to wait a minimum of 128 ticks " + + "between refreshes. Quite impactful on performance"; + + public override MethodBase TargetMethodInfo { get; } = AccessTools.Method( + typeof(ThoughtWorker_Precept_AltarSharing), nameof(ThoughtWorker_Precept_AltarSharing.SharedAltar)); + + public static bool Prefix(ThoughtWorker_Precept_AltarSharing __instance, Pawn pawn, ref Thing? __result, + out bool __state) { if (!pawn.Spawned) { __result = null; return __state = false; } - SharedAltarCacheValue cache; - var shouldStartRefreshing = false; - lock (Lock) - { - if (!SharedAltarCache.GetDirectly.TryGetValue(pawn.Ideo, out cache) || (!FishSettings.ThreadingEnabled && cache.ShouldRefreshNow)) - { - return __state = true; - } - else if (cache.ShouldRefreshNow) - { - cache.currentlyRefreshing = true; - SharedAltarCache.GetDirectly[pawn.Ideo] = cache; - shouldStartRefreshing = true; - } - } + + ref var cache = ref SharedAltarCache.Get.GetReference(pawn.Ideo); + + if (cache.Dirty) + return __state = true; __result = cache.thing; - if (shouldStartRefreshing) - AsyncThoughtWorker(__instance, pawn); return __state = false; } + public static void Postfix(Pawn pawn, Thing __result, bool __state) { if (!__state) return; - lock (Lock) - SharedAltarCache.GetDirectly[pawn.Ideo] = new(pawn, __result); + + SharedAltarCache.Get[pawn.Ideo] = new(pawn, __result); } - public static Thing? SharedAltarParallel(Pawn pawn, Thing[] allThings) - { - if (!pawn.Spawned || pawn.Ideo == null) - return null; - - for (var i = 0; i < allThings.Length; i++) - { - var item = allThings[i]; - var compStyleable = item.TryGetComp(); - if (compStyleable == null || compStyleable.SourcePrecept == null || compStyleable.SourcePrecept.ideo != pawn.Ideo) - continue; - - var room = item.GetRoom(); - if (room == null || room.TouchesMapEdge) - continue; - - var containedAndAdjacentThings = room.ContainedAndAdjacentThings; - for (var k = 0; k < containedAndAdjacentThings.Count; k++) - { - var containedAndAdjacentThing = containedAndAdjacentThings[k]; - if (containedAndAdjacentThing != item) - { - var compStyleable2 = containedAndAdjacentThing.TryGetComp(); - if (compStyleable2 != null && compStyleable2.SourcePrecept != null && compStyleable2.SourcePrecept.ideo != pawn.Ideo) - { - return item; - } - } - } - } - return null; - } - private static object Lock { get; } = new(); } - public struct SharedAltarCacheValue : Cache.IIsRefreshable + public record struct SharedAltarCacheValue { public Thing? thing; - public bool currentlyRefreshing; private int _nextLateRefreshTick; private int _nextEarlyRefreshTick; private int _allStructuresListVersion; @@ -106,30 +58,27 @@ public class ThoughtWorker_Precept_AltarSharingOptimization : ClassWithFishPatch public SharedAltarCacheValue(Pawn pawn, Thing? result) { thing = result; - var ticks = Current.Game.tickManager.TicksGame; - _nextLateRefreshTick = ticks + 3072 + Math.Abs(Rand.Int % 2048); - _nextEarlyRefreshTick = ticks + 128 + Math.Abs(Rand.Int % 128); + _nextLateRefreshTick = TickHelper.Add(3072, pawn.thingIDNumber, 2048); + _nextEarlyRefreshTick = TickHelper.Add(128, pawn.thingIDNumber, 128); _lister = pawn.Map.listerThings; - _allStructuresListVersion = _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial].Version(); - currentlyRefreshing = false; + _allStructuresListVersion = _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial]._version; } - public bool ShouldRefreshNow + public bool Dirty { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - var ticks = Current.Game.tickManager.TicksGame; - return !currentlyRefreshing && (_nextLateRefreshTick < ticks - || (_nextEarlyRefreshTick < ticks && _allStructuresListVersion != _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial].Version())); - } - set - { - var ticks = Current.Game.tickManager.TicksGame; - _nextLateRefreshTick = value ? 0 : ticks + 3072 + Math.Abs(Rand.Int % 2048); - _nextEarlyRefreshTick = value ? 0 : ticks + 128 + Math.Abs(Rand.Int % 128); + var ticks = TickHelper.TicksGame; + return _nextLateRefreshTick < ticks + || (_nextEarlyRefreshTick < ticks + && _allStructuresListVersion + != _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial]._version); } } - public SharedAltarCacheValue SetNewValue(Ideo key) => throw new NotImplementedException(); + public bool Equals(SharedAltarCacheValue other) => (thing == other.thing) & (_lister == other._lister); + + public override int GetHashCode() => HashCode.Combine(thing, _lister); } } \ No newline at end of file diff --git a/Source/PerformanceFish/TickHelper.cs b/Source/PerformanceFish/TickHelper.cs new file mode 100644 index 0000000..2ee93b7 --- /dev/null +++ b/Source/PerformanceFish/TickHelper.cs @@ -0,0 +1,32 @@ +// 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; + +public static class TickHelper +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Past(int tick) => tick < TicksGame; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool MatchesModulo(int powerOfTwo) => (TicksGame & (powerOfTwo - 1)) == 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Add(int ticks) => ticks + TicksGame; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Add(int ticks, int randomOffset) + => ticks + (randomOffset & 63) + TicksGame; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Add(int ticks, int randomOffset, int powerOfTwo) + => ticks + (randomOffset & (powerOfTwo - 1)) + TicksGame; + + public static int TicksGame + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Current.gameInt.tickManager.TicksGame; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Utility/CollectionExtensions.cs b/Source/PerformanceFish/Utility/CollectionExtensions.cs new file mode 100644 index 0000000..3117500 --- /dev/null +++ b/Source/PerformanceFish/Utility/CollectionExtensions.cs @@ -0,0 +1,302 @@ +// 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 System.Diagnostics.CodeAnalysis; +using Mono.Collections.Generic; + +namespace PerformanceFish.Utility; + +public static class CollectionExtensions +{ + public static bool Any(this T[] array, Predicate predicate) + { + var length = array.Length; + return length != 0 && Any(ref array[0], predicate, (nuint)length); + } + + // overlaps with method in GenCollection + // public static bool Any(this List list, Predicate predicate) + // { + // var count = list.Count; + // return count != 0 && Any(ref list._items[0], predicate, count); + // } + + public static bool Any(this IList iList, Predicate predicate) + { + var count = iList.Count; + for (var i = 0; i < count; i++) + { + if (predicate(iList[i])) + return true; + } + + return false; + } + + public static unsafe bool Any(this T[] array, delegate* predicate) + { + var length = array.Length; + return length != 0 && Any(ref array[0], predicate, (nuint)length); + } + + public static unsafe bool Any(this List list, delegate* predicate) + { + var count = list.Count; + return count != 0 && Any(ref list._items[0], predicate, (nuint)count); + } + + public static bool ExistsAndNotNull(this List? list, Predicate match) + { + if (list is null) + return false; + + var count = list.Count; + return count != 0 && Any(ref list._items[0], match, (nuint)count); + } + + public static bool Contains(this T[] array, T item) + { + var length = array.Length; + return length != 0 && Contains(ref array[0], item, (nuint)length); + } + + public static bool Contains(this IList iList, T item) + { + var count = iList.Count; + for (var i = 0; i < count; i++) + { + if (iList[i].Equals(item)) + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? FirstOrDefault(this T[] array, Predicate predicate) + { + var length = array.Length; + return length == 0 ? default : FirstOrDefault(ref array[0], predicate, (nuint)length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? FirstOrDefaultFast(this List list, Predicate predicate) + { + var count = list.Count; + return count == 0 ? default : FirstOrDefault(ref list._items[0], predicate, (nuint)count); + } + + public static T? FirstOrDefault(this IList iList, Predicate predicate) + { + var count = iList.Count; + for (var i = 0; i < count; i++) + { + if (predicate(iList[i])) + return iList[i]; + } + + return default; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe T? FirstOrDefault(this T[] array, delegate* predicate) + { + var length = array.Length; + return length == 0 ? default : FirstOrDefault(ref array[0], predicate, (nuint)length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe T? FirstOrDefault(this List list, delegate* predicate) + { + var count = list.Count; + return count == 0 ? default : FirstOrDefault(ref list._items[0], predicate, (nuint)count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? FirstOrDefault(this T[] array) => array.Length != 0 ? array[0] : default; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? FirstOrDefault(this List list) => list.Count != 0 ? list[0] : default; + + public static T? FirstOrDefault(this IList iList) => iList.Count != 0 ? iList[0] : default; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T First(this T[] array, Predicate predicate) + { + var length = array.Length; + return length == 0 ? ThrowForNoElements() : First(ref array[0], predicate, (nuint)length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T First(this List list, Predicate predicate) + { + var count = list.Count; + return count == 0 ? ThrowForNoElements() : First(ref list._items[0], predicate, (nuint)count); + } + + public static T First(this IList iList, Predicate predicate) + { + var count = iList.Count; + for (var i = 0; i < count; i++) + { + if (predicate(iList[i])) + return iList[i]; + } + + return ThrowForNoElements(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe T First(this T[] array, delegate* predicate) + { + var length = array.Length; + return length == 0 ? ThrowForNoElements() : First(ref array[0], predicate, (nuint)length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe T First(this List list, delegate* predicate) + { + var count = list.Count; + return count == 0 ? ThrowForNoElements() : First(ref list._items[0], predicate, (nuint)count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T First(this T[] array) => array.Length != 0 ? array[0] : ThrowForNoElements(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T First(this List list) => list.Count != 0 ? list[0] : ThrowForNoElements(); + + public static T First(this IList iList) => iList.Count != 0 ? iList[0] : ThrowForNoElements(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe bool Any(ref T reference, Predicate predicate, nuint length) + { + nuint i = 0; + length *= (nuint)sizeof(T); + + do + { + if (predicate(Unsafe.AddByteOffset(ref reference, i))) + return true; + + i += (nuint)sizeof(T); + } + while (i < length); + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe bool Any(ref T reference, delegate* predicate, nuint length) + { + nuint i = 0; + length *= (nuint)sizeof(T); + + do + { + if (predicate(Unsafe.AddByteOffset(ref reference, i))) + return true; + + i += (nuint)sizeof(T); + } + while (i < length); + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe bool Contains(ref T reference, T item, nuint length) + { + nuint i = 0; + length *= (nuint)sizeof(T); + + do + { + if (Unsafe.AddByteOffset(ref reference, i).Equals(item)) + return true; + + i += (nuint)sizeof(T); + } + while (i < length); + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe T? FirstOrDefault(ref T reference, Predicate predicate, nuint length) + { + nuint i = 0; + length *= (nuint)sizeof(T); + + do + { + if (predicate(Unsafe.AddByteOffset(ref reference, i))) + return Unsafe.AddByteOffset(ref reference, i); + + i += (nuint)sizeof(T); + } + while (i < length); + + return default; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe T First(ref T reference, Predicate predicate, nuint length) + { + nuint i = 0; + length *= (nuint)sizeof(T); + + do + { + if (predicate(Unsafe.AddByteOffset(ref reference, i))) + return Unsafe.AddByteOffset(ref reference, i); + + i += (nuint)sizeof(T); + } + while (i < length); + + return ThrowForNoElements(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe T? FirstOrDefault(ref T reference, delegate* predicate, nuint length) + { + nuint i = 0; + length *= (nuint)sizeof(T); + + do + { + if (predicate(Unsafe.AddByteOffset(ref reference, i))) + return Unsafe.AddByteOffset(ref reference, i); + + i += (nuint)sizeof(T); + } + while (i < length); + + return default; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe T First(ref T reference, delegate* predicate, nuint length) + { + nuint i = 0; + length *= (nuint)sizeof(T); + + do + { + if (predicate(Unsafe.AddByteOffset(ref reference, i))) + return Unsafe.AddByteOffset(ref reference, i); + + i += (nuint)sizeof(T); + } + while (i < length); + + return ThrowForNoElements(); + } + + [DoesNotReturn] + private static T ThrowForNoElements() + => throw new InvalidOperationException("Sequence contains no elements"); +} \ No newline at end of file diff --git a/Source/PerformanceFish/Utility/Exposable.cs b/Source/PerformanceFish/Utility/Exposable.cs index f4fd157..d54c750 100644 --- a/Source/PerformanceFish/Utility/Exposable.cs +++ b/Source/PerformanceFish/Utility/Exposable.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -6,12 +6,14 @@ namespace PerformanceFish.Utility; /// -/// Scribe_Deep normally creates a new instance when loading data. Scribe on this class prevents that. Also includes a try-catch block +/// Scribe_Deep normally creates a new instance when loading data. Scribe on this class prevents that. Also includes a +/// try-catch block /// public class Exposable : IExposable { - public IExposable Parent; + public readonly IExposable Parent; public Exposable(IExposable @this) => Parent = @this; + public void ExposeData() { try @@ -23,22 +25,21 @@ public class Exposable : IExposable Log.Error($"{ex}"); } } + public override string ToString() => TrimName(Parent.ToString()); public override bool Equals(object obj) => obj.Equals(Parent); public override int GetHashCode() => Parent.GetHashCode(); - public static void Scribe(IExposable iexposable, string label, bool trimLabel = true) + public static void Scribe(IExposable iExposable, string label, bool trimLabel = true) { - var wrappedExposable = new Exposable(iexposable); - Scribe_Deep.Look(ref wrappedExposable, - trimLabel ? TrimName(label) : label, - new[] { iexposable }); + var wrappedExposable = new Exposable(iExposable); + Scribe_Deep.Look(ref wrappedExposable, trimLabel ? TrimName(label) : label, iExposable); } internal static string TrimName(string name) => name.Replace('+', '.') is var fullName && $"{typeof(PerformanceFishMod).Namespace}." is var @namespace && fullName.StartsWith(@namespace) - ? fullName.Remove(0, @namespace.Length) - : fullName; + ? fullName.Remove(0, @namespace.Length) + : fullName; } \ No newline at end of file diff --git a/Source/PerformanceFish/Utility/KeyedList.cs b/Source/PerformanceFish/Utility/KeyedList.cs index e946615..8acd944 100644 --- a/Source/PerformanceFish/Utility/KeyedList.cs +++ b/Source/PerformanceFish/Utility/KeyedList.cs @@ -1,17 +1,27 @@ -// Copyright (c) 2022 bradson +// 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 System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; +using JetBrains.Annotations; namespace PerformanceFish.Utility; +[PublicAPI] public class KeyedList : List>, IDictionary { - public IList Keys { get; } - public IList Values { get; } + public IList Keys + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + public IList Values + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } public KeyedList() { @@ -33,16 +43,16 @@ public class KeyedList : List>, IDictio public TValue this[TKey key] { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Guard.IsNotNull(key); var items = _items; - for (var i = 0; i < Count; i++) + for (var i = Count - 1; i >= 0; i--) { - var (itemKey, itemValue) = items[i]; - if (GetKeyComparer().Equals(itemKey, key)) - return itemValue; + if (items[i].Key.Equals(key)) + return items[i].Value; } return ThrowHelperF.ThrowKeyNotFoundException(key); @@ -52,14 +62,14 @@ public class KeyedList : List>, IDictio Guard.IsNotNull(key); var items = _items; - for (var i = 0; i < Count; i++) + for (var i = Count - 1; i >= 0; i--) { ref var item = ref items[i]; - if (GetKeyComparer().Equals(item.Key, key)) - { - item = new(item.Key, value); - return; - } + if (!item.Key.Equals(key)) + continue; + + item = new(item.Key, value); + return; } Add(new(key, value)); @@ -68,29 +78,31 @@ public class KeyedList : List>, IDictio public void Add(TKey key, TValue value) => Add(new(key, value)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsKey(TKey item) { Guard.IsNotNull(item); var items = _items; - for (var i = 0; i < Count; i++) + for (var i = Count - 1; i >= 0; i--) { - if (GetKeyComparer().Equals(items[i].Key, item)) + if (items[i].Key.Equals(item)) return true; } return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsValue(TValue item) { if (item is null) return false; var items = _items; - for (var i = 0; i < Count; i++) + for (var i = Count - 1; i >= 0; i--) { - if (GetValueComparer().Equals(items[i].Value, item)) + if (items[i].Value.Equals(item)) return true; } @@ -98,20 +110,24 @@ public class KeyedList : List>, IDictio } #pragma warning disable CS8767 // missing nullable on IDictionary interface + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetValue(TKey key, out TValue? value) #pragma warning restore CS8767 { Guard.IsNotNull(key); - - var index = IndexOfKey(key); - if (index < 0) + + var items = _items; + for (var i = Count - 1; i >= 0; i--) { - value = default; - return false; + if (!items[i].Key.Equals(key)) + continue; + + value = items[i].Value; + return true; } - value = _items[index].Value; - return true; + value = default; + return false; } public void CopyTo(TKey[] array, int arrayIndex) @@ -122,6 +138,7 @@ public class KeyedList : List>, IDictio for (var i = 0; i < Count; i++) array[i + arrayIndex] = items[i].Key; } + public void CopyTo(TValue[] array, int arrayIndex) { CanCopyTo(array, arrayIndex); @@ -160,26 +177,28 @@ public class KeyedList : List>, IDictio return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int IndexOfKey(TKey key) { Guard.IsNotNull(key); var items = _items; - for (var i = 0; i < Count; i++) + for (var i = Count - 1; i >= 0; i--) { - if (GetKeyComparer().Equals(items[i].Key, key)) + if (items[i].Key.Equals(key)) return i; } return -1; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int IndexOfValue(TValue value) { var items = _items; - for (var i = 0; i < Count; i++) + for (var i = Count - 1; i >= 0; i--) { - if (GetValueComparer().Equals(items[i].Value, value)) + if (items[i].Value.Equals(value)) return i; } @@ -190,16 +209,21 @@ public class KeyedList : List>, IDictio { private readonly KeyedList _list; - public KeyCollection(KeyedList list) - => _list = list; + public KeyCollection(KeyedList list) => _list = list; public TKey this[int index] { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _list[index].Key; + // ReSharper disable once ValueParameterNotUsed set => ThrowHelper.ThrowNotSupportedException(); } - public int Count => _list.Count; + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _list.Count; + } public bool IsReadOnly => false; @@ -207,6 +231,7 @@ public class KeyedList : List>, IDictio public void Clear() => _list.Clear(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(TKey item) => _list.ContainsKey(item); public void CopyTo(TKey[] array, int arrayIndex) @@ -218,6 +243,7 @@ public class KeyedList : List>, IDictio array[i + arrayIndex] = items[i].Key; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator GetEnumerator() { var items = _list._items; @@ -225,6 +251,7 @@ public class KeyedList : List>, IDictio yield return items[i].Key; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int IndexOf(TKey item) => _list.IndexOfKey(item); public void Insert(int index, TKey item) => ThrowHelper.ThrowNotSupportedException(); @@ -240,11 +267,11 @@ public class KeyedList : List>, IDictio { private readonly KeyedList _list; - public ValueCollection(KeyedList list) - => _list = list; + public ValueCollection(KeyedList list) => _list = list; public TValue this[int index] { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _list[index].Value; set { @@ -253,7 +280,11 @@ public class KeyedList : List>, IDictio } } - public int Count => _list.Count; + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _list.Count; + } public bool IsReadOnly => false; @@ -261,6 +292,7 @@ public class KeyedList : List>, IDictio public void Clear() => _list.Clear(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(TValue item) => _list.ContainsValue(item); public void CopyTo(TValue[] array, int arrayIndex) @@ -272,6 +304,7 @@ public class KeyedList : List>, IDictio array[i + arrayIndex] = items[i].Value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator GetEnumerator() { var items = _list._items; @@ -279,6 +312,7 @@ public class KeyedList : List>, IDictio yield return items[i].Value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int IndexOf(TValue item) => _list.IndexOfValue(item); public void Insert(int index, TValue item) => ThrowHelper.ThrowNotSupportedException(); @@ -290,12 +324,6 @@ public class KeyedList : List>, IDictio IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected static IEqualityComparer GetKeyComparer() => EqualityComparer.Default; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected static IEqualityComparer GetValueComparer() => EqualityComparer.Default; - ICollection IDictionary.Keys => Keys; ICollection IDictionary.Values => Values; @@ -307,8 +335,7 @@ public class KeyedList : List>, IDictio protected static class ThrowHelperF { [DoesNotReturn] - internal static V ThrowKeyNotFoundException(T key) - => throw GetKeyNotFoundException(key); + internal static V ThrowKeyNotFoundException(T key) => throw GetKeyNotFoundException(key); [DoesNotReturn] internal static void ThrowKeyNotFoundException(T key) diff --git a/Source/PerformanceFish/Utility/ReflectionExtensions.cs b/Source/PerformanceFish/Utility/ReflectionExtensions.cs new file mode 100644 index 0000000..b898c9b --- /dev/null +++ b/Source/PerformanceFish/Utility/ReflectionExtensions.cs @@ -0,0 +1,98 @@ +// 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 JetBrains.Annotations; + +namespace PerformanceFish.Utility; + +[PublicAPI] +public static class ReflectionExtensions +{ + /// + /// Returns the first method that matches either by name OR by predicate + /// + public static MethodInfo? TryGetMethod(this Type type, string nameCaseInsensitive, + Predicate? predicate = null) + { + predicate ??= static _ => true; + var methods = type.GetMethods(AccessTools.all); + + for (var i = 0; i < methods.Length; i++) + { + if (methods[i].NameEqualsCaseInsensitive(nameCaseInsensitive) || predicate(methods[i])) + return methods[i]; + } + + return null; + } + + public static IEnumerable SubclassesWithNoMethodOverride(this Type type, params string[] names) + { + Guard.IsNotNull(type); + Guard.IsNotNull(names); + Guard.IsNotEmpty(names); + + var allSubclasses = type.AllSubclassesNonAbstract(); + for (var i = 0; i < allSubclasses.Count; i++) + { + var hasOverrides = false; + for (var j = names.Length; j-- > 0;) + { + if (AccessTools.DeclaredMethod(allSubclasses[i], names[j]) is null) + continue; + + hasOverrides = true; + break; + } + + if (!hasOverrides) + yield return allSubclasses[i]; + } + } + + public static IEnumerable SubclassesWithNoMethodOverrideAndSelf(this Type type, params string[] names) + { + yield return type; + + foreach (var subclass in type.SubclassesWithNoMethodOverride(names)) + yield return subclass; + } + + public static IEnumerable SubclassesWithNoMethodOverride(this Type type, Type[] allowedDeclaringTypes, + params string[] names) + { + Guard.IsNotNull(type); + Guard.IsNotNull(allowedDeclaringTypes); + Guard.IsNotEmpty(allowedDeclaringTypes); + Guard.IsNotNull(names); + Guard.IsNotEmpty(names); + + var allSubclasses = type.AllSubclassesNonAbstract(); + for (var i = 0; i < allSubclasses.Count; i++) + { + var hasOverrides = false; + for (var j = names.Length; j-- > 0;) + { + if (allowedDeclaringTypes.Contains(AccessTools.Method(allSubclasses[i], names[j])!.DeclaringType!)) + continue; + + hasOverrides = true; + break; + } + + if (!hasOverrides) + yield return allSubclasses[i]; + } + } + + public static IEnumerable SubclassesWithNoMethodOverrideAndSelf(this Type type, Type[] allowedDeclaringTypes, + params string[] names) + { + yield return type; + + foreach (var subclass in type.SubclassesWithNoMethodOverride(allowedDeclaringTypes, names)) + yield return subclass; + } +} \ No newline at end of file diff --git a/Source/PerformanceFish/Utility/StringExtensions.cs b/Source/PerformanceFish/Utility/StringExtensions.cs new file mode 100644 index 0000000..a996e8c --- /dev/null +++ b/Source/PerformanceFish/Utility/StringExtensions.cs @@ -0,0 +1,12 @@ +// 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.Utility; + +public static class StringExtensions +{ + public static bool NameEqualsCaseInsensitive(this MethodInfo methodInfo, string name) + => methodInfo.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase); +} \ No newline at end of file diff --git a/Source/PerformanceFish/UtilityPatches.cs b/Source/PerformanceFish/UtilityPatches.cs index b3892b2..de8fe51 100644 --- a/Source/PerformanceFish/UtilityPatches.cs +++ b/Source/PerformanceFish/UtilityPatches.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 bradson +// 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/. @@ -9,11 +9,18 @@ public class UtilityPatches : ClassWithFishPatches { public class FinalizeInit_Patch : FishPatch { - public override string Description => "Necessary for patches that require a loaded game to function"; - public override Expression TargetMethod => () => default(Game)!.FinalizeInit(); + public override bool Enabled => true; + + public override string Description { get; } + = "Necessary for patches that require a loaded game to function"; + + public override MethodBase TargetMethodInfo { get; } + = AccessTools.Method(typeof(Game), nameof(Game.FinalizeInit)); + + [HarmonyPriority(Priority.VeryLow)] public static void Postfix() { - foreach (var patch in PerformanceFishMod.AllPatchClasses) + foreach (var patch in PerformanceFishMod.AllPatchClasses!) { if (patch.RequiresLoadedGameForPatching) patch.Patches.PatchAll(); @@ -26,12 +33,19 @@ public class UtilityPatches : ClassWithFishPatches public class ClearAllMapsAndWorld_Patch : FishPatch { - public override string Description => "A patch to make the game clear all Performance Fish caches whenever saves get unloaded, to avoid anything getting carried over to a new game by mistake"; + public override bool Enabled => true; + + public override string Description { get; } + = "A patch to make the game clear all Performance Fish caches whenever saves get unloaded, to avoid " + + "anything getting carried over to a new game by mistake"; + public override Delegate TargetMethodGroup => Verse.Profile.MemoryUtility.ClearAllMapsAndWorld; + + [HarmonyPriority(Priority.VeryLow)] public static void Postfix() { Cache.Utility.Clear(); - foreach (var patch in PerformanceFishMod.AllPatchClasses) + foreach (var patch in PerformanceFishMod.AllPatchClasses!) { if (patch.RequiresLoadedGameForPatching) patch.Patches.UnpatchAll(); diff --git a/Source/PerformanceFish/UtilityPrepatches.cs b/Source/PerformanceFish/UtilityPrepatches.cs new file mode 100644 index 0000000..544ea2b --- /dev/null +++ b/Source/PerformanceFish/UtilityPrepatches.cs @@ -0,0 +1,50 @@ +// 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 System.Diagnostics; +using PerformanceFish.Prepatching; + +namespace PerformanceFish; + +public class UtilityPrepatches : ClassWithFishPrepatches +{ + public class CreateModClassesPatch : FishPrepatch + { + public override string? Description { get; } + = "Hook for very early loading of patches, right after the game finishes loading up game assemblies into " + + "its process and before it launches any processes on them. Certain patches require this."; + + public override bool Enabled => true; + + public override MethodBase TargetMethodBase { get; } + = methodof(LoadedModManager.CreateModClasses); + + public static void Prefix() + { + try + { + OnAssembliesLoaded.Start(); + } + catch (Exception e) + { + Log.Error($"Exception caught within OnAssembliesLoaded:\n{e}\n{ + new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}"); + } + } + } + + public class ThingDestroy : FishPrepatch + { + public override string? Description { get; } + = "Hook to invoke an event whenever things get destroyed. Certain functions require this."; + + public override bool Enabled => true; + + public override MethodBase TargetMethodBase { get; } + = AccessTools.DeclaredMethod(typeof(Thing), nameof(Thing.Destroy)); + + public static void Postfix(Thing __instance) => Cache.Utility.NotifyThingDestroyed(__instance); + } +} \ No newline at end of file