mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-01 05:49:23 +00:00
244 lines
No EOL
9.3 KiB
C#
244 lines
No EOL
9.3 KiB
C#
using System.Reflection;
|
|
using HarmonyLib;
|
|
using MelonLoader;
|
|
using UnityEngine;
|
|
|
|
namespace NAK.BullshitWatcher;
|
|
|
|
// Slapped together to log where bullshit values are coming from,
|
|
// instead of creating the same Unity Explorer patch for the 100th time
|
|
|
|
public class BullshitWatcherMod : MelonMod
|
|
{
|
|
#region Initialize
|
|
|
|
public override void OnInitializeMelon()
|
|
{
|
|
#region Transform Patches
|
|
|
|
Type transformType = typeof(Transform);
|
|
|
|
// Properties
|
|
PatchProperty(transformType, nameof(Transform.position));
|
|
PatchProperty(transformType, nameof(Transform.localPosition));
|
|
PatchProperty(transformType, nameof(Transform.rotation));
|
|
PatchProperty(transformType, nameof(Transform.localRotation));
|
|
PatchProperty(transformType, nameof(Transform.localScale));
|
|
PatchProperty(transformType, nameof(Transform.right));
|
|
PatchProperty(transformType, nameof(Transform.up));
|
|
PatchProperty(transformType, nameof(Transform.forward));
|
|
|
|
// Methods
|
|
PatchMethod(transformType, nameof(Transform.SetPositionAndRotation));
|
|
PatchMethod(transformType, nameof(Transform.SetLocalPositionAndRotation));
|
|
PatchMethod(transformType, nameof(Transform.Translate), new[] { typeof(Vector3), typeof(Space) });
|
|
PatchMethod(transformType, nameof(Transform.Translate), new[] { typeof(Vector3) });
|
|
PatchMethod(transformType, nameof(Transform.Translate), new[] { typeof(Vector3), typeof(Transform) });
|
|
PatchMethod(transformType, nameof(Transform.Rotate), new[] { typeof(Vector3), typeof(Space) });
|
|
PatchMethod(transformType, nameof(Transform.Rotate), new[] { typeof(Vector3) });
|
|
PatchMethod(transformType, nameof(Transform.Rotate), new[] { typeof(Vector3), typeof(float), typeof(Space) });
|
|
PatchMethod(transformType, nameof(Transform.RotateAround));
|
|
PatchMethod(transformType, nameof(Transform.LookAt), new[] { typeof(Vector3), typeof(Vector3) });
|
|
|
|
#endregion
|
|
|
|
#region Rigidbody Patches
|
|
|
|
Type rigidbodyType = typeof(Rigidbody);
|
|
|
|
// Properties
|
|
PatchProperty(rigidbodyType, nameof(Rigidbody.position));
|
|
PatchProperty(rigidbodyType, nameof(Rigidbody.rotation));
|
|
PatchProperty(rigidbodyType, nameof(Rigidbody.velocity));
|
|
PatchProperty(rigidbodyType, nameof(Rigidbody.angularVelocity));
|
|
PatchProperty(rigidbodyType, nameof(Rigidbody.centerOfMass));
|
|
|
|
// Methods
|
|
PatchMethod(rigidbodyType, nameof(Rigidbody.MovePosition));
|
|
PatchMethod(rigidbodyType, nameof(Rigidbody.MoveRotation));
|
|
PatchMethod(rigidbodyType, nameof(Rigidbody.AddForce), new[] { typeof(Vector3), typeof(ForceMode) });
|
|
PatchMethod(rigidbodyType, nameof(Rigidbody.AddRelativeForce), new[] { typeof(Vector3), typeof(ForceMode) });
|
|
PatchMethod(rigidbodyType, nameof(Rigidbody.AddTorque), new[] { typeof(Vector3), typeof(ForceMode) });
|
|
PatchMethod(rigidbodyType, nameof(Rigidbody.AddRelativeTorque), new[] { typeof(Vector3), typeof(ForceMode) });
|
|
PatchMethod(rigidbodyType, nameof(Rigidbody.AddForceAtPosition), new[] { typeof(Vector3), typeof(Vector3), typeof(ForceMode) });
|
|
|
|
#endregion
|
|
}
|
|
|
|
private void PatchProperty(Type type, string propertyName)
|
|
{
|
|
PropertyInfo property = type.GetProperty(propertyName);
|
|
if (property!.SetMethod != null)
|
|
{
|
|
HarmonyInstance.Patch(
|
|
property.SetMethod,
|
|
prefix: new HarmonyMethod(typeof(BullshitWatcherMod).GetMethod(nameof(OnSetValue),
|
|
BindingFlags.NonPublic | BindingFlags.Static))
|
|
);
|
|
}
|
|
}
|
|
|
|
private void PatchMethod(Type type, string methodName, Type[] parameters = null)
|
|
{
|
|
MethodInfo method;
|
|
if (parameters != null)
|
|
{
|
|
method = type.GetMethod(methodName,
|
|
BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy,
|
|
null,
|
|
parameters,
|
|
null);
|
|
}
|
|
else
|
|
{
|
|
var methods = type.GetMethods()
|
|
.Where(m => m.Name == methodName && !m.IsGenericMethod)
|
|
.ToArray();
|
|
|
|
// If there's only one method with this name, use it
|
|
if (methods.Length == 1)
|
|
{
|
|
method = methods[0];
|
|
}
|
|
else
|
|
{
|
|
// This is fine :)
|
|
LoggerInstance.Error($"Multiple methods found for {type.Name}.{methodName}, skipping ambiguous patch");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (method != null)
|
|
{
|
|
HarmonyInstance.Patch(
|
|
method,
|
|
prefix: new HarmonyMethod(typeof(BullshitWatcherMod).GetMethod(nameof(OnMethodCall),
|
|
BindingFlags.NonPublic | BindingFlags.Static))
|
|
);
|
|
}
|
|
else
|
|
{
|
|
MelonLogger.Warning($"Could not find method {type.Name}.{methodName}");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Validation Methods
|
|
|
|
private static bool ContainsBullshitValue(Vector3 vector)
|
|
{
|
|
return float.IsNaN(vector.x) || float.IsNaN(vector.y) || float.IsNaN(vector.z) ||
|
|
float.IsInfinity(vector.x) || float.IsInfinity(vector.y) || float.IsInfinity(vector.z) ||
|
|
float.IsNegativeInfinity(vector.x) || float.IsNegativeInfinity(vector.y) || float.IsNegativeInfinity(vector.z);
|
|
}
|
|
|
|
private static bool ContainsBullshitValue(Quaternion quaternion)
|
|
{
|
|
return float.IsNaN(quaternion.x) || float.IsNaN(quaternion.y) || float.IsNaN(quaternion.z) || float.IsNaN(quaternion.w) ||
|
|
float.IsInfinity(quaternion.x) || float.IsInfinity(quaternion.y) || float.IsInfinity(quaternion.z) || float.IsInfinity(quaternion.w) ||
|
|
float.IsNegativeInfinity(quaternion.x) || float.IsNegativeInfinity(quaternion.y) ||
|
|
float.IsNegativeInfinity(quaternion.z) || float.IsNegativeInfinity(quaternion.w);
|
|
}
|
|
|
|
private static bool ContainsBullshitValue(float value)
|
|
{
|
|
return float.IsNaN(value) || float.IsInfinity(value) || float.IsNegativeInfinity(value);
|
|
}
|
|
|
|
private static bool ContainsBullshitValues(object[] values)
|
|
{
|
|
foreach (var value in values)
|
|
{
|
|
if (value == null) continue;
|
|
|
|
switch (value)
|
|
{
|
|
case Vector3 v3:
|
|
if (ContainsBullshitValue(v3)) return true;
|
|
break;
|
|
case Quaternion q:
|
|
if (ContainsBullshitValue(q)) return true;
|
|
break;
|
|
case float f:
|
|
if (ContainsBullshitValue(f)) return true;
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Logging Methods
|
|
|
|
private static void LogBullshitValue(string componentType, string propertyName, Component component, object value)
|
|
{
|
|
MelonLogger.Error($"Bullshit {componentType}.{propertyName} value detected on GameObject '{GetGameObjectPath(component.gameObject)}': {value}");
|
|
MelonLogger.Error(Environment.StackTrace);
|
|
}
|
|
|
|
private static void LogBullshitMethod(string componentType, string methodName, Component component, object[] parameters)
|
|
{
|
|
MelonLogger.Error($"Bullshit parameters in {componentType}.{methodName} call on GameObject '{GetGameObjectPath(component.gameObject)}'");
|
|
for (int i = 0; i < parameters.Length; i++)
|
|
{
|
|
if (parameters[i] != null)
|
|
MelonLogger.Error($" Parameter {i}: {parameters[i]}");
|
|
}
|
|
MelonLogger.Error(Environment.StackTrace);
|
|
}
|
|
|
|
private static string GetGameObjectPath(GameObject obj)
|
|
{
|
|
string path = obj.name;
|
|
Transform parent = obj.transform.parent;
|
|
|
|
while (parent != null)
|
|
{
|
|
path = $"{parent.name}/{path}";
|
|
parent = parent.parent;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Harmony Patches
|
|
|
|
private static void OnSetValue(object value, Component __instance)
|
|
{
|
|
if (value == null) return;
|
|
|
|
var componentType = __instance.GetType().Name;
|
|
var propertyName = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().Name.Replace("set_", "");
|
|
|
|
switch (value)
|
|
{
|
|
case Vector3 v3 when ContainsBullshitValue(v3):
|
|
LogBullshitValue(componentType, propertyName, __instance, v3);
|
|
break;
|
|
case Quaternion q when ContainsBullshitValue(q):
|
|
LogBullshitValue(componentType, propertyName, __instance, q);
|
|
break;
|
|
case float f when ContainsBullshitValue(f):
|
|
LogBullshitValue(componentType, propertyName, __instance, f);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void OnMethodCall(object[] __args, Component __instance)
|
|
{
|
|
if (__args == null || __args.Length == 0) return;
|
|
|
|
if (ContainsBullshitValues(__args))
|
|
{
|
|
var componentType = __instance.GetType().Name;
|
|
var methodName = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().Name;
|
|
LogBullshitMethod(componentType, methodName, __instance, __args);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
} |