refactor: phase 5 transpiler docs, mod-compat helpers, and build quality gates

This commit is contained in:
2026-06-23 23:03:13 +08:00
parent cca8b8594d
commit c2016909ff
43 changed files with 2327 additions and 135 deletions
@@ -0,0 +1,82 @@
using System;
using System.Reflection;
using BepInEx;
using BepInEx.Bootstrap;
using HarmonyLib;
namespace UXAssist.Common.ModCompat;
public static class ModCompatHelper
{
public static bool TryGetLoadedPluginInfo(string guid, out BepInEx.PluginInfo pluginInfo)
{
return Chainloader.PluginInfos.TryGetValue(guid, out pluginInfo) && pluginInfo != null;
}
public static bool TryGetPluginType(BepInEx.PluginInfo pluginInfo, string typeName, out Type type)
{
type = null;
if (pluginInfo?.Instance == null) return false;
try
{
type = pluginInfo.Instance.GetType().Assembly.GetType(typeName, throwOnError: false);
}
catch
{
// ignored
}
return type != null;
}
public static bool TryGetPluginType(string guid, string typeName, out Type type)
{
type = null;
return TryGetLoadedPluginInfo(guid, out var pluginInfo) && TryGetPluginType(pluginInfo, typeName, out type);
}
public static bool TryGetField(Type type, string fieldName, out FieldInfo field)
{
field = null;
if (type == null) return false;
field = AccessTools.Field(type, fieldName);
return field != null;
}
public static bool TryGetFieldValue<T>(Type type, string fieldName, object instance, out T value)
{
value = default;
if (!TryGetField(type, fieldName, out var field)) return false;
try
{
var result = field.GetValue(instance);
if (result is T t)
{
value = t;
return true;
}
}
catch
{
// ignored
}
return false;
}
public static bool TryGetMethod(Type type, string methodName, out MethodInfo method)
{
method = null;
if (type == null) return false;
method = AccessTools.Method(type, methodName);
return method != null;
}
public static bool TryGetPropertySetter(Type type, string propertyName, out MethodInfo setter)
{
setter = null;
if (type == null) return false;
var property = AccessTools.Property(type, propertyName);
if (property == null) return false;
setter = property.GetSetMethod(nonPublic: true);
return setter != null;
}
}
@@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.Reflection.Emit;
using BepInEx.Logging;
using HarmonyLib;
namespace UXAssist.Common.Patching;
/// <summary>
/// Helper for Harmony transpilers. Provides a standardized way to bail out and return the original
/// instructions when a <see cref="CodeMatcher"/> fails to match, which makes version-fragile patches
/// easier to diagnose at runtime.
/// </summary>
public static class TranspilerGuard
{
public static IEnumerable<CodeInstruction> Finish(
this CodeMatcher matcher,
IEnumerable<CodeInstruction> originalInstructions,
ManualLogSource logger,
string transpilerName)
{
if (matcher.IsInvalid)
{
logger?.LogWarning($"Transpiler '{transpilerName}' failed to match; returning original instructions.");
return originalInstructions;
}
return matcher.InstructionEnumeration();
}
}
@@ -0,0 +1,37 @@
using System.Reflection;
using HarmonyLib;
namespace UXAssist.Common.Utils;
public static class DysonSphereReflection
{
private static readonly FieldInfo TotalNodeSpField = AccessTools.Field(typeof(DysonSphereLayer), "totalNodeSP");
private static readonly FieldInfo TotalFrameSpField = AccessTools.Field(typeof(DysonSphereLayer), "totalFrameSP");
private static readonly FieldInfo TotalCpField = AccessTools.Field(typeof(DysonSphereLayer), "totalCP");
public static bool IsAvailable => TotalNodeSpField != null && TotalFrameSpField != null && TotalCpField != null;
public static bool HasTotalNodeSP => TotalNodeSpField != null;
public static bool HasTotalFrameSP => TotalFrameSpField != null;
public static bool HasTotalCP => TotalCpField != null;
public static long? GetTotalNodeSP(DysonSphereLayer layer)
=> layer != null && TotalNodeSpField != null ? (long?)TotalNodeSpField.GetValue(layer) : null;
public static long? GetTotalFrameSP(DysonSphereLayer layer)
=> layer != null && TotalFrameSpField != null ? (long?)TotalFrameSpField.GetValue(layer) : null;
public static long? GetTotalCP(DysonSphereLayer layer)
=> layer != null && TotalCpField != null ? (long?)TotalCpField.GetValue(layer) : null;
public static void SetTotalNodeSP(DysonSphereLayer layer, long value)
=> TotalNodeSpField?.SetValue(layer, value);
public static void SetTotalFrameSP(DysonSphereLayer layer, long value)
=> TotalFrameSpField?.SetValue(layer, value);
public static void SetTotalCP(DysonSphereLayer layer, long value)
=> TotalCpField?.SetValue(layer, value);
}