mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 06:19:22 +00:00
BullshitWatcher: lame
This commit is contained in:
parent
19d7eb1c7c
commit
3d6b1bbd59
3 changed files with 287 additions and 0 deletions
11
BullshitWatcher/BullshitWatcher.csproj
Normal file
11
BullshitWatcher/BullshitWatcher.csproj
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="TheClapper">
|
||||
<HintPath>..\.ManagedLibs\TheClapper.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
244
BullshitWatcher/Main.cs
Normal file
244
BullshitWatcher/Main.cs
Normal file
|
@ -0,0 +1,244 @@
|
|||
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
|
||||
}
|
32
BullshitWatcher/Properties/AssemblyInfo.cs
Normal file
32
BullshitWatcher/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using NAK.BullshitWatcher.Properties;
|
||||
using MelonLoader;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyTitle(nameof(NAK.BullshitWatcher))]
|
||||
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
|
||||
[assembly: AssemblyProduct(nameof(NAK.BullshitWatcher))]
|
||||
|
||||
[assembly: MelonInfo(
|
||||
typeof(NAK.BullshitWatcher.BullshitWatcherMod),
|
||||
nameof(NAK.BullshitWatcher),
|
||||
AssemblyInfoParams.Version,
|
||||
AssemblyInfoParams.Author,
|
||||
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/BullshitWatcher"
|
||||
)]
|
||||
|
||||
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
||||
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
|
||||
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
|
||||
[assembly: HarmonyDontPatchAll]
|
||||
|
||||
namespace NAK.BullshitWatcher.Properties;
|
||||
internal static class AssemblyInfoParams
|
||||
{
|
||||
public const string Version = "1.0.0";
|
||||
public const string Author = "NotAKidoS";
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue