add 1.6 support and fix prepatch regressions

This commit is contained in:
localavaster
2026-04-07 03:56:12 -06:00
parent a99972001e
commit c020968638
32 changed files with 609 additions and 158 deletions
@@ -15,6 +15,9 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
{
public sealed class WorldObjectsHolderTickPatch : FishPrepatch
{
private static FishTable<Type, bool> _skippableCompTypes = null!;
private static FishTable<Type, bool> _skippableWorldObjectTypes = null!;
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 "
@@ -35,7 +38,11 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
var worldObjects = WorldObjectsHolder.tmpWorldObjects;
for (var i = worldObjects.Count; i-- > 0;)
#if V1_6
worldObjects[i].DoTick();
#else
worldObjects[i].Tick();
#endif
}
public static void UpdateCache(WorldObjectsHolder instance)
@@ -50,7 +57,7 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
var worldObject = instanceWorldObjects[i];
if (worldObject is not MapParent { HasMap: true }
&& SkippableWorldObjects.Contains(worldObject.GetType())
&& CanSkipWorldObjectTick(worldObject)
&& (worldObject is not Settlement settlement || settlement.trader?.stock is null)
&& CanSkipCompTick(worldObject))
{
@@ -64,6 +71,15 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
CachedMapsVersion = Current.gameInt.maps._version;
}
private static bool CanSkipWorldObjectTick(WorldObject worldObject)
{
#if V1_6
if (worldObject is IThingHolder)
return false;
#endif
return IsSkippableWorldObjectType(worldObject.GetType());
}
private static bool CanSkipCompTick(WorldObject worldObject)
{
var comps = worldObject.comps;
@@ -74,7 +90,7 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
if (comp is EnterCooldownComp { Active: true })
return false;
if (!SkippableComps.Contains(comp.GetType()))
if (!IsSkippableCompType(comp.GetType()))
return false;
comp.CompTick();
@@ -106,28 +122,69 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
public static void AddCompToWhiteList(Type compType)
{
_whitelistedTickingCompTypes = _whitelistedTickingCompTypes.Add(compType);
SkippableComps = InitializeSkippableComps();
ResetSkippableCompTypes();
}
public static void AddWorldObjectToWhiteList(Type worldObjectType)
{
_whitelistedWorldObjectTypes = _whitelistedWorldObjectTypes.Add(worldObjectType);
SkippableWorldObjects = InitializeSkippableWorldObjects();
ResetSkippableWorldObjectTypes();
}
public static HashSet<Type>
SkippableComps = InitializeSkippableComps(),
SkippableWorldObjects = InitializeSkippableWorldObjects();
private static void ResetSkippableCompTypes()
{
_skippableCompTypes = new()
{
ValueInitializer = static type => ComputeIsSkippableCompType(type)
};
}
private static HashSet<Type> InitializeSkippableComps()
=> MakeSubclassHashSet(typeof(WorldObjectComp), nameof(WorldObjectComp.CompTick),
_whitelistedTickingCompTypes);
private static void ResetSkippableWorldObjectTypes()
{
_skippableWorldObjectTypes = new()
{
ValueInitializer = static type => ComputeIsSkippableWorldObjectType(type)
};
}
private static HashSet<Type> InitializeSkippableWorldObjects()
=> MakeSubclassHashSet(typeof(WorldObject), nameof(WorldObject.Tick), _whitelistedWorldObjectTypes);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsSkippableCompType(Type type) => _skippableCompTypes.GetOrAdd(type);
private static HashSet<Type> MakeSubclassHashSet(Type type, string name, Type?[] allowedDeclaringTypes)
=> type.SubclassesWithNoMethodOverrideAndSelf(allowedDeclaringTypes, name).ToHashSet();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsSkippableWorldObjectType(Type type) => _skippableWorldObjectTypes.GetOrAdd(type);
private static bool ComputeIsSkippableCompType(Type type)
{
if (AccessTools.Method(type, nameof(WorldObjectComp.CompTick))?.DeclaringType is not { } declaringType
|| !_whitelistedTickingCompTypes.Contains(declaringType))
{
return false;
}
#if V1_6
return AccessTools.Method(type, nameof(WorldObjectComp.CompTickInterval), [typeof(int)])?.DeclaringType
is { } intervalDeclaringType
&& _whitelistedTickingCompTypes.Contains(intervalDeclaringType);
#else
return true;
#endif
}
private static bool ComputeIsSkippableWorldObjectType(Type type)
{
if (AccessTools.Method(type, "Tick")?.DeclaringType is not { } declaringType
|| !_whitelistedWorldObjectTypes.Contains(declaringType))
{
return false;
}
#if V1_6
return AccessTools.Method(type, "TickInterval", [typeof(int)])?.DeclaringType is { } intervalDeclaringType
&& _whitelistedWorldObjectTypes.Contains(intervalDeclaringType);
#else
return true;
#endif
}
public static bool CacheDirty(WorldObjectsHolder instance)
=> CachedWorldObjectsVersion != instance.worldObjects._version
@@ -135,7 +192,12 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
public static void SetDirty() => CachedWorldObjectsVersion = CachedMapsVersion = -2;
static WorldObjectsHolderTickPatch() => Cache.Utility.Cleared += SetDirty;
static WorldObjectsHolderTickPatch()
{
ResetSkippableCompTypes();
ResetSkippableWorldObjectTypes();
Cache.Utility.Cleared += SetDirty;
}
}
public sealed class ExpandingIconCaching : FishPrepatch
@@ -245,4 +307,4 @@ public sealed class WorldObjectsOptimization : ClassWithFishPrepatches
}
}
}
}
}