Decompiled source of MightyOaks v1.2.1

MightyOaks.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("MightyOaks")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Scales Oak trees in Valheim.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f024e1f3d64b04eb9523231aa597ccb5d68f8c82")]
[assembly: AssemblyProduct("MightyOaks")]
[assembly: AssemblyTitle("MightyOaks")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 MightyOaks
{
	[BepInPlugin("com.lailoken.mightyoaks", "MightyOaks", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class MightyOaksPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class ZNet_OnNewConnection_Patch
		{
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				peer.m_rpc.Register<string>("MightyOaks_VersionCheck", (Action<ZRpc, string>)RPC_MightyOaks_VersionCheck);
				if (!__instance.IsServer())
				{
					peer.m_rpc.Invoke("MightyOaks_VersionCheck", new object[1] { "1.2.1" });
				}
				else
				{
					((MonoBehaviour)__instance).StartCoroutine(VerifyPeerHasMod(peer));
				}
			}
		}

		[HarmonyPatch(typeof(ZNetView), "Awake")]
		private static class ZNetView_Awake_Patch
		{
			private static void Postfix(ZNetView __instance)
			{
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				//IL_010e: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
				if (!Object.op_Implicit((Object)(object)__instance) || !__instance.IsValid())
				{
					return;
				}
				ZDO zDO = __instance.GetZDO();
				if (zDO == null || !IsScalableTree(__instance, out var prefabName, out var chance))
				{
					return;
				}
				float num = zDO.GetFloat(OakScaleFactorHash, 0f);
				if (num > 0.1f)
				{
					ApplyScale(__instance, num);
					return;
				}
				int worldSeed = GetWorldSeed();
				float num2 = ComputeScale(((Component)__instance).transform.position, worldSeed, chance);
				bool flag = worldSeed != 0;
				if (zDO.IsOwner())
				{
					if (flag)
					{
						zDO.Set(OakScaleFactorHash, num2);
						ApplyScale(__instance, num2);
						ManualLogSource logger = _Logger;
						if (logger != null)
						{
							logger.LogInfo((object)$"[MightyOaks] {prefabName} scale written pos={((Component)__instance).transform.position} scale={num2:F2} seed={worldSeed} owner={GetOakWriteOwnerDescription()}");
						}
						return;
					}
					ApplyScale(__instance, num2);
					ManualLogSource logger2 = _Logger;
					if (logger2 != null)
					{
						logger2.LogInfo((object)$"[MightyOaks] {prefabName} no-write (seed not ready) pos={((Component)__instance).transform.position} scale={num2:F2} seed={worldSeed} owner={GetOakWriteOwnerDescription()} -> reapply next frame");
					}
					if ((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).gameObject != (Object)null)
					{
						((MonoBehaviour)__instance).StartCoroutine(ReapplyScaleWhenSeedReady(__instance));
					}
				}
				else
				{
					ApplyScale(__instance, num2);
					if (!flag && (Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).gameObject != (Object)null)
					{
						((MonoBehaviour)__instance).StartCoroutine(ReapplyScaleWhenSeedReady(__instance));
					}
				}
			}
		}

		public const string PluginGUID = "com.lailoken.mightyoaks";

		public const string PluginName = "MightyOaks";

		public const string PluginVersion = "1.2.1";

		private static ConfigEntry<float> ChanceScaleOak;

		private static ConfigEntry<float> ChanceScaleAshlandsOaks;

		private static ConfigEntry<float> ChanceScalePlainsStoneColumns;

		private static ConfigEntry<float> ChanceScaleSwampAncientTrees;

		private static ConfigEntry<float> ChanceScaleMistlandsTrees;

		private static ConfigEntry<float> MinScale;

		private static ConfigEntry<float> MaxScale;

		private static ConfigEntry<float> ScaleExponent;

		private static ConfigEntry<bool> ScaleToughness;

		private static ConfigEntry<float> InvulnerabilityThreshold;

		private static ConfigEntry<float> SpawnProtectionRadius;

		private static ManualLogSource _Logger;

		private static readonly int OakScaleFactorHash = GetStableHashCode("OakScaleFactor");

		private static readonly HashSet<string> AshlandsOakPrefabs = new HashSet<string> { "AshlandsTree6", "AshlandsTree6_big" };

		private static readonly HashSet<string> PlainsStoneColumnPrefabs = new HashSet<string> { "HeathRockPillar", "HeathRockPillar_frac" };

		private static readonly HashSet<string> SwampAncientTreePrefabs = new HashSet<string> { "SwampTree2", "SwampTree2_darkland" };

		private static readonly HashSet<string> MistlandsTreePrefabs = new HashSet<string> { "YggaShoot1", "YggaShoot2", "YggaShoot3" };

		private static readonly ConditionalWeakTable<ZNetPeer, object> ValidatedPeers = new ConditionalWeakTable<ZNetPeer, object>();

		private void Awake()
		{
			//IL_000b: 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_0018: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Expected O, but got Unknown
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Expected O, but got Unknown
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Expected O, but got Unknown
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Expected O, but got Unknown
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Expected O, but got Unknown
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Expected O, but got Unknown
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Expected O, but got Unknown
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Expected O, but got Unknown
			_Logger = ((BaseUnityPlugin)this).Logger;
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			AcceptableValueRange<float> val2 = new AcceptableValueRange<float>(0f, 100f);
			AcceptableValueRange<float> val3 = new AcceptableValueRange<float>(0.1f, 20f);
			AcceptableValueRange<float> val4 = new AcceptableValueRange<float>(0f, 21f);
			ChanceScaleOak = ((BaseUnityPlugin)this).Config.Bind<float>("Chances", "ChanceScaleOak", 15f, new ConfigDescription("Chance (0-100) to scale Oak trees. 0 = off.", (AcceptableValueBase)(object)val2, new object[1] { val }));
			ChanceScaleAshlandsOaks = ((BaseUnityPlugin)this).Config.Bind<float>("Chances", "ChanceScaleAshlandsOaks", 5f, new ConfigDescription("Chance (0-100) to scale Ashlands burnt oaks. 0 = off.", (AcceptableValueBase)(object)val2, new object[1] { val }));
			ChanceScalePlainsStoneColumns = ((BaseUnityPlugin)this).Config.Bind<float>("Chances", "ChanceScalePlainsStoneColumns", 0f, new ConfigDescription("Chance (0-100) to scale Plains stone columns. 0 = off.", (AcceptableValueBase)(object)val2, new object[1] { val }));
			ChanceScaleSwampAncientTrees = ((BaseUnityPlugin)this).Config.Bind<float>("Chances", "ChanceScaleSwampAncientTrees", 0f, new ConfigDescription("Chance (0-100) to scale Swamp ancient trees. 0 = off.", (AcceptableValueBase)(object)val2, new object[1] { val }));
			ChanceScaleMistlandsTrees = ((BaseUnityPlugin)this).Config.Bind<float>("Chances", "ChanceScaleMistlandsTrees", 0f, new ConfigDescription("Chance (0-100) to scale Mistlands trees (Yggdrasil shoots). 0 = off.", (AcceptableValueBase)(object)val2, new object[1] { val }));
			MinScale = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MinScale", 1f, new ConfigDescription("Minimum scale factor.", (AcceptableValueBase)(object)val3, new object[1] { val }));
			MaxScale = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxScale", 10f, new ConfigDescription("Maximum scale factor.", (AcceptableValueBase)(object)val3, new object[1] { val }));
			ScaleExponent = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ScaleExponent", 3f, new ConfigDescription("Exponent for scale distribution. 1.0 is linear (uniform). Higher values (e.g. 2.0, 3.0) make large trees rarer.", (AcceptableValueBase)(object)val3, new object[1] { val }));
			ScaleToughness = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ScaleToughness", true, new ConfigDescription("If true, scales the tree's health/toughness along with its size.", (AcceptableValueBase)null, new object[1] { val }));
			InvulnerabilityThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("General", "InvulnerabilityThreshold", 2f, new ConfigDescription("Scale threshold above which trees become invulnerable. Set to max to disable.", (AcceptableValueBase)(object)val4, new object[1] { val }));
			SpawnProtectionRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SpawnProtectionRadius", 300f, new ConfigDescription("Radius from world center (spawn) where no giant oaks spawn. Default 300 keeps spawn area clear (beyond ~180m render). Set 0 to allow everywhere.", (AcceptableValueBase)null, new object[1] { val }));
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.lailoken.mightyoaks");
		}

		private static IEnumerator VerifyPeerHasMod(ZNetPeer peer)
		{
			yield return (object)new WaitForSeconds(10f);
			if (peer.m_socket.IsConnected() && !ValidatedPeers.TryGetValue(peer, out var _))
			{
				_Logger.LogWarning((object)$"Peer {peer.m_uid} did not send MightyOaks version (missing mod?). Kicking.");
				ZNet.instance.Disconnect(peer);
			}
		}

		private static void RPC_MightyOaks_VersionCheck(ZRpc rpc, string clientVersion)
		{
			if (!ZNet.instance.IsServer())
			{
				return;
			}
			ZNetPeer val = null;
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.m_rpc == rpc)
				{
					val = peer;
					break;
				}
			}
			object value;
			if (val == null)
			{
				_Logger.LogWarning((object)"Received MightyOaks version check from unknown peer.");
			}
			else if (!IsVersionCompatible(clientVersion, "1.2.1"))
			{
				_Logger.LogWarning((object)string.Format("Peer {0} has incompatible MightyOaks version {1} (server: {2}). Kicking.", val.m_uid, clientVersion, "1.2.1"));
				ZNet.instance.Disconnect(val);
			}
			else if (!ValidatedPeers.TryGetValue(val, out value))
			{
				ValidatedPeers.Add(val, null);
			}
		}

		private static bool IsVersionCompatible(string v1, string v2)
		{
			try
			{
				Version version = new Version(v1);
				Version version2 = new Version(v2);
				return version.Major == version2.Major && version.Minor == version2.Minor;
			}
			catch
			{
				return false;
			}
		}

		private static int GetStableHashCode(string str)
		{
			int num = 5381;
			int num2 = num;
			for (int i = 0; i < str.Length && str[i] != 0; i += 2)
			{
				num = ((num << 5) + num) ^ str[i];
				if (i == str.Length - 1 || str[i + 1] == '\0')
				{
					break;
				}
				num2 = ((num2 << 5) + num2) ^ str[i + 1];
			}
			return num + num2 * 1566083941;
		}

		private static int GetWorldSeed()
		{
			if (WorldGenerator.instance != null)
			{
				World value = Traverse.Create((object)WorldGenerator.instance).Field("m_world").GetValue<World>();
				if (value != null)
				{
					return value.m_seed;
				}
			}
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				World value2 = Traverse.Create((object)ZNet.instance).Field("m_world").GetValue<World>();
				if (value2 != null)
				{
					return value2.m_seed;
				}
			}
			return 0;
		}

		private static string GetOakWriteOwnerDescription()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return "?";
			}
			if (ZNet.instance.IsServer())
			{
				return "server";
			}
			string text = TryGetLocalPlayerName();
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return "client";
		}

		private static string TryGetLocalPlayerName()
		{
			try
			{
				Traverse val = Traverse.Create(typeof(Player));
				object obj = val.Field("m_localPlayer").GetValue() ?? val.Field("s_localPlayer").GetValue();
				if (obj == null)
				{
					return null;
				}
				string value = Traverse.Create(obj).Method("GetPlayerName", Array.Empty<object>()).GetValue<string>();
				if (!string.IsNullOrEmpty(value))
				{
					return value;
				}
				Game instance = Game.instance;
				if ((Object)(object)instance != (Object)null)
				{
					object value2 = Traverse.Create((object)instance).Method("GetPlayerProfile", Array.Empty<object>()).GetValue();
					if (value2 != null)
					{
						string value3 = Traverse.Create(value2).Method("GetName", Array.Empty<object>()).GetValue<string>();
						if (!string.IsNullOrEmpty(value3))
						{
							return value3;
						}
					}
				}
				return null;
			}
			catch
			{
				return null;
			}
		}

		private static string GetTreePrefabName(ZNetView view)
		{
			if (!Object.op_Implicit((Object)(object)view) || !view.IsValid())
			{
				return "";
			}
			ZDO zDO = view.GetZDO();
			if (zDO != null && (Object)(object)ZNetScene.instance != (Object)null)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(zDO.GetPrefab());
				if (Object.op_Implicit((Object)(object)prefab))
				{
					return ((Object)prefab).name;
				}
			}
			return ((Object)((Component)view).gameObject).name.Replace("(Clone)", "");
		}

		private static bool IsScalableTree(ZNetView view, out string prefabName, out float chance)
		{
			prefabName = GetTreePrefabName(view);
			chance = 0f;
			if (string.IsNullOrEmpty(prefabName))
			{
				return false;
			}
			if (prefabName == "Oak1" && ChanceScaleOak.Value > 0f)
			{
				chance = ChanceScaleOak.Value;
				return true;
			}
			if (AshlandsOakPrefabs.Contains(prefabName) && ChanceScaleAshlandsOaks.Value > 0f)
			{
				chance = ChanceScaleAshlandsOaks.Value;
				return true;
			}
			if (PlainsStoneColumnPrefabs.Contains(prefabName) && ChanceScalePlainsStoneColumns.Value > 0f)
			{
				chance = ChanceScalePlainsStoneColumns.Value;
				return true;
			}
			if (SwampAncientTreePrefabs.Contains(prefabName) && ChanceScaleSwampAncientTrees.Value > 0f)
			{
				chance = ChanceScaleSwampAncientTrees.Value;
				return true;
			}
			if (MistlandsTreePrefabs.Contains(prefabName) && ChanceScaleMistlandsTrees.Value > 0f)
			{
				chance = ChanceScaleMistlandsTrees.Value;
				return true;
			}
			return false;
		}

		private static float ComputeScale(Vector3 pos, int worldSeed, float scalingChance)
		{
			//IL_004b: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_002a: 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_0037: 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)
			if (worldSeed == 0 || scalingChance <= 0f)
			{
				return 1f;
			}
			float value = SpawnProtectionRadius.Value;
			if (value > 0f && pos.x * pos.x + pos.z * pos.z < value * value)
			{
				return 1f;
			}
			int num = worldSeed + (int)(pos.x * 1000f) + (int)(pos.z * 1000f);
			State state = Random.state;
			Random.InitState(num);
			float result = 1f;
			if (Random.Range(0f, 100f) <= scalingChance)
			{
				float num2 = Mathf.Pow(Random.value, ScaleExponent.Value);
				result = Mathf.Lerp(MinScale.Value, MaxScale.Value, num2);
			}
			Random.state = state;
			return result;
		}

		private static IEnumerator ReapplyScaleWhenSeedReady(ZNetView view)
		{
			yield return null;
			if ((Object)(object)view == (Object)null || !view.IsValid())
			{
				yield break;
			}
			ZDO zDO = view.GetZDO();
			if (zDO == null || !IsScalableTree(view, out var prefabName, out var chance) || zDO.GetFloat(OakScaleFactorHash, 0f) > 0.1f)
			{
				yield break;
			}
			int worldSeed = GetWorldSeed();
			if (worldSeed == 0)
			{
				yield break;
			}
			float num = ComputeScale(((Component)view).transform.position, worldSeed, chance);
			ApplyScale(view, num);
			if (zDO.IsOwner())
			{
				zDO.Set(OakScaleFactorHash, num);
				ManualLogSource logger = _Logger;
				if (logger != null)
				{
					logger.LogInfo((object)$"[MightyOaks] {prefabName} reapply scale written pos={((Component)view).transform.position} scale={num:F2} seed={worldSeed} owner={GetOakWriteOwnerDescription()}");
				}
			}
		}

		private static void ApplyScale(ZNetView view, float scale)
		{
			//IL_000f: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: 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 (scale <= 1.01f || Mathf.Abs(((Component)view).transform.localScale.x - scale) < 0.01f)
			{
				return;
			}
			((Component)view).transform.localScale = Vector3.one * scale;
			Destructible component = ((Component)view).GetComponent<Destructible>();
			TreeBase component2 = ((Component)view).GetComponent<TreeBase>();
			if (ScaleToughness.Value)
			{
				if (Object.op_Implicit((Object)(object)component))
				{
					component.m_health *= scale * scale;
				}
				if (Object.op_Implicit((Object)(object)component2))
				{
					component2.m_health *= scale * scale;
				}
			}
			if (InvulnerabilityThreshold.Value > 0f && scale >= InvulnerabilityThreshold.Value)
			{
				if (Object.op_Implicit((Object)(object)component))
				{
					component.m_minToolTier = 1000;
					component.m_damages.m_chop = (DamageModifier)3;
					component.m_damages.m_pickaxe = (DamageModifier)3;
				}
				if (Object.op_Implicit((Object)(object)component2))
				{
					component2.m_minToolTier = 1000;
					component2.m_damageModifiers.m_chop = (DamageModifier)3;
					component2.m_damageModifiers.m_pickaxe = (DamageModifier)3;
				}
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "MightyOaks";

		public const string PLUGIN_NAME = "MightyOaks";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}