Consolidates two standalone Railroader mods into one UMM "everything mod" with an optional-module framework. Modules are disabled by default and toggled per-module from the S3 settings page. Core framework: - IModule contract plus ModuleRegistry, with each module owning a Harmony instance scoped by its id so only enabled modules patch the game - Per-module flat JSON settings (SettingsStore). On this Mono runtime JsonUtility silently drops nested custom-class fields, so settings stay flat - Foldout-per-module settings panel plus a detector that offers to disable the old standalone mods if they are still installed Modules (moved over to parity, verified in-game): - Physics Optimizer (was RailroaderPhysicsOverhaul): LOD fast-path and auto-freeze, profiler overlay, debug car tinting, /rpf console commands - Map Popout (was RRPopout): native map detach window. Pure-UMM install that drops the winhttp proxy and LoadLibrary's RRPopout.dll from the mod folder. Native Win32 + D3D11 + Dear ImGui engine included. Fixes a latent break where the now-private MapBuilder.UpdateForZoom() is reached via Traverse. Build: dotnet for the managed assembly (netstandard2.1) and CMake for the native DLL. build-local.ps1 installs into the game, build-release.ps1 packages the UMM drag-install zip.
99 lines
3.8 KiB
C#
99 lines
3.8 KiB
C#
using System;
|
|
using HarmonyLib;
|
|
using S3.Core;
|
|
using UnityEngine;
|
|
|
|
namespace S3.Modules.PhysicsOptimizer;
|
|
|
|
/// <summary>
|
|
/// S³ module wrapping the physics-CPU optimizations (LOD fast-path + auto-freeze)
|
|
/// plus the profiler overlay and debug visualizer.
|
|
///
|
|
/// Owns a Harmony instance keyed "S3.physics" and patches only its own types, so
|
|
/// the patches exist only while the module is enabled. <see cref="Settings"/> is
|
|
/// a static instance the overlay/visualizer read directly.
|
|
/// </summary>
|
|
public sealed class PhysicsOptimizerModule : IModule
|
|
{
|
|
private const string SettingsFile = "S3.physics.json";
|
|
|
|
public static PhysicsSettings Settings { get; private set; } = new();
|
|
|
|
private static Harmony? _harmony;
|
|
|
|
// Attribute-annotated patch classes the module owns. PosCarsTimerPatch is NOT
|
|
// here — it has no [HarmonyPatch] attribute and is applied dynamically by
|
|
// PhysicsTimer.TryPatchPositionCars (method name varies by game version).
|
|
private static readonly Type[] PatchTypes =
|
|
{
|
|
typeof(PositionWheelBoundsFrontLODPatch),
|
|
typeof(TrainControllerFixedUpdateCounterPatch),
|
|
typeof(ShouldSkipTickPatch),
|
|
typeof(TickTimerPatch),
|
|
typeof(FixedUpdateTimerPatch),
|
|
typeof(ConsoleCommandPatch),
|
|
};
|
|
|
|
public PhysicsOptimizerModule() => Settings = SettingsStore.Load<PhysicsSettings>(SettingsFile);
|
|
|
|
public string Id => "physics";
|
|
public string DisplayName => "Physics Optimizer";
|
|
public string Description =>
|
|
"Cuts CPU time spent on train physics (LOD fast-path + auto-freeze for far/slow consists). " +
|
|
"Includes a profiler overlay and debug car tinting. Console: /rpf";
|
|
|
|
public bool Enabled
|
|
{
|
|
get => Settings.enabled;
|
|
set => Settings.enabled = value;
|
|
}
|
|
|
|
public void OnEnable()
|
|
{
|
|
ApplySettingsToStatics();
|
|
|
|
_harmony = new Harmony("S3.physics");
|
|
foreach (Type t in PatchTypes)
|
|
_harmony.CreateClassProcessor(t).Patch();
|
|
PhysicsTimer.TryPatchPositionCars(_harmony, Main.ModEntry.Logger);
|
|
|
|
var go = new GameObject("S3.PhysicsOptimizer.Host");
|
|
UnityEngine.Object.DontDestroyOnLoad(go);
|
|
PhysicsOverlayGUI overlay = go.AddComponent<PhysicsOverlayGUI>();
|
|
overlay.Visible = Settings.ShowOverlay;
|
|
overlay.Opacity = Settings.OverlayOpacity;
|
|
go.AddComponent<CarDebugVisualizer>();
|
|
}
|
|
|
|
public void OnDisable()
|
|
{
|
|
// Reserved for live toggling. Today modules apply on next launch, so this
|
|
// is not called; when it is, unpatch via _harmony.UnpatchAll("S3.physics")
|
|
// and destroy the host GameObject.
|
|
_harmony?.UnpatchAll("S3.physics");
|
|
_harmony = null;
|
|
}
|
|
|
|
public void SaveSettings() => Persist();
|
|
internal static void Persist() => SettingsStore.Save(SettingsFile, Settings);
|
|
|
|
public void DrawSettings() => PhysicsSettingsUI.Draw();
|
|
|
|
private static void ApplySettingsToStatics()
|
|
{
|
|
ConsistLOD.Enabled = Settings.OptimizerEnabled;
|
|
ConsistLOD.DistanceThreshold = Settings.DistanceThreshold;
|
|
ConsistLOD.ResyncInterval = Settings.ResyncInterval;
|
|
ConsistLOD.BlacklistEnabled = Settings.BlacklistEnabled;
|
|
ConsistLOD.IsBlacklist = Settings.IsBlacklist;
|
|
ConsistLOD.RoadNumberList.Clear();
|
|
foreach (string name in Settings.RoadNumberList)
|
|
ConsistLOD.RoadNumberList.Add(name);
|
|
ConsistLOD.ExcludeLocomotives = Settings.ExcludeLocosFromLOD;
|
|
|
|
ConsistFreezer.ExcludeLocomotives = Settings.ExcludeLocosFromFreeze;
|
|
ConsistFreezer.AutoFreezeEnabled = Settings.AutoFreezeEnabled;
|
|
ConsistFreezer.AutoFreezeDistance = Settings.AutoFreezeDistance;
|
|
ConsistFreezer.AutoFreezeSpeedThreshold = Settings.AutoFreezeSpeedThreshold;
|
|
}
|
|
}
|