Decompiled source of StableStructures v1.0.0

StableStructures.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("StableStructures")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("StableStructures")]
[assembly: AssemblyTitle("StableStructures")]
[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 StableStructures
{
	[BepInPlugin("com.dreich.stablestructures", "Stable Structures", "1.0.0")]
	public class StableStructures : BaseUnityPlugin
	{
		public const string Guid = "com.dreich.stablestructures";

		public const string Name = "Stable Structures";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> SettleChecks;

		internal static ConfigEntry<float> RecheckSeconds;

		internal static ConfigEntry<float> WakeRadius;

		internal static ConfigEntry<bool> LogStats;

		private float nextStats;

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Let building pieces whose support hasn't changed stop re-checking it until something nearby changes.");
			SettleChecks = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SettleChecks", 5, new ConfigDescription("How many support checks in a row must come out unchanged before a piece settles.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 60), Array.Empty<object>()));
			RecheckSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RecheckSeconds", 60f, new ConfigDescription("A settled piece still re-checks its support about this often, as a safety net.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 600f), Array.Empty<object>()));
			WakeRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "WakeRadius", 8f, new ConfigDescription("Building, destroying or a support change wakes settled pieces within this many meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 32f), Array.Empty<object>()));
			LogStats = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "LogStats", false, "Every 30 seconds, write how many support checks ran, how many were skipped and how long they took.");
			new Harmony("com.dreich.stablestructures").PatchAll();
		}

		private void Update()
		{
			if (LogStats.Value && !(Time.time < nextStats))
			{
				nextStats = Time.time + 30f;
				SupportSleep.ReportStats();
			}
		}
	}
	internal static class SupportSleep
	{
		private sealed class State
		{
			public Vector3Int Cell;

			public int Stable;

			public bool Asleep;

			public float NextVerify;

			public float LastCheck = -1f;

			public float SupportBefore;
		}

		private const float CellSize = 8f;

		private const float GapSeconds = 5f;

		private static readonly FieldRef<WearNTear, float> SupportField = AccessTools.FieldRefAccess<WearNTear, float>("m_support");

		private static readonly Func<WearNTear, bool> HaveSupport = AccessTools.MethodDelegate<Func<WearNTear, bool>>(AccessTools.Method(typeof(WearNTear), "HaveSupport", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Dictionary<WearNTear, State> states = new Dictionary<WearNTear, State>();

		private static readonly Dictionary<Vector3Int, List<WearNTear>> grid = new Dictionary<Vector3Int, List<WearNTear>>();

		private static readonly Stopwatch watch = new Stopwatch();

		private static WearNTear updating;

		private static int ran;

		private static int skipped;

		private static Vector3Int CellOf(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3Int(Mathf.FloorToInt(position.x / 8f), Mathf.FloorToInt(position.y / 8f), Mathf.FloorToInt(position.z / 8f));
		}

		internal static void Register(WearNTear piece)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (!states.ContainsKey(piece))
			{
				State state = new State
				{
					Cell = CellOf(((Component)piece).transform.position)
				};
				states[piece] = state;
				if (!grid.TryGetValue(state.Cell, out var value))
				{
					value = new List<WearNTear>();
					grid[state.Cell] = value;
				}
				value.Add(piece);
				WakeAround(((Component)piece).transform.position, piece);
			}
		}

		internal static void Unregister(WearNTear piece)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (!states.TryGetValue(piece, out var value))
			{
				return;
			}
			states.Remove(piece);
			if (grid.TryGetValue(value.Cell, out var value2))
			{
				value2.Remove(piece);
				if (value2.Count == 0)
				{
					grid.Remove(value.Cell);
				}
			}
			WakeAround(((Component)piece).transform.position, piece);
		}

		internal static void Wake(WearNTear piece)
		{
			if (!((Object)(object)piece == (Object)null) && !((Object)(object)piece == (Object)(object)updating) && states.TryGetValue(piece, out var value))
			{
				value.Asleep = false;
				value.Stable = 0;
			}
		}

		private static void WakeAround(Vector3 position, WearNTear except)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: 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)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			float value = StableStructures.WakeRadius.Value;
			float num = value * value;
			Vector3Int val = CellOf(position - Vector3.one * value);
			Vector3Int val2 = CellOf(position + Vector3.one * value);
			for (int i = ((Vector3Int)(ref val)).x; i <= ((Vector3Int)(ref val2)).x; i++)
			{
				for (int j = ((Vector3Int)(ref val)).y; j <= ((Vector3Int)(ref val2)).y; j++)
				{
					for (int k = ((Vector3Int)(ref val)).z; k <= ((Vector3Int)(ref val2)).z; k++)
					{
						if (!grid.TryGetValue(new Vector3Int(i, j, k), out var value2))
						{
							continue;
						}
						foreach (WearNTear item in value2)
						{
							if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)except))
							{
								Vector3 val3 = ((Component)item).transform.position - position;
								if (!(((Vector3)(ref val3)).sqrMagnitude > num) && states.TryGetValue(item, out var value3))
								{
									value3.Asleep = false;
									value3.Stable = 0;
								}
							}
						}
					}
				}
			}
		}

		internal static bool BeforeUpdate(WearNTear piece)
		{
			if (!StableStructures.Enabled.Value || !states.TryGetValue(piece, out var value))
			{
				return true;
			}
			float time = Time.time;
			bool flag = value.LastCheck < 0f || time - value.LastCheck > 5f;
			value.LastCheck = time;
			if (value.Asleep && !flag && time < value.NextVerify)
			{
				skipped++;
				return false;
			}
			value.SupportBefore = SupportField.Invoke(piece);
			updating = piece;
			ran++;
			watch.Start();
			return true;
		}

		internal static void AfterUpdate(WearNTear piece)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)updating != (Object)(object)piece)
			{
				return;
			}
			watch.Stop();
			updating = null;
			if (!states.TryGetValue(piece, out var value))
			{
				return;
			}
			if (!Mathf.Approximately(SupportField.Invoke(piece), value.SupportBefore))
			{
				value.Stable = 0;
				value.Asleep = false;
				WakeAround(((Component)piece).transform.position, piece);
				return;
			}
			if (!HaveSupport(piece))
			{
				value.Stable = 0;
				return;
			}
			value.Stable++;
			if (value.Stable >= StableStructures.SettleChecks.Value)
			{
				value.Asleep = true;
				value.NextVerify = Time.time + StableStructures.RecheckSeconds.Value * Random.Range(0.75f, 1.25f);
			}
		}

		internal static void ReportStats()
		{
			int num = 0;
			foreach (State value in states.Values)
			{
				if (value.Asleep)
				{
					num++;
				}
			}
			double totalMilliseconds = watch.Elapsed.TotalMilliseconds;
			StableStructures.Log.LogInfo((object)("Support checks in the last 30 s: " + ran + " ran (" + totalMilliseconds.ToString("F1") + " ms total), " + skipped + " skipped. Pieces settled: " + num + " of " + states.Count + "."));
			ran = 0;
			skipped = 0;
			watch.Reset();
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Awake")]
	internal static class WearNTearAwakePatch
	{
		private static void Postfix(WearNTear __instance, ZNetView ___m_nview)
		{
			if ((Object)(object)___m_nview != (Object)null && ___m_nview.GetZDO() != null)
			{
				SupportSleep.Register(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "OnDestroy")]
	internal static class WearNTearDestroyPatch
	{
		private static void Prefix(WearNTear __instance)
		{
			SupportSleep.Unregister(__instance);
		}
	}
	[HarmonyPatch(typeof(WearNTear), "ClearCachedSupport")]
	internal static class WearNTearClearCachePatch
	{
		private static void Postfix(WearNTear __instance)
		{
			SupportSleep.Wake(__instance);
		}
	}
	[HarmonyPatch(typeof(WearNTear), "UpdateSupport")]
	internal static class WearNTearUpdateSupportPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(WearNTear __instance)
		{
			return SupportSleep.BeforeUpdate(__instance);
		}

		private static Exception Finalizer(WearNTear __instance, Exception __exception)
		{
			SupportSleep.AfterUpdate(__instance);
			return __exception;
		}
	}
}