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 RuneheimPerformance v0.9.3
RuneheimPerformance.dll
Decompiled an hour ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using RuneheimCore; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("RuneheimPerformance")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("RuneheimPerformance")] [assembly: AssemblyTitle("RuneheimPerformance")] [assembly: AssemblyVersion("1.0.0.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 RuneheimPerformance { public static class AiThrottle { private class Acc { public float Time; public Character Character; } [HarmonyPatch(typeof(MonoUpdatersExtra), "UpdateAI")] private static class Loop { private static bool Prefix(List<IUpdateAI> container, List<IUpdateAI> source, float deltaTime) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) if (!On.Value || (Object)(object)Player.m_localPlayer == (Object)null) { return true; } Vector3 position = ((Component)Player.m_localPlayer).transform.position; float num = Distance.Value * Distance.Value; float num2 = (float)Every.Value * deltaTime; int num3 = 0; container.AddRange(source); foreach (IUpdateAI item in container) { BaseAI val = (BaseAI)(object)((item is BaseAI) ? item : null); if (val == null || (Object)(object)val == (Object)null) { item.UpdateAI(deltaTime); continue; } Acc value = accs.GetValue(val, (BaseAI a) => new Acc { Character = ((Component)a).GetComponent<Character>() }); Character character = value.Character; if ((Object)(object)character != (Object)null && !character.IsBoss() && !val.IsAlerted()) { Vector3 val2 = ((Component)val).transform.position - position; if (((Vector3)(ref val2)).sqrMagnitude > num) { num3++; value.Time += deltaTime; if (!(value.Time + 0.0001f < num2)) { float time = value.Time; value.Time = 0f; item.UpdateAI(time); } continue; } } float num4 = deltaTime + value.Time; value.Time = 0f; item.UpdateAI(num4); } container.Clear(); Throttled = num3; return false; } } public static ConfigEntry<bool> On; public static ConfigEntry<float> Distance; public static ConfigEntry<int> Every; public static int Throttled; private static readonly ConditionalWeakTable<BaseAI, Acc> accs = new ConditionalWeakTable<BaseAI, Acc>(); public static void Init(ConfigFile c) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown On = c.Bind<bool>("Creature AI", "Throttle distant creature AI", false, "Calm creatures far from you (animals, tamed creatures in pens, idle monsters) think less often. Alerted creatures and bosses always think at full speed."); Distance = c.Bind<float>("Creature AI", "Distance", 40f, new ConfigDescription("Metres from you beyond which calm creatures are throttled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 150f), Array.Empty<object>())); Every = c.Bind<int>("Creature AI", "Update every (ticks)", 4, new ConfigDescription("Throttled creatures think once every this many AI ticks (vanilla: every tick, 20 a second).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 10), Array.Empty<object>())); } } public class AnimThrottle : MonoBehaviour { private class State { public Animator Anim; public bool Slow; public float Time; } public static ConfigEntry<bool> On; public static ConfigEntry<float> Distance; public static ConfigEntry<int> Every; public static int Throttled; private readonly Dictionary<Character, State> states = new Dictionary<Character, State>(); private float cleanAt; public static void Init(ConfigFile c) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown On = c.Bind<bool>("Animations", "Throttle distant animations", false, "Creatures far away animate at a lower rate (players and bosses never)."); Distance = c.Bind<float>("Animations", "Distance", 40f, new ConfigDescription("Metres from the camera beyond which creatures animate less often.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 150f), Array.Empty<object>())); Every = c.Bind<int>("Animations", "Animate every (frames)", 3, new ConfigDescription("Distant creatures animate once every this many frames.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 8), Array.Empty<object>())); } private void Update() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) Camera mainCamera = Utils.GetMainCamera(); if (!On.Value || (Object)(object)mainCamera == (Object)null) { if (states.Count > 0) { ReleaseAll(); } return; } Vector3 position = ((Component)mainCamera).transform.position; float num = Distance.Value * Distance.Value; int value = Every.Value; int frameCount = Time.frameCount; int num2 = 0; float deltaTime = Time.deltaTime; foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter == (Object)null || allCharacter is Player || allCharacter.IsBoss()) { continue; } if (!states.TryGetValue(allCharacter, out var value2)) { Dictionary<Character, State> dictionary = states; State obj = new State { Anim = ((Component)allCharacter).GetComponentInChildren<Animator>() }; value2 = obj; dictionary[allCharacter] = obj; } if ((Object)(object)value2.Anim == (Object)null) { continue; } Vector3 val = ((Component)allCharacter).transform.position - position; bool flag = ((Vector3)(ref val)).sqrMagnitude > num; if (flag != value2.Slow) { if (flag) { Stop(value2); } else { Release(value2); } } if (flag) { num2++; value2.Time += deltaTime; if ((frameCount + ((Object)allCharacter).GetInstanceID()) % value == 0) { value2.Anim.Update(value2.Time); value2.Time = 0f; } } } Throttled = num2; if (Time.unscaledTime > cleanAt) { cleanAt = Time.unscaledTime + 5f; Clean(); } } private static void Stop(State s) { s.Anim.keepAnimatorStateOnDisable = true; ((Behaviour)s.Anim).enabled = false; s.Slow = true; s.Time = 0f; } private static void Release(State s) { if ((Object)(object)s.Anim != (Object)null) { ((Behaviour)s.Anim).enabled = true; } s.Slow = false; s.Time = 0f; } private void ReleaseAll() { foreach (State value in states.Values) { if (value.Slow) { Release(value); } } states.Clear(); Throttled = 0; } private void Clean() { List<Character> list = null; foreach (KeyValuePair<Character, State> state in states) { if ((Object)(object)state.Key == (Object)null) { List<Character> obj = list ?? new List<Character>(); list = obj; obj.Add(state.Key); } } if (list == null) { return; } foreach (Character item in list) { states.Remove(item); } } private void OnDestroy() { ReleaseAll(); } } public class Benchmark : MonoBehaviour { private enum Phase { Idle, WarmA, MeasureA, WarmB, MeasureB } private Phase phase; private float until; private readonly List<float> frames = new List<float>(); private string resultA; private string resultB; private readonly Dictionary<ConfigEntry<bool>, bool> had = new Dictionary<ConfigEntry<bool>, bool>(); private float hadCycle; private int combinedA; public static string Status = ""; private void Update() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (phase == Phase.Idle) { if (Keys.Down(Plugin.BenchmarkKey.Value) && (Object)(object)Player.m_localPlayer != (Object)null) { Begin(); } return; } float unscaledTime = Time.unscaledTime; if (phase == Phase.MeasureA || phase == Phase.MeasureB) { frames.Add(Time.unscaledDeltaTime * 1000f); } Status = ((phase == Phase.WarmA || phase == Phase.MeasureA) ? $"Benchmark: with Runeheim Performance... {Mathf.CeilToInt(until - unscaledTime)} s" : $"Benchmark: without (vanilla)... {Mathf.CeilToInt(until - unscaledTime)} s"); if (unscaledTime < until) { return; } switch (phase) { case Phase.WarmA: phase = Phase.MeasureA; frames.Clear(); until = unscaledTime + 10f; break; case Phase.MeasureA: resultA = Summary(frames); combinedA = Combiner.CombinedPieces; had.Clear(); foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in ((BaseUnityPlugin)Plugin.Instance).Config) { if (item.Value is ConfigEntry<bool> val && item.Key.Section != "Server simulation") { had[val] = val.Value; val.Value = false; } } hadCycle = Plugin.WearCycle.Value; Plugin.WearCycle.Value = 0f; phase = Phase.WarmB; until = unscaledTime + 3f; break; case Phase.WarmB: phase = Phase.MeasureB; frames.Clear(); until = unscaledTime + 10f; break; case Phase.MeasureB: { resultB = Summary(frames); foreach (KeyValuePair<ConfigEntry<bool>, bool> item2 in had) { if (item2.Key != Preset.LowSpec) { item2.Key.Value = item2.Value; } } Plugin.WearCycle.Value = hadCycle; phase = Phase.Idle; string text = $"Benchmark ({WearNTear.GetAllInstances().Count} pieces, {combinedA} combined)\n With: {resultA}\n Without: {resultB}"; Plugin.Log.LogInfo((object)text.Replace("\n", " | ")); Status = text + "\n(combined areas rebuild over the next seconds)"; break; } } } private void Begin() { phase = Phase.WarmA; until = Time.unscaledTime + 3f; frames.Clear(); Overlay.ForceOpen(); ((Character)Player.m_localPlayer).Message((MessageType)2, "Benchmark: stand still and look at your build (23 s)", 0, (Sprite)null, false); } private static string Summary(List<float> ms) { if (ms.Count == 0) { return "no frames"; } List<float> list = ms.OrderBy((float x) => x).ToList(); float num = list.Average(); float num2 = list[Mathf.Clamp(Mathf.CeilToInt((float)list.Count * 0.99f) - 1, 0, list.Count - 1)]; return $"{1000f / num:0.0} fps avg ({num:0.0} ms), 1% low {1000f / num2:0.0} fps, worst frame {list[list.Count - 1]:0.0} ms"; } } public class ClothCull : MonoBehaviour { public static ConfigEntry<bool> On; public static ConfigEntry<float> Distance; public static int Paused; private readonly List<Cloth> cloths = new List<Cloth>(); private readonly HashSet<Cloth> paused = new HashSet<Cloth>(); private readonly HashSet<Cloth> skip = new HashSet<Cloth>(); private float findAt; private float checkAt; public static void Init(ConfigFile c) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown On = c.Bind<bool>("Cloth", "Pause distant cloth", false, "Banners, curtains, sails and capes too far away to see moving stop simulating until you come closer."); Distance = c.Bind<float>("Cloth", "Distance", 60f, new ConfigDescription("Metres from the camera beyond which cloth pauses.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 200f), Array.Empty<object>())); } private void Update() { //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) if (!On.Value || (Object)(object)Player.m_localPlayer == (Object)null) { if (paused.Count > 0) { ResumeAll(); } return; } float unscaledTime = Time.unscaledTime; if (unscaledTime >= findAt) { findAt = unscaledTime + 5f; cloths.Clear(); cloths.AddRange(Object.FindObjectsByType<Cloth>((FindObjectsSortMode)0)); paused.RemoveWhere((Cloth x) => (Object)(object)x == (Object)null); skip.RemoveWhere((Cloth x) => (Object)(object)x == (Object)null); foreach (Cloth item in paused) { if (!cloths.Contains(item)) { cloths.Add(item); } } } if (unscaledTime < checkAt) { return; } checkAt = unscaledTime + 0.5f; Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return; } Vector3 position = ((Component)main).transform.position; float value = Distance.Value; float num = value - 5f; float num2 = value * value; float num3 = num * num; foreach (Cloth cloth in cloths) { if ((Object)(object)cloth == (Object)null || skip.Contains(cloth)) { continue; } bool flag = paused.Contains(cloth); if (!flag && !cloth.enabled) { continue; } if (!flag && (Object)(object)((Component)cloth).GetComponentInParent<ArmorStand>() != (Object)null) { skip.Add(cloth); continue; } Vector3 val = ((Component)cloth).transform.position - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!flag && sqrMagnitude > num2) { cloth.enabled = false; paused.Add(cloth); } else if (flag && sqrMagnitude < num3) { cloth.enabled = true; paused.Remove(cloth); } else if (flag && cloth.enabled) { cloth.enabled = false; } } Paused = paused.Count; } private void ResumeAll() { foreach (Cloth item in paused) { if ((Object)(object)item != (Object)null) { item.enabled = true; } } paused.Clear(); Paused = 0; } private void OnDisable() { ResumeAll(); } } public class Combiner : MonoBehaviour { private class Chunk { public Vector2Int Key; public readonly HashSet<WearNTear> Pieces = new HashSet<WearNTear>(); public readonly List<Renderer> Hidden = new List<Renderer>(); public readonly List<Mesh> Meshes = new List<Mesh>(); public readonly List<LODGroup> LodGroups = new List<LODGroup>(); public GameObject Root; public bool Dirty = true; public bool Real; public float DirtyAt; public float RetryAt; } private struct Group { public int Level; public Material Mat; public ShadowCastingMode Shadows; public bool Receive; public int Layer; } public static Combiner Instance; private readonly Dictionary<Vector2Int, Chunk> chunks = new Dictionary<Vector2Int, Chunk>(); private readonly Dictionary<WearNTear, Vector2Int> where = new Dictionary<WearNTear, Vector2Int>(); private readonly Dictionary<WearNTear, int> looks = new Dictionary<WearNTear, int>(); private readonly Dictionary<WearNTear, string> skipWhy = new Dictionary<WearNTear, string>(); private float scanAt; private float buildCheckAt; private float size; public static int Areas; public static int CombinedPieces; public static int HiddenRenderers; public static int Meshes; public static int Unreadable; public static int Rebuilds; public static readonly Dictionary<string, int> Skipped = new Dictionary<string, int>(); private static readonly HashSet<string> Cosmetic = new HashSet<string> { "BannerColorizer" }; private void Awake() { Instance = this; } private void OnDestroy() { RestoreAll(); } public void SetEnabled(bool on) { if (!on) { RestoreAll(); } } private void Update() { if (!Plugin.CombineMeshes.Value || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { if (chunks.Count > 0) { RestoreAll(); } return; } if (!Mathf.Approximately(size, Plugin.ChunkSize.Value)) { RestoreAll(); size = Plugin.ChunkSize.Value; } if (Time.time >= scanAt) { scanAt = Time.time + 1f; Scan(); } if (Time.time >= buildCheckAt) { buildCheckAt = Time.time + 0.5f; BuildMode(); } RebuildOne(); } private Vector2Int KeyOf(Vector3 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) return new Vector2Int(Mathf.FloorToInt(p.x / size), Mathf.FloorToInt(p.z / size)); } private static int Look(WearNTear w) { return (((Object)(object)w.m_new != (Object)null && w.m_new.activeSelf) ? 1 : 0) | (((Object)(object)w.m_worn != (Object)null && w.m_worn.activeSelf) ? 2 : 0) | (((Object)(object)w.m_broken != (Object)null && w.m_broken.activeSelf) ? 4 : 0); } private void Dirty(Chunk c) { c.Dirty = true; c.DirtyAt = Time.time + 0.4f; } private void Scan() { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) foreach (WearNTear allInstance in WearNTear.GetAllInstances()) { if ((Object)(object)allInstance == (Object)null) { continue; } if (where.TryGetValue(allInstance, out var value)) { int num = Look(allInstance); if (looks.TryGetValue(allInstance, out var value2) && value2 != num && chunks.TryGetValue(value, out var value3) && !value3.Real) { looks[allInstance] = num; Dirty(value3); } continue; } value = KeyOf(((Component)allInstance).transform.position); where[allInstance] = value; looks[allInstance] = Look(allInstance); if (!chunks.TryGetValue(value, out var value4)) { Dictionary<Vector2Int, Chunk> dictionary = chunks; Vector2Int key = value; Chunk obj = new Chunk { Key = value }; value4 = obj; dictionary[key] = obj; } value4.Pieces.Add(allInstance); Dirty(value4); } List<WearNTear> list = null; foreach (KeyValuePair<WearNTear, Vector2Int> item in where) { if ((Object)(object)item.Key == (Object)null) { List<WearNTear> obj2 = list ?? new List<WearNTear>(); list = obj2; obj2.Add(item.Key); } } if (list != null) { foreach (WearNTear item2 in list) { Vector2Int key2 = where[item2]; where.Remove(item2); looks.Remove(item2); skipWhy.Remove(item2); if (chunks.TryGetValue(key2, out var value5)) { value5.Pieces.Remove(item2); if (value5.Pieces.Count == 0) { Restore(value5); chunks.Remove(key2); } else { Dirty(value5); } } } } Areas = 0; foreach (Chunk value6 in chunks.Values) { if ((Object)(object)value6.Root != (Object)null) { Areas++; } } } private void BuildMode() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; ItemData currentWeapon = ((Humanoid)localPlayer).GetCurrentWeapon(); bool flag = Plugin.ShowRealWhileBuilding.Value && currentWeapon != null && (Object)(object)currentWeapon.m_shared.m_buildPieces != (Object)null; Vector3 position = ((Component)localPlayer).transform.position; float value = Plugin.BuildToolRadius.Value; Vector2 val = default(Vector2); foreach (Chunk value2 in chunks.Values) { bool flag2 = false; if (flag) { ((Vector2)(ref val))..ctor((float)((Vector2Int)(ref value2.Key)).x * size, (float)((Vector2Int)(ref value2.Key)).y * size); float num = Mathf.Max(new float[3] { val.x - position.x, 0f, position.x - (val.x + size) }); float num2 = Mathf.Max(new float[3] { val.y - position.z, 0f, position.z - (val.y + size) }); flag2 = num * num + num2 * num2 < value * value; } if (flag2 && !value2.Real) { value2.Real = true; Restore(value2); } else if (!flag2 && value2.Real) { value2.Real = false; Dirty(value2); } } } private void RebuildOne() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) Chunk chunk = null; float num = float.MaxValue; Vector3 position = ((Component)Player.m_localPlayer).transform.position; foreach (Chunk value in chunks.Values) { if (value.Dirty && !value.Real && !(Time.time < value.DirtyAt) && !(Time.time < value.RetryAt)) { Vector3 val = new Vector3(((float)((Vector2Int)(ref value.Key)).x + 0.5f) * size, position.y, ((float)((Vector2Int)(ref value.Key)).y + 0.5f) * size) - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; chunk = value; } } } if (chunk != null) { Build(chunk); } } private string Why(WearNTear w) { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Invalid comparison between Unknown and I4 //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Invalid comparison between Unknown and I4 if (skipWhy.TryGetValue(w, out var value)) { return value; } value = null; ZNetView component = ((Component)w).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { value = "not networked"; } else { Component val = Blocking(w); if (val != null) { value = "interactable: " + ((object)val).GetType().Name; } else if ((Object)(object)((Component)w).GetComponentInParent<Rigidbody>() != (Object)null || (Object)(object)((Component)w).GetComponentInChildren<Rigidbody>(true) != (Object)null) { value = "moves (ship, cart)"; } else if ((Object)(object)((Component)w).GetComponentInChildren<Light>(true) != (Object)null) { value = "light"; } else if ((Object)(object)((Component)w).GetComponentInChildren<Animator>(true) != (Object)null) { value = "animated"; } else if ((Object)(object)((Component)w).GetComponentInChildren<SkinnedMeshRenderer>(true) != (Object)null || (Object)(object)((Component)w).GetComponentInChildren<Cloth>(true) != (Object)null) { value = "cloth / skinned"; } else if (DecorCull.Small(w)) { value = "small piece (draw distance)"; } else { Biome val2 = Heightmap.FindBiome(((Component)w).transform.position); if ((int)val2 == 32 || (int)val2 == 64) { value = "Ashlands / Deep North"; } } } skipWhy[w] = value; return value; } private static Component Blocking(WearNTear w) { Interactable[] componentsInChildren = ((Component)w).GetComponentsInChildren<Interactable>(true); foreach (Interactable obj in componentsInChildren) { Component val = (Component)(object)((obj is Component) ? obj : null); if (val != null && !Cosmetic.Contains(((object)val).GetType().Name)) { return val; } } return null; } private static bool Add(Dictionary<Group, List<CombineInstance>> groups, MeshRenderer r, int level, Matrix4x4 toLocal, WearNTear w) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)r == (Object)null || !((Renderer)r).enabled || !((Component)r).gameObject.activeInHierarchy || ((Object)(object)w.m_wet != (Object)null && ((Component)r).transform.IsChildOf(w.m_wet.transform))) { return false; } MeshFilter component = ((Component)r).GetComponent<MeshFilter>(); Mesh val = (((Object)(object)component != (Object)null) ? component.sharedMesh : null); if ((Object)(object)val == (Object)null || !val.isReadable || ((Renderer)r).HasPropertyBlock()) { return false; } Material[] sharedMaterials = ((Renderer)r).sharedMaterials; if (sharedMaterials.Length == 0) { return false; } Material[] array = sharedMaterials; foreach (Material val2 in array) { if ((Object)(object)val2 == (Object)null || val2.renderQueue >= 2500) { return false; } } Matrix4x4 transform = toLocal * ((Component)component).transform.localToWorldMatrix; for (int j = 0; j < val.subMeshCount && j < sharedMaterials.Length; j++) { Group key = new Group { Level = level, Mat = sharedMaterials[j], Shadows = ((Renderer)r).shadowCastingMode, Receive = ((Renderer)r).receiveShadows, Layer = ((Component)r).gameObject.layer }; if (!groups.TryGetValue(key, out var value)) { value = (groups[key] = new List<CombineInstance>()); } List<CombineInstance> list2 = value; CombineInstance item = default(CombineInstance); ((CombineInstance)(ref item)).mesh = val; ((CombineInstance)(ref item)).subMeshIndex = j; ((CombineInstance)(ref item)).transform = transform; list2.Add(item); } return true; } private void Build(Chunk c) { //IL_0687: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) //IL_0378: Expected O, but got Unknown //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_03f5: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Unknown result type (might be due to invalid IL or missing references) //IL_040f: Expected O, but got Unknown //IL_044a: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_0498: Unknown result type (might be due to invalid IL or missing references) //IL_04c4: Unknown result type (might be due to invalid IL or missing references) //IL_0537: Unknown result type (might be due to invalid IL or missing references) //IL_053c: Unknown result type (might be due to invalid IL or missing references) Restore(c); c.Dirty = false; if (c.Real) { return; } Dictionary<Group, List<CombineInstance>> dictionary = new Dictionary<Group, List<CombineInstance>>(); List<Renderer> list = new List<Renderer>(); List<LODGroup> list2 = new List<LODGroup>(); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor((float)((Vector2Int)(ref c.Key)).x * size, 0f, (float)((Vector2Int)(ref c.Key)).y * size); Matrix4x4 toLocal = Matrix4x4.Translate(-val); int num = 0; try { List<List<float>> list3 = new List<List<float>>(); List<(WearNTear, Renderer[], int)> list4 = new List<(WearNTear, Renderer[], int)>(); int num2 = 0; foreach (WearNTear piece in c.Pieces) { if ((Object)(object)piece == (Object)null || Why(piece) != null) { continue; } bool flag = false; LODGroup componentInChildren = ((Component)piece).GetComponentInChildren<LODGroup>(true); HashSet<Renderer> hashSet = new HashSet<Renderer>(); if ((Object)(object)componentInChildren != (Object)null) { LOD[] lODs = componentInChildren.GetLODs(); float num3 = Mathf.Max(0.5f, componentInChildren.size * Mathf.Max(new float[3] { ((Component)componentInChildren).transform.lossyScale.x, ((Component)componentInChildren).transform.lossyScale.y, ((Component)componentInChildren).transform.lossyScale.z })); for (int i = 0; i < lODs.Length; i++) { if (lODs[i].renderers == null) { continue; } Renderer[] renderers = lODs[i].renderers; foreach (Renderer val2 in renderers) { if (!((Object)(object)val2 == (Object)null)) { hashSet.Add(val2); MeshRenderer val3 = (MeshRenderer)(object)((val2 is MeshRenderer) ? val2 : null); if (val3 != null && Add(dictionary, val3, i, toLocal, piece)) { list.Add(val2); flag = true; } } } while (list3.Count <= i) { list3.Add(new List<float>()); } list3[i].Add(lODs[i].screenRelativeTransitionHeight / num3); } num2 = Mathf.Max(num2, lODs.Length); list2.Add(componentInChildren); if (lODs.Length != 0) { list4.Add((piece, lODs[^1].renderers, lODs.Length)); } } MeshRenderer[] componentsInChildren = ((Component)piece).GetComponentsInChildren<MeshRenderer>(false); foreach (MeshRenderer val4 in componentsInChildren) { if (!hashSet.Contains((Renderer)(object)val4) && Add(dictionary, val4, -1, toLocal, piece)) { list.Add((Renderer)(object)val4); flag = true; } } if (flag) { num++; } } foreach (var item in list4) { var (w, array, k) = item; for (; k < num2; k++) { if (array == null) { continue; } Renderer[] renderers = array; foreach (Renderer obj in renderers) { MeshRenderer val5 = (MeshRenderer)(object)((obj is MeshRenderer) ? obj : null); if (val5 != null) { Add(dictionary, val5, k, toLocal, w); } } } } if (dictionary.Count == 0) { return; } c.Root = new GameObject($"RP_Area {((Vector2Int)(ref c.Key)).x},{((Vector2Int)(ref c.Key)).y}"); c.Root.transform.position = val; Transform[] array2 = (Transform[])(object)new Transform[num2]; for (int l = 0; l < num2; l++) { Transform transform = new GameObject("LOD" + l).transform; transform.SetParent(c.Root.transform, false); array2[l] = transform; } foreach (KeyValuePair<Group, List<CombineInstance>> item2 in dictionary) { Mesh val6 = new Mesh { name = ((Object)c.Root).name, indexFormat = (IndexFormat)1 }; val6.CombineMeshes(item2.Value.ToArray(), true, true); val6.UploadMeshData(true); c.Meshes.Add(val6); GameObject val7 = new GameObject(((Object)item2.Key.Mat).name) { layer = item2.Key.Layer }; val7.transform.SetParent((item2.Key.Level >= 0) ? array2[item2.Key.Level] : c.Root.transform, false); val7.AddComponent<MeshFilter>().sharedMesh = val6; MeshRenderer obj2 = val7.AddComponent<MeshRenderer>(); ((Renderer)obj2).sharedMaterial = item2.Key.Mat; ((Renderer)obj2).shadowCastingMode = item2.Key.Shadows; ((Renderer)obj2).receiveShadows = item2.Key.Receive; } if (num2 > 0) { LODGroup val8 = c.Root.AddComponent<LODGroup>(); LOD[] array3 = (LOD[])(object)new LOD[num2]; for (int m = 0; m < num2; m++) { array3[m] = new LOD(0.5f / (float)(m + 1), ((Component)array2[m]).GetComponentsInChildren<Renderer>()); } val8.SetLODs(array3); val8.RecalculateBounds(); float num4 = 1f; for (int n = 0; n < num2; n++) { float num5 = ((n < list3.Count && list3[n].Count > 0) ? (Average(list3[n]) * val8.size) : (num4 * 0.5f)); num5 = Mathf.Clamp(num5, 0.0005f, num4 * 0.98f); num4 = num5; array3[n].screenRelativeTransitionHeight = num5; } val8.SetLODs(array3); } foreach (LODGroup item3 in list2) { if ((Object)(object)item3 != (Object)null) { item3.enabled = false; } } c.LodGroups.AddRange(list2); foreach (Renderer item4 in list) { item4.enabled = false; } c.Hidden.AddRange(list); Rebuilds++; } catch (Exception ex) { Plugin.Log.LogWarning((object)$"Combined area {c.Key}: {ex.Message}; drawing it normally for 30 s"); Restore(c); c.Dirty = true; c.RetryAt = Time.time + 30f; } finally { Count(); } } private static float Average(List<float> l) { float num = 0f; foreach (float item in l) { num += item; } return num / (float)l.Count; } private static void Restore(Chunk c) { foreach (Renderer item in c.Hidden) { if ((Object)(object)item != (Object)null) { item.enabled = true; } } c.Hidden.Clear(); foreach (LODGroup lodGroup in c.LodGroups) { if ((Object)(object)lodGroup != (Object)null) { lodGroup.enabled = true; } } c.LodGroups.Clear(); foreach (Mesh mesh in c.Meshes) { if ((Object)(object)mesh != (Object)null) { Object.Destroy((Object)(object)mesh); } } c.Meshes.Clear(); if ((Object)(object)c.Root != (Object)null) { Object.Destroy((Object)(object)c.Root); } c.Root = null; } public void RestoreAll() { foreach (Chunk value in chunks.Values) { Restore(value); } chunks.Clear(); where.Clear(); looks.Clear(); skipWhy.Clear(); Count(); } private void Count() { Areas = (CombinedPieces = (HiddenRenderers = (Meshes = (Unreadable = 0)))); Skipped.Clear(); HashSet<WearNTear> hashSet = new HashSet<WearNTear>(); foreach (Chunk value2 in chunks.Values) { if ((Object)(object)value2.Root != (Object)null) { Areas++; } Meshes += value2.Meshes.Count; HiddenRenderers += value2.Hidden.Count; foreach (Renderer item in value2.Hidden) { if ((Object)(object)item != (Object)null) { WearNTear componentInParent = ((Component)item).GetComponentInParent<WearNTear>(); if ((Object)(object)componentInParent != (Object)null) { hashSet.Add(componentInParent); } } } } CombinedPieces = hashSet.Count; foreach (KeyValuePair<WearNTear, string> item2 in skipWhy) { if (item2.Value != null && (Object)(object)item2.Key != (Object)null) { Skipped[item2.Value] = (Skipped.TryGetValue(item2.Value, out var value) ? value : 0) + 1; } } } } public class DecorCull : MonoBehaviour { private class Info { public float Size; public bool Hidden; public int Children; } public static ConfigEntry<bool> On; public static ConfigEntry<float> SmallSize; public static ConfigEntry<float> Distance; public static int HiddenCount; private static readonly Dictionary<WearNTear, Info> infos = new Dictionary<WearNTear, Info>(); private int cursor; private float cleanAt; public static void Init(ConfigFile c) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown On = c.Bind<bool>("Piece draw distance", "Hide distant small pieces", false, "Small pieces (rugs, shelves and the items on them, banners, small decor) stop drawing beyond the distance below."); SmallSize = c.Bind<float>("Piece draw distance", "Small piece size", 1.5f, new ConfigDescription("Pieces no bigger than this (metres, longest side) count as small.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 4f), Array.Empty<object>())); Distance = c.Bind<float>("Piece draw distance", "Distance", 50f, new ConfigDescription("Metres from the camera beyond which small pieces are hidden.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 200f), Array.Empty<object>())); On.SettingChanged += delegate { Combiner.Instance?.RestoreAll(); }; SmallSize.SettingChanged += delegate { if (On.Value) { Combiner.Instance?.RestoreAll(); } }; } public static bool Small(WearNTear w) { if (On.Value) { Info info = Get(w); if (info != null && info.Size > 0f) { return info.Size <= SmallSize.Value; } } return false; } private static Info Get(WearNTear w) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) if (infos.TryGetValue(w, out var value)) { return value; } value = new Info { Size = -1f }; if ((Object)(object)((Component)w).GetComponentInChildren<Light>(true) == (Object)null && (Object)(object)((Component)w).GetComponentInParent<Rigidbody>() == (Object)null) { bool flag = false; Bounds val = default(Bounds); MeshRenderer[] componentsInChildren = ((Component)w).GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val2 in componentsInChildren) { if (flag) { ((Bounds)(ref val)).Encapsulate(((Renderer)val2).bounds); continue; } val = ((Renderer)val2).bounds; flag = true; } if (flag) { Vector3 size = ((Bounds)(ref val)).size; value.Size = Mathf.Max(new float[3] { size.x, size.y, size.z }); } } infos[w] = value; return value; } private void Update() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) Camera mainCamera = Utils.GetMainCamera(); if (!On.Value || (Object)(object)mainCamera == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { if (HiddenCount > 0) { ShowAll(); } return; } List<WearNTear> allInstances = WearNTear.GetAllInstances(); int count = allInstances.Count; if (count == 0) { return; } Vector3 position = ((Component)mainCamera).transform.position; float num = Distance.Value * Distance.Value; float value = SmallSize.Value; int num2 = Mathf.Min(count, Mathf.Max(50, Mathf.CeilToInt((float)count * Time.unscaledDeltaTime))); for (int i = 0; i < num2; i++) { if (cursor >= allInstances.Count) { cursor = 0; } WearNTear val = allInstances[cursor++]; if ((Object)(object)val == (Object)null) { continue; } Info info = Get(val); int num3; if (info.Size > 0f && info.Size <= value) { Vector3 val2 = ((Component)val).transform.position - position; num3 = ((((Vector3)(ref val2)).sqrMagnitude > num) ? 1 : 0); } else { num3 = 0; } bool flag = (byte)num3 != 0; if (flag != info.Hidden || (flag && ((Component)val).transform.childCount != info.Children)) { Set(val, flag); if (flag != info.Hidden) { HiddenCount += (flag ? 1 : (-1)); } info.Hidden = flag; info.Children = ((Component)val).transform.childCount; } } if (Time.unscaledTime > cleanAt) { cleanAt = Time.unscaledTime + 10f; Clean(); } } private static void Set(WearNTear w, bool hide) { Renderer[] componentsInChildren = ((Component)w).GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].forceRenderingOff = hide; } } private static void ShowAll() { foreach (KeyValuePair<WearNTear, Info> info in infos) { if (info.Value.Hidden) { if ((Object)(object)info.Key != (Object)null) { Set(info.Key, hide: false); } info.Value.Hidden = false; } } HiddenCount = 0; } private static void Clean() { List<WearNTear> list = null; foreach (KeyValuePair<WearNTear, Info> info in infos) { if ((Object)(object)info.Key == (Object)null) { List<WearNTear> obj = list ?? new List<WearNTear>(); list = obj; obj.Add(info.Key); } } if (list == null) { return; } foreach (WearNTear item in list) { if (infos[item].Hidden) { HiddenCount--; } infos.Remove(item); } } private void OnDestroy() { ShowAll(); } } public static class DropMerge { [HarmonyPatch(typeof(ItemDrop), "SlowUpdate")] private static class Merge { private static void Postfix(ItemDrop __instance) { if (!On.Value) { return; } try { Run(__instance); } catch { } } } public static ConfigEntry<bool> On; public static ConfigEntry<float> Radius; private static int itemMask; private static readonly FieldRef<ItemDrop, ZNetView> NView = AccessTools.FieldRefAccess<ItemDrop, ZNetView>("m_nview"); private static readonly Action<ItemDrop> Save = AccessTools.MethodDelegate<Action<ItemDrop>>(AccessTools.Method(typeof(ItemDrop), "Save", (Type[])null, (Type[])null), (object)null, true); public static void Init(ConfigFile c) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown On = c.Bind<bool>("Item drops", "Merge nearby drops", false, "Stacks of the same item lying near each other merge into one drop (fewer objects after farming, mining or a big fight)."); Radius = c.Bind<float>("Item drops", "Merge radius", 4f, new ConfigDescription("Metres within which the same items merge (vanilla: 4).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>())); } private static void Run(ItemDrop me) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) ZNetView val = NView.Invoke(me); if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner() || me.IsPiece()) { return; } ItemData itemData = me.m_itemData; int maxStackSize = itemData.m_shared.m_maxStackSize; if (maxStackSize <= 1 || itemData.m_stack >= maxStackSize || !itemData.m_shared.m_autoStack) { return; } if (itemMask == 0) { itemMask = LayerMask.GetMask(new string[1] { "item" }); } bool flag = false; Collider[] array = Physics.OverlapSphere(((Component)me).transform.position, Radius.Value, itemMask); for (int i = 0; i < array.Length; i++) { Rigidbody attachedRigidbody = array[i].attachedRigidbody; if ((Object)(object)attachedRigidbody == (Object)null) { continue; } ItemDrop component = ((Component)attachedRigidbody).GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || (Object)(object)component == (Object)(object)me || component.IsPiece()) { continue; } ZNetView val2 = NView.Invoke(component); if ((Object)(object)val2 == (Object)null || !val2.IsValid() || !val2.IsOwner()) { continue; } ItemData itemData2 = component.m_itemData; if (itemData2.m_shared.m_autoStack && !(itemData2.m_shared.m_name != itemData.m_shared.m_name) && itemData2.m_quality == itemData.m_quality) { int num = maxStackSize - itemData.m_stack; if (num <= 0) { break; } if (itemData2.m_stack <= num) { itemData.m_stack += itemData2.m_stack; flag = true; val2.Destroy(); } } } if (flag) { Save(me); } } } public static class EffectFilter { public enum Kind { Building, Trees, Mining } [HarmonyPatch(typeof(Piece), "Awake")] private static class TagPiece { private static void Postfix(Piece __instance) { try { Tag(Kind.Building, __instance.m_placeEffect); } catch { } } } [HarmonyPatch(typeof(WearNTear), "Awake")] private static class TagWear { private static void Postfix(WearNTear __instance) { try { Tag(Kind.Building, __instance.m_destroyedEffect, __instance.m_hitEffect, __instance.m_switchEffect); } catch { } } } [HarmonyPatch(typeof(TreeBase), "Awake")] private static class TagTree { private static void Postfix(TreeBase __instance) { try { Tag(Kind.Trees, __instance.m_destroyedEffect, __instance.m_hitEffect); } catch { } } } [HarmonyPatch(typeof(TreeLog), "Awake")] private static class TagLog { private static void Postfix(TreeLog __instance) { try { Tag(Kind.Trees, __instance.m_destroyedEffect, __instance.m_hitEffect); } catch { } } } [HarmonyPatch(typeof(MineRock), "Start")] private static class TagRock { private static void Postfix(MineRock __instance) { try { Tag(Kind.Mining, __instance.m_destroyedEffect, __instance.m_hitEffect); } catch { } } } [HarmonyPatch(typeof(MineRock5), "Awake")] private static class TagRock5 { private static void Postfix(MineRock5 __instance) { try { Tag(Kind.Mining, __instance.m_destroyedEffect, __instance.m_hitEffect); } catch { } } } [HarmonyPatch(typeof(Destructible), "Awake")] private static class TagDestructible { private static void Postfix(Destructible __instance) { try { string text = ((Object)__instance).name.ToLowerInvariant(); Kind? kind = ((text.Contains("stub") || text.Contains("stump")) ? new Kind?(Kind.Trees) : ((text.Contains("rock") || text.Contains("ore") || text.Contains("stone")) ? new Kind?(Kind.Mining) : ((Kind?)null))); if (kind.HasValue) { Tag(kind.Value, __instance.m_destroyedEffect, __instance.m_hitEffect, __instance.m_hitEffectBig, __instance.m_hitEffectBuildup); } } catch { } } } [HarmonyPatch(typeof(EffectList), "Create")] private static class Strip { private static void Postfix(EffectList __instance, GameObject[] __result) { try { if (__result == null || __result.Length == 0 || !tags.TryGetValue(__instance, out var value) || !Hidden((Kind)value)) { return; } foreach (GameObject val in __result) { if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<ZNetView>() != (Object)null) { continue; } if ((Object)(object)val.GetComponentInChildren<ZSFX>(true) == (Object)null) { Object.Destroy((Object)(object)val); continue; } ParticleSystem[] componentsInChildren = val.GetComponentsInChildren<ParticleSystem>(true); for (int j = 0; j < componentsInChildren.Length; j++) { componentsInChildren[j].Stop(true, (ParticleSystemStopBehavior)0); } Renderer[] componentsInChildren2 = val.GetComponentsInChildren<Renderer>(true); for (int j = 0; j < componentsInChildren2.Length; j++) { componentsInChildren2[j].enabled = false; } Light[] componentsInChildren3 = val.GetComponentsInChildren<Light>(true); for (int j = 0; j < componentsInChildren3.Length; j++) { ((Behaviour)componentsInChildren3[j]).enabled = false; } } } catch { } } } [HarmonyPatch(typeof(SmokeSpawner), "Spawn")] private static class NoSmoke { private static bool Prefix() { if (Enabled.Value) { return !HideSmoke.Value; } return true; } } public static ConfigEntry<bool> Enabled; public static ConfigEntry<bool> HideSmoke; public static ConfigEntry<bool> HideBuilding; public static ConfigEntry<bool> HideTrees; public static ConfigEntry<bool> HideMining; private static readonly ConditionalWeakTable<EffectList, object> tags = new ConditionalWeakTable<EffectList, object>(); public static void Init(ConfigFile c) { Enabled = c.Bind<bool>("Effects", "Enabled", true, "Master switch for the effect switches below."); HideSmoke = c.Bind<bool>("Effects", "Hide smoke", false, "No smoke from chimneys, fires, smelters and kilns."); HideBuilding = c.Bind<bool>("Effects", "Hide building dust", false, "No dust or debris when placing, hitting or removing build pieces (sounds stay)."); HideTrees = c.Bind<bool>("Effects", "Hide tree dust", false, "No dust, leaves or wood chips when chopping and felling trees, logs and stumps (sounds stay)."); HideMining = c.Bind<bool>("Effects", "Hide mining dust", false, "No dust or rock chips when mining rocks and ore (sounds stay)."); } private static bool Hidden(Kind k) { if (!Enabled.Value) { return false; } return k switch { Kind.Trees => HideTrees.Value, Kind.Building => HideBuilding.Value, _ => HideMining.Value, }; } private static void Tag(Kind k, params EffectList[] lists) { foreach (EffectList val in lists) { if (val != null) { tags.Remove(val); tags.Add(val, k); } } } } public class FireSmoke : MonoBehaviour { [HarmonyPatch(typeof(Fireplace), "Awake")] private static class NewFire { private static void Postfix(Fireplace __instance) { if (LimitFire.Value) { try { Apply(__instance); } catch { } } } } public static ConfigEntry<bool> LimitSmoke; public static ConfigEntry<bool> SmokeIgnoreEachOther; public static ConfigEntry<bool> LimitFire; public static ConfigEntry<int> MaxSmoke; public static ConfigEntry<int> FirePercent; private static readonly Dictionary<ParticleSystem, (float rate, int max)> originals = new Dictionary<ParticleSystem, (float, int)>(); private static int smokeLayer = -2; private static bool ignoring; public static void Init(ConfigFile c) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown LimitSmoke = c.Bind<bool>("Fire and smoke", "Limit smoke", false, "Caps the number of smoke puffs (vanilla: 100); the most distant fade out first."); MaxSmoke = c.Bind<int>("Fire and smoke", "Max smoke puffs", 40, new ConfigDescription("Most smoke puffs at once.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 100), Array.Empty<object>())); SmokeIgnoreEachOther = c.Bind<bool>("Fire and smoke", "Smoke puffs ignore each other", false, "Smoke puffs stop pushing each other around (a physics cost with many chimneys). They still collide with roofs and walls, so indoor smoke and the Smoked status are unchanged."); LimitFire = c.Bind<bool>("Fire and smoke", "Fewer fire particles", false, "Fireplaces, torches and braziers emit fewer particles (flames, sparks, embers)."); FirePercent = c.Bind<int>("Fire and smoke", "Fire particles (%)", 50, new ConfigDescription("Share of fire particles kept.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 100), Array.Empty<object>())); LimitFire.SettingChanged += delegate { ApplyAllFires(); }; FirePercent.SettingChanged += delegate { ApplyAllFires(); }; } private void Update() { if (LimitSmoke.Value) { for (int i = 0; i < 20; i++) { if (Smoke.GetTotalSmoke() <= MaxSmoke.Value) { break; } Smoke.FadeMostDistant(); } } if (smokeLayer == -2) { smokeLayer = LayerMask.NameToLayer("smoke"); } if (smokeLayer >= 0 && ignoring != SmokeIgnoreEachOther.Value) { ignoring = SmokeIgnoreEachOther.Value; Physics.IgnoreLayerCollision(smokeLayer, smokeLayer, ignoring); } } private void OnDestroy() { if (ignoring && smokeLayer >= 0) { Physics.IgnoreLayerCollision(smokeLayer, smokeLayer, false); } } private static void Apply(Fireplace f) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)f == (Object)null) { return; } float num = (LimitFire.Value ? ((float)FirePercent.Value / 100f) : 1f); ParticleSystem[] componentsInChildren = ((Component)f).GetComponentsInChildren<ParticleSystem>(true); foreach (ParticleSystem val in componentsInChildren) { if (!originals.TryGetValue(val, out (float, int) value)) { if (num >= 1f) { continue; } Dictionary<ParticleSystem, (float rate, int max)> dictionary = originals; EmissionModule emission = val.emission; float rateOverTimeMultiplier = ((EmissionModule)(ref emission)).rateOverTimeMultiplier; MainModule main = val.main; value = (dictionary[val] = (rateOverTimeMultiplier, ((MainModule)(ref main)).maxParticles)); } EmissionModule emission2 = val.emission; ((EmissionModule)(ref emission2)).rateOverTimeMultiplier = value.Item1 * num; MainModule main2 = val.main; ((MainModule)(ref main2)).maxParticles = Mathf.Max(1, Mathf.RoundToInt((float)value.Item2 * num)); } } private static void ApplyAllFires() { Fireplace[] array = Object.FindObjectsByType<Fireplace>((FindObjectsInactive)1, (FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { Apply(array[i]); } List<ParticleSystem> list = null; foreach (ParticleSystem key in originals.Keys) { if ((Object)(object)key == (Object)null) { List<ParticleSystem> obj = list ?? new List<ParticleSystem>(); list = obj; obj.Add(key); } } if (list == null) { return; } foreach (ParticleSystem item in list) { originals.Remove(item); } } } public class Grass : MonoBehaviour { public static ConfigEntry<bool> On; public static ConfigEntry<int> Density; public static ConfigEntry<float> GrassDistance; private ClutterSystem applied; private float savedAmount; private float savedDistance; private float next; public static void Init(ConfigFile c) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown On = c.Bind<bool>("Grass", "Override grass", false, "Use the grass density and distance below."); Density = c.Bind<int>("Grass", "Grass density (%)", 60, new ConfigDescription("Amount of grass and ground clutter.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 150), Array.Empty<object>())); GrassDistance = c.Bind<float>("Grass", "Grass distance", 30f, new ConfigDescription("Metres to which grass is drawn (vanilla about 40).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 80f), Array.Empty<object>())); } private void Update() { if (Time.unscaledTime < next) { return; } next = Time.unscaledTime + 0.5f; ClutterSystem instance = ClutterSystem.instance; if ((Object)(object)applied != (Object)null && (Object)(object)applied != (Object)(object)instance) { applied = null; } if ((Object)(object)instance == (Object)null) { return; } if (!On.Value) { if (!((Object)(object)applied == (Object)null)) { Set(instance, savedAmount, savedDistance); applied = null; } return; } if ((Object)(object)applied == (Object)null) { savedAmount = instance.m_amountScale; savedDistance = instance.m_distance; applied = instance; } Set(instance, savedAmount * (float)Density.Value / 100f, GrassDistance.Value); } private static void Set(ClutterSystem cs, float amount, float distance) { if (!Mathf.Approximately(cs.m_amountScale, amount) || !Mathf.Approximately(cs.m_distance, distance)) { cs.m_amountScale = amount; cs.m_distance = distance; cs.ClearAll(); } } } public class HitchLog : MonoBehaviour { public static ConfigEntry<bool> On; public static ConfigEntry<int> Threshold; public static readonly List<string> Recent = new List<string>(); private int gc0; private int gc2; private int objects; private int pieces; private int rebuilds; private bool primed; public static void Init(ConfigFile c) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown On = c.Bind<bool>("Hitch logger", "Log hitches", false, "Frames slower than the threshold go into the log (BepInEx/LogOutput.log) with what was happening, to find what causes spikes."); Threshold = c.Bind<int>("Hitch logger", "Hitch threshold (ms)", 50, new ConfigDescription("Frames slower than this are logged.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(20, 500), Array.Empty<object>())); } private void Update() { HitchSystems.Sync(On.Value && HitchSystems.On.Value); if (!On.Value || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { primed = false; HitchSystems.Reset(); return; } int num = GC.CollectionCount(0); int num2 = GC.CollectionCount(2); int num3 = ZNetScene.instance.NrOfInstances(); int count = WearNTear.GetAllInstances().Count; int num4 = Combiner.Rebuilds; float num5 = Time.unscaledDeltaTime * 1000f; if (primed && num5 > (float)Threshold.Value) { List<string> list = new List<string>(); if (num != gc0 || num2 != gc2) { list.Add($"garbage collection x{num - gc0}" + ((num2 != gc2) ? " (full)" : "")); } if (num3 != objects) { list.Add($"objects {num3 - objects:+0;-0} (area loading)"); } if (count != pieces) { list.Add($"pieces {count - pieces:+0;-0}"); } if (num4 != rebuilds) { list.Add($"combined areas rebuilt {num4 - rebuilds}"); } list.Add($"wear {Overlay.WearUpdaterMs:0.0} ms"); list.Add($"smoke {Smoke.GetTotalSmoke()}"); if (SpreadLoading.On.Value && SpreadLoading.Waiting > 0) { list.Add($"{SpreadLoading.Waiting} objects still loading"); } string text = HitchSystems.Summary(); if (text.Length > 0) { list.Insert(0, text); } string text2 = $"Hitch {num5:0} ms: " + string.Join(", ", list); Plugin.Log.LogInfo((object)text2); Recent.Add($"{DateTime.Now:HH:mm:ss} {text2}"); if (Recent.Count > 4) { Recent.RemoveAt(0); } } HitchSystems.Reset(); gc0 = num; gc2 = num2; objects = num3; pieces = count; rebuilds = num4; primed = true; } } public static class HitchSystems { public static ConfigEntry<bool> On; private static readonly (Type type, string method, string label)[] Targets = new(Type, string, string)[16] { (typeof(MonoUpdaters), "Update", "creatures and objects"), (typeof(MonoUpdaters), "FixedUpdate", "physics-step code"), (typeof(MonoUpdaters), "LateUpdate", "late updates"), (typeof(ZNet), "Update", "network"), (typeof(ZNet), "LateUpdate", "network send"), (typeof(ZNetScene), "Update", "object loading"), (typeof(ZoneSystem), "Update", "zone generation"), (typeof(Pathfinding), "Update", "navmesh building"), (typeof(Heightmap), "Regenerate", "terrain rebuild"), (typeof(ClutterSystem), "LateUpdate", "grass"), (typeof(Minimap), "Update", "map"), (typeof(EnvMan), "Update", "weather"), (typeof(WearNTearUpdater), "Update", "building wear"), (typeof(Player), "Update", "players"), (typeof(GameCamera), "LateUpdate", "camera"), (typeof(Hud), "Update", "HUD") }; private static readonly Dictionary<MethodBase, int> index = new Dictionary<MethodBase, int>(); private static readonly long[] ticks = new long[Targets.Length]; private static readonly int fixedIndex = 1; private static int steps; private static Harmony harmony; public static void Init(ConfigFile c) { On = c.Bind<bool>("Hitch logger", "Break down by system", true, "Hitch lines also say which of the game's systems took the time (object loading, terrain, grass, network...)."); } public static void Sync(bool want) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown if (want == (harmony != null)) { return; } if (want) { harmony = new Harmony("daytreee.runeheimperformance.hitch"); HarmonyMethod val = new HarmonyMethod(typeof(HitchSystems), "Pre", (Type[])null); HarmonyMethod val2 = new HarmonyMethod(typeof(HitchSystems), "Post", (Type[])null); for (int i = 0; i < Targets.Length; i++) { MethodInfo methodInfo = AccessTools.Method(Targets[i].type, Targets[i].method, (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogWarning((object)("Hitch report: " + Targets[i].type.Name + "." + Targets[i].method + " not found")); } else { index[methodInfo] = i; try { harmony.Patch((MethodBase)methodInfo, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Hitch report: " + methodInfo.Name + ": " + ex.Message)); } } } } else { harmony.UnpatchSelf(); harmony = null; index.Clear(); } Reset(); } private static void Pre(out long __state) { __state = Stopwatch.GetTimestamp(); } private static void Post(MethodBase __originalMethod, long __state) { if (index.TryGetValue(__originalMethod, out var value)) { ticks[value] += Stopwatch.GetTimestamp() - __state; if (value == fixedIndex) { steps++; } } } public static void Reset() { Array.Clear(ticks, 0, ticks.Length); steps = 0; } public static string Summary() { if (harmony == null) { return ""; } double perMs = (double)Stopwatch.Frequency / 1000.0; List<string> list = (from i in (from i in Enumerable.Range(0, Targets.Length) where (double)ticks[i] / perMs >= 1.0 orderby ticks[i] descending select i).Take(4) select $"{Targets[i].label} {(double)ticks[i] / perMs:0} ms").ToList(); if (steps > 1) { list.Add($"{steps} physics steps"); } return string.Join(", ", list); } } public static class IdlePhysics { private class Biome { public float CheckAt; public bool Ash; } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate")] private static class RestShip { private static bool Prefix(Ship __instance) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Invalid comparison between Unknown and I4 if (!On.Value) { return true; } try { ZNetView val = ShipView.Invoke(__instance); if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner()) { return true; } List<Player> list = ShipPlayers.Invoke(__instance); if (list != null && list.Count > 0) { return true; } if (((Component)__instance).transform.up.y < 0.8f) { return true; } Rigidbody val2 = ShipBody.Invoke(__instance); if (!Idle(val2, ((Component)__instance).transform)) { return true; } Biome orCreateValue = biomes.GetOrCreateValue(__instance); if (Time.time >= orCreateValue.CheckAt) { orCreateValue.CheckAt = Time.time + 5f; orCreateValue.Ash = (int)Heightmap.FindBiome(((Component)__instance).transform.position) == 32; } if (orCreateValue.Ash) { return true; } val2.Sleep(); Note(); return false; } catch { return true; } } } [HarmonyPatch(typeof(Floating), "CustomFixedUpdate")] private static class RestFloating { private static bool Prefix(Floating __instance) { if (!On.Value) { return true; } try { Rigidbody val = FloatBody.Invoke(__instance); if (!Idle(val, ((Component)__instance).transform)) { return true; } val.Sleep(); Note(); return false; } catch { return true; } } } public static ConfigEntry<bool> On; public static ConfigEntry<float> Distance; public static int Resting; private static readonly FieldRef<Ship, Rigidbody> ShipBody = AccessTools.FieldRefAccess<Ship, Rigidbody>("m_body"); private static readonly FieldRef<Ship, ZNetView> ShipView = AccessTools.FieldRefAccess<Ship, ZNetView>("m_nview"); private static readonly FieldRef<Ship, List<Player>> ShipPlayers = AccessTools.FieldRefAccess<Ship, List<Player>>("m_players"); private static readonly FieldRef<Floating, Rigidbody> FloatBody = AccessTools.FieldRefAccess<Floating, Rigidbody>("m_body"); private static readonly ConditionalWeakTable<Ship, Biome> biomes = new ConditionalWeakTable<Ship, Biome>(); private static int restingCount; private static int countFrame; public static void Init(ConfigFile c) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown On = c.Bind<bool>("Physics", "Rest idle distant ships", false, "Empty, still ships and floating logs or items far from you stop simulating the waves until you come closer."); Distance = c.Bind<float>("Physics", "Distance", 60f, new ConfigDescription("Metres from you beyond which idle ships and floating things rest.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 200f), Array.Empty<object>())); } private static bool Idle(Rigidbody body, Transform t) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)body == (Object)null || (Object)(object)localPlayer == (Object)null) { return false; } float value = Distance.Value; Vector3 val = t.position - ((Component)localPlayer).transform.position; if (((Vector3)(ref val)).sqrMagnitude < value * value) { return false; } if (body.isKinematic) { return false; } if (!body.IsSleeping()) { val = body.linearVelocity; if (((Vector3)(ref val)).sqrMagnitude < 0.04f) { val = body.angularVelocity; return ((Vector3)(ref val)).sqrMagnitude < 0.04f; } return false; } return true; } private static void Note() { if (countFrame != Time.frameCount) { Resting = restingCount; restingCount = 0; countFrame = Time.frameCount; } restingCount++; } } public static class Keys { public static bool Down(KeyboardShortcut k) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref k)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref k)).MainKey)) { return ((KeyboardShortcut)(ref k)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } return false; } } public class LightLimiter : MonoBehaviour { public static ConfigEntry<bool> On; public static ConfigEntry<int> MaxLights; public static ConfigEntry<int> MaxShadows; private bool applied; private int savedLights; private int savedShadows; private int setLights; private int setShadows; private float next; public static void Init(ConfigFile c) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown On = c.Bind<bool>("Lights", "Limit lights", false, "Only the nearest lights light up and cast shadows (fires and torches keep their flame). Usually the biggest cost in a base full of torches."); MaxLights = c.Bind<int>("Lights", "Max lights", 12, new ConfigDescription("How many of the nearest point lights shine.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 64), Array.Empty<object>())); MaxShadows = c.Bind<int>("Lights", "Max light shadows", 2, new ConfigDescription("How many of the nearest point lights cast shadows.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 16), Array.Empty<object>())); } private void Update() { if (Time.unscaledTime < next) { return; } next = Time.unscaledTime + 1f; if (!On.Value) { if (applied) { if (LightLod.m_lightLimit == setLights) { LightLod.m_lightLimit = savedLights; } if (LightLod.m_shadowLimit == setShadows) { LightLod.m_shadowLimit = savedShadows; } applied = false; } } else { if (!applied || LightLod.m_lightLimit != setLights) { savedLights = LightLod.m_lightLimit; } if (!applied || LightLod.m_shadowLimit != setShadows) { savedShadows = LightLod.m_shadowLimit; } applied = true; LightLod.m_lightLimit = (setLights = MaxLights.Value); LightLod.m_shadowLimit = (setShadows = Mathf.Min(MaxShadows.Value, MaxLights.Value)); } } private void OnDestroy() { if (applied) { LightLod.m_lightLimit = savedLights; LightLod.m_shadowLimit = savedShadows; } } } public static class MachineChecks { private class Turn { public int N; } [HarmonyPatch(typeof(Plant), "SUpdate")] private static class Crops { private static void Prefix(Plant __instance, out float __state) { __state = PlantNext.Invoke(__instance); } private static void Postfix(Plant __instance, float __state) { try { if (!((Object)(object)__instance == (Object)null) && PlantNext.Invoke(__instance) != __state && Far((Component)(object)__instance)) { PlantNext.Invoke(__instance) += (float)(Every.Value - 1) * 10f; } } catch { } } } [HarmonyPatch(typeof(Smelter), "UpdateSmelter")] private static class Smelters { private static bool Prefix(Smelter __instance) { try { return !Far((Component)(object)__instance) || !SkipTurn((Object)(object)__instance); } catch { return true; } } } [HarmonyPatch(typeof(Fireplace), "CheckEnv")] private static class Fires { private static bool Prefix(Fireplace __instance) { try { return !Far((Component)(object)__instance) || !SkipTurn((Object)(object)__instance); } catch { return true; } } } public static ConfigEntry<bool> On; public static ConfigEntry<float> Distance; public static ConfigEntry<int> Every; private static readonly ConditionalWeakTable<Object, Turn> turns = new ConditionalWeakTable<Object, Turn>(); private static readonly FieldRef<Plant, float> PlantNext = AccessTools.FieldRefAccess<Plant, float>("m_updateTime"); public static void Init(ConfigFile c) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown On = c.Bind<bool>("Crops and machines", "Fewer checks far away", false, "Distant crops, smelters, kilns and fires run their routine checks less often. They catch up on the time in between, so crops grow and machines produce exactly as before."); Distance = c.Bind<float>("Crops and machines", "Distance", 40f, new ConfigDescription("Metres from you beyond which crops and machines check less often.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 150f), Array.Empty<object>())); Every = c.Bind<int>("Crops and machines", "Check every (times vanilla)", 4, new ConfigDescription("Distant ones check once every this many of vanilla's checks.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 10), Array.Empty<object>())); } private static bool Far(Component c) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!On.Value || (Object)(object)localPlayer == (Object)null || (Object)(object)c == (Object)null) { return false; } float value = Distance.Value; Vector3 val = c.transform.position - ((Component)localPlayer).transform.position; return ((Vector3)(ref val)).sqrMagnitude > value * value; } private static bool SkipTurn(Object o) { Turn orCreateValue = turns.GetOrCreateValue(o); if (++orCreateValue.N >= Every.Value) { orCreateValue.N = 0; return false; } return true; } } public class Overlay : MonoBehaviour { private bool open; private float countAt; private float fpsAvg = 60f; private string text = ""; public static double WearUpdaterMs; private static double wearMax; private static double wearShown; private static float wearResetAt; private readonly FrameTiming[] timings = (FrameTiming[])(object)new FrameTiming[1]; private GUIStyle style; private static Overlay instance; private float logAt; public static void NoteWear(double ms) { if (ms > wearMax) { wearMax = ms; } if (Time.unscaledTime > wearResetAt) { wearShown = wearMax; wearMax = 0.0; wearResetAt = Time.unscaledTime + 1f; } } private void Awake() { instance = this; } public static void ForceOpen() { if ((Object)(object)instance != (Object)null) { instance.open = true; } } private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (Keys.Down(Plugin.OverlayKey.Value)) { open = !open; logAt = 0f; } if (open) { FrameTimingManager.CaptureFrameTimings(); fpsAvg = Mathf.Lerp(fpsAvg, 1f / Mathf.Max(0.0001f, Time.unscaledDeltaTime), 0.05f); if (Time.unscaledTime >= countAt) { countAt = Time.unscaledTime + 1f; text = Build(); } if (Time.unscaledTime >= logAt) { logAt = Time.unscaledTime + 10f; Plugin.Log.LogInfo((object)("Overlay: " + Regex.Replace(text, "<.*?>", "").Replace("\r", "").Replace("\n", " | "))); } } } private string Build() { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_05c0: Unknown result type (might be due to invalid IL or missing references) //IL_05cf: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"<b>Runeheim Performance</b> {fpsAvg:0} fps {1000f / fpsAvg:0.0} ms"); if (FrameTimingManager.GetLatestTimings(1u, timings) != 0 && timings[0].cpuFrameTime > 0.0) { stringBuilder.AppendLine(string.Format("CPU {0:0.0} ms GPU {1}", timings[0].cpuFrameTime, (timings[0].gpuFrameTime > 0.0) ? (timings[0].gpuFrameTime.ToString("0.0") + " ms") : "n/a")); } int count = WearNTear.GetAllInstances().Count; int num = 0; int num2 = 0; int num3 = 0; Renderer[] array = Object.FindObjectsByType<Renderer>((FindObjectsSortMode)0); foreach (Renderer val in array) { if (!val.enabled) { continue; } num3++; if (val.isVisible) { num++; if ((int)val.shadowCastingMode != 0) { num2++; } } } int num4 = 0; int num5 = 0; Light[] array2 = Object.FindObjectsByType<Light>((FindObjectsSortMode)0); foreach (Light val2 in array2) { if (((Behaviour)val2).enabled && ((Behaviour)val2).isActiveAndEnabled) { num4++; if ((int)val2.shadows != 0) { num5++; } } } stringBuilder.AppendLine($"Pieces {count} renderers {num3} (visible {num}, casting shadows {num2})"); stringBuilder.AppendLine($"Lights {num4} ({num5} with shadows) piece shadows " + (Plugin.ShadowCullOn.Value ? $"to {Plugin.ShadowDistance.Value:0} m" : "vanilla") + " wear cycle " + ((Plugin.WearCycle.Value > 0f) ? $"{Plugin.WearCycle.Value:0} s" : "vanilla")); stringBuilder.AppendLine($"Wear updater {WearUpdaterMs:0.00} ms (worst this second {wearShown:0.00}) smoothing {WearSmoothing.LastFrameMs:0.00} ms (worst {WearSmoothing.ShownMax:0.00})" + (Plugin.SmoothWear.Value ? "" : " [smoothing off]")); if (Plugin.CombineMeshes.Value) { stringBuilder.AppendLine($"Combined: {Combiner.CombinedPieces} pieces in {Combiner.Areas} areas, {Combiner.Meshes} meshes replace {Combiner.HiddenRenderers} renderers rebuilds {Combiner.Rebuilds}"); if (Combiner.Skipped.Count > 0) { stringBuilder.AppendLine("Not combined (most first):"); int num6 = 0; foreach (KeyValuePair<string, int> item in Combiner.Skipped.OrderByDescending((KeyValuePair<string, int> x) => x.Value)) { if (num6++ >= 8) { break; } stringBuilder.AppendLine($" {item.Value,6} {item.Key}"); } } } else { stringBuilder.AppendLine("Combined meshes: off"); } List<string> list = new List<string>(); if (LightLimiter.On.Value) { list.Add($"lights {LightLimiter.MaxLights.Value}/{LightLimiter.MaxShadows.Value} shadows"); } if (DecorCull.On.Value) { list.Add($"small pieces hidden {DecorCull.HiddenCount}"); } if (FireSmoke.LimitSmoke.Value) { list.Add($"smoke {Smoke.GetTotalSmoke()}/{FireSmoke.MaxSmoke.Value}"); } if (AiThrottle.On.Value) { list.Add($"AI throttled {AiThrottle.Throttled}"); } if (AnimThrottle.On.Value) { list.Add($"animations throttled {AnimThrottle.Throttled}"); } if (IdlePhysics.On.Value) { list.Add($"resting {IdlePhysics.Resting}"); } if (SpreadLoading.On.Value && SpreadLoading.Waiting > 0) { list.Add($"loading {SpreadLoading.Waiting}"); } if (ClothCull.On.Value) { list.Add($"cloth paused {ClothCull.Paused}"); } if (list.Count > 0) { stringBuilder.AppendLine(string.Join(" ", list)); } string text = ServerSim.ClientLine(); if (text != null) { stringBuilder.AppendLine(text); } foreach (string item2 in HitchLog.Recent) { stringBuilder.AppendLine("<color=#e8a060>" + item2 + "</color>"); } if (!string.IsNullOrEmpty(Benchmark.Status)) { stringBuilder.AppendLine(Benchmark.Status); } stringBuilder.Append($"<size=11>{Plugin.OverlayKey.Value} closes {Plugin.BenchmarkKey.Value} benchmark</size>"); return stringBuilder.ToString(); } private void OnGUI() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown if (open && !string.IsNullOrEmpty(text)) { if (style == null) { style = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)0, richText = true, fontSize = 13, padding = new RectOffset(10, 10, 8, 8), wordWrap = false }; } Vector2 val = style.CalcSize(new GUIContent(text)); GUI.Box(new Rect(12f, 12f, val.x, val.y), text, style); } } } public static class PerfSettings { public static void Register(ConfigFile cfg) { RhServer.Watch(cfg); Page p = RhSettings.AddPage("rp_performance", "Runeheim Performance", "Mods", 110); p.Info("Every feature starts off. F10 shows the performance overlay, F11 runs a benchmark (in game)."); p.Section("Presets"); T("Presets", "Apply low-spec preset", "Apply the low-spec preset"); p.Section("Buildings"); T("Combined meshes", "Enabled", "Combined building meshes"); T("Combined meshes", "Real pieces while building", "Real pieces while building", On("Combined meshes", "Enabled"), indent: true); T("Wear updates", "Smooth the once-a-second pass", "Smooth the wear pass"); F("Wear updates", "Full cycle (seconds)", "Wear cycle", 0f, 20f, (float v) => (!(v < 0.5f)) ? $"{v:0} s" : "vanilla", null, indent: false); T("Shadows", "Cull distant piece shadows", "Cull distant piece shadows"); F("Shadows", "Piece shadow distance", "Piece shadow distance", 20f, 200f, M, On("Shadows", "Cull distant piece shadows")); T("Piece draw distance", "Hide distant small pieces", "Hide distant small pieces"); F("Piece draw distance", "Distance", "Small piece distance", 15f, 200f, M, On("Piece draw distance", "Hide distant small pieces")); T("Spread loading", "Spread object loading", "Spread object loading"); F("Spread loading", "Time per go (ms)", "Time per go", 2f, 20f, (float v) => $"{v:0} ms", On("Spread loading", "Spread object loading")); p.Section("Lights, fire and smoke"); T("Lights", "Limit lights", "Limit lights"); I("Lights", "Max lights", "Max lights", 1, 64, (float v) => $"{v:0}", On("Lights", "Limit lights")); I("Lights", "Max light shadows", "Max light shadows", 0, 16, (float v) => $"{v:0}", On("Lights", "Limit lights")); T("Fire and smoke", "Limit smoke", "Limit smoke"); I("Fire and smoke", "Max smoke puffs", "Max smoke puffs", 5, 100, (float v) => $"{v:0}", On("Fire and smoke", "Limit smoke")); T("Fire and smoke", "Smoke puffs ignore each other", "Smoke puffs ignore each other"); T("Fire and smoke", "Fewer fire particles", "Fewer fire particles"); I("Fire and smoke", "Fire particles (%)", "Fire particles", 10, 100, (float v) => $"{v:0}%", On("Fire and smoke", "Fewer fire particles")); p.Section("World"); T("Grass", "Override grass", "Override grass"); I("Grass", "Grass density (%)", "Grass density", 10, 150, (float v) => $"{v:0}%", On("Grass", "Override grass")); F("Grass", "Grass distance", "Grass distance", 10f, 80f, M, On("Grass", "Override grass")); T("Item drops", "Merge nearby drops", "Merge nearby item drops"); T("Crops and machines", "Fewer checks far away", "Fewer distant crop and machine checks"); T("Physics", "Rest idle distant ships", "Rest idle distant ships"); T("Physics", "Cap physics catch-up", "Cap physics catch-up"); T("Cloth", "Pause distant cloth", "Pause distant cloth"); p.Section("Creatures"); T("Creature AI", "Throttle distant creature AI", "Throttle distant creature AI"); F("Creature AI", "Distance", "AI distance", 20f, 150f, M, On("Creature AI", "Throttle distant creature AI")); T("Animations", "Throttle distant animations", "Throttle distant animations"); F("Animations", "Distance", "Animation distance", 15f, 150f, M, On("Animations", "Throttle distant animations")); p.Section("Effects"); T("Effects", "Hide smoke", "Hide chimney and fire smoke"); T("Effects", "Hide building dust", "Hide building dust"); T("Effects", "Hide tree dust", "Hide tree dust"); T("Effects", "Hide mining dust", "Hide mining dust"); p.Section("Tools"); T("Hitch logger", "Log hitches", "Log hitches"); T("Hitch logger", "Break down by system", "Break hitches down by system", On("Hitch logger", "Log hitches"), indent: true); p.Info("Server simulation is set in the server's own config file."); ConfigEntry<T> E<T>(string s, string k) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown ConfigEntry<T> result = default(ConfigEntry<T>); if (!cfg.TryGetEntry<T>(new ConfigDefinition(s, k), ref result)) { return null; } return result; } void F(string s, string k, string title, float min, float max, Func<float, string> fmt, Func<bool> active, bool indent = true) { ConfigEntry<float> val = E<float>(s, k); if (val != null) { p.Slider(val, title, (string)null, min, max, fmt, active, indent, false); } } void I(string s, string k, string title, int min, int max, Func<float, string> fmt, Func<bool> active, bool indent = true) { ConfigEntry<int> val = E<int>(s, k); if (val != null) { p.Slider(val, title, (string)null, min, max, fmt, active, indent); } } static string M(float v) { return $"{v:0} m"; } Func<bool> On(string s, string k) { ConfigEntry<bool> e = E<bool>(s, k); return () => e == null || e.Value; } void T(string s, string k, string title, Func<bool> active = null, bool indent = false) { ConfigEntry<bool> val = E<bool>(s, k); if (val != null) { p.Toggle(val, title, (string)null, active, indent); } } } } public class PhysicsCap : MonoBehaviour { public static ConfigEntry<bool> On; public static ConfigEntry<float> Cap; private float original = -1f; public static void Init(ConfigFile c) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown On = c.Bind<bool>("Physics", "Cap physics catch-up", false, "After a slow frame the game runs fewer catch-up physics steps at once, so one hitch doesn't snowball into several. Only matters below about 10 fps."); Cap = c.Bind<float>("Physics", "Longest catch-up (seconds)", 0.1f, new ConfigDescription("Most game time one frame may catch up (Unity's default is 0.333 s).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 0.333f), Array.Empty<object>())); } private void Update() { if (On.Value) { if (original < 0f) { original = Time.maximumDeltaTime; } if (!Mathf.Approximately(Time.maximumDeltaTime, Cap.Value)) { Time.maximumDeltaTime = Cap.Value; } } else if (original >= 0f) { Time.maximumDeltaTime = original; original = -1f; } } } [BepInPlugin("daytreee.runeheimperformance", "Runeheim Performance", "0.9.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "daytreee.runeheimperformance"; public const string ModName = "Runeheim Performance"; public const string Version = "0.9.3"; public static Plugin Instance; public static ManualLogSource Log; public static ConfigEntry<KeyboardShortcut> OverlayKey; public static ConfigEntry<KeyboardShortcut> BenchmarkKey; public static ConfigEntry<bool> CombineMeshes; public static ConfigEntry<bool> SmoothWear; public static ConfigEntry<bool> ShowRealWhileBuilding; public static ConfigEntry<bool> ShadowCullOn; public static ConfigEntry<float> ChunkSize; public static ConfigEntry<float> BuildToolRadius; public static ConfigEntry<float> WearCycle; public static ConfigEntry<float> ShadowDistance; private void Awake() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Expected O, but got Unknown //IL_02bc: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; OverlayKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Overlay key", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Shows the performance overlay."); BenchmarkKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Benchmark key", new KeyboardShortcut((KeyCode)292, Array.Empty<KeyCode>()), "A/B benchmark: 10 s with the mod's features, 10 s without, results on screen and in the log."); CombineMeshes = ((BaseUnityPlugin)this).Config.Bind<bool>("Combined meshes", "Enabled", false, "Draw building pieces as a few combined meshes per area (visual only: pieces stay real for building, damage, support and the server). Machines, chests, doors and other interactables are never combined."); ChunkSize = ((BaseUnityPlugin)this).Config.Bind<float>("Combined meshes", "Area size", 32f, new ConfigDescription("Metres per combined area. Smaller areas rebuild faster after a change; bigger ones draw fewer meshes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(8f, 64f), Array.Empty<object>())); ShowRealWhileBuilding = ((BaseUnityPlugin)this).Config.Bind<bool>("Combined meshes", "Real pieces while building", true, "With a hammer, hoe or cultivator out, areas near you show the real pieces (highlights, repair and placement look exactly as normal)."); BuildToolRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Combined meshes", "Building radius", 20f, "How far from you the real pieces show while building."); SmoothWear = ((BaseUnityPlugin)this).Config.Bind<bool>("Wear updates", "Smooth the once-a-second pass", false, "Vanilla checks roof cover and Ashlands wear on every piece in one frame once a second (a hitch in big builds). This spreads the same work over the second."); WearCycle = ((BaseUnityPlugin)this).Config.Bind<float>("Wear updates", "Full cycle (seconds)", 0f, new ConfigDescription("How long one round of structural checks over the whole build takes. Vanilla checks up to 100 pieces a frame (about 3 s for 15,000 pieces); 8 s or more checks fewer per frame for a fraction of the cost. Rain wear and a collapse after removing support can take this long to happen. 0 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>())); ShadowCullOn = ((BaseUnityPlugin)this).Config.Bind<bool>("Shadows", "Cull distant piece shadows", false, "Building pieces stop casting sun shadows beyond the distance below (Unity's own per-layer shadow cull: costs nothing). Up close shadows are unchanged."); ShadowDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Shadows", "Piece shadow distance", 60f, new ConfigDescription("Metres from the camera beyond which pieces cast no sun shadow.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 200f), Array.Empty<object>())); CombineMeshes.SettingChanged += delegate { Combiner.Instance?.SetEnabled(CombineMeshes.Value); }; EffectFilter.Init(((BaseUnityPlugin)this).Config); LightLimiter.Init(((BaseUnityPlugin)this).Config); DecorCull.Init(((BaseUnityPlugin)this).Config); FireSmoke.Init(((BaseUnityPlugin)this).Config); AiThrottle.Init(((BaseUnityPlugin)this).Config); AnimThrottle.Init(((BaseUnityPlugin)this).Config); DropMerge.Init(((BaseUnityPlugin)this).Config); Grass.Init(((BaseUnityPlugin)this).Config); IdlePhysics.Init(((BaseUnityPlugin)this).Config); HitchLog.Init(((BaseUnityPlugin)this).Config); HitchSystems.Init(((BaseUnityPlugin)this).Config); SpreadLoading.Init(((BaseUnityPlugin)this).Config); PhysicsCap.Init(((BaseUnityPlugin)this).Config); ClothCull.Init(((BaseUnityPlugin)this).Config); MachineChecks.Init(((BaseUnityPlugin)this).Config); Preset.Init(((BaseUnityPlugin)this).Config); ServerSim.Init(((BaseUnityPlugin)this).Config); PerfSettings.Register(((BaseUnityPlugin)this).Config); new Harmony("daytreee.runeheimperformance").PatchAll(); ((Component)this).gameObject.AddComponent<Overlay>(); ((Component)this).gameObject.AddComponent<WearSmoothing>(); ((Component)this).gameObject.AddComponent<Combiner>(); ((Component)this).gameObject.AddComponent<Benchmark>(); ((Component)this).gameObject.AddComponent<ShadowCull>(); ((Component)this).gameObject.AddComponent<LightLimiter>(); ((Component)this).gameObject.AddComponent<DecorCull>(); ((Component)this).gameObject.AddComponent<FireSmoke>(); ((Component)this).gameObject.AddComponent<AnimThrottle>(); ((Component)this).gameObject.AddComponent<Grass>(); ((Component)this).gameObject.AddComponent<HitchLog>(); ((Component)this).gameObject.AddComponent<PhysicsCap>(); ((Component)this).gameObject.AddComponent<ClothCull>(); ((Component)this).gameObject.AddComponent<ServerSim>(); Log.LogInfo((object)"Runeheim Performance 0.9.3 loaded."); } } public static class Preset { public static ConfigEntry<bool> LowSpec; public static void Init(ConfigFile c) { LowSpec = c.Bind<bool>("Presets", "Apply low-spec preset", false, "Turns on combined meshes, wear smoothing (8 s cycle), piece shadow cull, the light limiter, small piec