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 BetterTames v0.9.0
BetterTames.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BetterTames.Scripts.CatchUp; using BetterTames.Scripts.Commands; using BetterTames.Scripts.Protection; using BetterTames.Scripts.Tuning; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("BetterTames")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BetterTames")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: Guid("77E687FB-35AD-4B35-9C5E-9EDC00C4AF74")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: ComVisible(false)] [assembly: AssemblyVersion("0.1.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace BetterTames { [BepInPlugin("xtav1m.BetterTames", "BetterTames", "0.9.0")] public class Plugin : BaseUnityPlugin { public static ConfigEntry<bool> showTamingTimer; public static ConfigEntry<bool> showBreedingTimer; public static ConfigEntry<bool> showGrowthTimer; public static ConfigEntry<int> tamingTime; public static ConfigEntry<int> lovePoints; public static ConfigEntry<int> growthTime; public static ConfigEntry<int> maxCreaturesNearby; public static ConfigEntry<float> maxCreaturesRange; public static ConfigEntry<int> offspringStarChance; public static ConfigEntry<bool> commandAllTames; public static ConfigEntry<KeyboardShortcut> massFollowKey; public static ConfigEntry<KeyboardShortcut> massStayKey; public static ConfigEntry<float> massCommandRange; public static ConfigEntry<bool> tamesFightOwnKind; public static ConfigEntry<bool> catchUpTeleport; public static ConfigEntry<float> catchUpDistance; public static ConfigEntry<KeyboardShortcut> teleportFollowersKey; public static ConfigEntry<bool> bigTamesEnterDungeons; public static ConfigEntry<bool> portalTeleport; public static ConfigEntry<bool> starUpOnKill; public static ConfigEntry<int> starUpBaseChance; public static ConfigEntry<int> starUpBonusPerStar; public static ConfigEntry<bool> healOnStarUp; public static ConfigEntry<int> regenTime; public static ConfigEntry<bool> protectAllTames; public static ConfigEntry<int> recoveryTime; public static ConfigEntry<KeyboardShortcut> toggleProtectionKey; public static ConfigEntry<bool> showDownedTames; public static ConfigEntry<bool> wildEatFromChests; public static ConfigEntry<bool> tamesEatFromChests; public static ConfigEntry<float> chestRange; public static ConfigEntry<bool> chestRequireOnlyFood; public static ConfigEntry<bool> debugMode; public static readonly ManualLogSource Logger = Logger.CreateLogSource("BetterTames"); private static readonly ConfigSync configSync = new ConfigSync("xtav1m.BetterTames") { DisplayName = "BetterTames", CurrentVersion = "0.9.0", MinimumRequiredVersion = "0.9.0" }; private void Awake() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) InitializeConfig(); new Harmony("xtav1m.BetterTames").PatchAll(); Logger.LogInfo((object)"BetterTames 0.9.0 loaded"); } private void Update() { Commands.Tick(); CatchUp.Tick(); Protection.Tick(); } private ConfigEntry<T> ConfigSync<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags); ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val); configSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting; return val2; } private void InitializeConfig() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Expected O, but got Unknown //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Expected O, but got Unknown //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Expected O, but got Unknown //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Expected O, but got Unknown //IL_0331: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Expected O, but got Unknown //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Expected O, but got Unknown //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03ac: Expected O, but got Unknown //IL_03f4: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Expected O, but got Unknown //IL_042d: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Expected O, but got Unknown //IL_0458: Unknown result type (might be due to invalid IL or missing references) //IL_0468: Unknown result type (might be due to invalid IL or missing references) //IL_0473: Expected O, but got Unknown //IL_048f: Unknown result type (might be due to invalid IL or missing references) //IL_049a: Expected O, but got Unknown //IL_04b6: Unknown result type (might be due to invalid IL or missing references) //IL_04c1: Expected O, but got Unknown //IL_04dd: Unknown result type (might be due to invalid IL or missing references) //IL_04e8: Expected O, but got Unknown //IL_050c: Unknown result type (might be due to invalid IL or missing references) //IL_0517: Expected O, but got Unknown //IL_053a: Unknown result type (might be due to invalid IL or missing references) //IL_0545: Expected O, but got Unknown //IL_0561: Unknown result type (might be due to invalid IL or missing references) //IL_056c: Expected O, but got Unknown //IL_0597: Unknown result type (might be due to invalid IL or missing references) //IL_05a2: Expected O, but got Unknown //IL_05ea: Unknown result type (might be due to invalid IL or missing references) //IL_05f5: Expected O, but got Unknown //IL_061d: Unknown result type (might be due to invalid IL or missing references) //IL_0628: Expected O, but got Unknown //IL_0648: Unknown result type (might be due to invalid IL or missing references) //IL_0658: Unknown result type (might be due to invalid IL or missing references) //IL_0663: Expected O, but got Unknown //IL_067f: Unknown result type (might be due to invalid IL or missing references) //IL_068a: Expected O, but got Unknown //IL_06a6: Unknown result type (might be due to invalid IL or missing references) //IL_06b1: Expected O, but got Unknown //IL_06cd: Unknown result type (might be due to invalid IL or missing references) //IL_06d8: Expected O, but got Unknown //IL_0706: Unknown result type (might be due to invalid IL or missing references) //IL_0711: Expected O, but got Unknown //IL_072d: Unknown result type (might be due to invalid IL or missing references) //IL_0738: Expected O, but got Unknown //IL_0754: Unknown result type (might be due to invalid IL or missing references) //IL_075f: Expected O, but got Unknown ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; ConfigEntry<bool> lockingConfig = ConfigSync("1 - General", "Lock Configuration", value: true, new ConfigDescription("If enabled, the configuration is locked and can be changed by server admins only.", (AcceptableValueBase)null, Array.Empty<object>())); configSync.AddLockingConfigEntry<bool>(lockingConfig); showTamingTimer = ConfigSync("2 - Timers", "Show Taming Timer", value: true, new ConfigDescription("Show how long a creature you are taming has left, counting any taming boost from players nearby. The timer is marked paused while the creature is hungry or frightened, since taming stops then.", (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting: false); showBreedingTimer = ConfigSync("2 - Timers", "Show Breeding Timer", value: true, new ConfigDescription("Show how long a pregnant tame has left before giving birth. A tame that is not pregnant shows its love points instead: it gains one now and then while fed, calm and next to a partner, and gets pregnant when they are full.", (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting: false); showGrowthTimer = ConfigSync("2 - Timers", "Show Growth Timer", value: true, new ConfigDescription("Show how long offspring have left before growing up.", (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting: false); tamingTime = ConfigSync("3 - Taming", "Taming Time", 30, new ConfigDescription("Minutes of feeding it takes to tame any creature.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 180), Array.Empty<object>())); lovePoints = ConfigSync("4 - Breeding", "Love Points Needed", 3, new ConfigDescription("Love points a tame needs before it gets pregnant. It gains one now and then while fed, calm and next to a partner.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>())); growthTime = ConfigSync("4 - Breeding", "Growth Time", 30, new ConfigDescription("Minutes offspring take to grow up.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 180), Array.Empty<object>())); maxCreaturesNearby = ConfigSync("4 - Breeding", "Crowd Limit", 6, new ConfigDescription("How many creatures of the same kind can be nearby before breeding stops.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>())); maxCreaturesRange = ConfigSync("4 - Breeding", "Crowd Range", 10f, new ConfigDescription("How far, in meters, Crowd Limit counts around a breeding pair.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 200f), Array.Empty<object>())); offspringStarChance = ConfigSync("4 - Breeding", "Offspring Star Chance", 25, new ConfigDescription("Percent chance a newborn is one star above the tame giving birth, up to 2 stars. Eggs are not affected.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); tamingTime.SettingChanged += delegate { Tuning.ApplyAll(); }; lovePoints.SettingChanged += delegate { Tuning.ApplyAll(); }; growthTime.SettingChanged += delegate { Tuning.ApplyAll(); }; maxCreaturesNearby.SettingChanged += delegate { Tuning.ApplyAll(); }; maxCreaturesRange.SettingChanged += delegate { Tuning.ApplyAll(); }; commandAllTames = ConfigSync("5 - Commands", "Command Any Tame", value: true, new ConfigDescription("Press E on any tame to make it follow you or stay, not only on wolves, lox and the others the game allows. Off leaves each creature as the game or its mod made it.", (AcceptableValueBase)null, Array.Empty<object>())); massFollowKey = ConfigSync<KeyboardShortcut>("5 - Commands", "Follow All Key", new KeyboardShortcut((KeyCode)102, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), new ConfigDescription("Every tame in range that is not following anyone starts following you.", (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting: false); massStayKey = ConfigSync<KeyboardShortcut>("5 - Commands", "Stay All Key", new KeyboardShortcut((KeyCode)103, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), new ConfigDescription("Every tame in range that is following you stays where it is.", (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting: false); massCommandRange = ConfigSync("5 - Commands", "Follow/Stay All Range", 30f, new ConfigDescription("How far, in meters, the Follow All and Stay All keys reach.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>())); tamesFightOwnKind = ConfigSync("1 - General", "Tames Fight Their Own Kind", value: true, new ConfigDescription("A tame fights wild creatures of its own kind that are in a fight, like a tamed wolf defending you from wild wolves. Calm ones, and ones you are partway through taming, are left alone. The game normally keeps them on the same side.", (AcceptableValueBase)null, Array.Empty<object>())); commandAllTames.SettingChanged += delegate { Commands.ApplyAll(); }; catchUpTeleport = ConfigSync("6 - Teleport", "Teleport Lost Tames", value: true, new ConfigDescription("A tame that is following you teleports to you if it falls too far behind, and after you die and respawn. Vanilla following has no distance limit, so a tame stuck on terrain or left behind at your death is otherwise stuck or lost for good.", (AcceptableValueBase)null, Array.Empty<object>())); catchUpDistance = ConfigSync("6 - Teleport", "Teleport Distance", 30f, new ConfigDescription("How far, in meters, a following tame can fall behind before it teleports to you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 50f), Array.Empty<object>())); teleportFollowersKey = ConfigSync<KeyboardShortcut>("6 - Teleport", "Teleport Tames Key", new KeyboardShortcut((KeyCode)104, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), new ConfigDescription("Every tame following you teleports to you right away.", (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting: false); bigTamesEnterDungeons = ConfigSync("6 - Teleport", "Big Tames Enter Dungeons", value: false, new ConfigDescription("Following tames come with you into dungeons and back out. Turn off to leave big tames, like lox, outside, since they get stuck in narrow corridors.", (AcceptableValueBase)null, Array.Empty<object>())); portalTeleport = ConfigSync("6 - Teleport", "Tames Follow Through Portals", value: true, new ConfigDescription("Tames following you near a portal come through it with you. The game normally leaves them behind.", (AcceptableValueBase)null, Array.Empty<object>())); starUpOnKill = ConfigSync("7 - Leveling", "Gain Stars From Kills", value: true, new ConfigDescription("A tame has a chance to gain a star whenever it kills an enemy, up to 2 stars. The chance is higher the more stars the enemy has over the tame; killing something the same star or lower gives no bonus.", (AcceptableValueBase)null, Array.Empty<object>())); starUpBaseChance = ConfigSync("7 - Leveling", "Star Chance", 10, new ConfigDescription("Base percent chance to gain a star on a kill.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); starUpBonusPerStar = ConfigSync("7 - Leveling", "Bonus Chance Per Star", 5, new ConfigDescription("Percent added per star the killed enemy has over the tame.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); healOnStarUp = ConfigSync("7 - Leveling", "Heal On New Star", value: true, new ConfigDescription("A tame that gains a star from a kill is healed to full.", (AcceptableValueBase)null, Array.Empty<object>())); regenTime = ConfigSync("8 - Health", "Full Heal Time", 3600, new ConfigDescription("Seconds a tame takes to heal from empty to full. Lower is faster. Regeneration pauses while a tame is hungry, the same as the game's own creatures.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(60, 7200), Array.Empty<object>())); regenTime.SettingChanged += delegate { Tuning.ApplyAll(); }; protectAllTames = ConfigSync("8 - Health", "Protect All Tames", value: true, new ConfigDescription("A tame that would die lies down to recover instead. Enemies leave it alone while it is down. A butcher knife still kills it. Individual tames can be switched with the Protection Key.", (AcceptableValueBase)null, Array.Empty<object>())); recoveryTime = ConfigSync("8 - Health", "Recovery Time", 60, new ConfigDescription("Seconds a recovering tame rests, healing back to full, before it gets up.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 600), Array.Empty<object>())); toggleProtectionKey = ConfigSync<KeyboardShortcut>("8 - Health", "Protection Key", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), new ConfigDescription("Switch protection on or off for the tame you are looking at, overriding Protect All Tames for that tame.", (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting: false); showDownedTames = ConfigSync("8 - Health", "Show Recovering Tames", value: true, new ConfigDescription("Show the name and health bar of a recovering tame when you look at it. The game hides sleeping creatures from the crosshair, so with this off a recovering tame shows no name or health bar and the Protection Key can't reach it.", (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting: false); wildEatFromChests = ConfigSync("9 - Feeding", "Wild Creatures Eat From Chests", value: true, new ConfigDescription("A hungry creature you are taming eats from a nearby chest when there is no food on the ground, so taming keeps going without dropping food by hand.", (AcceptableValueBase)null, Array.Empty<object>())); tamesEatFromChests = ConfigSync("9 - Feeding", "Tames Eat From Chests", value: true, new ConfigDescription("A hungry tame eats from a nearby chest when there is no food on the ground, which keeps it healing and breeding.", (AcceptableValueBase)null, Array.Empty<object>())); chestRange = ConfigSync("9 - Feeding", "Chest Range", 10f, new ConfigDescription("How far, in meters, a creature reaches for a chest. It eats in place and never walks to it. Only chests a player built count, not dungeon or other world loot.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), Array.Empty<object>())); chestRequireOnlyFood = ConfigSync("9 - Feeding", "Only Food Chests", value: false, new ConfigDescription("Only eat from a chest that holds nothing but food this creature eats, so a feeding chest can sit next to your storage without being raided.", (AcceptableValueBase)null, Array.Empty<object>())); debugMode = ConfigSync("Debug", "Debug Mode", value: false, new ConfigDescription("Log what the mod is doing to tamed creatures.", (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting: false); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; ((BaseUnityPlugin)this).Config.Save(); } } internal static class PluginInfo { public const string PLUGIN_NAME = "BetterTames"; public const string PLUGIN_GUID = "xtav1m.BetterTames"; public const string PLUGIN_VERSION = "0.9.0"; } } namespace BetterTames.Scripts.Breeding { public static class Offspring { private const int MaxLevel = 3; private static Character? birthingParent; public static void BeginProcreate(Procreation procreation) { birthingParent = ((Component)procreation).GetComponent<Character>(); } public static void EndProcreate() { birthingParent = null; } public static void AdjustNewbornLevel(Character character, ref int level) { if (!((Object)(object)birthingParent == (Object)null) && !((Object)(object)character == (Object)(object)birthingParent)) { birthingParent = null; if (level < 3 && Random.Range(0f, 100f) < (float)Plugin.offspringStarChance.Value) { level++; } } } } [HarmonyPatch] public static class OffspringPatches { [HarmonyPrefix] [HarmonyPatch(typeof(Procreation), "Procreate")] private static void Procreation_Procreate_Prefix(Procreation __instance) { Offspring.BeginProcreate(__instance); } [HarmonyFinalizer] [HarmonyPatch(typeof(Procreation), "Procreate")] private static void Procreation_Procreate_Finalizer() { Offspring.EndProcreate(); } [HarmonyPrefix] [HarmonyPatch(typeof(Character), "SetLevel")] private static void Character_SetLevel_Prefix(Character __instance, ref int level) { Offspring.AdjustNewbornLevel(__instance, ref level); } } } namespace BetterTames.Scripts.Protection { public static class Protection { private static readonly int RecoverUntilKey = StringExtensionMethods.GetStableHashCode("BetterTames_RecoverUntil"); private static readonly int ProtectionKey = StringExtensionMethods.GetStableHashCode("BetterTames_Protection"); private static readonly FieldRef<Character, ZNetView> NView = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview"); private static readonly FieldRef<Character, HitData> LastHit = AccessTools.FieldRefAccess<Character, HitData>("m_lastHit"); private static readonly FieldRef<Player, Character> HoveringCreature = AccessTools.FieldRefAccess<Player, Character>("m_hoveringCreature"); private static readonly Func<Character, float> GetHealth = AccessTools.MethodDelegate<Func<Character, float>>(AccessTools.Method(typeof(Character), "GetHealth", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<Character, float> GetMaxHealth = AccessTools.MethodDelegate<Func<Character, float>>(AccessTools.Method(typeof(Character), "GetMaxHealth", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<Character, float> SetHealth = AccessTools.MethodDelegate<Action<Character, float>>(AccessTools.Method(typeof(Character), "SetHealth", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<Humanoid, ItemData> GetCurrentWeapon = AccessTools.MethodDelegate<Func<Humanoid, ItemData>>(AccessTools.Method(typeof(Humanoid), "GetCurrentWeapon", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<MonsterAI> Sleep = AccessTools.MethodDelegate<Action<MonsterAI>>(AccessTools.Method(typeof(MonsterAI), "Sleep", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<MonsterAI> Wakeup = AccessTools.MethodDelegate<Action<MonsterAI>>(AccessTools.Method(typeof(MonsterAI), "Wakeup", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<BaseAI> StopMoving = AccessTools.MethodDelegate<Action<BaseAI>>(AccessTools.Method(typeof(BaseAI), "StopMoving", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<MonsterAI, bool> SetAlerted = AccessTools.MethodDelegate<Action<MonsterAI, bool>>(AccessTools.Method(typeof(MonsterAI), "SetAlerted", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<BaseAI, ZDOID> SetTargetInfo = AccessTools.MethodDelegate<Action<BaseAI, ZDOID>>(AccessTools.Method(typeof(BaseAI), "SetTargetInfo", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<MonsterAI, bool> IsSleeping = AccessTools.MethodDelegate<Func<MonsterAI, bool>>(AccessTools.Method(typeof(MonsterAI), "IsSleeping", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<ZNetView> ClaimOwnership = AccessTools.MethodDelegate<Action<ZNetView>>(AccessTools.Method(typeof(ZNetView), "ClaimOwnership", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<Player, bool> TakeInput = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldRef<Character, Vector3> MoveDir = AccessTools.FieldRefAccess<Character, Vector3>("m_moveDir"); private static readonly FieldRef<Character, Vector3> CurrentVel = AccessTools.FieldRefAccess<Character, Vector3>("m_currentVel"); private static readonly FieldRef<Character, Rigidbody> Body = AccessTools.FieldRefAccess<Character, Rigidbody>("m_body"); private static readonly FieldRef<Character, ZSyncAnimation> ZAnim = AccessTools.FieldRefAccess<Character, ZSyncAnimation>("m_zanim"); private static readonly Action<ZSyncAnimation, int, float> SetAnimFloat = AccessTools.MethodDelegate<Action<ZSyncAnimation, int, float>>(AccessTools.Method(typeof(ZSyncAnimation), "SetFloat", new Type[2] { typeof(int), typeof(float) }, (Type[])null), (object)null, true); private static readonly int ForwardSpeed = ZSyncAnimation.GetHash("forward_speed"); private static readonly int SidewaySpeed = ZSyncAnimation.GetHash("sideway_speed"); private static readonly int TurnSpeed = ZSyncAnimation.GetHash("turn_speed"); private static readonly FieldRef<Player, int> InteractMask = AccessTools.FieldRefAccess<Player, int>("m_interactMask"); private static readonly HashSet<Character> Downed = new HashSet<Character>(); private static readonly Predicate<Character> IsGone = (Character c) => (Object)(object)c == (Object)null; private static readonly RaycastHit[] HoverHits = (RaycastHit[])(object)new RaycastHit[32]; private static float lastDebugLog; private const string ProtectedColor = "#4FA3FF"; private const string RecoveringColor = "#FFD24A"; private static readonly FieldInfo HudsField = AccessTools.Field(typeof(EnemyHud), "m_huds"); private static readonly FieldInfo HudNameField = AccessTools.Field(AccessTools.Inner(typeof(EnemyHud), "HudData"), "m_name"); private static long Now => ZNet.instance.GetTime().Ticks; private static void DebugLog(Character character, MonsterAI monsterAI, string when) { //IL_00a8: 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_0035: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.debugMode.Value) { return; } Animator componentInChildren = ((Component)character).GetComponentInChildren<Animator>(); bool flag = false; if ((Object)(object)componentInChildren != (Object)null) { AnimatorControllerParameter[] parameters = componentInChildren.parameters; for (int i = 0; i < parameters.Length; i++) { if (parameters[i].name == "sleeping") { flag = true; } } } Plugin.Logger.LogInfo((object)($"[Protection] {when} {character.m_name}: sleepParam={flag} sleeping={IsSleeping(monsterAI)} " + $"moveDir={MoveDir.Invoke(character)} currentVel={CurrentVel.Invoke(character)} " + string.Format("forward_speed={0:0.00} ", ((Object)(object)componentInChildren != (Object)null) ? componentInChildren.GetFloat("forward_speed") : (-1f)) + string.Format("animSleeping={0}", (Object)(object)componentInChildren != (Object)null && flag && componentInChildren.GetBool("sleeping")))); } public static bool WildWillFight(Character wild) { BaseAI baseAI = wild.GetBaseAI(); if ((Object)(object)baseAI == (Object)null || !baseAI.IsAlerted()) { return false; } Tameable val = default(Tameable); if (!((Component)wild).TryGetComponent<Tameable>(ref val)) { return true; } ZNetView val2 = NView.Invoke(wild); if ((Object)(object)val2 == (Object)null || !val2.IsValid()) { return true; } return val2.GetZDO().GetFloat(ZDOVars.s_tameTimeLeft, val.m_tamingTime) >= val.m_tamingTime; } public static bool IsDowned(Character? character) { if ((Object)(object)character == (Object)null || !character.IsTamed()) { return false; } ZNetView val = NView.Invoke(character); if ((Object)(object)val != (Object)null && val.IsValid()) { return val.GetZDO().GetLong(RecoverUntilKey, 0L) != 0; } return false; } public static bool TryDown(Character character) { if (!character.IsTamed() || GetHealth(character) > 0f) { return false; } MonsterAI val = default(MonsterAI); if (!((Component)character).TryGetComponent<MonsterAI>(ref val)) { return false; } ZNetView val2 = NView.Invoke(character); if ((Object)(object)val2 == (Object)null || !val2.IsValid() || !val2.IsOwner()) { return false; } ZDO zDO = val2.GetZDO(); if (!IsProtected(zDO) || KilledWithButcherKnife(character)) { return false; } zDO.Set(RecoverUntilKey, Now + TimeSpan.FromSeconds(Plugin.recoveryTime.Value).Ticks); SetHealth(character, 1f); Calm(val, character); Sleep(val); Downed.Add(character); DebugLog(character, val, "downed"); return true; } public static bool UpdateRecovery(MonsterAI monsterAI) { Character val = default(Character); if (!((Component)monsterAI).TryGetComponent<Character>(ref val)) { return false; } ZNetView val2 = NView.Invoke(val); if ((Object)(object)val2 == (Object)null || !val2.IsValid()) { return false; } ZDO zDO = val2.GetZDO(); long num = zDO.GetLong(RecoverUntilKey, 0L); if (num == 0L) { return false; } Downed.Add(val); if (!val2.IsOwner()) { return true; } Calm(monsterAI, val); if (!IsSleeping(monsterAI)) { Sleep(monsterAI); } if (Time.time - lastDebugLog > 2f) { lastDebugLog = Time.time; DebugLog(val, monsterAI, "recovering"); } long now = Now; float num2 = GetMaxHealth(val); if (now >= num) { zDO.Set(RecoverUntilKey, 0L); SetHealth(val, num2); Wakeup(monsterAI); Downed.Remove(val); return true; } long ticks = TimeSpan.FromSeconds(Plugin.recoveryTime.Value).Ticks; float num3 = num2 * (1f - (float)(num - now) / (float)ticks); if (num3 > GetHealth(val)) { SetHealth(val, num3); } return true; } private static void Calm(MonsterAI monsterAI, Character character) { //IL_001d: 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_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) StopMoving((BaseAI)(object)monsterAI); SetAlerted(monsterAI, arg2: false); SetTargetInfo((BaseAI)(object)monsterAI, ZDOID.None); CurrentVel.Invoke(character) = Vector3.zero; Rigidbody val = Body.Invoke(character); if ((Object)(object)val != (Object)null) { val.linearVelocity = new Vector3(0f, val.linearVelocity.y, 0f); } ZSyncAnimation val2 = ZAnim.Invoke(character); if (!((Object)(object)val2 == (Object)null)) { SetAnimFloat(val2, ForwardSpeed, 0f); SetAnimFloat(val2, SidewaySpeed, 0f); SetAnimFloat(val2, TurnSpeed, 0f); } } public static string? HoverLine(Character character, ZDO zdo) { MonsterAI val = default(MonsterAI); if (!character.IsTamed() || !((Component)character).TryGetComponent<MonsterAI>(ref val)) { return null; } long num = zdo.GetLong(RecoverUntilKey, 0L); if (num == 0L) { return null; } return "Recovering " + Mathf.Max(0, Mathf.CeilToInt((float)(num - Now) / 10000000f)) + "s"; } public static string AddPrompt(Character character, string hoverText) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) MonsterAI val = default(MonsterAI); if (hoverText.Length == 0 || !character.IsTamed() || !((Component)character).TryGetComponent<MonsterAI>(ref val)) { return hoverText; } ZNetView val2 = NView.Invoke(character); if ((Object)(object)val2 == (Object)null || !val2.IsValid()) { return hoverText; } string text = (IsProtected(val2.GetZDO()) ? "On" : "Off"); return hoverText + "\n[<color=yellow><b>" + KeyText(Plugin.toggleProtectionKey.Value) + "</b></color>] Protection: " + text; } public static void TintNameplates(EnemyHud enemyHud) { Downed.RemoveWhere(IsGone); if (!(HudsField.GetValue(enemyHud) is IDictionary dictionary)) { return; } foreach (DictionaryEntry item in dictionary) { object key = item.Key; Character val = (Character)((key is Character) ? key : null); if (val == null || (Object)(object)val == (Object)null) { continue; } string text = NameColor(val); if (text != null) { object? value = HudNameField.GetValue(item.Value); TMP_Text val2 = (TMP_Text)((value is TMP_Text) ? value : null); if (val2 != null && ((Component)val2).gameObject.activeInHierarchy) { val2.text = "<color=" + text + ">" + val2.text + "</color>"; } } } } private static string? NameColor(Character character) { MonsterAI val = default(MonsterAI); if (!character.IsTamed() || !((Component)character).TryGetComponent<MonsterAI>(ref val)) { return null; } ZNetView val2 = NView.Invoke(character); if ((Object)(object)val2 == (Object)null || !val2.IsValid()) { return null; } ZDO zDO = val2.GetZDO(); if (zDO.GetLong(RecoverUntilKey, 0L) != 0L) { Downed.Add(character); return "#FFD24A"; } Downed.Remove(character); if (!IsProtected(zDO)) { return null; } return "#4FA3FF"; } public static void FindDownedHover(Player player, ref Character hoverCreature) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hoverCreature != (Object)null || Downed.Count == 0 || !Plugin.showDownedTames.Value || (Object)(object)GameCamera.instance == (Object)null) { return; } Transform transform = ((Component)GameCamera.instance).transform; int num = Physics.RaycastNonAlloc(transform.position, transform.forward, HoverHits, 50f, InteractMask.Invoke(player)); Character val = null; float num2 = float.MaxValue; for (int i = 0; i < num; i++) { RaycastHit val2 = HoverHits[i]; Character val3 = (((Object)(object)((RaycastHit)(ref val2)).collider.attachedRigidbody != (Object)null) ? ((Component)((RaycastHit)(ref val2)).collider.attachedRigidbody).GetComponent<Character>() : ((Component)((RaycastHit)(ref val2)).collider).GetComponent<Character>()); if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3 == (Object)(object)player) && !(((RaycastHit)(ref val2)).distance >= num2)) { val = val3; num2 = ((RaycastHit)(ref val2)).distance; } } if ((Object)(object)val != (Object)null && Downed.Contains(val)) { hoverCreature = val; } } public static bool SwallowsInteract(Tameable tameable) { //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) KeyboardShortcut value = Plugin.toggleProtectionKey.Value; Character val = default(Character); if (((KeyboardShortcut)(ref value)).IsPressed() && ((Component)tameable).TryGetComponent<Character>(ref val)) { return val.IsTamed(); } return false; } public static void Tick() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !TakeInput(localPlayer)) { return; } KeyboardShortcut value = Plugin.toggleProtectionKey.Value; if (!((KeyboardShortcut)(ref value)).IsDown()) { return; } Character val = HoveringCreature.Invoke(localPlayer); MonsterAI val2 = default(MonsterAI); if (!((Object)(object)val == (Object)null) && val.IsTamed() && ((Component)val).TryGetComponent<MonsterAI>(ref val2)) { ZNetView val3 = NView.Invoke(val); if (!((Object)(object)val3 == (Object)null) && val3.IsValid()) { ClaimOwnership(val3); ZDO zDO = val3.GetZDO(); bool flag = !IsProtected(zDO); zDO.Set(ProtectionKey, flag ? 1 : (-1), false); ((Character)localPlayer).Message((MessageType)2, val.GetHoverName() + " " + (flag ? "protected" : "not protected"), 0, (Sprite)null, false); } } } private static string KeyText(KeyboardShortcut shortcut) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_004e: Unknown result type (might be due to invalid IL or missing references) string text = ""; foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { text = text + KeyName(modifier) + " + "; } return text + KeyName(((KeyboardShortcut)(ref shortcut)).MainKey); } private unsafe static string KeyName(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown switch (key - 303) { case 4: case 5: return "Alt"; case 0: case 1: return "Shift"; case 2: case 3: return "Ctrl"; default: return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(); } } private static bool IsProtected(ZDO zdo) { return zdo.GetInt(ProtectionKey, 0) switch { 1 => true, -1 => false, _ => Plugin.protectAllTames.Value, }; } private static bool KilledWithButcherKnife(Character character) { HitData obj = LastHit.Invoke(character); Character obj2 = ((obj != null) ? obj.GetAttacker() : null); Humanoid val = (Humanoid)(object)((obj2 is Humanoid) ? obj2 : null); if (val != null && ((Character)val).IsPlayer()) { return GetCurrentWeapon(val)?.m_shared.m_tamedOnly ?? false; } return false; } } [HarmonyPatch] public static class ProtectionPatches { [HarmonyPrefix] [HarmonyPatch(typeof(Character), "CheckDeath")] private static bool Character_CheckDeath_Prefix(Character __instance) { return !Protection.TryDown(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(Character), "ApplyDamage")] private static bool Character_ApplyDamage_Prefix(Character __instance) { return !Protection.IsDowned(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(Procreation), "Procreate")] private static bool Procreation_Procreate_Prefix(Procreation __instance) { return !Protection.IsDowned(((Component)__instance).GetComponent<Character>()); } [HarmonyPostfix] [HarmonyPatch(typeof(Procreation), "ReadyForProcreation")] private static void Procreation_ReadyForProcreation_Postfix(Procreation __instance, ref bool __result) { if (__result && Protection.IsDowned(((Component)__instance).GetComponent<Character>())) { __result = false; } } [HarmonyPrefix] [HarmonyPatch(typeof(MonsterAI), "UpdateSleep")] private static bool MonsterAI_UpdateSleep_Prefix(MonsterAI __instance) { return !Protection.UpdateRecovery(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(Tameable), "Interact")] private static bool Tameable_Interact_Prefix(Tameable __instance, ref bool __result) { if (!Protection.SwallowsInteract(__instance)) { return true; } __result = true; return false; } [HarmonyPostfix] [HarmonyPatch(typeof(Character), "GetHoverText")] private static void Character_GetHoverText_Postfix(Character __instance, ref string __result) { __result = Protection.AddPrompt(__instance, __result); } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyHud), "UpdateHuds")] private static void EnemyHud_UpdateHuds_Postfix(EnemyHud __instance) { Protection.TintNameplates(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "FindHoverObject")] private static void Player_FindHoverObject_Postfix(Player __instance, ref Character hoverCreature) { Protection.FindDownedHover(__instance, ref hoverCreature); } [HarmonyPostfix] [HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[] { typeof(Character), typeof(Character) })] private static void BaseAI_IsEnemy_Postfix(Character a, Character b, ref bool __result) { if (!__result && Plugin.tamesFightOwnKind.Value && (Object)(object)a != (Object)null && (Object)(object)b != (Object)null && (Object)(object)a != (Object)(object)b && a.IsTamed() != b.IsTamed() && !a.IsPlayer() && !b.IsPlayer()) { string text = a.GetGroup(); if (text.Length > 0 && text == b.GetGroup() && Protection.WildWillFight(a.IsTamed() ? b : a)) { __result = true; } } if (__result && (Protection.IsDowned(a) || Protection.IsDowned(b))) { __result = false; } } } } namespace BetterTames.Scripts.Feeding { public static class Feeding { private static readonly FieldRef<MonsterAI, List<ItemDrop>> ConsumeItems = AccessTools.FieldRefAccess<MonsterAI, List<ItemDrop>>("m_consumeItems"); private static readonly FieldRef<MonsterAI, Action<ItemDrop>> OnConsumedItem = AccessTools.FieldRefAccess<MonsterAI, Action<ItemDrop>>("m_onConsumedItem"); private static readonly FieldRef<BaseAI, ZSyncAnimation> Animator = AccessTools.FieldRefAccess<BaseAI, ZSyncAnimation>("m_animator"); private static readonly FieldRef<Humanoid, EffectList> ConsumeEffects = AccessTools.FieldRefAccess<Humanoid, EffectList>("m_consumeItemEffects"); private static readonly Func<Container, Inventory> GetInventory = AccessTools.MethodDelegate<Func<Container, Inventory>>(AccessTools.Method(typeof(Container), "GetInventory", (Type[])null, (Type[])null), (object)null, true); private static readonly List<Container> Chests = new List<Container>(); public static void Register(Container container) { if (!Chests.Contains(container)) { Chests.Add(container); } } public static void TryEatFromChest(MonsterAI monsterAI) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) Character val = default(Character); Tameable val2 = default(Tameable); if (!((Component)monsterAI).TryGetComponent<Character>(ref val) || !((Component)monsterAI).TryGetComponent<Tameable>(ref val2) || !(val.IsTamed() ? Plugin.tamesEatFromChests.Value : Plugin.wildEatFromChests.Value)) { return; } ZNetView component = ((Component)monsterAI).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return; } List<ItemDrop> list = ConsumeItems.Invoke(monsterAI); if (list == null || list.Count == 0) { return; } Vector3 position = ((Component)val).transform.position; float value = Plugin.chestRange.Value; for (int num = Chests.Count - 1; num >= 0; num--) { Container val3 = Chests[num]; ZNetView chestView; if ((Object)(object)val3 == (Object)null) { Chests.RemoveAt(num); } else if (!(Vector3.Distance(((Component)val3).transform.position, position) > value) && IsPlayerChest(val3, out chestView) && chestView.IsOwner()) { Inventory val4 = GetInventory(val3); if (val4 != null && FindFood(val4, list, out ItemData food, out ItemDrop foodPrefab)) { val4.RemoveItem(food, 1); ZSyncAnimation obj = Animator.Invoke((BaseAI)(object)monsterAI); if (obj != null) { obj.SetTrigger("consume"); } Humanoid val5 = (Humanoid)(object)((val is Humanoid) ? val : null); if (val5 != null) { EffectList obj2 = ConsumeEffects.Invoke(val5); if (obj2 != null) { obj2.Create(position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } OnConsumedItem.Invoke(monsterAI)?.Invoke(foodPrefab); break; } } } } private static bool IsPlayerChest(Container chest, out ZNetView chestView) { chestView = ((Component)chest).GetComponent<ZNetView>(); if ((Object)(object)chestView != (Object)null && chestView.IsValid()) { return chestView.GetZDO().GetLong(ZDOVars.s_creator, 0L) != 0; } return false; } private static bool FindFood(Inventory inventory, List<ItemDrop> consumeItems, out ItemData food, out ItemDrop foodPrefab) { food = null; foodPrefab = null; foreach (ItemData allItem in inventory.GetAllItems()) { ItemDrop val = Match(allItem, consumeItems); if ((Object)(object)val == (Object)null) { if (Plugin.chestRequireOnlyFood.Value) { return false; } } else if (food == null) { food = allItem; foodPrefab = val; if (!Plugin.chestRequireOnlyFood.Value) { return true; } } } return food != null; } private static ItemDrop? Match(ItemData item, List<ItemDrop> consumeItems) { foreach (ItemDrop consumeItem in consumeItems) { if (consumeItem.m_itemData.m_shared.m_name == item.m_shared.m_name) { return consumeItem; } } return null; } } [HarmonyPatch] public static class FeedingPatches { [HarmonyPostfix] [HarmonyPatch(typeof(Container), "Awake")] private static void Container_Awake_Postfix(Container __instance) { Feeding.Register(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(MonsterAI), "FindClosestConsumableItem")] private static void MonsterAI_FindClosestConsumableItem_Postfix(MonsterAI __instance, ItemDrop __result) { if ((Object)(object)__result == (Object)null) { Feeding.TryEatFromChest(__instance); } } } } namespace BetterTames.Scripts.Leveling { public static class Leveling { private const int MaxLevel = 3; private static readonly FieldRef<Character, HitData> LastHit = AccessTools.FieldRefAccess<Character, HitData>("m_lastHit"); private static readonly Func<Character, float> GetMaxHealth = AccessTools.MethodDelegate<Func<Character, float>>(AccessTools.Method(typeof(Character), "GetMaxHealth", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<Character, float> SetHealth = AccessTools.MethodDelegate<Action<Character, float>>(AccessTools.Method(typeof(Character), "SetHealth", (Type[])null, (Type[])null), (object)null, true); public static void OnDeath(Character victim) { if (!Plugin.starUpOnKill.Value) { return; } HitData obj = LastHit.Invoke(victim); Character val = ((obj != null) ? obj.GetAttacker() : null); if ((Object)(object)val == (Object)null || !val.IsTamed() || val.GetLevel() >= 3) { return; } ZNetView component = ((Component)val).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return; } int num = Mathf.Max(0, victim.GetLevel() - val.GetLevel()); int num2 = Plugin.starUpBaseChance.Value + num * Plugin.starUpBonusPerStar.Value; if (!(Random.Range(0f, 100f) >= (float)num2)) { val.SetLevel(val.GetLevel() + 1); if (Plugin.healOnStarUp.Value) { SetHealth(val, GetMaxHealth(val)); } } } } [HarmonyPatch] public static class LevelingPatches { [HarmonyPostfix] [HarmonyPatch(typeof(Character), "OnDeath")] private static void Character_OnDeath_Postfix(Character __instance) { Leveling.OnDeath(__instance); } } } namespace BetterTames.Scripts.Portals { public static class Portals { private static readonly FieldRef<TeleportWorld, ZNetView> PortalView = AccessTools.FieldRefAccess<TeleportWorld, ZNetView>("m_nview"); private static readonly FieldRef<TeleportWorld, float> ExitDistance = AccessTools.FieldRefAccess<TeleportWorld, float>("m_exitDistance"); public static void OnTeleport(TeleportWorld portal, Player player) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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) if (!Plugin.portalTeleport.Value || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsTeleporting() || !TryGetExit(portal, out var exit)) { return; } float value = Plugin.massCommandRange.Value; foreach (Character item in BetterTames.Scripts.CatchUp.CatchUp.FollowersOf(player.GetPlayerName())) { if (!(Vector3.Distance(((Component)item).transform.position, ((Component)player).transform.position) > value)) { Vector3 position = BetterTames.Scripts.CatchUp.CatchUp.MoveTo(item, exit); ZNetView component = ((Component)item).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner()) { component.GetZDO().SetPosition(position); } } } } private static bool TryGetExit(TeleportWorld portal, out Vector3 exit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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) //IL_0059: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) exit = Vector3.zero; ZNetView val = PortalView.Invoke(portal); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } ZDO zDO = ZDOMan.instance.GetZDO(val.GetZDO().GetConnectionZDOID((ConnectionType)1)); if (zDO == null) { return false; } Quaternion rotation = zDO.GetRotation(); exit = zDO.GetPosition() + rotation * Vector3.forward * ExitDistance.Invoke(portal) + Vector3.up; return true; } } [HarmonyPatch] public static class PortalPatches { [HarmonyPostfix] [HarmonyPatch(typeof(TeleportWorld), "Teleport")] private static void TeleportWorld_Teleport_Postfix(TeleportWorld __instance, Player player) { Portals.OnTeleport(__instance, player); } } } namespace BetterTames.Scripts.CatchUp { public static class CatchUp { private static readonly FieldRef<Tameable, ZNetView> TameView = AccessTools.FieldRefAccess<Tameable, ZNetView>("m_nview"); private static readonly FieldRef<Character, Rigidbody> CharacterBody = AccessTools.FieldRefAccess<Character, Rigidbody>("m_body"); private static readonly Func<Player, bool> TakeInput = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldRef<ZDOMan, Dictionary<ZDOID, ZDO>> AllZDOs = AccessTools.FieldRefAccess<ZDOMan, Dictionary<ZDOID, ZDO>>("m_objectsByID"); private static readonly FieldRef<BaseAI, AgentType> PathAgent = AccessTools.FieldRefAccess<BaseAI, AgentType>("m_pathAgentType"); private static readonly Action<ZNetView> ClaimOwnership = AccessTools.MethodDelegate<Action<ZNetView>>(AccessTools.Method(typeof(ZNetView), "ClaimOwnership", (Type[])null, (Type[])null), (object)null, true); private const float CheckInterval = 2f; private static float lastCheck; public static void Tick() { //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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.catchUpTeleport.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && TakeInput(localPlayer)) { KeyboardShortcut value = Plugin.teleportFollowersKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { TeleportFollowers(localPlayer); } } if ((Object)(object)localPlayer == (Object)null || Time.time - lastCheck < 2f) { return; } lastCheck = Time.time; foreach (Character item in FollowersOf(localPlayer.GetPlayerName())) { if (Vector3.Distance(((Component)item).transform.position, ((Component)localPlayer).transform.position) > Plugin.catchUpDistance.Value && CanReach(item, localPlayer)) { MoveTo(item, localPlayer); } } } public static void OnRespawn(Player player) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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) if (!Plugin.catchUpTeleport.Value) { return; } string playerName = player.GetPlayerName(); HashSet<ZDOID> hashSet = new HashSet<ZDOID>(); foreach (Character item in FollowersOf(playerName)) { MoveTo(item, player); ZNetView val = TameView.Invoke(((Component)item).GetComponent<Tameable>()); hashSet.Add(val.GetZDO().m_uid); } foreach (ZDO value in AllZDOs.Invoke(ZDOMan.instance).Values) { if (!hashSet.Contains(value.m_uid) && value.IsOwner() && value.GetBool(ZDOVars.s_tamed, false) && !(value.GetString(ZDOVars.s_follow, "") != playerName)) { value.SetPosition(((Component)player).transform.position + RandomOffset()); } } } private static void TeleportFollowers(Player player) { int num = 0; foreach (Character item in FollowersOf(player.GetPlayerName())) { if (CanReach(item, player)) { MoveTo(item, player); num++; } } string text = ((num == 0) ? "No tames following you" : string.Format("{0} {1} teleported to you", num, (num == 1) ? "tame" : "tames")); ((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false); } public static IEnumerable<Character> FollowersOf(string playerName) { Tameable val = default(Tameable); foreach (Character allCharacter in Character.GetAllCharacters()) { if (allCharacter.IsTamed() && ((Component)allCharacter).TryGetComponent<Tameable>(ref val)) { ZNetView val2 = TameView.Invoke(val); if (!((Object)(object)val2 == (Object)null) && val2.IsValid() && !(val2.GetZDO().GetString(ZDOVars.s_follow, "") != playerName)) { yield return allCharacter; } } } } private static bool CanReach(Character character, Player player) { //IL_0024: 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) if (Plugin.bigTamesEnterDungeons.Value) { return true; } if (!IsBig(character)) { return true; } return Character.InInterior(((Component)character).transform.position) == Character.InInterior(((Component)player).transform.position); } private static bool IsBig(Character character) { //IL_0021: 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_0024: Invalid comparison between Unknown and I4 //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 BaseAI baseAI = character.GetBaseAI(); if ((Object)(object)baseAI == (Object)null) { return false; } AgentType val = PathAgent.Invoke(baseAI); if ((int)val == 1 || val - 5 <= 1) { return false; } return true; } private static void MoveTo(Character character, Player player) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) MoveTo(character, ((Component)player).transform.position); } public static Vector3 MoveTo(Character character, Vector3 target) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //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_0046: 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_0066: Unknown result type (might be due to invalid IL or missing references) Vector3 val = target + RandomOffset(); ZNetView component = ((Component)character).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid() && !component.IsOwner()) { ClaimOwnership(component); } ((Component)character).transform.position = val; Rigidbody val2 = CharacterBody.Invoke(character); if ((Object)(object)val2 != (Object)null) { val2.position = val; } return val; } private static Vector3 RandomOffset() { //IL_0000: 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_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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) Vector2 val = Random.insideUnitCircle * 2f; return new Vector3(val.x, 0f, val.y); } } [HarmonyPatch] public static class CatchUpPatches { [HarmonyPostfix] [HarmonyPatch(typeof(Player), "OnSpawned")] private static void Player_OnSpawned_Postfix(Player __instance) { CatchUp.OnRespawn(__instance); } } } namespace BetterTames.Scripts.Commands { public static class Commands { private static readonly FieldRef<Tameable, ZNetView> TameView = AccessTools.FieldRefAccess<Tameable, ZNetView>("m_nview"); private static readonly Func<Player, bool> TakeInput = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null), (object)null, true); private static float lastMassCommand; public static void Apply(Tameable tameable) { if (Plugin.commandAllTames.Value && (Object)(object)((Component)tameable).GetComponent<MonsterAI>() != (Object)null) { tameable.m_commandable = true; return; } GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(Utils.GetPrefabName(((Component)tameable).gameObject)) : null); Tameable val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Tameable>() : null); if ((Object)(object)val2 != (Object)null) { tameable.m_commandable = val2.m_commandable; } } public static void ApplyAll() { Tameable tameable = default(Tameable); foreach (Character allCharacter in Character.GetAllCharacters()) { if (((Component)allCharacter).TryGetComponent<Tameable>(ref tameable)) { Apply(tameable); } } } public static void Tick() { //IL_0028: 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_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 ((Object)(object)localPlayer == (Object)null || !TakeInput(localPlayer)) { return; } KeyboardShortcut value = Plugin.massFollowKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { MassCommand(localPlayer, follow: true); return; } value = Plugin.massStayKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { MassCommand(localPlayer, follow: false); } } private static void MassCommand(Player player, bool follow) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if (Time.time - lastMassCommand < 1f) { return; } lastMassCommand = Time.time; string playerName = player.GetPlayerName(); float value = Plugin.massCommandRange.Value; int num = 0; Tameable val = default(Tameable); foreach (Character allCharacter in Character.GetAllCharacters()) { if (!allCharacter.IsTamed() || !((Component)allCharacter).TryGetComponent<Tameable>(ref val) || !val.m_commandable || (Object)(object)((Component)allCharacter).GetComponent<MonsterAI>() == (Object)null || Vector3.Distance(((Component)allCharacter).transform.position, ((Component)player).transform.position) > value) { continue; } ZNetView val2 = TameView.Invoke(val); if (!((Object)(object)val2 == (Object)null) && val2.IsValid()) { string text = val2.GetZDO().GetString(ZDOVars.s_follow, ""); if (!(follow ? (text.Length > 0) : (text != playerName))) { val.Command((Humanoid)(object)player, false); num++; } } } string text2 = ((num != 0) ? string.Format("{0} {1} {2}", num, (num == 1) ? "tame" : "tames", follow ? "following" : "staying") : (follow ? "No tames to follow you" : "No tames following you")); ((Character)player).Message((MessageType)2, text2, 0, (Sprite)null, false); } } [HarmonyPatch] public static class CommandPatches { [HarmonyPostfix] [HarmonyPatch(typeof(Tameable), "Awake")] private static void Tameable_Awake_Postfix(Tameable __instance) { Commands.Apply(__instance); } } } namespace BetterTames.Scripts.Tuning { public static class Tuning { private static readonly FieldRef<Tameable, ZNetView> TameView = AccessTools.FieldRefAccess<Tameable, ZNetView>("m_nview"); private static readonly FieldRef<Character, float> RegenAllHpTime = AccessTools.FieldRefAccess<Character, float>("m_regenAllHPTime"); public static void Apply(Tameable tameable) { tameable.m_tamingTime = (float)Plugin.tamingTime.Value * 60f; ZNetView val = TameView.Invoke(tameable); if (!((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner()) { ZDO zDO = val.GetZDO(); if (zDO.GetFloat(ZDOVars.s_tameTimeLeft, tameable.m_tamingTime) > tameable.m_tamingTime) { zDO.Set(ZDOVars.s_tameTimeLeft, tameable.m_tamingTime); } } } public static void Apply(Procreation procreation) { procreation.m_requiredLovePoints = Plugin.lovePoints.Value; procreation.m_maxCreatures = Plugin.maxCreaturesNearby.Value; procreation.m_totalCheckRange = Plugin.maxCreaturesRange.Value; } public static void Apply(Growup growup) { growup.m_growTime = (float)Plugin.growthTime.Value * 60f; } public static void Apply(Character character) { RegenAllHpTime.Invoke(character) = Plugin.regenTime.Value; } public static void ApplyAll() { Tameable tameable = default(Tameable); Procreation procreation = default(Procreation); Growup growup = default(Growup); foreach (Character allCharacter in Character.GetAllCharacters()) { if (((Component)allCharacter).TryGetComponent<Tameable>(ref tameable)) { Apply(tameable); } if (((Component)allCharacter).TryGetComponent<Procreation>(ref procreation)) { Apply(procreation); } if (((Component)allCharacter).TryGetComponent<Growup>(ref growup)) { Apply(growup); } if (allCharacter.IsTamed()) { Apply(allCharacter); } } } } [HarmonyPatch] public static class TuningPatches { [HarmonyPostfix] [HarmonyPatch(typeof(Tameable), "Awake")] private static void Tameable_Awake_Postfix(Tameable __instance) { Tuning.Apply(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(Procreation), "Awake")] private static void Procreation_Awake_Postfix(Procreation __instance) { Tuning.Apply(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(Growup), "Start")] private static void Growup_Start_Postfix(Growup __instance) { Tuning.Apply(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(Character), "RPC_SetTamed")] private static void Character_RPC_SetTamed_Postfix(Character __instance, bool tamed) { if (tamed) { Tuning.Apply(__instance); } } [HarmonyPostfix] [HarmonyPatch(typeof(Character), "Awake")] private static void Character_Awake_Postfix(Character __instance) { if (__instance.IsTamed()) { Tuning.Apply(__instance); } } } } namespace BetterTames.Scripts.Timers { public static class Timers { private static readonly FieldRef<Character, ZNetView> NView = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview"); private static readonly List<Player> NearbyPlayers = new List<Player>(); private static readonly List<string> Lines = new List<string>(); public static string AddTo(Character character, string hoverText) { ZNetView val = NView.Invoke(character); if ((Object)(object)val == (Object)null || !val.IsValid() || (Object)(object)ZNet.instance == (Object)null) { return hoverText; } ZDO zDO = val.GetZDO(); Lines.Clear(); Add(Taming(character, zDO)); Add(Breeding(character, zDO)); Add(Growth(character, zDO)); Add(BetterTames.Scripts.Protection.Protection.HoverLine(character, zDO)); if (Lines.Count == 0) { return hoverText; } string text = string.Join("\n", Lines); if (hoverText.Length == 0) { return character.GetHoverName() + "\n" + text; } int num = hoverText.IndexOf('\n'); if (num >= 0) { return hoverText.Substring(0, num) + "\n" + text + hoverText.Substring(num); } return hoverText + "\n" + text; } private static void Add(string? line) { if (line != null) { Lines.Add(line); } } private static string? Taming(Character character, ZDO zdo) { if (!Plugin.showTamingTimer.Value || character.IsTamed()) { return null; } Tameable component = ((Component)character).GetComponent<Tameable>(); if ((Object)(object)component == (Object)null) { return null; } float num = zdo.GetFloat(ZDOVars.s_tameTimeLeft, component.m_tamingTime); if (num >= component.m_tamingTime) { return null; } string text = "Tamed in " + Format(num / TamingSpeed(component)); BaseAI baseAI = character.GetBaseAI(); if (component.IsHungry() || ((Object)(object)baseAI != (Object)null && baseAI.IsAlerted())) { text += " (paused)"; } return text; } private static float TamingSpeed(Tameable tameable) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) float num = 1f; NearbyPlayers.Clear(); Player.GetPlayersInRange(((Component)tameable).transform.position, tameable.m_tamingSpeedMultiplierRange, NearbyPlayers); foreach (Player nearbyPlayer in NearbyPlayers) { if (((Character)nearbyPlayer).GetSEMan().HaveStatusAttribute((StatusAttribute)8)) { num *= tameable.m_tamingBoostMultiplier; } } return num; } private static string? Breeding(Character character, ZDO zdo) { if (!Plugin.showBreedingTimer.Value || !character.IsTamed()) { return null; } Procreation component = ((Component)character).GetComponent<Procreation>(); if ((Object)(object)component == (Object)null) { return null; } long num = zdo.GetLong(ZDOVars.s_pregnant, 0L); if (num == 0L) { return $"Love {zdo.GetInt(ZDOVars.s_lovePoints, 0)}/{component.m_requiredLovePoints}"; } double num2 = (double)component.m_pregnancyDuration - SecondsSince(num); if (!(num2 > 0.0)) { return "Giving birth"; } return "Gives birth in " + Format(num2); } private static string? Growth(Character character, ZDO zdo) { if (!Plugin.showGrowthTimer.Value) { return null; } Growup component = ((Component)character).GetComponent<Growup>(); if ((Object)(object)component == (Object)null) { return null; } long num = zdo.GetLong(ZDOVars.s_spawnTime, 0L); if (num == 0L) { return null; } double num2 = (double)component.m_growTime - SecondsSince(num); if (!(num2 > 0.0)) { return "Growing up"; } return "Grows up in " + Format(num2); } private static double SecondsSince(long ticks) { return (ZNet.instance.GetTime() - new DateTime(ticks)).TotalSeconds; } private static string Format(double seconds) { int num = Mathf.CeilToInt((float)seconds); if (num >= 3600) { return $"{num / 3600}h {num % 3600 / 60}m"; } if (num >= 60) { return $"{num / 60}m {num % 60}s"; } return $"{num}s"; } } [HarmonyPatch] public static class TimerPatches { [HarmonyPostfix] [HarmonyPatch(typeof(Character), "GetHoverText")] private static void Character_GetHoverText_Postfix(Character __instance, ref string __result) { __result = Timers.AddTo(__instance, __result); } } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [ServerSync.Embedded] internal sealed class ServerSync.EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [ServerSync.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class ServerSync.NullableAttribute : Attribute { public readonly byte[] NullableFlags; public ServerSync.NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public ServerSync.NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [ServerSync.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class ServerSync.NullableContextAttribute : Attribute { public readonly byte Flag; public ServerSync.NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [ServerSync.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 ServerSync { [ServerSync.NullableContext(1)] [ServerSync.Nullable(0)] [PublicAPI] public abstract class OwnConfigEntryBase { [ServerSync.Nullable(2)] public object LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [ServerSync.NullableContext(1)] [ServerSync.Nullable(0)] [PublicAPI] public class SyncedConfigEntry<[ServerSync.Nullable(2)] T>(ConfigEntry<T> sourceConfig) : OwnConfigEntryBase() { public readonly ConfigEntry<T> SourceConfig = sourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } [ServerSync.NullableContext(2)] [ServerSync.Nullable(0)] public abstract class CustomSyncedValueBase { public object LocalBaseValue; [ServerSync.Nullable(1)] public readonly string Identifier; [ServerSync.Nullable(1)] public readonly Type Type; private object boxedValue; protected bool localIsOwner; public readonly int Priority; public object BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action ValueChanged; [ServerSync.NullableContext(1)] protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { Priority = priority; Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [ServerSync.NullableContext(1)] [ServerSync.Nullable(0)] [PublicAPI] public sealed class CustomSyncedValue<[ServerSync.Nullable(2)] T> : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; } } public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0) : base(configSync, identifier, typeof(T), priority) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [ServerSync.NullableContext(1)] [ServerSync.Nullable(0)] [PublicAPI] public class ConfigSync { [ServerSync.NullableContext(0)] [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static class SnatchCurrentlyHandlingRPC { [ServerSync.Nullable(2)] public static ZRpc currentRpc; [ServerSync.NullableContext(1)] [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [ServerSync.NullableContext(0)] [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [ServerSync.NullableContext(1)] [HarmonyPostfix] private static void Postfix(ZNet __instance) { isServer = __instance.IsServer(); foreach (ConfigSync configSync2 in configSyncs) { ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync); if (isServer) { configSync2.InitialSyncDone = true; Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections")); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } [ServerSync.NullableContext(1)] static void SendAdmin(List<ZNetPeer> peers, bool isAdmin) { ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1] { new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = isAdmin } }); ConfigSync configSync = configSyncs.First(); if (configSync != null) { ((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package)); } } [ServerSync.NullableContext(1)] static IEnumerator WatchAdminListChanges() { MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); List<string> CurrentList = new List<string>(adminList.GetList()); while (true) { yield return (object)new WaitForSeconds(30f); if (!adminList.GetList().SequenceEqual(CurrentList)) { CurrentList = new List<string>(adminList.GetList()); List<ZNetPeer> adminPeer = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return ((object)listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName })); }).ToList(); List<ZNetPeer> nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList(); SendAdmin(nonAdminPeer, isAdmin: false); SendAdmin(adminPeer, isAdmin: true); } } } } } [ServerSync.NullableContext(0)] [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterClientRPCPatch { [ServerSync.NullableContext(1)] [HarmonyPostfix] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in configSyncs) { peer.m_rpc.Register<ZPackage>(configSync.Name + " ConfigSync", (Action<ZRpc, ZPackage>)configSync.RPC_FromServerConfigSync); } } } [ServerSync.NullableContext(0)] private class ParsedConfigs { [ServerSync.Nullable(new byte[] { 1, 1, 2 })] public readonly Dictionary<OwnConfigEntryBase, object> configValues = new Dictionary<OwnConfigEntryBase, object>(); [ServerSync.Nullable(new byte[] { 1, 1, 2 })] public readonly Dictionary<CustomSyncedValueBase, object> customValues = new Dictionary<CustomSyncedValueBase, object>(); } [ServerSync.NullableContext(0)] [HarmonyPatch(typeof(ZNet), "Shutdown")] private class ResetConfigsOnShutdown { [HarmonyPostfix] private static void Postfix() { ProcessingServerUpdate = true; foreach (ConfigSync configSync in configSyncs) { configSync.resetConfigsFromServer(); configSync.IsSourceOfTruth = true; configSync.InitialSyncDone = false; } ProcessingServerUpdate = false; } } [ServerSync.Nullable(0)] [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { [ServerSync.Nullable(0)] private class BufferingSocket : ZPlayFabSocket, ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List<ZPackage> Package = new List<ZPackage>(); public readonly ISocket Original; public BufferingSocket(ISocket original) { Original = original; ((ZPlayFabSocket)this)..ctor(); } public bool IsConnected() { return Original.IsConnected(); } public ZPackage Recv() { return Original.Recv(); } public int GetSendQueueSize() { return Original.GetSendQueueSize(); } public int GetCurrentSendRate() { return Original.GetCurrentSendRate(); } public bool IsHost() { return Original.IsHost(); } public void Dispose() { Original.Dispose(); } public bool GotNewData() { return Original.GotNewData(); } public void Close() { Original.Close(); } public string GetEndPointString() { return Original.GetEndPointString(); } public void GetAndResetStats(out int totalSent, out int totalRecv) { Original.GetAndResetStats(ref totalSent, ref totalRecv); } public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec) { Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec); } public ISocket Accept() { return Original.Accept(); } public int GetHostPort() { return Original.GetHostPort(); } public bool Flush() { return Original.Flush(); } public string GetHostName() { return Original.GetHostName(); } public void VersionMatch() { if (finished) { Original.VersionMatch(); } else { versionMatchQueued = Package.Count; } } public void Send(ZPackage pkg) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown int pos = pkg.GetPos(); pkg.SetPos(0); int num = pkg.ReadInt(); if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished) { ZPackage val = new ZPackage(pkg.GetArray()); val.SetPos(pos); Package.Add(val); } else { pkg.SetPos(pos); Original.Send(pkg); } } } [HarmonyPriority(800)] [HarmonyPrefix] private static void Prefix([ServerSync.Nullable(new byte[] { 2, 1, 1 })] ref Dictionary<Assembly, BufferingSocket> __state, ZNet __instance, ZRpc rpc) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Invalid comparison between Unknown and I4 if (!__instance.IsServer()) { return; } BufferingSocket bufferingSocket = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket); object? obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (val != null && (int)ZNet.m_onlineBackend > 0) { FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket"); object? value = fieldInfo.GetValue(val); ZPlayFabSocket val2 = (ZPlayFabSocket)((value is ZPlayFabSocket) ? value : null); if (val2 != null) { typeof(ZPlayFabSocket).GetField("m_remotePlayerId").SetValue(bufferingSocket, val2.m_remotePlayerId); } fieldInfo.SetValue(val, bufferingSocket); } if (__state == null) { __state = new Dictionary<Assembly, BufferingSocket>(); } __state[Assembly.GetExecutingAssembly()] = bufferingSocket; } [HarmonyPostfix] private static void Postfix(Dictionary<Assembly, BufferingSocket> __state, ZNet __instance, ZRpc rpc) { ZNetPeer peer; if (__instance.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (peer == null) { SendBufferedData(); } else { ((MonoBehaviour)__instance).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (rpc.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = __state[Assembly.GetExecutingAssembly()]; bufferingSocket2.finished = true; for (int i = 0; i < bufferingSocket2.Package.Count; i++) { if (i == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } bufferingSocket2.Original.Send(bufferingSocket2.Package[i]); } if (bufferingSocket2.Package.Count == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } } IEnumerator sendAsync() { foreach (ConfigSync configSync in configSyncs) { List<PackageEntry> entries = new List<PackageEntry>(); if (configSync.CurrentVersion != null) { entries.Add(new PackageEntry { section = "Internal", key = "serverversion", type = typeof(string), value = configSync.CurrentVersion }); } MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); entries.Add(new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = (((object)listContainsId == null) ? ((object)adminList.Contains(rpc.GetSocket().GetHostName())) : listContainsId.Invoke(ZNet.instance, new object[2] { adminList, rpc.GetSocket().GetHostName() })) }); ZPackage package = ConfigsToPackage(configSync.allConfigs.Select([ServerSync.NullableContext(0)] (OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false); yield return ((MonoBehaviour)__instance).StartCoroutine(configSync.sendZPackage(new List<ZNetPeer> { peer }, package)); } SendBufferedData(); } } } [ServerSync.Nullable(0)] private class PackageEntry { public string section = null; public string key = null; public Type type = null; [ServerSync.Nullable(2)] public object value; } [ServerSync.NullableContext(0)] [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] private static class PreventSavingServerInfo { [ServerSync.NullableContext(1)] [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || isWritableConfig(ownConfigEntryBase)) { return true; } __result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType); return false; } } [ServerSync.NullableContext(0)] [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] private static class PreventConfigRereadChangingValues { [ServerSync.NullableContext(1)] [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null) { return true; } try { ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType); } catch (Exception ex) { Debug.LogWarning((object)$"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } return false; } } [ServerSync.Nullable(0)] private class InvalidDeserializationTypeException : Exception { public string expected = null; public string received = null; public string field = ""; } public static bool ProcessingServerUpdate; public readonly string Name; [ServerSync.Nullable(2)] public string DisplayName; [ServerSync.Nullable(2)] public string CurrentVersion; [ServerSync.Nullable(2)] public string MinimumRequiredVersion; public bool ModRequired = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet<ConfigSync> configSyncs; private readonly HashSet<OwnConfigEntryBase> allConfigs = new HashSet<OwnConfigEntryBase>(); private HashSet<CustomSyncedValueBase> allCustomValues = new HashSet<CustomSyncedValueBase>(); private static bool isServer; private static bool lockExempt; [ServerSync.Nullable(2)] private OwnConfigEntryBase lockedConfig = null; private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; private readonly Dictionary<string, SortedDictionary<int, byte[]>> configValueCache = new Dictionary<string, SortedDictionary<int, byte[]>>(); [ServerSync.Nullable(new byte[] { 1, 0, 1 })] private readonly List<KeyValuePair<long, string>> cacheExpirations = new List<KeyValuePair<long, string>>(); private static long packageCounter; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { if (lockedConfig == null) { goto IL_0064; } num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag == true; } if (!num) { goto IL_0064; } int result = ((!lockExempt) ? 1 : 0); goto IL_0065; IL_0064: result = 0; goto IL_0065; IL_0065: return (byte)result != 0; } set { forceConfigLocking = value; } } public bool IsAdmin => lockExempt || isSourceOfTruth; public bool IsSourceOfTruth { get { return isSourceOfTruth; } private set { if (value != isSourceOfTruth) { isSourceOfTruth = value; this.SourceOfTruthChanged?.Invoke(value); } } } public bool InitialSyncDone { get; private set; } = false; [ServerSync.Nullable(2)] [method: ServerSync.NullableContext(2)] [field: ServerSync.Nullable(2)] public event Action<bool> SourceOfTruthChanged; [ServerSync.Nullable(2)] [method: ServerSync.NullableContext(2)] [field: ServerSync.Nullable(2)] private event Action lockedConfigChanged; static ConfigSync() { ProcessingServerUpdate = false; configSyncs = new HashSet<ConfigSync>(); lockExempt = false; packageCounter = 0L; RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry<T> AddConfigEntry<[ServerSync.Nullable(2)] T>(ConfigEntry<T> configEntry) { OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry); SyncedConfigEntry<T> syncedEntry = ownConfigEntryBase as SyncedConfigEntry<T>; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry<T>(configEntry); AccessTools.DeclaredField(typeof(ConfigDescription), "<Tags>k__BackingField").SetValue(((ConfigEntryBase)configEntry).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry).Description.Tags ?? Array.Empty<object>()).Concat(new SyncedConfigEntry<T>[1] { syncedEntry }).ToArray()); configEntry.SettingChanged += [ServerSync.NullableContext(0)] (object _, EventArgs _) => { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(0L, (ConfigEntryBase)configEntry); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry<T> AddLockingConfigEntry<[ServerSync.Nullable(0)] T>(ConfigEntry<T> lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry<T>(lockingConfig); lockingConfig.SettingChanged += [ServerSync.NullableContext(0)] (object _, EventArgs _) => { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry<T>)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Select([ServerSync.NullableContext(0)] (CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue); allCustomValues = new HashSet<CustomSyncedValueBase>(allCustomValues.OrderByDescending([ServerSync.NullableContext(0)] (CustomSyncedValueBase v) => v.Priority)); customValue.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(0L, customValue); } }; } private void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package) { lockedConfigChanged += serverLockedSettingChanged; IsSourceOfTruth = false; if (HandleConfigSyncRPC(0L, package, clientUpdate: false)) { InitialSyncDone = true; } } private void RPC_FromOtherClientConfigSync(long sender, ZPackage package) { HandleConfigSyncRPC(sender, package, clientUpdate: true); } private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Expected O, but got Unknown //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Expected O, but got Unknown try { if (isServer && IsLocked) { ZRpc currentRpc = SnatchCurrentlyHandlingRPC.currentRpc; object obj; if (currentRpc == null) { obj = null; } else { ISocket socket = currentRpc.GetSocket(); obj = ((socket != null) ? socket.GetHostName() : null); } string text = (string)obj; if (text != null) { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); if (!(((object)methodInfo == null) ? val.Contains(text) : ((bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, text })))) { return false; } } } cacheExpirations.RemoveAll(([ServerSync.Nullable(new byte[] { 0, 1 })] KeyValuePair<long, string> kv) => { if (kv.Key < DateTimeOffset.Now.Ticks) { configValueCache.Remove(kv.Value); return true; } return false; }); byte b = package.ReadByte(); if ((b & 2) != 0) { long num = package.ReadLong(); string text2 = sender.ToString() + num; if (!configValueCache.TryGetValue(text2, out var value)) { value = new SortedDictionary<int, byte[]>(); configValueCache[text2] = value; cacheExpirations.Add(new KeyValuePair<long, string>(DateTimeOffset.Now.AddSeconds(60.0).Ticks, text2)); } int key = package.ReadInt(); int num2 = package.ReadInt(); value.Add(key, package.ReadByteArray()); if (value.Count < num2) { return false; } configValueCache.Remove(text2); package = new ZPackage(value.Values.SelectMany([ServerSync.NullableContext(0)] (byte[] a) => a).ToArray()); b = package.ReadByte(); } ProcessingServerUpdate = true; if ((b & 4) != 0) { byte[] buffer = package.ReadByteArray(); MemoryStream stream = new MemoryStream(buffer); MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) { deflateStream.CopyTo(memoryStream); } package = new ZPackage(memoryStream.ToArray()); b = package.ReadByte(); } if ((b & 1) == 0) { resetConfigsFromServer(); } ParsedConfigs parsedConfigs = ReadConfigsFromPackage(package); ConfigFile val2 = null; bool saveOnConfigSet = false; foreach (KeyValuePair<OwnConfigEntryBase, object> configValue in parsedConfigs.configValues) { if (!isServer && configValue.Key.LocalBaseValue == null) { configValue.Key.LocalBaseValue = configValue.Key.BaseConfig.BoxedValue; } if (val2 == null) { val2 = configValue.Key.BaseConfig.ConfigFile; saveOnConfigSet = val2.SaveOnConfigSet; val2.SaveOnConfigSet = false; } configValue.Key.BaseConfig.BoxedValue = configValue.Value; } if (val2 != null) { val2.SaveOnConfigSet = saveOnConfigSet; val2.Save(); } foreach (KeyValuePair<CustomSyncedValueBase, object> customValue in parsedConfigs.customValues) { if (!isServer) { CustomSyncedValueBase key2 = customValue.Key; if (key2.LocalBaseValue == null) { key2.LocalBaseValue = customValue.Key.BoxedValue; } } customValue.Key.BoxedValue = customValue.Value; } Debug.Log((object)string.Format("Received {0} configs and {1} custom values from {2} for mod {3}", parsedConfigs.configValues.Count, parsedConfigs.customValues.Count, (isServer || clientUpdate) ? $"client {sender}" : "the server", DisplayName ?? Name)); if (!isServer) { serverLockedSettingChanged(); } return true; } finally { ProcessingServerUpdate = false; } } private ParsedConfigs ReadConfigsFromPackage(ZPackage package) { ParsedConfigs parsedConfigs = new ParsedConfigs(); Dictionary<string, OwnConfigEntryBase> dictionary = allConfigs.Where([ServerSync.NullableContext(0)] (OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary([ServerSync.NullableContext(0)] (OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, [ServerSync.NullableContext(0)] (OwnConfigEntryBase c) => c); Dictionary<string, CustomSyncedValueBase> dictionary2 = allCustomValues.ToDictionary([ServerSync.NullableContext(0)] (CustomSyncedValueBase c) => c.Identifier, [ServerSync.NullableContext(0)] (CustomSyncedValueBase c) => c); int num = package.ReadInt(); for (int num2 = 0; num2 < num; num2++) { string text = package.ReadString(); string text2 = package.ReadString(); string text3 = package.ReadString(); Type type = Type.GetType(text3); if (text3 == "" || type != null) { object obj; try { obj = ((text3 == "") ? null : ReadValueWithTypeFromZPackage(package, type)); } catch (InvalidDeserializationTypeException ex) { Debug.LogWarning((object)("Got unexpected struct