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 DecorativePet v1.5.0
plugins/DecorativePet/DecorativePet.dll
Decompiled 2 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("DecorativePet")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.6.0")] [assembly: AssemblyInformationalVersion("1.2.6")] [assembly: AssemblyProduct("DecorativePet")] [assembly: AssemblyTitle("DecorativePet")] [assembly: AssemblyVersion("1.2.6.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace DecorativePet { [BepInPlugin("ua.bohdan.decorativepet", "Decorative Pet", "1.5.0")] public sealed class DecorativePetPlugin : BaseUnityPlugin { private sealed class PetRecord { public object View; public object GameObject; public object Zdo; public string Name; public string Owner; public string Prefab; public bool Follow; public bool IgnoreEnemies; public object OwnerPlayer; public object Character; public object Tameable; public object MonsterAI; public IList AttackList; public List<object> AttackEntries; public object SecondaryAttack; public object AttackContainer; public object EmptyAttackContainer; public Dictionary<string, bool> AttackFlags; } [HarmonyPatch] private static class TerminalInitPatch { private static MethodBase TargetMethod() { return (from m in FindType("Terminal")?.GetMethods(AccessTools.all) where m.Name == "InitTerminal" orderby m.GetParameters().Length select m).FirstOrDefault(); } private static void Postfix() { _instance?.TryRegisterCommand(); } } [HarmonyPatch] private static class ChatInputPatch { private static MethodBase TargetMethod() { return (from m in FindType("Chat")?.GetMethods(AccessTools.all) where m.Name == "InputText" orderby m.GetParameters().Length select m).FirstOrDefault(); } private static bool Prefix(object __instance) { if (_instance == null) { return true; } string text = ReadChatInput(__instance)?.Trim(); if (string.IsNullOrWhiteSpace(text) || !text.StartsWith("/", StringComparison.Ordinal)) { return true; } List<string> list = Tokenize(text.Substring(1)); if (list.Count == 0 || !string.Equals(list[0], "pet", StringComparison.OrdinalIgnoreCase)) { return true; } ClearChatInput(__instance); _instance.SendChatCommand(list); return false; } } [HarmonyPatch] private static class ChatRpcPatch { private static MethodBase TargetMethod() { return (from m in FindType("Chat")?.GetMethods(AccessTools.all) where m.Name == "RPC_ChatMessage" orderby m.GetParameters().Length select m).FirstOrDefault(); } private static bool Prefix(object[] __args) { try { if (!TryParseChatCommand(__args, out var tokens, out var requestId, out var ownerName, out var position)) { return true; } if (_instance != null && IsServer()) { _instance.HandleChatCommand(tokens, requestId, ownerName, position); } return false; } catch (Exception ex) { DecorativePetPlugin instance = _instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogError((object)("Chat command handling failed: " + ex)); } return true; } } } [HarmonyPatch] private static class CharacterDamagePatch { private static MethodBase TargetMethod() { return (from m in FindType("Character")?.GetMethods(AccessTools.all) where m.Name == "Damage" orderby m.GetParameters().Length select m).FirstOrDefault(); } private static bool Prefix(object __instance) { return !IsPetComponent(__instance); } } [HarmonyPatch] private static class TameableHoverPatch { private static IEnumerable<MethodBase> TargetMethods() { Type type = FindType("Tameable"); if (!(type == null)) { return (from m in type.GetMethods(AccessTools.all) where m.Name == "GetHoverText" && m.ReturnType == typeof(string) select m).Cast<MethodBase>(); } return new MethodBase[0]; } private static void Postfix(object __instance, ref string __result) { object petZdo = GetPetZdo(__instance); if (IsPetZdo(petZdo)) { _instance?.SetHoveredPet(__instance, petZdo); string text = ((GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1") ? LocalText("дозволити атаки", "Allow attacks") : LocalText("нікого не чіпати", "Ignore enemies")); string zdoString = GetZdoString(petZdo, "ua.bohdan.decorativepet.name"); if ((__result ?? "").IndexOf("режим:", StringComparison.OrdinalIgnoreCase) < 0 && (__result ?? "").IndexOf("mode:", StringComparison.OrdinalIgnoreCase) < 0) { __result = (__result ?? zdoString ?? "Pet") + "\n" + LocalText("Режим: ", "Mode: ") + text; } } } } [HarmonyPatch] private static class CharacterHoverPatch { private static IEnumerable<MethodBase> TargetMethods() { Type type = FindType("Character"); if (!(type == null)) { return (from m in type.GetMethods(AccessTools.all) where m.Name == "GetHoverText" && m.ReturnType == typeof(string) select m).Cast<MethodBase>(); } return new MethodBase[0]; } private static void Postfix(object __instance, ref string __result) { object petZdo = GetPetZdo(__instance); if (IsPetZdo(petZdo) && (__result ?? "").IndexOf("режим:", StringComparison.OrdinalIgnoreCase) < 0 && (__result ?? "").IndexOf("mode:", StringComparison.OrdinalIgnoreCase) < 0) { _instance?.SetHoveredPet(__instance, petZdo); bool flag = GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1"; GetComponent(GetMember(__instance, "gameObject"), FindType("Tameable")); __result = (__result ?? GetZdoString(petZdo, "ua.bohdan.decorativepet.name")) + "\n" + LocalText("Режим: ", "Mode: ") + (flag ? LocalText("дозволити атаки", "Allow attacks") : LocalText("нікого не чіпати", "Ignore enemies")); } } } [HarmonyPatch] private static class CharacterHoverNamePatch { private static IEnumerable<MethodBase> TargetMethods() { Type type = FindType("Character"); if (!(type == null)) { return (from m in type.GetMethods(AccessTools.all) where m.Name == "GetHoverName" && m.ReturnType == typeof(string) select m).Cast<MethodBase>(); } return new MethodBase[0]; } private static void Postfix(object __instance, ref string __result) { object petZdo = GetPetZdo(__instance); if (IsPetZdo(petZdo) && (__result ?? "").IndexOf("режим:", StringComparison.OrdinalIgnoreCase) < 0 && (__result ?? "").IndexOf("mode:", StringComparison.OrdinalIgnoreCase) < 0) { _instance?.SetHoveredPet(__instance, petZdo); bool flag = GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1"; __result = (__result ?? GetZdoString(petZdo, "ua.bohdan.decorativepet.name")) + "\n" + LocalText("Режим: ", "Mode: ") + (flag ? LocalText("дозволити атаки", "Allow attacks") : LocalText("нікого не чіпати", "Ignore enemies")); } } } [HarmonyPatch] private static class IgnorePetTargetPatch { private static IEnumerable<MethodBase> TargetMethods() { return (from m in new Type[2] { FindType("BaseAI"), FindType("MonsterAI") }.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all)) where (m.Name == "SetTarget" || m.Name == "OnDamaged") && (m.ReturnType == typeof(void) || m.ReturnType == typeof(bool)) select m).Cast<MethodBase>().Distinct(); } private static bool Prefix(object __instance) { return !IsIgnoredPetAI(__instance); } } [HarmonyPatch] private static class IgnorePetAttackMethodsPatch { private static IEnumerable<MethodBase> TargetMethods() { return (from m in new Type[2] { FindType("BaseAI"), FindType("MonsterAI") }.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all)) where (m.Name == "Attack" || m.Name == "AttackTarget" || m.Name == "DoAttack") && (m.ReturnType == typeof(void) || m.ReturnType == typeof(bool)) select m).Cast<MethodBase>().Distinct(); } private static bool Prefix(object __instance) { return !IsIgnoredPetAI(__instance); } } [HarmonyPatch] private static class TameableInteractPatch { private static MethodBase TargetMethod() { return (from m in FindType("Tameable")?.GetMethods(AccessTools.all) where m.Name == "Interact" && m.ReturnType == typeof(bool) orderby m.GetParameters().Length select m).FirstOrDefault(); } private static bool Prefix(object __instance, object[] __args, ref bool __result) { object zdo = GetPetZdo(__instance); if (!IsPetZdo(zdo)) { return true; } string zdoString = GetZdoString(zdo, "ua.bohdan.decorativepet.owner"); object localPlayer = GetLocalPlayer(); if (string.IsNullOrWhiteSpace(zdoString) || !string.Equals(zdoString, GetPlayerName(localPlayer), StringComparison.Ordinal)) { return true; } if (HasFoodInHand(localPlayer)) { return true; } bool flag = GetZdoString(zdo, "ua.bohdan.decorativepet.ignore_enemies") == "1"; if (_instance != null && IsServer()) { PetRecord petRecord = _instance._cachedPets.FirstOrDefault((PetRecord p) => p.Zdo == zdo); if (petRecord != null) { petRecord.IgnoreEnemies = !flag; SetZdoString(petRecord.Zdo, "ua.bohdan.decorativepet.ignore_enemies", petRecord.IgnoreEnemies ? "1" : "0"); _instance.ApplyIgnoreMode(petRecord); _instance.CommandMessage("[DecorativePet] Pet aggression: " + (petRecord.IgnoreEnemies ? "disabled" : "enabled") + ".", fromChat: false); } } else { _instance?.SendChatCommand(new List<string> { "pet", flag ? "attack" : "ignore" }); } __result = true; return false; } } [HarmonyPatch] private static class PetPickupBlockPatch { private static MethodBase TargetMethod() { return (from m in FindType("Pickable")?.GetMethods(AccessTools.all) where m.Name == "Interact" && m.ReturnType == typeof(bool) orderby m.GetParameters().Length select m).FirstOrDefault(); } private static bool Prefix(object __instance) { return !IsPetComponent(__instance); } } [HarmonyPatch] private static class PetItemPickupBlockPatch { private static MethodBase TargetMethod() { return (from m in FindType("ItemDrop")?.GetMethods(AccessTools.all) where m.Name == "Interact" && m.ReturnType == typeof(bool) orderby m.GetParameters().Length select m).FirstOrDefault(); } private static bool Prefix(object __instance) { return !IsPetComponent(__instance); } } [HarmonyPatch] private static class FishPickupBlockPatch { private static IEnumerable<MethodBase> TargetMethods() { return (from m in new Type[3] { FindType("Fish"), FindType("Pickable"), FindType("ItemDrop") }.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all)) where (m.Name == "Interact" || m.Name == "Pick" || m.Name == "Pickup") && (m.ReturnType == typeof(bool) || m.ReturnType == typeof(void)) select m).Cast<MethodBase>().Distinct(); } private static bool Prefix(object __instance) { return !IsPetComponent(__instance); } } [HarmonyPatch] private static class FishHoverPatch { private static IEnumerable<MethodBase> TargetMethods() { return (from m in new Type[3] { FindType("Fish"), FindType("ItemDrop"), FindType("Pickable") }.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all)) where (m.Name == "GetHoverText" || m.Name == "GetHoverName") && m.ReturnType == typeof(string) select m).Cast<MethodBase>().Distinct(); } private static void Postfix(object __instance, ref string __result) { object petZdo = GetPetZdo(__instance); if (IsPetZdo(petZdo) && (__result ?? "").IndexOf("[Decorative Pet]", StringComparison.Ordinal) < 0) { bool flag = GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1"; __result = (__result ?? GetZdoString(petZdo, "ua.bohdan.decorativepet.name")) + "\n" + LocalText("Режим: ", "Mode: ") + (flag ? "Allow attacks" : "Ignore enemies"); } } } [HarmonyPatch] private static class FishPickupMethodPatch { private static IEnumerable<MethodBase> TargetMethods() { return (from m in new Type[3] { FindType("Fish"), FindType("Pickable"), FindType("ItemDrop") }.Where((Type t) => t != null).SelectMany((Type t) => t.GetMethods(AccessTools.all)) where (m.Name == "Pickup" || m.Name == "Pick" || m.Name == "Take") && (m.ReturnType == typeof(bool) || m.ReturnType == typeof(void)) select m).Cast<MethodBase>().Distinct(); } private static bool Prefix(object __instance) { return !IsPetComponent(__instance); } } public const string PluginGuid = "ua.bohdan.decorativepet"; public const string PluginName = "Decorative Pet"; public const string PluginVersion = "1.5.0"; private const string MarkerKey = "ua.bohdan.decorativepet.marker"; private const string NameKey = "ua.bohdan.decorativepet.name"; private const string OwnerKey = "ua.bohdan.decorativepet.owner"; private const string PrefabKey = "ua.bohdan.decorativepet.prefab"; private const string FollowKey = "ua.bohdan.decorativepet.follow"; private const string IgnoreEnemiesKey = "ua.bohdan.decorativepet.ignore_enemies"; private const string ChatCommandPrefix = "\u200b[DecorativePetCmd] "; private static DecorativePetPlugin _instance; private Harmony _harmony; private readonly List<PetRecord> _cachedPets = new List<PetRecord>(); private DateTime _nextBehaviourUtc = DateTime.MinValue; private const double BehaviourIntervalSeconds = 0.5; private const int ChatRequestHistoryLimit = 256; private readonly Queue<string> _handledChatRequestOrder = new Queue<string>(); private readonly HashSet<string> _handledChatRequests = new HashSet<string>(StringComparer.Ordinal); private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>(StringComparer.Ordinal); private object _hoveredPet; private object _hoveredPetZdo; private DateTime _hoveredUntilUtc; private ConfigEntry<string> _defaultPrefab; private ConfigEntry<string> _defaultName; private ConfigEntry<float> _followDistance; private ConfigEntry<float> _teleportDistance; private void Awake() { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown _instance = this; _defaultPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Pet", "DefaultPrefab", "Wolf", "Vanilla prefab name used by 'pet spawn' (Wolf, Boar, Lox, Chicken, Deer, ...). "); _defaultName = ((BaseUnityPlugin)this).Config.Bind<string>("Pet", "DefaultName", "Рекс", "Name used by 'pet spawn' when no name is supplied."); _followDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Pet", "FollowDistance", 3f, "How close the pet stays to its owner, in metres."); _teleportDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Pet", "TeleportDistance", 15f, "If the pet is farther than this many metres from its owner, teleport it back."); try { _harmony = new Harmony("ua.bohdan.decorativepet"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Decorative Pet 1.5.0 loaded. Type '/pet help' in the Valheim chat."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to initialize Decorative Pet: " + ex)); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _cachedPets.Clear(); if (_instance == this) { _instance = null; } } private void Update() { if (_cachedPets.Count != 0) { DateTime utcNow = DateTime.UtcNow; if (!(utcNow < _nextBehaviourUtc)) { _nextBehaviourUtc = utcNow.AddSeconds(0.5); ApplyPetBehaviour(); } } } private void SetHoveredPet(object tameable, object zdo) { _hoveredPet = tameable; _hoveredPetZdo = zdo; _hoveredUntilUtc = DateTime.UtcNow.AddMilliseconds(300.0); } private void OnGUI() { if (_hoveredPetZdo == null || DateTime.UtcNow > _hoveredUntilUtc) { return; } Type type = FindType("UnityEngine.Screen"); Type type2 = FindType("UnityEngine.Rect"); Type type3 = FindType("UnityEngine.GUI"); if (type == null || type2 == null || type3 == null) { return; } try { float num = Convert.ToSingle(GetStaticMember(type, "width"), CultureInfo.InvariantCulture); float num2 = Convert.ToSingle(GetStaticMember(type, "height"), CultureInfo.InvariantCulture); ConstructorInfo constructor = type2.GetConstructor(new Type[4] { typeof(float), typeof(float), typeof(float), typeof(float) }); if (!(constructor == null)) { object obj = constructor.Invoke(new object[4] { num * 0.5f - 120f, num2 * 0.5f + 45f, 240f, 44f }); InvokeStaticCompatible(type3, "Box", obj, ""); string text = ((GetZdoString(_hoveredPetZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1") ? LocalText("Ігнор", "Ignore") : LocalText("Атака", "Attack")); InvokeStaticCompatible(type3, "Label", constructor.Invoke(new object[4] { num * 0.5f - 110f, num2 * 0.5f + 51f, 220f, 18f }), LocalText("Їжа: ", "Food: ") + FoodBarText(_hoveredPet) + " " + LocalText("Режим: ", "Mode: ") + text); } } catch { } } private static string FoodBarText(object tameable) { string text = FoodText(tameable); if (text.IndexOf("0%", StringComparison.Ordinal) >= 0) { return "---------- 0%"; } if (text.IndexOf("100%", StringComparison.Ordinal) >= 0) { return "########## 100%"; } return "----------"; } private static bool InvokeStaticCompatible(Type type, string name, params object[] args) { if (type == null) { return false; } foreach (MethodInfo item in from m in type.GetMethods(AccessTools.all) where m.Name == name && m.IsStatic select m) { if (item.GetParameters().Length == args.Length) { try { item.Invoke(null, args); return true; } catch { } } } return false; } private void TryRegisterCommand() { try { Type type = FindType("Terminal"); if (type == null) { return; } FieldInfo fieldInfo = FindFieldNoLog(type, "commands", isStatic: true); if (fieldInfo == null || fieldInfo.GetValue(null) == null) { return; } Type nestedType = type.GetNestedType("ConsoleCommand", BindingFlags.Public | BindingFlags.NonPublic); if (nestedType == null) { return; } ConstructorInfo constructorInfo = (from c in nestedType.GetConstructors(AccessTools.all) where c.GetParameters().Length >= 3 where c.GetParameters().Any((ParameterInfo p) => typeof(Delegate).IsAssignableFrom(p.ParameterType)) select c).OrderByDescending(delegate(ConstructorInfo c) { MethodInfo methodInfo = c.GetParameters().FirstOrDefault((ParameterInfo p) => typeof(Delegate).IsAssignableFrom(p.ParameterType))?.ParameterType.GetMethod("Invoke"); return (methodInfo != null && methodInfo.ReturnType == typeof(void)) ? 1 : 0; }).FirstOrDefault(); if (constructorInfo == null) { return; } ParameterInfo[] parameters = constructorInfo.GetParameters(); object[] array = new object[parameters.Length]; int num = 0; bool flag = false; for (int num2 = 0; num2 < parameters.Length; num2++) { ParameterInfo parameterInfo = parameters[num2]; Type parameterType = parameterInfo.ParameterType; if (parameterType == typeof(string)) { array[num2] = ((num++ == 0) ? "pet" : "Spawn and control a named, passive vanilla companion."); } else if (!flag && typeof(Delegate).IsAssignableFrom(parameterType)) { array[num2] = BuildConsoleDelegate(parameterType); flag = array[num2] != null; } else if (parameterInfo.HasDefaultValue) { array[num2] = parameterInfo.DefaultValue; } else { array[num2] = GetDefault(parameterType); } } if (flag) { constructorInfo.Invoke(array); TryInvokeCompatible(GetStaticMember(type, "m_terminal") ?? GetStaticMember(type, "instance"), "updateCommandList"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered console command: pet"); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Decorative Pet command registration is waiting: " + ex.Message)); } } private static Delegate BuildConsoleDelegate(Type delegateType) { MethodInfo method = delegateType.GetMethod("Invoke"); if (method == null || method.GetParameters().Length != 1) { return null; } ParameterExpression parameterExpression = Expression.Parameter(method.GetParameters()[0].ParameterType, "args"); Expression expression = Expression.Call(typeof(DecorativePetPlugin).GetMethod("DispatchConsole", BindingFlags.Static | BindingFlags.NonPublic), Expression.Convert(parameterExpression, typeof(object))); Expression body = expression; if (method.ReturnType != typeof(void)) { body = Expression.Block(expression, Expression.Default(method.ReturnType)); } return Expression.Lambda(delegateType, body, parameterExpression).Compile(); } private static void DispatchConsole(object eventArgs) { _instance?.HandleConsole(eventArgs); } private void HandleConsole(object eventArgs) { string text = Convert.ToString(GetMember(eventArgs, "FullLine"), CultureInfo.InvariantCulture); if (string.IsNullOrWhiteSpace(text)) { text = Convert.ToString(GetMember(eventArgs, "ArgsAll"), CultureInfo.InvariantCulture); } List<string> list = Tokenize(text); if (list.Count == 0) { PrintHelp(fromChat: false); return; } if (!string.Equals(list[0], "pet", StringComparison.OrdinalIgnoreCase)) { list.Insert(0, "pet"); } object position = GetPosition(GetMember(GetLocalPlayer(), "transform")); ExecuteCommand(list, GetLocalPlayerName(), position, fromChat: false); } private void HandleChatCommand(List<string> tokens, string requestId, string ownerName, object chatPosition) { if (IsServer() && (string.IsNullOrWhiteSpace(requestId) || MarkChatRequest(requestId))) { if (string.IsNullOrWhiteSpace(ownerName)) { ownerName = "Pet owner"; } ExecuteCommand(tokens, ownerName, chatPosition, fromChat: true); } } private bool MarkChatRequest(string requestId) { if (_handledChatRequests.Contains(requestId)) { return false; } _handledChatRequests.Add(requestId); _handledChatRequestOrder.Enqueue(requestId); while (_handledChatRequestOrder.Count > 256) { _handledChatRequests.Remove(_handledChatRequestOrder.Dequeue()); } return true; } private void ExecuteCommand(List<string> tokens, string ownerName, object requestedPosition, bool fromChat) { if (tokens == null || tokens.Count == 0) { PrintHelp(fromChat); return; } if (!string.Equals(tokens[0], "pet", StringComparison.OrdinalIgnoreCase)) { tokens.Insert(0, "pet"); } switch ((tokens.Count > 1) ? tokens[1].ToLowerInvariant() : "help") { case "spawn": SpawnFromCommand(tokens, ownerName, requestedPosition, fromChat); break; case "name": case "rename": RenameFromCommand(tokens, ownerName, fromChat); break; case "follow": SetFollowForOwner(follow: true, ownerName, fromChat); break; case "stay": case "stop": SetFollowForOwner(follow: false, ownerName, fromChat); break; case "ignore": case "passive": SetIgnoreEnemiesForOwner(ignore: true, ownerName, fromChat); break; case "attack": case "aggressive": SetIgnoreEnemiesForOwner(ignore: false, ownerName, fromChat); break; case "remove": case "delete": RemoveOwnedPets(ownerName, fromChat); break; case "list": ListPets(ownerName, fromChat); break; default: PrintHelp(fromChat); break; } } private void SpawnFromCommand(List<string> tokens, string ownerName, object requestedPosition, bool fromChat) { string text = ((tokens.Count > 2 && !string.IsNullOrWhiteSpace(tokens[2])) ? tokens[2] : _defaultPrefab.Value); string text2 = ((tokens.Count > 3) ? string.Join(" ", tokens.Skip(3).ToArray()) : _defaultName.Value); if (string.IsNullOrWhiteSpace(text2)) { text2 = "Pet"; } if (!IsServer()) { CommandMessage("[DecorativePet] Spawn must be handled by the world host/server.", fromChat); return; } object obj = FindPlayerByName(ownerName); object obj2 = GetPosition(GetMember(obj, "transform")); if (obj2 == null) { obj2 = OffsetPosition(requestedPosition, 0f, -1.5f, 0f); } if (obj2 == null) { CommandMessage("[DecorativePet] The requesting player is not ready yet.", fromChat); return; } obj2 = OffsetPosition(obj2, 2f, 0f, 0f); object member = GetMember(GetMember(obj, "transform"), "rotation"); if (CreateNetworkPet(text, text2, obj2, ownerName, obj, member) != null) { CommandMessage("[DecorativePet] Spawned " + text + " '" + text2 + "' for " + ownerName + ".", fromChat); } } private void RenameFromCommand(List<string> tokens, string ownerName, bool fromChat) { if (tokens.Count < 3) { CommandMessage("Usage: /pet name <new name>", fromChat); return; } string text = string.Join(" ", tokens.Skip(2).ToArray()); int num = 0; foreach (PetRecord item in _cachedPets.ToList()) { if (string.Equals(item.Owner, ownerName, StringComparison.Ordinal)) { SetZdoString(item.Zdo, "ua.bohdan.decorativepet.name", text); ApplyName(item.GameObject, item.Zdo, text); item.Name = text; num++; } } CommandMessage((num == 0) ? "[DecorativePet] You do not own a pet yet." : ("[DecorativePet] Renamed " + num + " pet(s) to '" + text + "'."), fromChat); } private void SetFollowForOwner(bool follow, string ownerName, bool fromChat) { int num = 0; foreach (PetRecord item in _cachedPets.ToList()) { if (string.Equals(item.Owner, ownerName, StringComparison.Ordinal)) { SetZdoString(item.Zdo, "ua.bohdan.decorativepet.follow", follow ? "1" : "0"); item.Follow = follow; ApplyNativeFollow(item, follow); num++; } } CommandMessage((num == 0) ? "[DecorativePet] You do not own a pet yet." : ("[DecorativePet] Pet mode: " + (follow ? "follow" : "stay") + "."), fromChat); } private void SetIgnoreEnemiesForOwner(bool ignore, string ownerName, bool fromChat) { int num = 0; foreach (PetRecord item in _cachedPets.ToList()) { if (string.Equals(item.Owner?.Trim(), ownerName?.Trim(), StringComparison.OrdinalIgnoreCase)) { item.IgnoreEnemies = ignore; SetZdoString(item.Zdo, "ua.bohdan.decorativepet.ignore_enemies", ignore ? "1" : "0"); ApplyIgnoreMode(item); num++; } } if (num == 0) { List<PetRecord> list = _cachedPets.Where((PetRecord p) => string.Equals(p.Owner?.Trim(), ownerName?.Trim(), StringComparison.OrdinalIgnoreCase)).ToList(); if (list.Count == 0 && _cachedPets.Count == 1) { list = _cachedPets.ToList(); } foreach (PetRecord item2 in list) { item2.IgnoreEnemies = ignore; SetZdoString(item2.Zdo, "ua.bohdan.decorativepet.ignore_enemies", ignore ? "1" : "0"); ApplyIgnoreMode(item2); num++; } } CommandMessage((num == 0) ? "[DecorativePet] You do not own a pet yet." : ("[DecorativePet] Pet aggression: " + (ignore ? "disabled" : "enabled") + "."), fromChat); } private void RemoveOwnedPets(string ownerName, bool fromChat) { if (!IsServer()) { CommandMessage("[DecorativePet] Remove must be handled by the world host/server.", fromChat); return; } int num = 0; foreach (PetRecord item in _cachedPets.ToList()) { if (string.Equals(item.Owner, ownerName, StringComparison.Ordinal) && DestroyNetworkPet(item)) { _cachedPets.Remove(item); num++; } } CommandMessage("[DecorativePet] Removed " + num + " pet(s).", fromChat); } private void ListPets(string ownerName, bool fromChat) { List<PetRecord> list = _cachedPets.ToList(); if (fromChat) { list = list.Where((PetRecord p) => string.Equals(p.Owner, ownerName, StringComparison.Ordinal)).ToList(); } if (list.Count == 0) { CommandMessage("[DecorativePet] No decorative pets found for this player.", fromChat); return; } foreach (PetRecord item in list) { CommandMessage("[DecorativePet] " + item.Prefab + " '" + item.Name + "' — owner: " + item.Owner, fromChat); } } private void PrintHelp(bool fromChat) { CommandMessage("[DecorativePet] /pet spawn [Prefab] [Name] | /pet name <Name> | /pet follow | /pet stay | /pet ignore | /pet attack | /pet list | /pet remove", fromChat); CommandMessage("[DecorativePet] Default prefab: " + _defaultPrefab.Value + "; default name: " + _defaultName.Value + ".", fromChat); } private void CommandMessage(string message, bool fromChat) { if (fromChat) { SendChatNotice(message); } else { Print(message); } } private void SendChatCommand(List<string> tokens) { object staticMember = GetStaticMember(FindType("Chat"), "instance"); Type type = FindType("Talker+Type") ?? FindType("Talker/Type") ?? FindType("Talker")?.GetNestedType("Type", AccessTools.all); if (staticMember == null || type == null) { Print("[DecorativePet] Chat is not ready yet."); return; } string text = Guid.NewGuid().ToString("N"); string text2 = string.Join(" ", tokens.ToArray()); string text3 = "\u200b[DecorativePetCmd] " + text + "|" + text2; object obj = Enum.ToObject(type, 2); if (!TryInvokeCompatible(staticMember, "SendText", obj, text3)) { Print("[DecorativePet] Could not send the pet command to the host."); } } private void SendChatNotice(string message) { try { object staticMember = GetStaticMember(FindType("Chat"), "instance"); Type type = FindType("Talker+Type") ?? FindType("Talker/Type") ?? FindType("Talker")?.GetNestedType("Type", AccessTools.all); if (staticMember != null && type != null && TryInvokeCompatible(staticMember, "SendText", Enum.ToObject(type, 2), message)) { return; } } catch { } Print(message); } private static string ReadChatInput(object chat) { object member = GetMember(GetMember(GetMember(chat, "m_terminalInstance") ?? chat, "m_input") ?? GetMember(chat, "m_input"), "text"); if (member != null) { return Convert.ToString(member, CultureInfo.InvariantCulture); } return ""; } private static void ClearChatInput(object chat) { SetMember(GetMember(GetMember(chat, "m_terminalInstance") ?? chat, "m_input") ?? GetMember(chat, "m_input"), "text", ""); } private static bool TryParseChatCommand(object[] arguments, out List<string> tokens, out string requestId, out string ownerName, out object position) { tokens = null; requestId = ""; ownerName = ""; position = null; if (arguments == null || arguments.Length < 5) { return false; } string text = arguments[4] as string; if (string.IsNullOrWhiteSpace(text)) { return false; } string text2 = null; string[] array = new string[2] { "\u200b[DecorativePetCmd] ", "[DecorativePetCmd] " }; foreach (string text3 in array) { if (text.StartsWith(text3, StringComparison.Ordinal)) { text2 = text.Substring(text3.Length).Trim(); break; } } if (text2 == null) { return false; } int num = text2.IndexOf('|'); if (num > 0) { requestId = text2.Substring(0, num); text2 = text2.Substring(num + 1).Trim(); } tokens = Tokenize(text2); if (tokens.Count == 0) { return false; } if (!string.Equals(tokens[0], "pet", StringComparison.OrdinalIgnoreCase)) { tokens.Insert(0, "pet"); } position = arguments[1]; ownerName = GetChatSenderName(arguments[3]); return true; } private static string GetChatSenderName(object sender) { if (sender is string result) { return result; } object obj = GetMember(sender, "Name") ?? GetMember(sender, "m_name") ?? GetMember(sender, "UserName"); if (obj != null) { return Convert.ToString(obj, CultureInfo.InvariantCulture); } return ""; } private static List<string> Tokenize(string line) { List<string> list = new List<string>(); if (string.IsNullOrWhiteSpace(line)) { return list; } foreach (Match item in Regex.Matches(line, "\"([^\"]*)\"|(\\S+)")) { list.Add(item.Groups[1].Success ? item.Groups[1].Value : item.Groups[2].Value); } return list; } private void Print(string message) { try { Type type = FindType("Terminal"); object obj = GetStaticMember(type, "m_terminal") ?? GetStaticMember(type, "instance"); if (obj != null) { MethodInfo methodInfo = (from m in obj.GetType().GetMethods(AccessTools.all) where m.Name == "AddString" where m.GetParameters().Length >= 1 where m.GetParameters()[0].ParameterType == typeof(string) orderby m.GetParameters().Length select m).FirstOrDefault(); if (methodInfo != null) { ParameterInfo[] parameters = methodInfo.GetParameters(); object[] array = new object[parameters.Length]; array[0] = message; for (int num = 1; num < array.Length; num++) { array[num] = GetDefault(parameters[num].ParameterType); } methodInfo.Invoke(obj, array); return; } } } catch { } ((BaseUnityPlugin)this).Logger.LogInfo((object)message); } private object CreateNetworkPet(string prefabName, string petName, object position, string ownerName, object ownerPlayer, object rotation) { object staticMember = GetStaticMember(FindType("ZNetScene"), "instance"); if (staticMember == null) { Print("[DecorativePet] ZNetScene is not ready."); return null; } object obj = InvokeCompatible(staticMember, "GetPrefab", prefabName); int num = StableHash(prefabName); if (obj == null) { obj = InvokeCompatible(staticMember, "GetPrefab", num); } if (obj == null) { Print("[DecorativePet] Vanilla prefab not found: " + prefabName + ". Try Wolf, Boar, Lox, Chicken or Deer."); return null; } object obj2; try { obj2 = InstantiatePrefab(obj, position, rotation); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to instantiate pet prefab: " + ex)); return null; } if (obj2 == null) { Print("[DecorativePet] Valheim could not instantiate prefab: " + prefabName + "."); return null; } object netView = GetNetView(obj2); object zdo = GetZdo(netView); if (zdo == null) { Print("[DecorativePet] Spawned prefab has no network object: " + prefabName + "."); return null; } SetZdoString(zdo, "ua.bohdan.decorativepet.marker", "1"); SetZdoString(zdo, "ua.bohdan.decorativepet.name", petName); SetZdoString(zdo, "ua.bohdan.decorativepet.prefab", prefabName); SetZdoString(zdo, "ua.bohdan.decorativepet.owner", ownerName ?? ""); SetZdoString(zdo, "ua.bohdan.decorativepet.follow", "1"); SetZdoString(zdo, "ua.bohdan.decorativepet.ignore_enemies", "0"); SetMember(zdo, "Persistent", false); TrackPetView(netView, ownerPlayer); return zdo; } private static object InstantiatePrefab(object prefab, object position, object rotation) { if (prefab == null || position == null) { return null; } Type type = FindType("UnityEngine.Quaternion"); if (rotation == null) { rotation = GetStaticMember(type, "identity"); } return ((from m in FindType("UnityEngine.Object")?.GetMethods(AccessTools.all) where m.Name == "Instantiate" && m.IsStatic where !m.ContainsGenericParameters where m.GetParameters().Length == 3 select m).FirstOrDefault(delegate(MethodInfo m) { ParameterInfo[] parameters = m.GetParameters(); return parameters[0].ParameterType.IsInstanceOfType(prefab) && parameters[1].ParameterType.IsInstanceOfType(position) && rotation != null && parameters[2].ParameterType.IsInstanceOfType(rotation); }))?.Invoke(null, new object[3] { prefab, position, rotation }); } private void TrackPetView(object view, object ownerPlayer) { if (view == null || _cachedPets.Any((PetRecord p) => p.View == view)) { return; } object zdo = GetZdo(view); if (!IsPetZdo(zdo)) { return; } object member = GetMember(view, "gameObject"); PetRecord pet = new PetRecord { View = view, GameObject = member, Zdo = zdo, Name = GetZdoString(zdo, "ua.bohdan.decorativepet.name"), Owner = GetZdoString(zdo, "ua.bohdan.decorativepet.owner"), Prefab = GetZdoString(zdo, "ua.bohdan.decorativepet.prefab"), Follow = (GetZdoString(zdo, "ua.bohdan.decorativepet.follow") != "0"), IgnoreEnemies = (GetZdoString(zdo, "ua.bohdan.decorativepet.ignore_enemies") == "1"), OwnerPlayer = ownerPlayer, Character = GetComponent(member, FindType("Character")), Tameable = GetComponent(member, FindType("Tameable")), MonsterAI = GetComponent(member, FindType("MonsterAI")) }; if (pet.MonsterAI != null) { pet.AttackContainer = GetMember(pet.MonsterAI, "m_attacks"); pet.AttackList = pet.AttackContainer as IList; if (pet.AttackList != null) { pet.AttackEntries = pet.AttackList.Cast<object>().ToList(); } pet.SecondaryAttack = GetMember(pet.MonsterAI, "m_secondaryAttack"); pet.AttackFlags = (from f in pet.MonsterAI.GetType().GetFields(AccessTools.all) where f.FieldType == typeof(bool) && f.Name.IndexOf("attack", StringComparison.OrdinalIgnoreCase) >= 0 select f).ToDictionary((FieldInfo f) => f.Name, (FieldInfo f) => (bool)f.GetValue(pet.MonsterAI)); } _cachedPets.Add(pet); string name = (string.IsNullOrWhiteSpace(pet.Name) ? "Pet" : pet.Name); ApplyName(pet.GameObject, pet.Zdo, name); MakePassiveAndImmortal(pet); ApplyIgnoreMode(pet); ApplyNativeFollow(pet, pet.Follow); } private void ApplyPetBehaviour() { foreach (PetRecord item in _cachedPets.ToList()) { if (item.IgnoreEnemies) { ClearMonsterTarget(item); } if (item.Follow) { object obj = item.OwnerPlayer; if (obj == null) { obj = (item.OwnerPlayer = FindPlayerByName(item.Owner)); ApplyNativeFollow(item, follow: true); } if (obj != null) { TeleportIfTooFar(item, obj); } } } } private void MakePassiveAndImmortal(PetRecord pet) { if (pet != null && pet.GameObject != null && pet.Character != null) { InvokeCompatible(pet.Character, "SetTamed", true); object member = GetMember(pet.Character, "m_maxHealth"); if (member != null) { SetMember(pet.Character, "m_health", member); } } } private void ApplyIgnoreMode(PetRecord pet) { if (pet == null || pet.MonsterAI == null) { return; } if (pet.IgnoreEnemies) { IEnumerable<string> enumerable = pet.AttackFlags?.Keys; foreach (string item in enumerable ?? Enumerable.Empty<string>()) { SetMember(pet.MonsterAI, item, false); } SetMember(pet.MonsterAI, "m_attackPlayerObjects", false); SetMember(pet.MonsterAI, "m_attackPlayerObjectsWhenAlerted", false); SetMember(pet.MonsterAI, "m_targetCreature", null); SetMember(pet.MonsterAI, "m_targetPlayer", null); SetMember(pet.MonsterAI, "m_targetStatic", null); if (pet.AttackList != null) { pet.AttackList.Clear(); } else if (pet.AttackContainer != null) { Type type = pet.AttackContainer.GetType(); if (type.IsArray) { pet.EmptyAttackContainer = Array.CreateInstance(type.GetElementType(), 0); } else { pet.EmptyAttackContainer = null; } if (pet.EmptyAttackContainer != null) { SetMember(pet.MonsterAI, "m_attacks", pet.EmptyAttackContainer); } } SetMember(pet.MonsterAI, "m_secondaryAttack", null); InvokeCompatible(pet.MonsterAI, "SetAlerted", false); ClearMonsterTarget(pet); } else if (pet.AttackList != null && pet.AttackEntries != null && pet.AttackList.Count == 0) { RestoreAttackFlags(pet); foreach (object attackEntry in pet.AttackEntries) { pet.AttackList.Add(attackEntry); } SetMember(pet.MonsterAI, "m_secondaryAttack", pet.SecondaryAttack); } else if (!pet.IgnoreEnemies && pet.AttackContainer != null) { RestoreAttackFlags(pet); SetMember(pet.MonsterAI, "m_attacks", pet.AttackContainer); SetMember(pet.MonsterAI, "m_secondaryAttack", pet.SecondaryAttack); } } private static void RestoreAttackFlags(PetRecord pet) { if (pet == null || pet.MonsterAI == null || pet.AttackFlags == null) { return; } foreach (KeyValuePair<string, bool> attackFlag in pet.AttackFlags) { SetMember(pet.MonsterAI, attackFlag.Key, attackFlag.Value); } } private static void ClearMonsterTarget(PetRecord pet) { if (pet != null && pet.MonsterAI != null) { SetMember(pet.MonsterAI, "m_targetCreature", null); SetMember(pet.MonsterAI, "m_targetPlayer", null); SetMember(pet.MonsterAI, "m_targetStatic", null); SetMember(pet.MonsterAI, "m_targetZDO", null); SetMember(pet.MonsterAI, "m_alerted", false); InvokeCompatible(pet.MonsterAI, "SetAlerted", false); } } private void ApplyName(object gameObject, object zdo, string name) { if (gameObject != null) { object component = GetComponent(gameObject, FindType("Character")); if (component != null) { InvokeCompatible(component, "SetTamed", true); } Type componentType = FindType("Tameable"); object component2 = GetComponent(gameObject, componentType); if (component2 != null) { InvokeCompatible(component2, "Tame"); InvokeCompatible(component2, "SetName", name); SetMember(component2, "m_name", name); } object staticMember = GetStaticMember(FindType("ZDOVars"), "s_tamedName"); if (staticMember != null) { InvokeCompatible(zdo, "Set", staticMember, name); } } } private void ApplyNativeFollow(PetRecord pet, bool follow) { if (pet == null || pet.MonsterAI == null) { return; } object obj = null; if (follow) { if (pet.OwnerPlayer == null) { pet.OwnerPlayer = FindPlayerByName(pet.Owner); } obj = GetMember(pet.OwnerPlayer, "gameObject"); } InvokeCompatible(pet.MonsterAI, "SetFollowTarget", obj); } private void TeleportIfTooFar(PetRecord pet, object owner) { if (pet != null && pet.GameObject != null && owner != null) { object member; object member2; try { member = GetMember(pet.GameObject, "transform"); member2 = GetMember(owner, "transform"); } catch { return; } object position = GetPosition(member); object position2 = GetPosition(member2); if (position != null && position2 != null && VectorDistance(position, position2) >= Math.Max(_teleportDistance.Value, _followDistance.Value + 1f)) { object obj2 = OffsetPosition(position2, 1.5f, 0f, 0f); SetMember(member, "position", obj2); InvokeCompatible(pet.Zdo, "SetPosition", obj2); } } } private bool DestroyNetworkPet(PetRecord pet) { object netView = GetNetView(pet.GameObject); if (netView != null && TryInvokeCompatible(netView, "Destroy")) { return true; } object staticMember = GetStaticMember(FindType("ZDOMan"), "instance"); if (staticMember != null && TryInvokeCompatible(staticMember, "DestroyZDO", pet.Zdo)) { return true; } return false; } private static bool IsPetZdo(object zdo) { if (zdo != null) { return GetZdoString(zdo, "ua.bohdan.decorativepet.marker") == "1"; } return false; } private static bool IsPetComponent(object component) { if (component == null) { return false; } return IsPetZdo(GetZdo(GetMember(component, "m_nview") ?? GetNetView(GetMember(component, "gameObject")))); } private static object GetPetZdo(object component) { return GetZdo(GetMember(component, "m_nview") ?? GetNetView(GetMember(component, "gameObject"))); } private static string FoodText(object tameable) { object obj = InvokeCompatible(tameable, "GetFoodLevel") ?? GetMember(tameable, "m_foodLevel"); if (obj == null) { string text = Convert.ToString(InvokeCompatible(tameable, "GetStatusString"), CultureInfo.InvariantCulture) ?? ""; bool flag = text.IndexOf("hungry", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("голод", StringComparison.OrdinalIgnoreCase) >= 0; return LocalText("Їжа: " + (flag ? "░░░░░░░░░░ 0%" : "██████████ 100%"), "Food: " + (flag ? "░░░░░░░░░░ 0%" : "██████████ 100%")); } float num; try { num = Convert.ToSingle(obj, CultureInfo.InvariantCulture); } catch { return ""; } if (num > 1f) { object obj3 = GetMember(tameable, "m_foodLevelMax") ?? GetMember(tameable, "m_maxFood"); float num2 = 0f; try { num2 = ((obj3 == null) ? 0f : Convert.ToSingle(obj3, CultureInfo.InvariantCulture)); } catch { } num = ((num2 > 0f) ? (num / num2) : (num / 100f)); } num = Math.Max(0f, Math.Min(1f, num)); int num3 = (int)Math.Round(num * 10f); return "Food: " + new string('█', num3) + new string('░', 10 - num3) + " " + (int)Math.Round(num * 100f) + "%"; } private static string LocalText(string ukrainian, string english) { object staticMember = GetStaticMember(FindType("Localization"), "instance"); string text = Convert.ToString(GetMember(staticMember, "m_language"), CultureInfo.InvariantCulture); if (string.IsNullOrWhiteSpace(text)) { text = Convert.ToString(InvokeCompatible(staticMember, "GetSelectedLanguage"), CultureInfo.InvariantCulture); } if ((text ?? "").IndexOf("Ukrain", StringComparison.OrdinalIgnoreCase) < 0) { return english; } return ukrainian; } private static bool HasFoodInHand(object player) { if (player == null) { return false; } string[] array = new string[2] { "m_rightItem", "m_leftItem" }; foreach (string name in array) { object member = GetMember(GetMember(GetMember(player, name), "m_shared"), "m_food"); try { if (member != null && Convert.ToSingle(member, CultureInfo.InvariantCulture) > 0f) { return true; } } catch { } } return false; } private static bool IsIgnoredPetAI(object ai) { if (_instance == null || ai == null) { return false; } foreach (PetRecord cachedPet in _instance._cachedPets) { if (cachedPet.MonsterAI == ai) { return cachedPet.IgnoreEnemies; } } object petZdo = GetPetZdo(ai); if (IsPetZdo(petZdo)) { return GetZdoString(petZdo, "ua.bohdan.decorativepet.ignore_enemies") == "1"; } return false; } private static Type FindType(string name) { if (string.IsNullOrWhiteSpace(name)) { return null; } if (TypeCache.TryGetValue(name, out var value)) { return value; } Type type = AccessTools.TypeByName(name); if (type != null) { TypeCache[name] = type; return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { type = assembly.GetType(name, throwOnError: false); if (type != null) { TypeCache[name] = type; return type; } } catch { } } TypeCache[name] = null; return null; } private static object GetStaticMember(Type type, string name) { if (type == null) { return null; } FieldInfo fieldInfo = FindFieldNoLog(type, name, isStatic: true); if (fieldInfo != null) { return fieldInfo.GetValue(null); } return FindPropertyNoLog(type, name, isStatic: true)?.GetValue(null, null); } private static object GetMember(object instance, string name) { if (instance == null) { return null; } try { FieldInfo fieldInfo = FindFieldNoLog(instance.GetType(), name, isStatic: false); if (fieldInfo != null) { return fieldInfo.GetValue(instance); } return FindPropertyNoLog(instance.GetType(), name, isStatic: false)?.GetValue(instance, null); } catch { return null; } } private static bool SetMember(object instance, string name, object value) { if (instance == null) { return false; } try { FieldInfo fieldInfo = FindFieldNoLog(instance.GetType(), name, isStatic: false); if (fieldInfo != null) { fieldInfo.SetValue(instance, value); return true; } PropertyInfo propertyInfo = FindPropertyNoLog(instance.GetType(), name, isStatic: false); if (propertyInfo != null && propertyInfo.CanWrite) { propertyInfo.SetValue(instance, value, null); return true; } } catch { } return false; } private static FieldInfo FindFieldNoLog(Type type, string name, bool isStatic) { BindingFlags bindingAttr = (BindingFlags)(0x32 | (isStatic ? 8 : 4)); Type type2 = type; while (type2 != null) { FieldInfo field = type2.GetField(name, bindingAttr); if (field != null) { return field; } type2 = type2.BaseType; } return null; } private static PropertyInfo FindPropertyNoLog(Type type, string name, bool isStatic) { BindingFlags bindingAttr = (BindingFlags)(0x32 | (isStatic ? 8 : 4)); Type type2 = type; while (type2 != null) { PropertyInfo property = type2.GetProperty(name, bindingAttr); if (property != null) { return property; } type2 = type2.BaseType; } return null; } private static object GetComponent(object gameObject, Type componentType) { if (gameObject == null || componentType == null) { return null; } MethodInfo[] methods = gameObject.GetType().GetMethods(AccessTools.all); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name != "GetComponent") { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(Type)) { try { return methodInfo.Invoke(gameObject, new object[1] { componentType }); } catch { return null; } } } return null; } private static object GetNetView(object gameObject) { if (gameObject == null) { return null; } Type componentType = FindType("ZNetView"); return GetComponent(gameObject, componentType); } private static object GetZdo(object instance) { if (instance == null) { return null; } if (string.Equals(instance.GetType().Name, "ZDO", StringComparison.Ordinal)) { return instance; } return InvokeCompatible(instance, "GetZDO") ?? GetMember(instance, "m_zdo"); } private static object InvokeCompatible(object instance, string name, params object[] arguments) { if (instance == null) { return null; } foreach (MethodInfo item in from m in instance.GetType().GetMethods(AccessTools.all) where m.Name == name where m.GetParameters().Length == arguments.Length select m) { ParameterInfo[] parameters = item.GetParameters(); bool flag = true; for (int num = 0; num < parameters.Length; num++) { object obj = arguments[num]; if (obj != null && !parameters[num].ParameterType.IsInstanceOfType(obj) && (!parameters[num].ParameterType.IsValueType || !(parameters[num].ParameterType == obj.GetType()))) { flag = false; break; } } if (flag) { try { return item.Invoke(instance, arguments); } catch { } } } return null; } private static bool TryInvokeCompatible(object instance, string name, params object[] arguments) { if (instance == null) { return false; } foreach (MethodInfo item in from m in instance.GetType().GetMethods(AccessTools.all) where m.Name == name where m.GetParameters().Length == arguments.Length select m) { ParameterInfo[] parameters = item.GetParameters(); bool flag = true; for (int num = 0; num < parameters.Length; num++) { object obj = arguments[num]; if (obj != null && !parameters[num].ParameterType.IsInstanceOfType(obj) && (!parameters[num].ParameterType.IsValueType || !(parameters[num].ParameterType == obj.GetType()))) { flag = false; break; } } if (flag) { try { item.Invoke(instance, arguments); return true; } catch { } } } return false; } private static object GetDefault(Type type) { if (type == null || !type.IsValueType) { return null; } try { return Activator.CreateInstance(type); } catch { return null; } } private static string GetZdoString(object zdo, string key) { if (zdo == null) { return ""; } object obj = InvokeCompatible(zdo, "GetString", key, ""); if (obj == null) { obj = InvokeCompatible(zdo, "GetString", key); } if (obj != null) { return Convert.ToString(obj, CultureInfo.InvariantCulture); } return ""; } private static void SetZdoString(object zdo, string key, string value) { if (zdo != null) { InvokeCompatible(zdo, "Set", key, value ?? ""); } } private static object GetLocalPlayer() { return GetStaticMember(FindType("Player"), "m_localPlayer"); } private static object FindPlayerByName(string name) { if (string.IsNullOrWhiteSpace(name)) { return null; } object localPlayer = GetLocalPlayer(); if (string.Equals(GetPlayerName(localPlayer), name, StringComparison.Ordinal)) { return localPlayer; } Type type = FindType("Player"); if (type == null) { return null; } object obj = null; try { obj = type.GetMethods(AccessTools.all).FirstOrDefault((MethodInfo m) => m.Name == "GetAllPlayers" && m.IsStatic && m.GetParameters().Length == 0)?.Invoke(null, null) ?? GetStaticMember(type, "s_players") ?? GetStaticMember(type, "m_players"); } catch { return null; } if (!(obj is IEnumerable enumerable)) { return null; } foreach (object item in enumerable) { if (string.Equals(GetPlayerName(item), name, StringComparison.Ordinal)) { return item; } } return null; } private static string GetPlayerName(object player) { if (player == null) { return ""; } object obj = InvokeCompatible(player, "GetPlayerName") ?? GetMember(player, "m_name") ?? GetMember(player, "m_playerName"); if (obj != null) { return Convert.ToString(obj, CultureInfo.InvariantCulture); } return ""; } private static string GetLocalPlayerName() { return GetPlayerName(GetLocalPlayer()); } private static bool IsServer() { object staticMember = GetStaticMember(FindType("ZNet"), "instance"); if (staticMember == null) { return false; } object obj = InvokeCompatible(staticMember, "IsServer"); if (obj != null) { return Convert.ToBoolean(obj, CultureInfo.InvariantCulture); } return false; } private static object GetPosition(object transform) { return GetMember(transform, "position"); } private static object OffsetPosition(object position, float x, float y, float z) { Type type = FindType("UnityEngine.Vector3"); if (type == null || position == null) { return position; } try { float num = Convert.ToSingle(GetMember(position, "x"), CultureInfo.InvariantCulture); float num2 = Convert.ToSingle(GetMember(position, "y"), CultureInfo.InvariantCulture); float num3 = Convert.ToSingle(GetMember(position, "z"), CultureInfo.InvariantCulture); return type.GetConstructor(new Type[3] { typeof(float), typeof(float), typeof(float) })?.Invoke(new object[3] { num + x, num2 + y, num3 + z }) ?? position; } catch { return position; } } private static float VectorDistance(object first, object second) { Type type = FindType("UnityEngine.Vector3"); if (type == null) { return float.MaxValue; } MethodInfo methodInfo = (from m in type.GetMethods(AccessTools.all) where m.Name == "Distance" where m.GetParameters().Length == 2 select m).FirstOrDefault(); try { object obj = methodInfo?.Invoke(null, new object[2] { first, second }); return (obj == null) ? float.MaxValue : Convert.ToSingle(obj, CultureInfo.InvariantCulture); } catch { return float.MaxValue; } } private static object MoveTowards(object current, object target, float distance) { MethodInfo methodInfo = (from m in FindType("UnityEngine.Vector3")?.GetMethods(AccessTools.all) where m.Name == "MoveTowards" where m.GetParameters().Length == 3 select m).FirstOrDefault(); try { return methodInfo?.Invoke(null, new object[3] { current, target, distance }); } catch { return null; } } private static int StableHash(string value) { int num = 5381; int num2 = num; if (value == null) { return num; } for (int i = 0; i < value.Length && value[i] != 0; i += 2) { num = ((num << 5) + num) ^ value[i]; if (i == value.Length - 1 || value[i + 1] == '\0') { break; } num2 = ((num2 << 5) + num2) ^ value[i + 1]; } return num + num2 * 1566083941; } } }