Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LegacyBaseFovCompat v0.2.1
plugins/LegacyBaseFovCompat.dll
Decompiled 14 hours agousing System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HG.BlendableTypes; using IL.RoR2.CameraModes; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Cil; using RoR2; using RoR2.CameraModes; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LegacyBaseFovCompat")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.1.0")] [assembly: AssemblyInformationalVersion("0.2.1+28f179be37f2221235573792ed3e5bea41b748c2")] [assembly: AssemblyProduct("LegacyBaseFovCompat")] [assembly: AssemblyTitle("LegacyBaseFovCompat")] [assembly: AssemblyVersion("0.2.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LegacyBaseFovCompat { public sealed class AddFoVSettingsBridge { public const string Guid = "Quizzik.AddFoVSettings"; private readonly object main; private readonly Func<float> sliderValue; private readonly Func<bool> sprintFovEnabled; private readonly Func<object, object> currentCamera; public float SliderFov => sliderValue(); public bool SprintFovEnabled => sprintFovEnabled(); public object CurrentCamera => currentCamera(main); private AddFoVSettingsBridge(object main, Func<float> sliderValue, Func<bool> sprintFovEnabled, Func<object, object> currentCamera) { this.main = main; this.sliderValue = sliderValue; this.sprintFovEnabled = sprintFovEnabled; this.currentCamera = currentCamera; } public static AddFoVSettingsBridge TryCreate(object main, out string error) { error = null; if (main == null) { error = "plugin instance is null"; return null; } Type type = main.GetType(); FieldInfo field = type.GetField("FoVSlider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field2 = type.GetField("sprintFoVEffectsCheckbox", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field3 = type.GetField("currentCamera", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null || field2 == null || field3 == null || field3.FieldType.IsValueType) { error = "expected fields FoVSlider, sprintFoVEffectsCheckbox, currentCamera not found"; return null; } object value = field.GetValue(main); object value2 = field2.GetValue(main); if (value == null || value2 == null) { error = "settings objects are not created yet"; return null; } MethodInfo method = value.GetType().GetMethod("GetValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); MethodInfo method2 = value2.GetType().GetMethod("GetValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method?.ReturnType != typeof(float) || method2?.ReturnType != typeof(bool)) { error = "expected MenuSlider.GetValue(): float and MenuCheckbox.GetValue(): bool not found"; return null; } return new AddFoVSettingsBridge(main, (Func<float>)Delegate.CreateDelegate(typeof(Func<float>), value, method), (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), value2, method2), CreateFieldGetter(field3)); } private static Func<object, object> CreateFieldGetter(FieldInfo field) { DynamicMethod dynamicMethod = new DynamicMethod("LBFC_get_" + field.Name, typeof(object), new Type[1] { typeof(object) }, typeof(AddFoVSettingsBridge).Module, skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Castclass, field.DeclaringType); iLGenerator.Emit(OpCodes.Ldfld, field); iLGenerator.Emit(OpCodes.Ret); return (Func<object, object>)dynamicMethod.CreateDelegate(typeof(Func<object, object>)); } } public static class FovPatch { private const float MinFov = 1f; private const float MaxFov = 170f; private const float TraceInterval = 5f; public static Action<object> LogInfo = delegate { }; public static Action<object> LogError = delegate { }; public static Func<bool> TraceEnabled = () => false; public static AddFoVSettingsBridge AddFoVSettings; private static float nextTraceTime; private static bool lastWasScaled; public static bool HookApplied { get; private set; } public static void PatchUpdateInternal(ILContext il) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) HookApplied = false; ILCursor val = new ILCursor(il); int num = default(int); FieldReference val2 = default(FieldReference); if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[7] { (Instruction x) => ILPatternMatchingExt.MatchCall(x, typeof(CharacterCameraParamsData), "Blend"), (Instruction x) => ILPatternMatchingExt.MatchLdloc(x, ref num), (Instruction x) => ILPatternMatchingExt.MatchLdfld<CharacterCameraParamsData>(x, "fov"), (Instruction x) => ILPatternMatchingExt.MatchLdfld<BlendableFloat>(x, "value"), (Instruction x) => ILPatternMatchingExt.MatchLdloc(x, ref num), (Instruction x) => ILPatternMatchingExt.MatchLdfld(x, ref val2) && ((MemberReference)val2).Name == "sprintFovMultiplier" && ((MemberReference)((MemberReference)val2).DeclaringType).FullName == "RoR2.CameraModes.CameraModePlayerBasic/InstanceData", (Instruction x) => ILPatternMatchingExt.MatchMul(x) })) { LogError("UpdateInternal pattern not found; the game's camera code changed again."); return; } val.Index -= 1; Instruction next = val.Next; val.MoveAfterLabels(); val.Emit(OpCodes.Ldarg_2); next.OpCode = OpCodes.Call; next.Operand = il.Import((MethodBase)typeof(FovPatch).GetMethod("ComputeFov", BindingFlags.Static | BindingFlags.Public)); HookApplied = true; } public static float ComputeFov(float parameterFov, float sprintMultiplier, in CameraModeContext context) { CameraRigController cameraRigController = context.cameraInfo.cameraRigController; if (!Object.op_Implicit((Object)(object)cameraRigController) || cameraRigController.localUserViewer == null) { return parameterFov * sprintMultiplier; } float baseFov = context.cameraInfo.baseFov; float value = CharacterCameraParamsData.basic.fov.value; AddFoVSettingsBridge addFoVSettings = AddFoVSettings; bool flag = addFoVSettings != null && addFoVSettings.CurrentCamera == cameraRigController; float num = (flag ? addFoVSettings.SliderFov : 0f); bool flag2 = !flag || addFoVSettings.SprintFovEnabled; bool scaled; float result = Calculate(parameterFov, sprintMultiplier, baseFov, value, flag, num, flag2, out scaled); if (TraceEnabled()) { Trace(cameraRigController, scaled, flag, flag2, baseFov, num, parameterFov, sprintMultiplier, result); } if (SprintTransitionTrace.Enabled) { SprintTransitionTrace.Record(Time.frameCount, context.targetInfo.isSprinting, flag2, sprintMultiplier, baseFov, num, parameterFov, result, Object.op_Implicit((Object)(object)cameraRigController.sceneCam) ? cameraRigController.sceneCam.fieldOfView : (-1f)); } return result; } public static float Calculate(float parameterFov, float sprintMultiplier, float baseFov, float defaultFov, bool useModSettings, float sliderFov, bool sprintFovEnabled, out bool scaled) { if (useModSettings && sliderFov > 0f && defaultFov > 0f) { float num = Scale(parameterFov, sliderFov, defaultFov, out scaled); if (sprintFovEnabled) { return num * sprintMultiplier; } scaled = true; return num; } return Scale(parameterFov, baseFov, defaultFov, out scaled) * sprintMultiplier; } public static float Scale(float parameterFov, float baseFov, float defaultFov, out bool scaled) { scaled = baseFov > 0f && defaultFov > 0f && Math.Abs(baseFov - defaultFov) > 0.001f; if (!scaled) { return parameterFov; } return Clamp(parameterFov * (baseFov / defaultFov)); } private static float Clamp(float fov) { if (!(fov < 1f)) { if (!(fov > 170f)) { return fov; } return 170f; } return 1f; } private static void Trace(CameraRigController rig, bool scaled, bool modCamera, bool sprintFov, float baseFov, float sliderFov, float parameterFov, float sprintMultiplier, float result) { float unscaledTime = Time.unscaledTime; if (scaled != lastWasScaled || !(unscaledTime < nextTraceTime)) { lastWasScaled = scaled; nextTraceTime = unscaledTime + 5f; LogInfo($"[LBFC] frame={Time.frameCount} rig={((Object)rig).name} scaled={scaled} modCamera={modCamera} sprintFov={sprintFov} " + $"baseFov={baseFov:0.###} slider={sliderFov:0.###} paramFov={parameterFov:0.###} multiplier={sprintMultiplier:0.###} -> {result:0.###}"); } } } [BepInPlugin("Nhiblam.LegacyBaseFovCompat", "LegacyBaseFovCompat", "0.2.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class LegacyBaseFovCompatPlugin : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static Manipulator <0>__PatchUpdateInternal; } public const string Guid = "Nhiblam.LegacyBaseFovCompat"; public const string PluginName = "LegacyBaseFovCompat"; public const string PluginVersion = "0.2.1"; private bool hookInstalled; private void Awake() { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown ConfigEntry<bool> trace = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "TraceAppliedFov", false, "Troubleshooting: log the adjusted player camera FOV at most once every 5 seconds, and whenever scaling starts or stops."); FovPatch.LogInfo = ((BaseUnityPlugin)this).Logger.LogInfo; FovPatch.LogError = ((BaseUnityPlugin)this).Logger.LogError; FovPatch.TraceEnabled = () => trace.Value; SprintTransitionTrace.Log = ((BaseUnityPlugin)this).Logger.LogInfo; SprintTransitionTrace.Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "TraceSprintTransitions", false, $"Troubleshooting: log the frame before and {12} frames after a sprint start or stop, " + $"for the first {10} transitions of each session.").Value; ((BaseUnityPlugin)this).Logger.LogInfo((object)("LegacyBaseFovCompat 0.2.1 build=" + GetBuildId() + " sha256=" + GetOwnHash())); if (BindAddFoVSettings()) { object obj = <>O.<0>__PatchUpdateInternal; if (obj == null) { Manipulator val = FovPatch.PatchUpdateInternal; <>O.<0>__PatchUpdateInternal = val; obj = (object)val; } CameraModePlayerBasic.UpdateInternal += (Manipulator)obj; hookInstalled = true; if (FovPatch.HookApplied) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"CameraModePlayerBasic.UpdateInternal IL hook applied."); } else { ((BaseUnityPlugin)this).Logger.LogError((object)"CameraModePlayerBasic.UpdateInternal IL hook NOT applied; AddFoVSettings' FOV will not reach the camera."); } } } private void OnDestroy() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if (hookInstalled) { object obj = <>O.<0>__PatchUpdateInternal; if (obj == null) { Manipulator val = FovPatch.PatchUpdateInternal; <>O.<0>__PatchUpdateInternal = val; obj = (object)val; } CameraModePlayerBasic.UpdateInternal -= (Manipulator)obj; } } private bool BindAddFoVSettings() { if (!Chainloader.PluginInfos.TryGetValue("Quizzik.AddFoVSettings", out var value) || !Object.op_Implicit((Object)(object)value.Instance)) { ((BaseUnityPlugin)this).Logger.LogError((object)"AddFoVSettings is not loaded; LegacyBaseFovCompat is inactive."); return false; } FovPatch.AddFoVSettings = AddFoVSettingsBridge.TryCreate(value.Instance, out var error); if (FovPatch.AddFoVSettings == null) { ((BaseUnityPlugin)this).Logger.LogError((object)$"AddFoVSettings {value.Metadata.Version} found but its settings could not be read ({error}); LegacyBaseFovCompat is inactive."); return false; } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"AddFoVSettings {value.Metadata.Version} bound: slider={FovPatch.AddFoVSettings.SliderFov} sprintFov={FovPatch.AddFoVSettings.SprintFovEnabled}."); return true; } private static string GetBuildId() { return typeof(LegacyBaseFovCompatPlugin).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "unknown"; } private string GetOwnHash() { try { using SHA256 sHA = SHA256.Create(); using FileStream inputStream = File.OpenRead(((BaseUnityPlugin)this).Info.Location); return BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", ""); } catch (Exception ex) { return "unavailable (" + ex.GetType().Name + ")"; } } } public static class SprintTransitionTrace { private struct Sample { public int Frame; public bool Sprinting; public bool SprintFov; public float Multiplier; public float BaseFov; public float Slider; public float ParamFov; public float Result; public float UnityFovPrevFrame; } public const int FramesAfter = 12; public const int MaxTransitions = 10; public static Action<object> Log = delegate { }; public static bool Enabled; private static int transitions; private static int remaining; private static int lastFrame = -1; private static bool hasPrevious; private static Sample previous; public static void Record(int frame, bool sprinting, bool sprintFov, float multiplier, float baseFov, float slider, float paramFov, float result, float unityFovPrevFrame) { if (Enabled && frame != lastFrame) { lastFrame = frame; Sample s = new Sample { Frame = frame, Sprinting = sprinting, SprintFov = sprintFov, Multiplier = multiplier, BaseFov = baseFov, Slider = slider, ParamFov = paramFov, Result = result, UnityFovPrevFrame = unityFovPrevFrame }; if (hasPrevious && sprinting != previous.Sprinting && transitions < 10) { transitions++; remaining = 12; Log($"[LBFC-SPRINT] transition {transitions}/{10} sprint {previous.Sprinting} -> {sprinting}"); Write("before", previous); } if (remaining > 0) { remaining--; Write("after", s); } previous = s; hasPrevious = true; } } private static void Write(string label, Sample s) { Log($"[LBFC-SPRINT] {label} frame={s.Frame} sprinting={s.Sprinting} sprintFov={s.SprintFov} multiplier={s.Multiplier:0.####} " + $"baseFov={s.BaseFov:0.###} slider={s.Slider:0.###} paramFov={s.ParamFov:0.###} result={s.Result:0.###} unityFovPrevFrame={s.UnityFovPrevFrame:0.###}"); } } }