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("MightyOaks_VersionCheck", (Action)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 ChanceScaleOak; private static ConfigEntry ChanceScaleAshlandsOaks; private static ConfigEntry ChanceScalePlainsStoneColumns; private static ConfigEntry ChanceScaleSwampAncientTrees; private static ConfigEntry ChanceScaleMistlandsTrees; private static ConfigEntry MinScale; private static ConfigEntry MaxScale; private static ConfigEntry ScaleExponent; private static ConfigEntry ScaleToughness; private static ConfigEntry InvulnerabilityThreshold; private static ConfigEntry SpawnProtectionRadius; private static ManualLogSource _Logger; private static readonly int OakScaleFactorHash = GetStableHashCode("OakScaleFactor"); private static readonly HashSet AshlandsOakPrefabs = new HashSet { "AshlandsTree6", "AshlandsTree6_big" }; private static readonly HashSet PlainsStoneColumnPrefabs = new HashSet { "HeathRockPillar", "HeathRockPillar_frac" }; private static readonly HashSet SwampAncientTreePrefabs = new HashSet { "SwampTree2", "SwampTree2_darkland" }; private static readonly HashSet MistlandsTreePrefabs = new HashSet { "YggaShoot1", "YggaShoot2", "YggaShoot3" }; private static readonly ConditionalWeakTable ValidatedPeers = new ConditionalWeakTable(); 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 val2 = new AcceptableValueRange(0f, 100f); AcceptableValueRange val3 = new AcceptableValueRange(0.1f, 20f); AcceptableValueRange val4 = new AcceptableValueRange(0f, 21f); ChanceScaleOak = ((BaseUnityPlugin)this).Config.Bind("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("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("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("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("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("General", "MinScale", 1f, new ConfigDescription("Minimum scale factor.", (AcceptableValueBase)(object)val3, new object[1] { val })); MaxScale = ((BaseUnityPlugin)this).Config.Bind("General", "MaxScale", 10f, new ConfigDescription("Maximum scale factor.", (AcceptableValueBase)(object)val3, new object[1] { val })); ScaleExponent = ((BaseUnityPlugin)this).Config.Bind("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("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("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("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(); 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(); 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()).GetValue(); 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()).GetValue(); if (value2 != null) { string value3 = Traverse.Create(value2).Method("GetName", Array.Empty()).GetValue(); 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(); TreeBase component2 = ((Component)view).GetComponent(); 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"; } }