mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 07:29:00 -07:00
349 lines
8.3 KiB
C#
349 lines
8.3 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using HarmonyLib;
|
|
using UnityEngine;
|
|
|
|
internal static class ColliderCastGuardPatch
|
|
{
|
|
internal static void Apply(Harmony harmony)
|
|
{
|
|
if (!Enabled) return;
|
|
int installed = 0;
|
|
foreach (var method in TargetMethods())
|
|
{
|
|
try
|
|
{
|
|
Install(harmony, method);
|
|
installed++;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RadicadeEacBypassPlugin.LogSource.LogError("[ColliderGuard] failed on " + Describe(method) + ": " + ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static void Install(Harmony harmony, MethodBase method)
|
|
{
|
|
var prefix = IsColliderFrame(method)
|
|
? new HarmonyMethod(typeof(ColliderCastGuardPatch), nameof(NoteCollider)) : null;
|
|
harmony.Patch(method, prefix: prefix,
|
|
finalizer: new HarmonyMethod(typeof(ColliderCastGuardPatch), nameof(Swallow)));
|
|
}
|
|
private static readonly bool _disabled = IsDisabled();
|
|
|
|
private static int _swallows;
|
|
|
|
private static readonly Dictionary<MethodBase, string> LastCollider = new Dictionary<MethodBase, string>();
|
|
|
|
|
|
private static readonly HashSet<MethodBase> ColliderFrames = new HashSet<MethodBase>();
|
|
|
|
private static readonly string[] ColliderParameterTypes = new string[2] { "AOCLEIAJHKP", "AKCGGCOFMHJ" };
|
|
|
|
private static long _inspections;
|
|
|
|
private static readonly string[] CastTypes = new string[3] { "BPJMFIFDPJO", "CBKENBPBBPK", "AOCLEIAJHKP" };
|
|
|
|
private const string RbexAssembly = "RecRoom.Rbex.Runtime";
|
|
|
|
private const string GameAssembly = "Assembly-CSharp";
|
|
|
|
private static readonly (string Assembly, string Type, string Method, int Args, string Role)[] Specs = new(string, string, string, int, string)[5]
|
|
{
|
|
("RecRoom.Rbex.Runtime", "NFMLBBHIOHJ", "JAMGDJKMPGM", 2, "locomotion: attach/detach a collider child"),
|
|
("RecRoom.Rbex.Runtime", "NPDJILHGCBI", "JAMGDJKMPGM", 2, "locomotion: collider-child update (twin implementation)"),
|
|
("RecRoom.Rbex.Runtime", "MOBAMIJAHPJ", "JDAFCHGGCBG", 1, "teardown: unregister a collider from its body"),
|
|
("RecRoom.Rbex.Runtime", "CBKENBPBBPK", "JDIBOGNGEFK", 0, "teardown: body Dispose -> collider walk"),
|
|
("Assembly-CSharp", "RecRoom.Core.Locomotion.PlayerMovement", "AuthorityUpdateMovement", 2, "locomotion: authority tick (backstop for every caller of the cast)")
|
|
};
|
|
|
|
internal static bool Enabled => !_disabled;
|
|
|
|
[HarmonyPrefix]
|
|
private static void NoteCollider(object __0, MethodBase __originalMethod)
|
|
{
|
|
try
|
|
{
|
|
if (__0 == null || __originalMethod == null || !ColliderFrames.Contains(__originalMethod))
|
|
{
|
|
return;
|
|
}
|
|
long num = ++_inspections;
|
|
if (num > 200 && num % 50 != 0L)
|
|
{
|
|
return;
|
|
}
|
|
string text = DescribeCollider(__0);
|
|
if (text != null)
|
|
{
|
|
if (__originalMethod != null)
|
|
{
|
|
LastCollider[__originalMethod] = text;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
private static string DescribeCollider(object collider)
|
|
{
|
|
string text = null;
|
|
try
|
|
{
|
|
CBKENBPBBPK val = (CBKENBPBBPK)((collider is CBKENBPBBPK) ? collider : null);
|
|
if (val != null)
|
|
{
|
|
text = NameOf(val.JPBNIBGFKJO);
|
|
}
|
|
else
|
|
{
|
|
BPJMFIFDPJO val2 = (BPJMFIFDPJO)((collider is BPJMFIFDPJO) ? collider : null);
|
|
if (val2 != null)
|
|
{
|
|
text = NameOf(val2.JPBNIBGFKJO);
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
string name;
|
|
try
|
|
{
|
|
name = collider.GetType().Name;
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
if (text != null)
|
|
{
|
|
return name + " on '" + text + "'";
|
|
}
|
|
return name;
|
|
}
|
|
|
|
private static string NameOf(GameObject gameObject)
|
|
{
|
|
try
|
|
{
|
|
return gameObject == null ? null : gameObject.name;
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static bool IsDisabled()
|
|
{
|
|
try
|
|
{
|
|
string environmentVariable = Environment.GetEnvironmentVariable("RADICADE_COLLIDER_GUARD");
|
|
if (string.IsNullOrWhiteSpace(environmentVariable))
|
|
{
|
|
return false;
|
|
}
|
|
bool result;
|
|
switch (environmentVariable.Trim().ToLowerInvariant())
|
|
{
|
|
case "0":
|
|
case "off":
|
|
case "false":
|
|
case "no":
|
|
result = true;
|
|
break;
|
|
default:
|
|
result = false;
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<MethodBase> TargetMethods()
|
|
{
|
|
List<MethodBase> list = new List<MethodBase>();
|
|
(string, string, string, int, string)[] specs = Specs;
|
|
for (int i = 0; i < specs.Length; i++)
|
|
{
|
|
(string, string, string, int, string) tuple = specs[i];
|
|
foreach (MethodBase item in Resolve(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4))
|
|
{
|
|
list.Add(item);
|
|
if (IsColliderFrame(item))
|
|
{
|
|
ColliderFrames.Add(item);
|
|
}
|
|
}
|
|
}
|
|
if (list.Count == 0)
|
|
{
|
|
RadicadeEacBypassPlugin.LogSource.LogWarning((object)("[ColliderGuard] none of the " + Specs.Length + " known collider frames resolved - the physics cast failure is NOT guarded."));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private static bool IsColliderFrame(MethodBase method)
|
|
{
|
|
try
|
|
{
|
|
ParameterInfo[] parameters = method.GetParameters();
|
|
if (parameters.Length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
string name = parameters[0].ParameterType.Name;
|
|
string[] colliderParameterTypes = ColliderParameterTypes;
|
|
foreach (string b in colliderParameterTypes)
|
|
{
|
|
if (string.Equals(name, b, StringComparison.Ordinal))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static Type FindType(string assemblyName, string typeName)
|
|
{
|
|
try
|
|
{
|
|
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
|
foreach (Assembly assembly in assemblies)
|
|
{
|
|
try
|
|
{
|
|
if (string.Equals(assembly.GetName().Name, assemblyName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Type type = assembly.GetType(typeName, throwOnError: false);
|
|
if (type != null)
|
|
{
|
|
return type;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
try
|
|
{
|
|
return Type.GetType(typeName + ", " + assemblyName, throwOnError: false);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
try
|
|
{
|
|
return Assembly.Load(new AssemblyName(assemblyName)).GetType(typeName, throwOnError: false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RadicadeEacBypassPlugin.LogSource.LogWarning((object)("[ColliderGuard] " + assemblyName + " could not be loaded: " + ex.Message));
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<MethodBase> Resolve(string assemblyName, string typeName, string methodName, int args)
|
|
{
|
|
var type = FindType(assemblyName, typeName);
|
|
if (type == null) yield break;
|
|
foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
|
|
if (method.Name == methodName && method.GetParameters().Length == args)
|
|
yield return method;
|
|
}
|
|
[HarmonyFinalizer]
|
|
private static Exception Swallow(Exception __exception, MethodBase __originalMethod)
|
|
{
|
|
if (__exception == null || !Enabled)
|
|
{
|
|
return __exception;
|
|
}
|
|
if (!IsPhysicsCastFailure(__exception))
|
|
{
|
|
return __exception;
|
|
}
|
|
int num = ++_swallows;
|
|
if (num <= 5 || num % 100 == 0)
|
|
{
|
|
RadicadeEacBypassPlugin.LogSource.LogWarning((object)("[ColliderGuard] caught a collider cast failure in " + Describe(__originalMethod) + " (#" + num + ")" + DescribeArgument(__originalMethod) + ": " + __exception.GetType().Name + ": " + __exception.Message + " - the failed operation was aborted; remaining work in that method did not run."));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static string DescribeArgument(MethodBase method)
|
|
{
|
|
try
|
|
{
|
|
if (method != null && LastCollider.TryGetValue(method, out var value))
|
|
{
|
|
return " collider=" + value;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return string.Empty;
|
|
}
|
|
|
|
private static string Describe(MethodBase method)
|
|
{
|
|
try
|
|
{
|
|
return (method == null) ? "<unknown frame>" : (method.DeclaringType?.ToString() + "." + method.Name);
|
|
}
|
|
catch
|
|
{
|
|
return "<unknown frame>";
|
|
}
|
|
}
|
|
|
|
private static bool IsPhysicsCastFailure(Exception exception)
|
|
{
|
|
string text;
|
|
try
|
|
{
|
|
text = exception.Message ?? string.Empty;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
if (text.IndexOf("Unable to cast object of type", StringComparison.OrdinalIgnoreCase) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
string[] castTypes = CastTypes;
|
|
foreach (string value in castTypes)
|
|
{
|
|
if (text.IndexOf(value, StringComparison.Ordinal) >= 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|