mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-27 16:09:07 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,348 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user