Skip to content

Commit

Permalink
Improve and fix some DebugActions (#412)
Browse files Browse the repository at this point in the history
- Added `MapComponent.MapComponentUpdate` and `WorldComponent.WorldComponentUpdate` to non-ticking update methods
- Fixed check for overrides of non-ticking update methods
- Fixed GetHashCode check including GetHashCode methods themselves (will still include non-default overloads)
- Hopefully made type checks more thorough by using `IsAssignableFrom`, instead of just doing simple type comparison
  • Loading branch information
SokyranTheDragon authored Jan 9, 2024
1 parent 821501e commit 949ce19
Showing 1 changed file with 50 additions and 15 deletions.
65 changes: 50 additions & 15 deletions Source/DebugActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading.Tasks;
using HarmonyLib;
using RimWorld;
using RimWorld.Planet;
using UnityEngine;
using Verse;
using Random = System.Random;
Expand Down Expand Up @@ -61,7 +62,9 @@ internal enum StuffToSearch

private static readonly HashSet<MethodInfo> NonTickingUpdateMethodsOverrides = new[]
{
AccessTools.DeclaredMethod(typeof(MapComponent), nameof(MapComponent.MapComponentUpdate)),
AccessTools.DeclaredMethod(typeof(GameComponent), nameof(GameComponent.GameComponentUpdate)),
AccessTools.DeclaredMethod(typeof(WorldComponent), nameof(WorldComponent.WorldComponentUpdate)),
AccessTools.DeclaredMethod("HugsLib.ModBase:Update"),
AccessTools.DeclaredMethod("HugsLib.ModBase:FixedUpdate"),
AccessTools.DeclaredMethod("HugsLib.ModBase:OnGUI"),
Expand Down Expand Up @@ -278,7 +281,7 @@ internal static void FindUnpatchedInType(Type type, string[] unsupportedTypes, D
{
try
{
foreach (var found in FindRng(type, method))
foreach (var found in FindRng(method))
{
lock (log[found])
log[found].Add($"{type.FullName}:{method.Name} ({type.Assembly.GetName().Name})");
Expand All @@ -296,60 +299,61 @@ internal static void FindUnpatchedInType(Type type, string[] unsupportedTypes, D
}
}

internal static HashSet<StuffToSearch> FindRng(Type baseType, MethodBase baseMethod)
internal static HashSet<StuffToSearch> FindRng(MethodBase baseMethod)
{
var instr = PatchProcessor.GetCurrentInstructions(baseMethod);
var foundStuff = new HashSet<StuffToSearch>();
var baseMethodInfo = baseMethod as MethodInfo; // Potentially null

if (baseType != baseMethod.DeclaringType && NonTickingUpdateMethodsOverrides.Contains(baseMethod))
if (IsOverrideOfAny(baseMethodInfo, NonTickingUpdateMethodsOverrides))
foundStuff.Add(StuffToSearch.NonTickingUpdate);

foreach (var ci in instr)
{
switch (ci.operand)
{
// Constructors
case ConstructorInfo { DeclaringType: not null } ctor when ctor.DeclaringType == typeof(Random):
case ConstructorInfo { DeclaringType: not null } ctor when ctor.DeclaringType != typeof(PatchingUtilities.RandRedirector) && typeof(Random).IsAssignableFrom(ctor.DeclaringType):
foundStuff.Add(StuffToSearch.SystemRng);
break;
case ConstructorInfo { DeclaringType: not null } ctor when ctor.DeclaringType == typeof(Thread) || ctor.DeclaringType == typeof(ThreadStart):
case ConstructorInfo { DeclaringType: not null } ctor when typeof(Thread).IsAssignableFrom(ctor.DeclaringType) || typeof(ThreadStart).IsAssignableFrom(ctor.DeclaringType):
foundStuff.Add(StuffToSearch.Multithreading);
break;
case ConstructorInfo { DeclaringType: not null } ctor when ctor.DeclaringType == typeof(Stopwatch):
case ConstructorInfo { DeclaringType: not null } ctor when typeof(Stopwatch).IsAssignableFrom(ctor.DeclaringType):
foundStuff.Add(StuffToSearch.Stopwatch);
break;
// Methods
case MethodInfo { DeclaringType: not null } method when method.DeclaringType == typeof(UnityEngine.Random):
case MethodInfo { DeclaringType: not null } method when typeof(UnityEngine.Random).IsAssignableFrom(method.DeclaringType):
foundStuff.Add(StuffToSearch.UnityRng);
break;
case MethodInfo { DeclaringType: not null } method when method.DeclaringType == typeof(GenView):
case MethodInfo { DeclaringType: not null } method when typeof(GenView).IsAssignableFrom(method.DeclaringType):
foundStuff.Add(StuffToSearch.GenView);
break;
// StartCoroutine, etc.
// We could check for the methods themselves, but it's much easier to just check for return
// as there'll probably not be all that many methods returning it.
case MethodInfo { DeclaringType: not null } method when method.ReturnType == typeof(Coroutine):
case MethodInfo { DeclaringType: not null } method when typeof(Coroutine).IsAssignableFrom(method.ReturnType):
foundStuff.Add(StuffToSearch.Coroutines);
break;
// Player dependent stuff, like current camera position, current map, currently selected things
case MethodInfo { DeclaringType: not null } method when method.ReturnType == typeof(CameraDriver):
case MethodInfo { DeclaringType: not null } method when typeof(CameraDriver).IsAssignableFrom(method.ReturnType):
foundStuff.Add(StuffToSearch.CameraDriver);
break;
case MethodInfo method when method == FindCurrentMap || method == GameCurrentMap:
foundStuff.Add(StuffToSearch.CurrentMap);
break;
case MethodInfo method when method.DeclaringType == typeof(Selector):
case MethodInfo method when typeof(Selector).IsAssignableFrom(method.DeclaringType):
foundStuff.Add(StuffToSearch.Selector);
break;
// Operating on time instead of ticks
case MethodInfo method when method.DeclaringType == typeof(Time):
case MethodInfo method when typeof(Time).IsAssignableFrom(method.DeclaringType):
foundStuff.Add(StuffToSearch.TimeManager);
break;
// Calls .GetHashCode, unless it's the method we're currently checking
case MethodInfo method when method == GetHashCodeMethod && baseMethod != GetHashCodeMethod:
// Calls GetHashCode, unless it's an override of GetHashCode (no base.GetHashCode calls)
case MethodInfo method when method == GetHashCodeMethod && !IsOverrideOf(baseMethodInfo, GetHashCodeMethod):
foundStuff.Add(StuffToSearch.GetHashCode);
break;
case MethodInfo method when NonTickingUpdateMethodCalls.Contains(method):
case MethodInfo method when IsOverrideOfAny(method, NonTickingUpdateMethodCalls):
foundStuff.Add(StuffToSearch.NonTickingUpdate);
break;
}
Expand Down Expand Up @@ -413,6 +417,37 @@ internal static void FindPatchedSyncMethods(Dictionary<StuffToSearch, List<strin
});
}

private static bool IsOverrideOf(MethodInfo method, MethodInfo target)
{
if (method == null)
return false;

var baseDefinition = method.GetBaseDefinition();
// If base definition is the same as current method, then
// it's not an override but a method declared in current type.
if (baseDefinition == method)
return false;

return baseDefinition == target;
}

private static bool IsOverrideOfAny(MethodInfo method, HashSet<MethodInfo> possibleTargets)
{
if (method == null)
return false;
// Skip if empty collection
if (!possibleTargets.Any())
return false;

var baseDefinition = method.GetBaseDefinition();
// If base definition is the same as current method, then
// it's not an override but a method declared in current type.
if (baseDefinition == method)
return false;

return possibleTargets.Contains(baseDefinition);
}

#endregion
}
}

0 comments on commit 949ce19

Please sign in to comment.