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 UnlimitedBreeding v1.1.1
UnlimitedBreeding.dll
Decompiled 5 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; 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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("UnlimitedBreeding")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("UnlimitedBreeding")] [assembly: AssemblyTitle("UnlimitedBreeding")] [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 UnlimitedBreeding { internal static class ConfigLogDump { internal static void Schedule(BaseUnityPlugin plugin, ManualLogSource log) { ((MonoBehaviour)plugin).StartCoroutine(Run(plugin, log)); } private static IEnumerator Run(BaseUnityPlugin plugin, ManualLogSource log) { yield return null; yield return null; try { StringBuilder stringBuilder = new StringBuilder(); foreach (ConfigDefinition key in plugin.Config.Keys) { string text = key.Key.ToLowerInvariant(); if (text.Contains("pass") || text.Contains("token") || text.Contains("secret")) { continue; } ConfigEntryBase val = plugin.Config[key]; if (!object.Equals(val.BoxedValue, val.DefaultValue)) { if (stringBuilder.Length > 0) { stringBuilder.Append("; "); } stringBuilder.Append(key.Section).Append('.').Append(key.Key) .Append('=') .Append(val.BoxedValue); if (stringBuilder.Length > 1500) { stringBuilder.Append("; ..."); break; } } } log.LogInfo((object)("[UnlimitedBreeding] config (changed from default): " + ((stringBuilder.Length == 0) ? "none" : stringBuilder.ToString()))); } catch (Exception ex) { log.LogWarning((object)("[UnlimitedBreeding] config dump failed: " + ex.Message)); } } } [BepInPlugin("com.cube.unlimitedbreeding", "Unlimited Breeding", "1.1.1")] public class UnlimitedBreedingPlugin : BaseUnityPlugin { public const string PluginGUID = "com.cube.unlimitedbreeding"; public const string PluginName = "Unlimited Breeding"; public const string PluginVersion = "1.1.1"; internal static ManualLogSource Log; internal static ConfigEntry<bool> BypassNearbyBreedingLimit; internal static ConfigEntry<bool> BypassNearbyEggLimit; internal static ConfigEntry<int> MaxNearbyCreatures; internal static ConfigEntry<float> NearbyRadius; internal static ConfigEntry<bool> DebugLogging; private readonly Harmony harmony = new Harmony("com.cube.unlimitedbreeding"); internal static ConfigEntry<KeyboardShortcut> PickupEggsKey; internal static ConfigEntry<bool> EggAutoPickup; internal static ConfigEntry<float> EggPickupRadius; internal static ConfigEntry<bool> EggPickupSkipHatching; internal static ConfigEntry<bool> ClaimOwnershipOfBreeders; internal static ConfigEntry<float> ClaimOwnershipRadius; private const string ClaimMarkerKey = "UnlimitedBreedingClaim"; private const float AutoPickupInterval = 2f; private const float ClaimInterval = 5f; private const int MaxEggsPerKeyPress = 200; private const int MaxEggsPerAutoTick = 25; private static readonly FieldInfo ItemDropInstancesField = typeof(ItemDrop).GetField("s_instances", BindingFlags.Static | BindingFlags.NonPublic); private float nextAutoPickupTime; private float nextClaimTime; private bool reportedExtrasError; private void Awake() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown ConfigLogDump.Schedule((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger); Log = ((BaseUnityPlugin)this).Logger; BypassNearbyBreedingLimit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BypassNearbyBreedingLimit", true, "Bypass the vanilla nearby population/radius restriction for animal breeding."); BypassNearbyEggLimit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BypassNearbyEggLimit", true, "Bypass the vanilla nearby population/radius restriction affecting egg laying."); MaxNearbyCreatures = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxNearbyCreatures", 0, new ConfigDescription("0 = no limit at all (default). Any number above 0 = your own cap instead of removing the limit completely: breeding / egg laying stops once that many animals + offspring/eggs are nearby. Only applies to the categories whose Bypass option above is true.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>())); NearbyRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "NearbyRadius", 0f, new ConfigDescription("Radius in meters in which animals and eggs are counted against MaxNearbyCreatures. 0 = the vanilla radius of that animal. Has no effect while MaxNearbyCreatures is 0 (no limit).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>())); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Log when the vanilla nearby limit would have blocked an otherwise-valid action."); BindExtrasConfig(); try { InstallPatches(); } catch (Exception arg) { Log.LogError((object)$"[UnlimitedBreeding] Failed to install patches - the plugin will do nothing this session. {arg}"); } } [MethodImpl(MethodImplOptions.NoInlining)] private void InstallPatches() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown MethodInfo method = typeof(Procreation).GetMethod("Procreate", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { Log.LogError((object)"[UnlimitedBreeding] Patch target Procreation.Procreate() not found in assembly_valheim - this Valheim version differs from what the plugin was written against. Nothing patched."); return; } harmony.Patch((MethodBase)method, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ProcreationPatch), "Transpiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"[UnlimitedBreeding] v1.1.1 loaded - patch registered on Procreation.Procreate()."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private void BindExtrasConfig() { //IL_0020: 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_008c: Expected O, but got Unknown //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown PickupEggsKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Eggs", "PickupKey", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Press to collect all eggs within EggPickupRadius into your inventory. Set to None to disable the key."); EggAutoPickup = ((BaseUnityPlugin)this).Config.Bind<bool>("Eggs", "AutoPickup", false, "If true, eggs within EggPickupRadius are collected automatically (about every 2 seconds, while your inventory has room)."); EggPickupRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Eggs", "PickupRadius", 10f, new ConfigDescription("Radius in meters for the egg pickup key and the automatic pickup.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>())); EggPickupSkipHatching = ((BaseUnityPlugin)this).Config.Bind<bool>("Eggs", "SkipHatchingEggs", true, "If true, eggs that are currently warm/hatching are left alone, so the pickup never steals eggs you want to hatch."); ClaimOwnershipOfBreeders = ((BaseUnityPlugin)this).Config.Bind<bool>("Multiplayer", "ClaimOwnershipOfBreeders", false, "EXPERIMENTAL. Normally the nearby limit is only lifted for animals simulated by a player who has this mod. With this on, you take over ownership of the tamed breeding animals around you, so the mod works for them even if other players near the farm don't have it installed. Players without the mod see no difference. Takes effect only while you are near the animals; when you leave, ownership falls back to whoever is nearby."); ClaimOwnershipRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Multiplayer", "ClaimOwnershipRadius", 30f, new ConfigDescription("Radius in meters around you in which tamed breeding animals are claimed (only used while ClaimOwnershipOfBreeders is on).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), Array.Empty<object>())); } private void Update() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } try { UpdateExtras(localPlayer); } catch (Exception arg) { if (!reportedExtrasError) { reportedExtrasError = true; Log.LogError((object)$"[UnlimitedBreeding] Error in the egg pickup / ownership extras (reported once, they keep running): {arg}"); } } } private void UpdateExtras(Player player) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = PickupEggsKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0 && ((KeyboardShortcut)(ref value)).IsDown() && !TypingOrMenuOpen()) { int num = PickupEggsAround(player, 200); ((Character)player).Message((MessageType)1, (num > 0) ? $"Collecting {num} egg(s)" : "No eggs nearby", 0, (Sprite)null, false); } float time = Time.time; if (EggAutoPickup.Value && time >= nextAutoPickupTime) { nextAutoPickupTime = time + 2f; PickupEggsAround(player, 25); } if (ClaimOwnershipOfBreeders.Value && time >= nextClaimTime) { nextClaimTime = time + 5f; ClaimNearbyBreeders(player); } } private static bool TypingOrMenuOpen() { if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !Menu.IsVisible() && !TextInput.IsVisible()) { return InventoryGui.IsVisible(); } return true; } private static IEnumerable<ItemDrop> AllItemDrops() { if (ItemDropInstancesField != null && ItemDropInstancesField.GetValue(null) is List<ItemDrop> collection) { return new List<ItemDrop>(collection); } return Object.FindObjectsOfType<ItemDrop>(); } private static int PickupEggsAround(Player player, int maxEggs) { //IL_0006: 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) //IL_0062: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)player).transform.position; float num = EggPickupRadius.Value * EggPickupRadius.Value; Inventory inventory = ((Humanoid)player).GetInventory(); int num2 = 0; foreach (ItemDrop item in AllItemDrops()) { if (num2 >= maxEggs) { break; } if ((Object)(object)item == (Object)null) { continue; } Vector3 val = ((Component)item).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude > num || (Object)(object)((Component)item).GetComponent<EggGrow>() == (Object)null) { continue; } ZNetView component = ((Component)item).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid() && (!EggPickupSkipHatching.Value || !(component.GetZDO().GetFloat(ZDOVars.s_growStart, 0f) > 0f))) { if (!inventory.CanAddItem(item.m_itemData, -1) || item.m_itemData.GetWeight(-1) + inventory.GetTotalWeight() > player.GetMaxCarryWeight()) { break; } item.Pickup((Humanoid)(object)player); num2++; } } return num2; } private void ClaimNearbyBreeders(Player player) { //IL_0006: 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) //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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)player).transform.position; float num = ClaimOwnershipRadius.Value * ClaimOwnershipRadius.Value; long sessionID = ZDOMan.GetSessionID(); int num2 = 0; foreach (BaseAI item in new List<BaseAI>(BaseAI.BaseAIInstances)) { if ((Object)(object)item == (Object)null) { continue; } Vector3 val = ((Component)item).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude > num || (Object)(object)((Component)item).GetComponent<Procreation>() == (Object)null) { continue; } Tameable component = ((Component)item).GetComponent<Tameable>(); if ((Object)(object)component == (Object)null || !component.IsTamed()) { continue; } ZNetView component2 = ((Component)item).GetComponent<ZNetView>(); if ((Object)(object)component2 == (Object)null || !component2.IsValid()) { continue; } ZDO zDO = component2.GetZDO(); if (component2.IsOwner()) { if (zDO.GetLong("UnlimitedBreedingClaim", 0L) != sessionID) { zDO.Set("UnlimitedBreedingClaim", sessionID); } continue; } long num3 = zDO.GetLong("UnlimitedBreedingClaim", 0L); if (num3 == 0L || num3 != zDO.GetOwner()) { component2.ClaimOwnership(); if (component2.IsOwner()) { zDO.Set("UnlimitedBreedingClaim", sessionID); num2++; } } } if (num2 > 0 && DebugLogging.Value) { Log.LogInfo((object)$"[UnlimitedBreeding] Took over ownership of {num2} tamed breeding animal(s) within {ClaimOwnershipRadius.Value}m."); } } } internal static class ProcreationPatch { private static readonly FieldInfo MaxCreaturesField = typeof(Procreation).GetField("m_maxCreatures", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo EvaluateMethod = typeof(ProcreationPatch).GetMethod("EvaluateNearbyCount", BindingFlags.Static | BindingFlags.Public); private static readonly Dictionary<int, float> lastLogTime = new Dictionary<int, float>(); private const float LogIntervalPerCreature = 60f; private static bool reportedError; private static readonly FieldInfo MyPrefabField = typeof(Procreation).GetField("m_myPrefab", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo OffspringPrefabField = typeof(Procreation).GetField("m_offspringPrefab", BindingFlags.Instance | BindingFlags.NonPublic); internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); int num = -1; int num2 = 0; for (int i = 2; i < list.Count - 1; i++) { if (list[i].opcode == OpCodes.Ldfld && list[i].operand is FieldInfo fieldInfo && fieldInfo == MaxCreaturesField && list[i - 1].opcode == OpCodes.Ldarg_0 && list[i - 2].opcode == OpCodes.Add && (list[i + 1].opcode == OpCodes.Blt || list[i + 1].opcode == OpCodes.Blt_S)) { num = i; num2++; } } if (num2 != 1) { UnlimitedBreedingPlugin.Log.LogError((object)($"[UnlimitedBreeding] Procreation.Procreate() IL did not match the expected nearby-limit pattern exactly once (found {num2}). " + "Patch NOT applied - breeding stays fully vanilla. This Valheim version likely changed that method.")); return list; } int num3 = num - 2; list[num3].opcode = OpCodes.Ldarg_0; list[num3].operand = null; list.Insert(num3 + 1, new CodeInstruction(OpCodes.Call, (object)EvaluateMethod)); return list; } public static int EvaluateNearbyCount(int ownCount, int offspringCount, Procreation self) { int num = ownCount + offspringCount; try { bool flag = (Object)(object)self.m_offspring != (Object)null && (Object)(object)self.m_offspring.GetComponent<ItemDrop>() != (Object)null; if (!(flag ? UnlimitedBreedingPlugin.BypassNearbyEggLimit.Value : UnlimitedBreedingPlugin.BypassNearbyBreedingLimit.Value)) { return num; } int value = UnlimitedBreedingPlugin.MaxNearbyCreatures.Value; if (value <= 0) { if (num < self.m_maxCreatures) { return num; } if (UnlimitedBreedingPlugin.DebugLogging.Value) { LogBypass(self, flag, $"{num} nearby (within {self.m_totalCheckRange}m), vanilla limit {self.m_maxCreatures}, no limit configured"); } return int.MinValue; } float value2 = UnlimitedBreedingPlugin.NearbyRadius.Value; int count = num; if (value2 > 0f && !TryCountWithin(self, value2, out count)) { count = num; } bool flag2 = count < value; if (UnlimitedBreedingPlugin.DebugLogging.Value && (!flag2 || num >= self.m_maxCreatures)) { LogBypass(self, flag, string.Format("{0} nearby (within {1}m), own cap {2}, vanilla limit {3} -> {4}", count, (value2 > 0f) ? value2 : self.m_totalCheckRange, value, self.m_maxCreatures, flag2 ? "allowed" : "blocked by own cap")); } return flag2 ? int.MinValue : int.MaxValue; } catch (Exception arg) { if (!reportedError) { reportedError = true; UnlimitedBreedingPlugin.Log.LogError((object)$"[UnlimitedBreeding] Error while evaluating the nearby limit, falling back to vanilla behavior: {arg}"); } return num; } } private static bool TryCountWithin(Procreation self, float radius, out int count) { //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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) count = 0; if (MyPrefabField == null || OffspringPrefabField == null) { return false; } object? value = MyPrefabField.GetValue(self); GameObject val = (GameObject)((value is GameObject) ? value : null); object? value2 = OffspringPrefabField.GetValue(self); GameObject val2 = (GameObject)((value2 is GameObject) ? value2 : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return false; } Vector3 position = ((Component)self).transform.position; count = SpawnSystem.GetNrOfInstances(val, position, radius, false, false) + SpawnSystem.GetNrOfInstances(val2, position, radius, false, false); return true; } private static void LogBypass(Procreation self, bool isEgg, string details) { int instanceID = ((Object)self).GetInstanceID(); float realtimeSinceStartup = Time.realtimeSinceStartup; if (!lastLogTime.TryGetValue(instanceID, out var value) || !(realtimeSinceStartup - value < 60f)) { if (lastLogTime.Count > 512) { lastLogTime.Clear(); } lastLogTime[instanceID] = realtimeSinceStartup; UnlimitedBreedingPlugin.Log.LogInfo((object)("[UnlimitedBreeding] '" + ((Object)((Component)self).gameObject).name.Replace("(Clone)", "") + "' (" + (isEgg ? "egg laying" : "breeding") + "): " + details + ".")); } } } }