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.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using AwayFromHome.Compat; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("AwayFromHome")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Keeps ranches breeding, smelters smelting, and outposts running while you're elsewhere in the world.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("AwayFromHome")] [assembly: AssemblyTitle("AwayFromHome")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.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; } } [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 ServerSync { [PublicAPI] public abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] public class SyncedConfigEntry(ConfigEntry sourceConfig) : OwnConfigEntryBase() { public readonly ConfigEntry 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; } } } public abstract class CustomSyncedValueBase { public object? LocalBaseValue; public readonly string Identifier; 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; 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; }; } } [PublicAPI] public sealed class CustomSyncedValue : 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; } [PublicAPI] public class ConfigSync { [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static class SnatchCurrentlyHandlingRPC { public static ZRpc? currentRpc; [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { isServer = __instance.IsServer(); foreach (ConfigSync configSync2 in configSyncs) { ZRoutedRpc.instance.Register(configSync2.Name + " ConfigSync", (Action)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()); } static void SendAdmin(List 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)); } } 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 CurrentList = new List(adminList.GetList()); while (true) { yield return (object)new WaitForSeconds(30f); if (!adminList.GetList().SequenceEqual(CurrentList)) { CurrentList = new List(adminList.GetList()); List 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 nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList(); SendAdmin(nonAdminPeer, isAdmin: false); SendAdmin(adminPeer, isAdmin: true); } } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterClientRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in configSyncs) { peer.m_rpc.Register(configSync.Name + " ConfigSync", (Action)configSync.RPC_FromServerConfigSync); } } } private class ParsedConfigs { public readonly Dictionary configValues = new Dictionary(); public readonly Dictionary customValues = new Dictionary(); } [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; } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { private class BufferingSocket : ZPlayFabSocket, ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List Package = new List(); 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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(ref Dictionary? __state, ZNet __instance, ZRpc rpc) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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(); } __state[Assembly.GetExecutingAssembly()] = bufferingSocket; } [HarmonyPostfix] private static void Postfix(Dictionary __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 entries = new List(); 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((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false); yield return ((MonoBehaviour)__instance).StartCoroutine(configSync.sendZPackage(new List { peer }, package)); } SendBufferedData(); } } } private class PackageEntry { public string section = null; public string key = null; public Type type = null; public object? value; } [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] private static class PreventSavingServerInfo { [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; } } [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] private static class PreventConfigRereadChangingValues { [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; } } private class InvalidDeserializationTypeException : Exception { public string expected = null; public string received = null; public string field = ""; } public static bool ProcessingServerUpdate; public readonly string Name; public string? DisplayName; public string? CurrentVersion; public string? MinimumRequiredVersion; public bool ModRequired = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet configSyncs; private readonly HashSet allConfigs = new HashSet(); private HashSet allCustomValues = new HashSet(); private static bool isServer; private static bool lockExempt; 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> configValueCache = new Dictionary>(); private readonly List> cacheExpirations = new List>(); private static long packageCounter; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { if (lockedConfig == null) { goto IL_0052; } num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag == true; } if (!num) { goto IL_0052; } int result = ((!lockExempt) ? 1 : 0); goto IL_0053; IL_0052: result = 0; goto IL_0053; IL_0053: 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; public event Action? SourceOfTruthChanged; private event Action? lockedConfigChanged; static ConfigSync() { ProcessingServerUpdate = false; configSyncs = new HashSet(); lockExempt = false; packageCounter = 0L; RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry AddConfigEntry(ConfigEntry configEntry) { OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry); SyncedConfigEntry syncedEntry = ownConfigEntryBase as SyncedConfigEntry; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry(configEntry); AccessTools.DeclaredField(typeof(ConfigDescription), "k__BackingField").SetValue(((ConfigEntryBase)configEntry).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry).Description.Tags ?? Array.Empty()).Concat(new SyncedConfigEntry[1] { syncedEntry }).ToArray()); configEntry.SettingChanged += delegate { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(ZRoutedRpc.Everybody, (ConfigEntryBase)configEntry); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry AddLockingConfigEntry(ConfigEntry lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry(lockingConfig); lockingConfig.SettingChanged += delegate { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Select((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(allCustomValues.OrderByDescending((CustomSyncedValueBase v) => v.Priority)); customValue.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(ZRoutedRpc.Everybody, 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: 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(delegate(KeyValuePair 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(); configValueCache[text2] = value; cacheExpirations.Add(new KeyValuePair(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((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 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 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 dictionary = allConfigs.Where((OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c); Dictionary dictionary2 = allCustomValues.ToDictionary((CustomSyncedValueBase c) => c.Identifier, (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 internal type " + ex.received + " for field " + ex.field + " struct " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + ex.expected)); continue; } OwnConfigEntryBase value2; if (text == "Internal") { CustomSyncedValueBase value; if (text2 == "serverversion") { if (obj?.ToString() != CurrentVersion) { Debug.LogWarning((object)("Received server version is not equal: server version = " + (obj?.ToString() ?? "null") + "; local version = " + (CurrentVersion ?? "unknown"))); } } else if (text2 == "lockexempt") { if (obj is bool flag) { lockExempt = flag; } } else if (dictionary2.TryGetValue(text2, out value)) { if ((text3 == "" && (!value.Type.IsValueType || Nullable.GetUnderlyingType(value.Type) != null)) || GetZPackageTypeString(value.Type) == text3) { parsedConfigs.customValues[value] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for internal value " + text2 + " for mod " + (DisplayName ?? Name) + ", expecting " + value.Type.AssemblyQualifiedName)); } } else if (dictionary.TryGetValue(text + "_" + text2, out value2)) { Type type2 = configType(value2.BaseConfig); if ((text3 == "" && (!type2.IsValueType || Nullable.GetUnderlyingType(type2) != null)) || GetZPackageTypeString(type2) == text3) { parsedConfigs.configValues[value2] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + type2.AssemblyQualifiedName)); } else { Debug.LogWarning((object)("Received unknown config entry " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ". This may happen if client and server versions of the mod do not match.")); } continue; } Debug.LogWarning((object)("Got invalid type " + text3 + ", abort reading of received configs")); return new ParsedConfigs(); } return parsedConfigs; } private static bool isWritableConfig(OwnConfigEntryBase config) { ConfigSync configSync = configSyncs.FirstOrDefault((ConfigSync cs) => cs.allConfigs.Contains(config)); if (configSync == null) { return true; } return configSync.IsSourceOfTruth || !config.SynchronizedConfig || config.LocalBaseValue == null || (!configSync.IsLocked && (config != configSync.lockedConfig || lockExempt)); } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { configAttribute(allConfig.BaseConfig).ReadOnly = !isWritableConfig(allConfig); } } private void resetConfigsFromServer() { ConfigFile val = null; bool saveOnConfigSet = false; foreach (OwnConfigEntryBase item in allConfigs.Where((OwnConfigEntryBase config) => config.LocalBaseValue != null)) { if (val == null) { val = item.BaseConfig.ConfigFile; saveOnConfigSet = val.SaveOnConfigSet; val.SaveOnConfigSet = false; } item.BaseConfig.BoxedValue = item.LocalBaseValue; item.LocalBaseValue = null; } if (val != null) { val.SaveOnConfigSet = saveOnConfigSet; } foreach (CustomSyncedValueBase item2 in allCustomValues.Where((CustomSyncedValueBase config) => config.LocalBaseValue != null)) { item2.BoxedValue = item2.LocalBaseValue; item2.LocalBaseValue = null; } lockedConfigChanged -= serverLockedSettingChanged; serverLockedSettingChanged(); } private IEnumerator distributeConfigToPeers(ZNetPeer peer, ZPackage package) { ZRoutedRpc rpc = ZRoutedRpc.instance; if (rpc == null) { yield break; } byte[] data = package.GetArray(); if (data != null && data.LongLength > 250000) { int fragments = (int)(1 + (data.LongLength - 1) / 250000); long packageIdentifier = ++packageCounter; int fragment = 0; while (fragment < fragments) { foreach (bool item in waitForQueue()) { yield return item; } if (peer.m_socket.IsConnected()) { ZPackage fragmentedPackage = new ZPackage(); fragmentedPackage.Write((byte)2); fragmentedPackage.Write(packageIdentifier); fragmentedPackage.Write(fragment); fragmentedPackage.Write(fragments); fragmentedPackage.Write(data.Skip(250000 * fragment).Take(250000).ToArray()); SendPackage(fragmentedPackage); if (fragment != fragments - 1) { yield return true; } int num = fragment + 1; fragment = num; continue; } break; } yield break; } foreach (bool item2 in waitForQueue()) { yield return item2; } SendPackage(package); void SendPackage(ZPackage pkg) { string text = Name + " ConfigSync"; if (isServer) { peer.m_rpc.Invoke(text, new object[1] { pkg }); } else { rpc.InvokeRoutedRPC(peer.m_server ? 0 : peer.m_uid, text, new object[1] { pkg }); } } IEnumerable waitForQueue() { float timeout = Time.time + 30f; while (peer.m_socket.GetSendQueueSize() > 20000) { if (Time.time > timeout) { Debug.Log((object)$"Disconnecting {peer.m_uid} after 30 seconds config sending timeout"); peer.m_rpc.Invoke("Error", new object[1] { (object)(ConnectionStatus)5 }); ZNet.instance.Disconnect(peer); break; } yield return false; } } } private IEnumerator sendZPackage(long target, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return Enumerable.Empty().GetEnumerator(); } List list = (List)AccessTools.DeclaredField(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance); if (target != ZRoutedRpc.Everybody) { list = list.Where((ZNetPeer p) => p.m_uid == target).ToList(); } return sendZPackage(list, package); } private IEnumerator sendZPackage(List peers, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { yield break; } byte[] rawData = package.GetArray(); if (rawData != null && rawData.LongLength > 10000) { ZPackage compressedPackage = new ZPackage(); compressedPackage.Write((byte)4); MemoryStream output = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(output, CompressionLevel.Optimal)) { deflateStream.Write(rawData, 0, rawData.Length); } compressedPackage.Write(output.ToArray()); package = compressedPackage; } List> writers = (from p in peers where p.IsReady() select distributeConfigToPeers(p, package)).ToList(); writers.RemoveAll((IEnumerator writer) => !writer.MoveNext()); while (writers.Count > 0) { yield return null; writers.RemoveAll((IEnumerator writer) => !writer.MoveNext()); } } private void Broadcast(long target, params ConfigEntryBase[] configs) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(configs); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private void Broadcast(long target, params CustomSyncedValueBase[] customValues) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(null, customValues); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private static OwnConfigEntryBase? configData(ConfigEntryBase config) { return config.Description.Tags?.OfType().SingleOrDefault(); } public static SyncedConfigEntry? ConfigData(ConfigEntry config) { return ((ConfigEntryBase)config).Description.Tags?.OfType>().SingleOrDefault(); } private static T configAttribute(ConfigEntryBase config) { return config.Description.Tags.OfType().First(); } private static Type configType(ConfigEntryBase config) { return configType(config.SettingType); } private static Type configType(Type type) { return type.IsEnum ? Enum.GetUnderlyingType(type) : type; } private static ZPackage ConfigsToPackage(IEnumerable? configs = null, IEnumerable? customValues = null, IEnumerable? packageEntries = null, bool partial = true) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown List list = configs?.Where((ConfigEntryBase config) => configData(config).SynchronizedConfig).ToList() ?? new List(); List list2 = customValues?.ToList() ?? new List(); ZPackage val = new ZPackage(); val.Write((byte)(partial ? 1 : 0)); val.Write(list.Count + list2.Count + (packageEntries?.Count() ?? 0)); foreach (PackageEntry item in packageEntries ?? Array.Empty()) { AddEntryToPackage(val, item); } foreach (CustomSyncedValueBase item2 in list2) { AddEntryToPackage(val, new PackageEntry { section = "Internal", key = item2.Identifier, type = item2.Type, value = item2.BoxedValue }); } foreach (ConfigEntryBase item3 in list) { AddEntryToPackage(val, new PackageEntry { section = item3.Definition.Section, key = item3.Definition.Key, type = configType(item3), value = item3.BoxedValue }); } return val; } private static void AddEntryToPackage(ZPackage package, PackageEntry entry) { package.Write(entry.section); package.Write(entry.key); package.Write((entry.value == null) ? "" : GetZPackageTypeString(entry.type)); AddValueToZPackage(package, entry.value); } private static string GetZPackageTypeString(Type type) { return type.AssemblyQualifiedName; } private static void AddValueToZPackage(ZPackage package, object? value) { Type type = value?.GetType(); if (value is Enum) { value = ((IConvertible)value).ToType(Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture); } else { if (value is ICollection collection) { package.Write(collection.Count); { foreach (object item in collection) { AddValueToZPackage(package, item); } return; } } if ((object)type != null && type.IsValueType && !type.IsPrimitive) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); package.Write(fields.Length); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { package.Write(GetZPackageTypeString(fieldInfo.FieldType)); AddValueToZPackage(package, fieldInfo.GetValue(value)); } return; } } ZRpc.Serialize(new object[1] { value }, ref package); } private static object ReadValueWithTypeFromZPackage(ZPackage package, Type type) { if ((object)type != null && type.IsValueType && !type.IsPrimitive && !type.IsEnum) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num = package.ReadInt(); if (num != fields.Length) { throw new InvalidDeserializationTypeException { received = $"(field count: {num})", expected = $"(field count: {fields.Length})" }; } object uninitializedObject = FormatterServices.GetUninitializedObject(type); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { string text = package.ReadString(); if (text != GetZPackageTypeString(fieldInfo.FieldType)) { throw new InvalidDeserializationTypeException { received = text, expected = GetZPackageTypeString(fieldInfo.FieldType), field = fieldInfo.Name }; } fieldInfo.SetValue(uninitializedObject, ReadValueWithTypeFromZPackage(package, fieldInfo.FieldType)); } return uninitializedObject; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { int num2 = package.ReadInt(); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); Type type2 = typeof(KeyValuePair<, >).MakeGenericType(type.GenericTypeArguments); FieldInfo field = type2.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type2.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int j = 0; j < num2; j++) { object obj = ReadValueWithTypeFromZPackage(package, type2); dictionary.Add(field.GetValue(obj), field2.GetValue(obj)); } return dictionary; } if (type != typeof(List) && type.IsGenericType) { Type type3 = typeof(ICollection<>).MakeGenericType(type.GenericTypeArguments[0]); if ((object)type3 != null && type3.IsAssignableFrom(type)) { int num3 = package.ReadInt(); object obj2 = Activator.CreateInstance(type); MethodInfo method = type3.GetMethod("Add"); for (int k = 0; k < num3; k++) { method.Invoke(obj2, new object[1] { ReadValueWithTypeFromZPackage(package, type.GenericTypeArguments[0]) }); } return obj2; } } ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.DeclaredField(typeof(ParameterInfo), "ClassImpl").SetValue(parameterInfo, type); List source = new List(); ZRpc.Deserialize(new ParameterInfo[2] { null, parameterInfo }, package, ref source); return source.First(); } } [PublicAPI] [HarmonyPatch] public class VersionCheck { private static readonly HashSet versionChecks; private static readonly Dictionary notProcessedNames; public string Name; private string? displayName; private string? currentVersion; private string? minimumRequiredVersion; public bool ModRequired = true; private string? ReceivedCurrentVersion; private string? ReceivedMinimumRequiredVersion; private readonly List ValidatedClients = new List(); private ConfigSync? ConfigSync; public string DisplayName { get { return displayName ?? Name; } set { displayName = value; } } public string CurrentVersion { get { return currentVersion ?? "0.0.0"; } set { currentVersion = value; } } public string MinimumRequiredVersion { get { return minimumRequiredVersion ?? (ModRequired ? CurrentVersion : "0.0.0"); } set { minimumRequiredVersion = value; } } private static void PatchServerSync() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown Patches patchInfo = PatchProcessor.GetPatchInfo((MethodBase)AccessTools.DeclaredMethod(typeof(ZNet), "Awake", (Type[])null, (Type[])null)); if (patchInfo != null && patchInfo.Postfixes.Count((Patch p) => p.PatchMethod.DeclaringType == typeof(ConfigSync.RegisterRPCPatch)) > 0) { return; } Harmony val = new Harmony("org.bepinex.helpers.ServerSync"); foreach (Type item in from t in typeof(ConfigSync).GetNestedTypes(BindingFlags.NonPublic).Concat(new Type[1] { typeof(VersionCheck) }) where t.IsClass select t) { val.PatchAll(item); } } static VersionCheck() { versionChecks = new HashSet(); notProcessedNames = new Dictionary(); typeof(ThreadingHelper).GetMethod("StartSyncInvoke").Invoke(ThreadingHelper.Instance, new object[1] { new Action(PatchServerSync) }); } public VersionCheck(string name) { Name = name; ModRequired = true; versionChecks.Add(this); } public VersionCheck(ConfigSync configSync) { ConfigSync = configSync; Name = ConfigSync.Name; versionChecks.Add(this); } public void Initialize() { ReceivedCurrentVersion = null; ReceivedMinimumRequiredVersion = null; if (ConfigSync != null) { Name = ConfigSync.Name; DisplayName = ConfigSync.DisplayName; CurrentVersion = ConfigSync.CurrentVersion; MinimumRequiredVersion = ConfigSync.MinimumRequiredVersion; ModRequired = ConfigSync.ModRequired; } } private bool IsVersionOk() { if (ReceivedMinimumRequiredVersion == null || ReceivedCurrentVersion == null) { return !ModRequired; } bool flag = new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion); bool flag2 = new Version(ReceivedCurrentVersion) >= new Version(MinimumRequiredVersion); return flag && flag2; } private string ErrorClient() { if (ReceivedMinimumRequiredVersion == null) { return DisplayName + " is not installed on the server."; } return (new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion)) ? (DisplayName + " may not be higher than version " + ReceivedCurrentVersion + ". You have version " + CurrentVersion + ".") : (DisplayName + " needs to be at least version " + ReceivedMinimumRequiredVersion + ". You have version " + CurrentVersion + "."); } private string ErrorServer(ZRpc rpc) { return "Disconnect: The client (" + rpc.GetSocket().GetHostName() + ") doesn't have the correct " + DisplayName + " version " + MinimumRequiredVersion; } private string Error(ZRpc? rpc = null) { return (rpc == null) ? ErrorClient() : ErrorServer(rpc); } private static VersionCheck[] GetFailedClient() { return versionChecks.Where((VersionCheck check) => !check.IsVersionOk()).ToArray(); } private static VersionCheck[] GetFailedServer(ZRpc rpc) { return versionChecks.Where((VersionCheck check) => check.ModRequired && !check.ValidatedClients.Contains(rpc)).ToArray(); } private static void Logout() { Game.instance.Logout(true, true); AccessTools.DeclaredField(typeof(ZNet), "m_connectionStatus").SetValue(null, (object)(ConnectionStatus)3); } private static void DisconnectClient(ZRpc rpc) { rpc.Invoke("Error", new object[1] { 3 }); } private static void CheckVersion(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, null); } private static void CheckVersion(ZRpc rpc, ZPackage pkg, Action? original) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); string text3 = pkg.ReadString(); bool flag = false; foreach (VersionCheck versionCheck in versionChecks) { if (!(text != versionCheck.Name)) { Debug.Log((object)("Received " + versionCheck.DisplayName + " version " + text3 + " and minimum version " + text2 + " from the " + (ZNet.instance.IsServer() ? "client" : "server") + ".")); versionCheck.ReceivedMinimumRequiredVersion = text2; versionCheck.ReceivedCurrentVersion = text3; if (ZNet.instance.IsServer() && versionCheck.IsVersionOk()) { versionCheck.ValidatedClients.Add(rpc); } flag = true; } } if (flag) { return; } pkg.SetPos(0); if (original != null) { original(rpc, pkg); if (pkg.GetPos() == 0) { notProcessedNames.Add(text, text3); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPrefix] private static bool RPC_PeerInfo(ZRpc rpc, ZNet __instance) { VersionCheck[] array = (__instance.IsServer() ? GetFailedServer(rpc) : GetFailedClient()); if (array.Length == 0) { return true; } VersionCheck[] array2 = array; foreach (VersionCheck versionCheck in array2) { Debug.LogWarning((object)versionCheck.Error(rpc)); } if (__instance.IsServer()) { DisconnectClient(rpc); } else { Logout(); } return false; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPrefix] private static void RegisterAndCheckVersion(ZNetPeer peer, ZNet __instance) { //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown notProcessedNames.Clear(); IDictionary dictionary = (IDictionary)typeof(ZRpc).GetField("m_functions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(peer.m_rpc); if (dictionary.Contains(StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck"))) { object obj = dictionary[StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck")]; Action action = (Action)obj.GetType().GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj); peer.m_rpc.Register("ServerSync VersionCheck", (Action)delegate(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, action); }); } else { peer.m_rpc.Register("ServerSync VersionCheck", (Action)CheckVersion); } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.Initialize(); if (versionCheck.ModRequired || __instance.IsServer()) { Debug.Log((object)("Sending " + versionCheck.DisplayName + " version " + versionCheck.CurrentVersion + " and minimum version " + versionCheck.MinimumRequiredVersion + " to the " + (__instance.IsServer() ? "client" : "server") + ".")); ZPackage val = new ZPackage(); val.Write(versionCheck.Name); val.Write(versionCheck.MinimumRequiredVersion); val.Write(versionCheck.CurrentVersion); peer.m_rpc.Invoke("ServerSync VersionCheck", new object[1] { val }); } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] [HarmonyPrefix] private static void RemoveDisconnected(ZNetPeer peer, ZNet __instance) { if (!__instance.IsServer()) { return; } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.ValidatedClients.Remove(peer.m_rpc); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] [HarmonyPostfix] private static void ShowConnectionError(FejdStartup __instance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) if (!__instance.m_connectionFailedPanel.activeSelf || (int)ZNet.GetConnectionStatus() != 3) { return; } bool flag = false; VersionCheck[] failedClient = GetFailedClient(); if (failedClient.Length != 0) { string text = string.Join("\n", failedClient.Select((VersionCheck check) => check.Error())); TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + text; flag = true; } foreach (KeyValuePair item in notProcessedNames.OrderBy((KeyValuePair kv) => kv.Key)) { if (!__instance.m_connectionFailedError.text.Contains(item.Key)) { TMP_Text connectionFailedError2 = __instance.m_connectionFailedError; connectionFailedError2.text = connectionFailedError2.text + "\nServer expects you to have " + item.Key + " (Version: " + item.Value + ") installed."; flag = true; } } if (flag) { RectTransform component = ((Component)__instance.m_connectionFailedPanel.transform.Find("Image")).GetComponent(); Vector2 sizeDelta = component.sizeDelta; sizeDelta.x = 675f; component.sizeDelta = sizeDelta; __instance.m_connectionFailedError.ForceMeshUpdate(false, false); float num = __instance.m_connectionFailedError.renderedHeight + 105f; RectTransform component2 = ((Component)((Component)component).transform.Find("ButtonOk")).GetComponent(); component2.anchoredPosition = new Vector2(component2.anchoredPosition.x, component2.anchoredPosition.y - (num - component.sizeDelta.y) / 2f); sizeDelta = component.sizeDelta; sizeDelta.y = num; component.sizeDelta = sizeDelta; } } } } namespace AwayFromHome { public static class AdminSites { public const string AdminKey = "afh_admin"; private static readonly Dictionary AdminByPlayerId = new Dictionary(); private static readonly List PeerTrace = new List(); public static void Refresh(IReadOnlyList sites) { //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) if (sites == null || sites.Count == 0 || !Configuration.adminBypass.Value) { return; } try { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { return; } if ((Object)(object)Player.m_localPlayer != (Object)null) { long playerID = Player.m_localPlayer.GetPlayerID(); if (playerID != 0) { AdminByPlayerId[playerID] = true; } } PeerTrace.Clear(); List peers = ZNet.instance.GetPeers(); if (peers != null) { foreach (ZNetPeer item in peers) { if (item == null) { PeerTrace.Add(""); continue; } string text = string.Format("uid={0} host='{1}' name='{2}'", item.m_uid, (item.m_socket != null) ? item.m_socket.GetHostName() : "", item.m_playerName); if (((ZDOID)(ref item.m_characterID)).IsNone()) { PeerTrace.Add(text + " -> NO characterID (character select?)"); continue; } ZDO zDO = ZDOMan.instance.GetZDO(item.m_characterID); if (zDO == null) { PeerTrace.Add($"{text} char={item.m_characterID} -> ZDO NOT FOUND"); continue; } if (!zDO.IsValid()) { PeerTrace.Add($"{text} char={item.m_characterID} -> ZDO INVALID"); continue; } long num = zDO.GetLong(ZDOVars.s_playerID, 0L); if (num == 0) { PeerTrace.Add($"{text} char={item.m_characterID} -> playerID field ABSENT/0"); continue; } bool flag = SiteRegistry.IsAuthorizedAdmin(item.m_uid); AdminByPlayerId[num] = flag; PeerTrace.Add($"{text} playerID={num} -> admin={flag}"); } } int num2 = 0; int num3 = 0; int num4 = 0; foreach (SiteRecord site in sites) { if (site != null && site.OwnerId != 0 && AdminByPlayerId.TryGetValue(site.OwnerId, out var value)) { num4++; if (value) { num3++; } if (Stamp(site.Id, value)) { num2++; } } } Diagnose(sites, num4, num3, num2); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: refreshing which sites belong to admins failed (non-fatal, the previous answer stands). Reason: {arg}"); } } public static bool IsAdminOwned(ZDOID siteId, long ownerId) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (!Configuration.adminBypass.Value) { return false; } if (ownerId != 0L && AdminByPlayerId.TryGetValue(ownerId, out var value)) { return value; } return IsAdminOwned(siteId); } public static bool IsAdminOwned(ZDOID siteId) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!Configuration.adminBypass.Value) { return false; } try { ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(siteId) : null); return val != null && val.IsValid() && val.GetBool("afh_admin", false); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: reading a site's admin flag failed (non-fatal, treated as not exempt). Reason: {arg}"); return false; } } private static bool Stamp(ZDOID siteId, bool isAdmin) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(siteId) : null); if (val == null || !val.IsValid()) { return false; } bool flag = val.GetBool("afh_admin", false); if (flag == isAdmin) { return isAdmin; } if (val.GetOwner() != ZDOMan.GetSessionID()) { return flag; } val.Set("afh_admin", isAdmin); bool flag2 = val.GetBool("afh_admin", false); if (flag2 != isAdmin) { Plugin.Log.LogWarning((object)$"AwayFromHome: wrote admin={isAdmin} to site {siteId} and read back {flag2} - the ZDO refused the write."); return false; } if (Configuration.verboseLogging.Value) { Plugin.Log.LogInfo((object)string.Format("AwayFromHome: site {0} is {1} flagged as admin-owned, so it is {2} MaxSitesPerPlayer.", siteId, isAdmin ? "now" : "no longer", isAdmin ? "exempt from" : "subject to")); } return isAdmin; } private static void Diagnose(IReadOnlyList sites, int matched, int exempt, int wrote) { if (!Configuration.verboseLogging.Value) { return; } try { Dictionary dictionary = new Dictionary(); foreach (SiteRecord site in sites) { if (site != null) { dictionary.TryGetValue(site.OwnerId, out var value); dictionary[site.OwnerId] = value + 1; } } List list = new List(); foreach (KeyValuePair item in dictionary) { bool value2; bool flag = AdminByPlayerId.TryGetValue(item.Key, out value2); list.Add(string.Format("{0} x{1} ({2})", item.Key, item.Value, flag ? (value2 ? "connected admin" : "connected, NOT admin") : "no connected peer")); } Plugin.Log.LogInfo((object)(string.Format("AwayFromHome [admin-bypass diag]: {0} site(s); peers=[{1}]; ", sites.Count, string.Join(" | ", PeerTrace)) + string.Format("owners=[{0}]; matched={1}, exempt={2}, flag-written-this-pass={3}.", string.Join(" | ", list), matched, exempt, wrote))); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: admin-bypass diagnostics failed (non-fatal). Reason: {arg}"); } } } [HarmonyPatch] public static class Commands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; public static ConsoleEvent <>9__0_1; public static ConsoleEvent <>9__0_2; public static ConsoleEvent <>9__0_3; public static ConsoleEvent <>9__0_4; public static Func <>9__4_0; internal void b__0_0(ConsoleEventArgs args) { RunList(args); } internal void b__0_1(ConsoleEventArgs args) { RunStatus(args); } internal void b__0_2(ConsoleEventArgs args) { RunAdminList(args); } internal void b__0_3(ConsoleEventArgs args) { RunAdminRemove(args); } internal void b__0_4(ConsoleEventArgs args) { RunDiag(args); } internal unsafe string b__4_0(PieceCategory c) { return ((object)(*(PieceCategory*)(&c))/*cast due to .constrained prefix*/).ToString(); } } public static void OnZNetStart() { //IL_0034: 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown try { object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { RunList(args); }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("afh_list", "lists the Keeper Stones you have placed.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>c.<>9__0_1; if (obj2 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { RunStatus(args); }; <>c.<>9__0_1 = val2; obj2 = (object)val2; } new ConsoleCommand("afh_status", "shows what the keeper is doing right now.", (ConsoleEvent)obj2, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj3 = <>c.<>9__0_2; if (obj3 == null) { ConsoleEvent val3 = delegate(ConsoleEventArgs args) { RunAdminList(args); }; <>c.<>9__0_2 = val3; obj3 = (object)val3; } new ConsoleCommand("afh_admin_list", "admin only - lists every Keeper Stone placed by every player on this server.", (ConsoleEvent)obj3, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj4 = <>c.<>9__0_3; if (obj4 == null) { ConsoleEvent val4 = delegate(ConsoleEventArgs args) { RunAdminRemove(args); }; <>c.<>9__0_3 = val4; obj4 = (object)val4; } new ConsoleCommand("afh_admin_remove", " - admin only. Removes ANY player's Keeper Stone by its number in afh_admin_list, including one you cannot reach.", (ConsoleEvent)obj4, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj5 = <>c.<>9__0_4; if (obj5 == null) { ConsoleEvent val5 = delegate(ConsoleEventArgs args) { RunDiag(args); }; <>c.<>9__0_4 = val5; obj5 = (object)val5; } new ConsoleCommand("afh_diag", "dumps why the Keeper Stone is or is not showing in the hammer's build menu.", (ConsoleEvent)obj5, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); Plugin.Log.LogInfo((object)"AwayFromHome: console commands afh_list / afh_status / afh_diag / afh_admin_list / afh_admin_remove registered."); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: could not register console commands. Reason: {arg}"); } } private static void Say(ConsoleEventArgs args, string msg) { try { Terminal context = args.Context; if (context != null) { context.AddString(msg); } } catch { } Plugin.Log.LogInfo((object)("AwayFromHome: " + msg)); } private static void RunList(ConsoleEventArgs args) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) IReadOnlyList mySites = SiteRegistry.GetMySites(); if (mySites.Count == 0) { Say(args, "You have no Keeper Stones standing. Build one with the hammer at the ranch or workshop you want kept running."); return; } Say(args, $"{mySites.Count} Keeper Stone(s):"); foreach (SiteRecord item in mySites) { string text = ((KeeperManager.ActiveSiteId == item.Id) ? (" <- currently " + KeeperManager.ActivePhase) : ""); Say(args, " " + item.Label + text); } } private static void RunStatus(ConsoleEventArgs args) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!Configuration.enabled.Value) { Say(args, "Away From Home is disabled (server setting)."); return; } Say(args, (KeeperManager.ActiveSiteId == ZDOID.None) ? "Idle - no site currently held (nothing standing, or between rotation passes)." : ("Holding " + KeeperManager.ActiveSiteLabel + " (owner: " + KeeperManager.ActiveSiteOwner + "): " + KeeperManager.ActivePhase + ".")); if (!string.IsNullOrEmpty(SiteRegistry.LastReply)) { Say(args, "Last server reply: " + (SiteRegistry.LastReplyOk ? "OK" : "FAILED") + " - " + SiteRegistry.LastReply); } } private unsafe static void RunDiag(ConsoleEventArgs args) { //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected I4, but got Unknown //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) try { Say(args, "--- AwayFromHome diagnostics ---"); GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab("AFH_KeeperStone") : null); Say(args, string.Format("ZNetScene knows '{0}': {1} (hash {2})", "AFH_KeeperStone", ((Object)(object)val != (Object)null) ? "YES" : "NO", KeeperStone.PrefabHash)); if ((Object)(object)val == (Object)null) { Say(args, "-> registration never happened. Check the BepInEx log for an AwayFromHome error at startup."); return; } Piece p = val.GetComponent(); if ((Object)(object)p == (Object)null) { Say(args, "-> the prefab has NO Piece component. That alone would hide it."); return; } Say(args, string.Format("Piece: name='{0}' enabled={1} category={2} ({3}) icon={4} station={5}", p.m_name, p.m_enabled, p.m_category, (int)p.m_category, ((Object)(object)p.m_icon != (Object)null) ? "set" : "NULL", ((Object)(object)p.m_craftingStation != (Object)null) ? ((Object)p.m_craftingStation).name : "none")); Say(args, $"Requirements: {((p.m_resources != null) ? p.m_resources.Length : 0)}"); if (p.m_resources != null) { Requirement[] resources = p.m_resources; foreach (Requirement val2 in resources) { string arg = (((Object)(object)val2?.m_resItem != (Object)null) ? ((Object)val2.m_resItem).name : "NULL"); Say(args, $" {arg} x{val2?.m_amount}"); } } MeshFilter componentInChildren = val.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { Say(args, $"Renderer xform: pos={((Component)componentInChildren).transform.localPosition} rot={((Component)componentInChildren).transform.localEulerAngles} scale={((Component)componentInChildren).transform.localScale}"); Say(args, $"Root scale: {val.transform.localScale}"); if ((Object)(object)componentInChildren.sharedMesh != (Object)null) { string name = ((Object)componentInChildren.sharedMesh).name; object arg2 = componentInChildren.sharedMesh.vertexCount; Bounds bounds = componentInChildren.sharedMesh.bounds; Say(args, $"Mesh '{name}': {arg2} verts, bounds size={((Bounds)(ref bounds)).size}"); } else { Say(args, "Mesh: NULL - the bundle mesh never got assigned."); } } BoxCollider componentInChildren2 = val.GetComponentInChildren(true); if ((Object)(object)componentInChildren2 != (Object)null) { Say(args, $"Collider: center={componentInChildren2.center} size={componentInChildren2.size} xformScale={((Component)componentInChildren2).transform.localScale}"); } GameObject val3 = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab("Hammer") : null); PieceTable val4 = ((!((Object)(object)val3 != (Object)null)) ? null : val3.GetComponent()?.m_itemData?.m_shared?.m_buildPieces); Say(args, "Hammer PieceTable: " + (((Object)(object)val4 != (Object)null) ? "found" : "NOT FOUND")); if ((Object)(object)val4 != (Object)null) { Say(args, string.Format(" m_pieces count={0}, contains our prefab: {1}", val4.m_pieces.Count, val4.m_pieces.Contains(val) ? "YES" : "NO")); Say(args, " m_categories: " + string.Join(", ", val4.m_categories.Select((PieceCategory c) => ((object)(*(PieceCategory*)(&c))/*cast due to .constrained prefix*/).ToString()).ToArray())); Say(args, $" m_availablePieces buckets={val4.m_availablePieces.Count}"); for (int num = 0; num < val4.m_availablePieces.Count; num++) { bool flag = val4.m_availablePieces[num].Any((Piece x) => (Object)(object)x == (Object)(object)p); Say(args, string.Format(" [{0}] {1}: {2} piece(s){3}", num, (object)(PieceCategory)num, val4.m_availablePieces[num].Count, flag ? " <-- OURS IS HERE" : "")); } } if ((Object)(object)Player.m_localPlayer != (Object)null) { HashSet knownRecipes = Player.m_localPlayer.m_knownRecipes; Say(args, "Player knows recipe '" + p.m_name + "': " + ((knownRecipes != null && knownRecipes.Contains(p.m_name)) ? "YES" : "NO")); Say(args, $"HaveRequirements(IsKnown): {Player.m_localPlayer.HaveRequirements(p, (RequirementMode)1)}"); Say(args, $"HaveRequirements(CanBuild): {Player.m_localPlayer.HaveRequirements(p, (RequirementMode)0)}"); HashSet knownMaterial = Player.m_localPlayer.m_knownMaterial; if (p.m_resources == null) { return; } Requirement[] resources2 = p.m_resources; foreach (Requirement val5 in resources2) { if (!((Object)(object)val5?.m_resItem == (Object)null)) { string name2 = val5.m_resItem.m_itemData.m_shared.m_name; Say(args, " material known '" + name2 + "': " + ((knownMaterial != null && knownMaterial.Contains(name2)) ? "YES" : "NO")); } } } else { Say(args, "No local player - run this from inside a game for the recipe checks."); } } catch (Exception ex) { Say(args, "diagnostics threw: " + ex.Message); Plugin.Log.LogError((object)$"AwayFromHome: afh_diag failed. Reason: {ex}"); } } private static void RunAdminList(ConsoleEventArgs args) { if (!Configuration.IsAdmin) { Say(args, "Admins only."); return; } IReadOnlyList allKnownSites = SiteRegistry.GetAllKnownSites(); if (allKnownSites.Count == 0) { Say(args, "No Keeper Stones standing anywhere on this server."); return; } Say(args, $"{allKnownSites.Count} Keeper Stone(s) server-wide:"); for (int i = 0; i < allKnownSites.Count; i++) { SiteRecord siteRecord = allKnownSites[i]; string arg = (string.IsNullOrEmpty(siteRecord.OwnerName) ? "unknown" : siteRecord.OwnerName); Say(args, $" [{i}] {arg}: {siteRecord.Label}"); } Say(args, "Remove one with afh_admin_remove ."); } private static void RunAdminRemove(ConsoleEventArgs args) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) if (!Configuration.IsAdmin) { Say(args, "Admins only."); return; } if (args.Length < 2) { Say(args, "Usage: afh_admin_remove . See afh_admin_list."); return; } if (!int.TryParse(args[1], out var result)) { Say(args, "'" + args[1] + "' is not a number. See afh_admin_list."); return; } IReadOnlyList allKnownSites = SiteRegistry.GetAllKnownSites(); if (result < 0 || result >= allKnownSites.Count) { Say(args, $"No stone at index {result} - there are {allKnownSites.Count}. Re-run afh_admin_list."); return; } SiteRecord siteRecord = allKnownSites[result]; SiteRegistry.RequestAdminRemove(siteRecord.Id); Say(args, "Requested removal of the stone at " + siteRecord.Label + "."); } } public static class Configuration { public static ConfigSync configSync = new ConfigSync("wubarrk.AwayFromHome") { DisplayName = "Away From Home", CurrentVersion = "1.0.0", MinimumRequiredVersion = "1.0.0" }; public static ConfigEntry lockConfiguration; public static ConfigEntry enabled; public static ConfigEntry ring; public static ConfigEntry scanIntervalSeconds; public static ConfigEntry dwellSeconds; public static ConfigEntry minimumCycleSeconds; public static ConfigEntry settleTimeoutSeconds; public static ConfigEntry settleRadius; public static ConfigEntry claimIntervalSeconds; public static ConfigEntry maxSitesPerPlayer; public static ConfigEntry adminBypass; public static ConfigEntry livestockLeashMeters; public static ConfigEntry maxLeashMeters; public static ConfigEntry autoFeed; public static ConfigEntry restockProduction; public static ConfigEntry productionReachMeters; public static ConfigEntry creditOfflineProduction; public static ConfigEntry allowRemoteSites; public static ConfigEntry verboseLogging; public static ConfigEntry menuKey; public static ConfigEntry uiGoldColour; public static bool IsAdmin => configSync.IsAdmin; public static void Init(ConfigFile config) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Expected O, but got Unknown //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Expected O, but got Unknown //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Expected O, but got Unknown //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Expected O, but got Unknown //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Expected O, but got Unknown //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Expected O, but got Unknown //IL_0463: Unknown result type (might be due to invalid IL or missing references) lockConfiguration = config.Bind("0 - Server Sync", "LockConfiguration", true, "If true (default), every setting below is locked to the server's value for anyone who is not an admin - the whole point of making this mod server-authoritative. Turn off only for local testing."); configSync.AddLockingConfigEntry(lockConfiguration); enabled = config.Bind("1 - General", "Enabled", true, "Master switch. If false, no sites are kept alive - the mod is completely inert."); configSync.AddConfigEntry(enabled); ring = config.Bind("1 - General", "Ring", 1, new ConfigDescription("How many zones out from a site's centre get loaded and claimed, per axis. 1 = 3x3 zones (192m across), which covers any sane pen or workshop. 2 = 5x5 (320m). Clamped - a wider ring is a guaranteed hitch on any hardware.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 2), Array.Empty())); configSync.AddConfigEntry(ring); scanIntervalSeconds = config.Bind("1 - General", "StoneScanIntervalSeconds", 60f, new ConfigDescription("How often the server re-scans the world for standing Keeper Stones. Placing or destroying one triggers an immediate re-scan anyway, so this only catches changes nothing told the server about - a stone destroyed by a raid, or a world edited offline. The scan walks the whole ZDO index, so on a very large world do not set it aggressively low.", (AcceptableValueBase)(object)new AcceptableValueRange(10f, 600f), Array.Empty())); configSync.AddConfigEntry(scanIntervalSeconds); dwellSeconds = config.Bind("2 - Rotation", "DwellSeconds", 180f, new ConfigDescription("How long the keeper holds each site alive per visit, after it settles. Long enough for a smelter/kiln/spinning wheel to drain its full backlog (their production accumulator resets and starts draining the instant the site is owned and loaded - one natural tick is enough) and for a few real taming/love-point ticks to land for animals (those genuinely need loaded+owned wall-clock time, not just a visit). Very small pens/workshops need less; ranches you actually want breeding faster benefit from more.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 300f), Array.Empty())); configSync.AddConfigEntry(dwellSeconds); minimumCycleSeconds = config.Bind("2 - Rotation", "MinimumCycleSeconds", 600f, new ConfigDescription("How long a full rotation must take, at minimum. If a lap of every site finishes sooner, the keeper waits out the difference before starting the next one. This is a SAFETY dial, not a performance one. Each visit loads a site and then unloads it again, and every one of those cycles re-instantiates the buildings and re-seats the animals from their saved positions - which is a chance for a creature standing against a fence to be resolved onto the wrong side of it. Left unpaced, three sites are each loaded and unloaded roughly once a minute, about a thousand times a day, and those chances add up: penned animals drift out over time. Fewer, longer visits give the same loaded wall-clock time with a fraction of the churn. The cap on how high this can go is the smelter forfeiture window - keep the whole cycle under ~55 minutes.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 3000f), Array.Empty())); configSync.AddConfigEntry(minimumCycleSeconds); settleTimeoutSeconds = config.Bind("2 - Rotation", "SettleTimeoutSeconds", 20f, "How long the keeper waits for a site's terrain/objects to finish streaming in before giving up on that visit and moving to the next site. A site that times out is retried next rotation - nothing is lost, the visit is just skipped once."); configSync.AddConfigEntry(settleTimeoutSeconds); settleRadius = config.Bind("2 - Rotation", "SettleCheckRadiusMeters", 100f, "Radius, in metres, checked for queued terrain-mesh rebuilds when deciding a site has finished loading. Matches the anchor's own settle test."); configSync.AddConfigEntry(settleRadius); claimIntervalSeconds = config.Bind("2 - Rotation", "ReclaimIntervalSeconds", 5f, "While a site is held, how often (seconds) the keeper re-scans for newly-unowned ZDOs in range and claims them. Claims are polite: a ZDO already owned by a real player, or by another keeper, is never touched."); configSync.AddConfigEntry(claimIntervalSeconds); maxSitesPerPlayer = config.Bind("1 - General", "MaxSitesPerPlayer", 3, new ConfigDescription("How many Keeper Stones one character may have standing at once. 0 = unlimited. This is the mod's real economy: every extra site lengthens the rotation for EVERYONE on the server, and a cycle slower than ~55 minutes starts costing smelters production between visits, so an unlimited server is one player away from making the feature useless for the rest. Enforced twice - the hammer refuses to place past the limit, and the server independently tends only this many per character, so a client running an edited config gains nothing.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 50), Array.Empty())); configSync.AddConfigEntry(maxSitesPerPlayer); adminBypass = config.Bind("1 - General", "AdminBypass", true, "Whether server admins are exempt from MaxSitesPerPlayer. On by default, matching the rest of this stable - an admin is the person who SETS the economy and routinely needs stones the limit would refuse (test sites, a server hub, a build they are fixing for someone else). This is enforced in both places the cap is: the hammer stops refusing placements, and the server tends every one of an admin's stones instead of the first few. Turn it off for a server where admins are expected to live under the same rule as everyone else. Note the honest limit of the mechanism: which stones are exempt is remembered ON THE STONE, so it survives the admin logging off - which also means a determined client running a patched assembly could stamp its own. MaxSitesPerPlayer is a courtesy dial for keeping the rotation short, not a security boundary, and never was."); configSync.AddConfigEntry(adminBypass); livestockLeashMeters = config.Bind("2 - Rotation", "LivestockLeashMeters", 12f, new ConfigDescription("The leash a Keeper Stone starts with, in metres, before anyone sets one on it. A new stone starts as a CIRCLE of this radius; its owner can change it to a square or a rectangle at the stone, which is what pens that are not round need. Either way it is centred on the stone, which is why the stone belongs in the MIDDLE of the pen: an animal is judged by its distance from the stone, so an off-centre stone means the far corner of the pen is outside the leash and its occupants get walked back from it forever. 0 means new stones start unleashed. This is only the starting value - each stone carries its own radius, set by its owner at the stone, and MaxLeashMeters is the ceiling. The leash exists because a held site has NO PLAYER in it: creature AI still runs, but with nobody to orient on, animals wander continuously and lean on fence colliders for minutes at a time until they penetrate them - drifts of 80-100m in a single visit have been measured.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 200f), Array.Empty())); configSync.AddConfigEntry(livestockLeashMeters); maxLeashMeters = config.Bind("2 - Rotation", "MaxLeashMeters", 40f, new ConfigDescription("The largest leash any player may set on their own Keeper Stone, measured as distance FROM THE STONE. This is the dial that actually binds, and it is deliberately SEPARATE from LivestockLeashMeters: folding the two together looked tidy and was wrong, because it made the starting value the ceiling as well, so anybody whose pen was larger than the default could not set a leash big enough to fit it and their animals were hauled back from their own fence line forever. Lower it to rein in every site at once - existing stones are clamped to it on read, so it takes effect immediately without editing a single stone. Note what this means for the square and rectangular pens: it bounds each half-extent, so a 40 here allows a pen 80m across, whose CORNER sits about 57m from the stone. That is deliberate rather than an oversight - clamping the corner instead would mean the size a player set and the pen they got were different numbers, which is a far worse thing for them to discover. Keep it comfortably larger than the biggest pen on the server and comfortably smaller than the distance an escaped animal reaches, since everything inside the outline is territory the keeper will not correct.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 200f), Array.Empty())); configSync.AddConfigEntry(maxLeashMeters); autoFeed = config.Bind("1 - General", "AutoFeed", true, new ConfigDescription("Whether a stocked Keeper Stone puts food out for the animals around it. Valheim only advances taming and breeding while an animal is FED, so on an unattended site this is not a convenience on top of the keeper - it is the thing that lets taming actually finish rather than sitting at the same percentage forever. The stone places ONE real serving on the ground in front of itself and replaces it once it has been eaten, because the game's animals can only ever find food as a physical item in the world; there is no way to make them eat out of a chest, and pretending otherwise would mean inventing meals that never happened. Nothing is spent unless a kept animal within the leash is genuinely hungry, and nothing is put out while a site is still loading. Turn this off to leave feeding entirely to the player, or to another mod.", (AcceptableValueBase)null, Array.Empty())); configSync.AddConfigEntry(autoFeed); restockProduction = config.Bind("1 - General", "RestockProduction", true, new ConfigDescription("Whether a stocked Keeper Stone reloads the smelters, kilns, blast furnaces, windmills, spinning wheels and eitr refineries around it from its own six slots. This is the other half of CreditOfflineProduction and the two are only worth much together: that one hands a furnace back the time a frozen server clock owed it, so it genuinely burns through its load while nobody is there - and then stops, because nobody is there to reload it. A full hopper is about seventeen minutes of a rotation that can run for days. The stone walks exactly the path a player's hand walks (the same allowed-item check, then vanilla's own add-ore/add-fuel), so nothing is created, nothing skips a validation, and a furnace that would refuse an item from you refuses it from the stone. While AutoFeed is on, food that animals near the stone eat is never spent this way, so a lox pen beside a windmill does not get its barley ground into flour - turning AutoFeed off makes that stone a pure supply depot and lifts the reservation, which is the right reading of 'this stone does not feed animals'. Turn this off to leave restocking entirely to the player, or to another mod.", (AcceptableValueBase)null, Array.Empty())); configSync.AddConfigEntry(restockProduction); productionReachMeters = config.Bind("1 - General", "ProductionReachMeters", 24f, new ConfigDescription("How far from a Keeper Stone its six slots will reload production pieces, in metres. 0 switches restocking off by radius alone. Deliberately much smaller than a site (which is 192m across) and deliberately NOT tied to the leash: the leash is a pen for animals and this is an arm's reach for a workshop, and a player who wants a big pen has no reason to also want their stone feeding a neighbour's furnaces on the far side of the site. Capped below one zone (64m) because the scan for producers only ever looks at the stone's own zone and its eight neighbours, so a larger number here would silently find nothing beyond that.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 48f), Array.Empty())); configSync.AddConfigEntry(productionReachMeters); creditOfflineProduction = config.Bind("2 - Rotation", "CreditOfflineProduction", true, "Whether smelters, kilns, blast furnaces, spinning wheels and windmills at a kept site are given the time a DEDICATED SERVER'S FROZEN CLOCK owed them. Valheim's server stops advancing world time entirely while no players are connected (ZNet.UpdateNetTime returns early on zero players), and every one of those machines measures its progress against that clock - so on an empty server they do not advance by a single second no matter who owns them or how long the keeper holds the site. With this on, each machine is given the real elapsed time since the keeper last tended it, and vanilla's own production code does the rest under its own rules, including its one-hour-per-gap ceiling. Nothing global is touched: the world clock, day/night and everything else stay exactly as Valheim intends. Turn this off for strictly vanilla timing, and accept that production stops whenever the server is empty. Has no effect on a server with players on it, where the clock runs normally and the correction computes to zero by itself."); configSync.AddConfigEntry(creditOfflineProduction); allowRemoteSites = config.Bind("3 - Dedicated Server", "AllowRemoteSites", true, "Server-side authority. If this client is a pure client of a DEDICATED server (not the host), sites more than one active-area away need the server to stream their sectors on request. Set false on the server to refuse that streaming entirely - every connected client's Away From Home then only reaches sites inside their own normal view range, same as vanilla."); configSync.AddConfigEntry(allowRemoteSites); verboseLogging = config.Bind("9 - Debug", "VerboseLogging", false, "Logs every settle/claim/hold step at Info level instead of just rotation summaries. Useful for diagnosing a site that never seems to settle."); configSync.AddConfigEntry(verboseLogging); menuKey = config.Bind("8 - Menu", "MenuKey", (KeyCode)288, "Opens/closes the ADMIN panel - every Keeper Stone on the server, with the power to remove any of them. Does nothing if you are not an admin: players mark a site by building a Keeper Stone and read it by looking at it, so there is no player menu to open. Purely local - never locked, never synced."); uiGoldColour = config.Bind("8 - Menu", "MenuAccentColour", new Color(0.8f, 0.62f, 0.26f, 1f), "The menu's metal accent colour. Purely local cosmetics."); } } public static class IconRenderer { private const int Layer = 30; private const int Size = 128; private const float MinCoverage = 0.02f; private const float MinLuminance = 0.02f; public static float LastCoverage { get; private set; } public static float LastLuminance { get; private set; } public static string LastPath { get; private set; } = "not attempted"; public static Sprite Render(Mesh mesh, Material material) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 if ((Object)(object)mesh == (Object)null) { LastPath = "no mesh"; return null; } if ((int)SystemInfo.graphicsDeviceType == 4) { LastPath = "headless - skipped"; return null; } Sprite val = TryRender(mesh, material, "piece material"); if ((Object)(object)val != (Object)null) { return val; } Material val2 = BuildUnlitMaterial(material); if ((Object)(object)val2 != (Object)null) { val = TryRender(mesh, val2, "unlit fallback"); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static Material BuildUnlitMaterial(Material source) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown string[] array = new string[4] { "Unlit/Texture", "Legacy Shaders/Diffuse", "Sprites/Default", "Standard" }; foreach (string text in array) { Shader val = Shader.Find(text); if ((Object)(object)val == (Object)null) { continue; } Material val2 = new Material(val) { name = "AFH_IconFallback" }; if ((Object)(object)source != (Object)null) { string[] array2 = new string[4] { "_MainTex", "_BaseMap", "_MainTexture", "_BaseColorTexture" }; foreach (string text2 in array2) { if (!source.HasProperty(text2)) { continue; } Texture texture = source.GetTexture(text2); if (!((Object)(object)texture == (Object)null)) { if (val2.HasProperty("_MainTex")) { val2.SetTexture("_MainTex", texture); } else if (val2.HasProperty("_BaseMap")) { val2.SetTexture("_BaseMap", texture); } break; } } } return val2; } return null; } private static Sprite TryRender(Mesh mesh, Material material, string label) { //IL_002d: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)material == (Object)null) { return null; } GameObject val = null; GameObject val2 = null; GameObject val3 = null; RenderTexture val4 = null; RenderTexture active = RenderTexture.active; Texture2D val5 = null; Texture2D val6 = null; try { Bounds bounds = mesh.bounds; Vector3 extents = ((Bounds)(ref bounds)).extents; float num = Mathf.Max(((Vector3)(ref extents)).magnitude, 0.01f); val2 = new GameObject("AFH_IconModel") { layer = 30 }; val2.AddComponent().sharedMesh = mesh; MeshRenderer val7 = val2.AddComponent(); ((Renderer)val7).sharedMaterial = material; ((Renderer)val7).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val7).receiveShadows = false; val2.transform.position = new Vector3(0f, 20000f, 0f) - ((Bounds)(ref bounds)).center; val2.transform.rotation = Quaternion.Euler(0f, 30f, 0f); val3 = new GameObject("AFH_IconLight") { layer = 30 }; Light val8 = val3.AddComponent(); val8.type = (LightType)1; val8.intensity = 1.25f; val8.cullingMask = 1073741824; val3.transform.rotation = Quaternion.Euler(35f, -35f, 0f); Vector3 val9 = default(Vector3); ((Vector3)(ref val9))..ctor(0f, 20000f, 0f); val = new GameObject("AFH_IconCam") { layer = 30 }; Camera val10 = val.AddComponent(); val10.orthographic = true; val10.orthographicSize = num * 1.05f; val10.cullingMask = 1073741824; val10.clearFlags = (CameraClearFlags)2; val10.nearClipPlane = 0.01f; val10.farClipPlane = num * 8f; ((Behaviour)val10).enabled = false; val10.renderingPath = (RenderingPath)1; val10.allowHDR = false; val10.allowMSAA = false; val.transform.position = val9 + Quaternion.Euler(12f, 0f, 0f) * new Vector3(0f, 0f, (0f - num) * 3f); val.transform.LookAt(val9); val4 = (val10.targetTexture = RenderTexture.GetTemporary(128, 128, 24, (RenderTextureFormat)0, (RenderTextureReadWrite)2)); val5 = Shoot(val10, val4, Color.black); val6 = Shoot(val10, val4, Color.white); Texture2D val11 = Compose(val5, val6); if ((Object)(object)val11 == (Object)null) { Plugin.Log.LogWarning((object)$"AwayFromHome: icon render via {label} drew {LastCoverage:P1} coverage at {LastLuminance:F3} luminance - rejecting it."); return null; } LastPath = label; Plugin.Log.LogInfo((object)$"AwayFromHome: rendered the Keeper Stone's icon via {label} ({LastCoverage:P1} coverage, {LastLuminance:F2} luminance)."); Sprite val12 = Sprite.Create(val11, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f)); ((Object)val12).hideFlags = (HideFlags)61; return val12; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: rendering the Keeper Stone's icon via {label} failed (non-fatal). Reason: {arg}"); return null; } finally { RenderTexture.active = active; if ((Object)(object)val4 != (Object)null) { RenderTexture.ReleaseTemporary(val4); } if ((Object)(object)val5 != (Object)null) { Object.DestroyImmediate((Object)(object)val5); } if ((Object)(object)val6 != (Object)null) { Object.DestroyImmediate((Object)(object)val6); } if ((Object)(object)val != (Object)null) { Object.DestroyImmediate((Object)(object)val); } if ((Object)(object)val2 != (Object)null) { Object.DestroyImmediate((Object)(object)val2); } if ((Object)(object)val3 != (Object)null) { Object.DestroyImmediate((Object)(object)val3); } } } private static Texture2D Shoot(Camera cam, RenderTexture rt, Color background) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) cam.backgroundColor = new Color(background.r, background.g, background.b, 1f); cam.Render(); RenderTexture.active = rt; Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false, false); val.ReadPixels(new Rect(0f, 0f, 128f, 128f), 0, 0); val.Apply(false, false); return val; } private static Texture2D Compose(Texture2D onBlack, Texture2D onWhite) { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Expected O, but got Unknown if ((Object)(object)onBlack == (Object)null || (Object)(object)onWhite == (Object)null) { return null; } Color[] pixels = onBlack.GetPixels(); Color[] pixels2 = onWhite.GetPixels(); if (pixels.Length != pixels2.Length) { return null; } Color[] array = (Color[])(object)new Color[pixels.Length]; int num = 0; float num2 = 0f; for (int i = 0; i < pixels.Length; i++) { float num3 = (pixels2[i].r - pixels[i].r + (pixels2[i].g - pixels[i].g) + (pixels2[i].b - pixels[i].b)) / 3f; float num4 = Mathf.Clamp01(1f - num3); if (num4 <= 0.004f) { array[i] = new Color(0f, 0f, 0f, 0f); continue; } Color val = pixels[i] / num4; array[i] = new Color(Mathf.Clamp01(val.r), Mathf.Clamp01(val.g), Mathf.Clamp01(val.b), num4); if (num4 > 0.05f) { num++; num2 += 0.2126f * array[i].r + 0.7152f * array[i].g + 0.0722f * array[i].b; } } LastCoverage = (float)num / (float)pixels.Length; LastLuminance = ((num > 0) ? (num2 / (float)num) : 0f); if (LastCoverage < 0.02f) { return null; } if (LastLuminance < 0.02f) { return null; } Texture2D val2 = new Texture2D(128, 128, (TextureFormat)4, false, false) { name = "AFH_KeeperStone_icon", wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1, hideFlags = (HideFlags)61 }; val2.SetPixels(array); val2.Apply(false, false); return val2; } } public sealed class KeeperFeeder : MonoBehaviour { public const string ServingKey = "afh_serving"; private const float TickSeconds = 1f; private const float ServeDistance = 1.6f; private const float DefaultFeedRange = 12f; private ZNetView _nview; private KeeperStore _store; private static readonly List ScratchItems = new List(); private const float StoneReach = 8f; private void Start() { _nview = ((Component)this).GetComponent(); _store = ((Component)this).GetComponent(); if (!((Object)(object)_nview == (Object)null) && _nview.IsValid()) { ((MonoBehaviour)this).InvokeRepeating("Tick", 1f, 1f); } } private void Tick() { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) try { if (!Configuration.autoFeed.Value || (Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner() || LivestockPin.FrozenCount > 0) { return; } ZDO zDO = _nview.GetZDO(); if (zDO == null) { return; } float leash = FeedRange(zDO); if (!AnyHungryNearby(leash, out var hungry)) { WidenPerception(leash); return; } if (ServingAlive(zDO)) { if (ServingWanted(zDO, hungry)) { WidenPerception(leash); return; } zDO.Set("afh_serving", ZDOID.None); } Serve(zDO, hungry); WidenPerception(leash); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: the Keeper Stone's feeder failed a tick (non-fatal, it will try again). Reason: {arg}"); } } internal static float FeedRange(ZDO stone) { PenArea penArea = LivestockPin.PenFor(stone); return penArea.IsNone ? 12f : penArea.OuterRadius; } internal static void CollectEdibleNames(Vector3 centre, float range, HashSet into) { //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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) float num = range * range; foreach (BaseAI baseAIInstance in BaseAI.BaseAIInstances) { if ((Object)(object)baseAIInstance == (Object)null) { continue; } Vector3 val = ((Component)baseAIInstance).transform.position - centre; if (((Vector3)(ref val)).sqrMagnitude > num || (Object)(object)((Component)baseAIInstance).GetComponent() == (Object)null) { continue; } MonsterAI val2 = (MonsterAI)(object)((baseAIInstance is MonsterAI) ? baseAIInstance : null); if ((Object)(object)val2 == (Object)null || val2.m_consumeItems == null) { continue; } foreach (ItemDrop consumeItem in val2.m_consumeItems) { if (!((Object)(object)consumeItem == (Object)null) && consumeItem.m_itemData != null && consumeItem.m_itemData.m_shared != null) { into.Add(consumeItem.m_itemData.m_shared.m_name); } } } } private bool ServingAlive(ZDO stone) { //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) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) ZDOID zDOID = stone.GetZDOID("afh_serving"); if (zDOID == ZDOID.None) { return false; } ZDO val = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(zDOID) : null); if (val == null || !val.IsValid()) { stone.Set("afh_serving", ZDOID.None); return false; } return true; } private static bool ServingWanted(ZDO stone, List hungry) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_0036: Unknown result type (might be due to invalid IL or missing references) try { ZDOID zDOID = stone.GetZDOID("afh_serving"); if (zDOID == ZDOID.None) { return false; } ZDO val = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(zDOID) : null); if (val == null || !val.IsValid()) { return false; } GameObject val2 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(val.GetPrefab()) : null); ItemDrop val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent() : null); if ((Object)(object)val3 == (Object)null || val3.m_itemData == null || val3.m_itemData.m_shared == null) { return false; } return AnyoneEats(hungry, val3.m_itemData.m_shared.m_name); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: could not tell what the Keeper Stone's current serving was (non-fatal, leaving it alone). Reason: {arg}"); return true; } } private void Serve(ZDO stone, List hungry) { //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) Inventory val = (((Object)(object)_store != (Object)null && (Object)(object)_store.Container != (Object)null) ? _store.Container.GetInventory() : null); if (val == null) { return; } ScratchItems.Clear(); ScratchItems.AddRange(val.GetAllItems()); if (ScratchItems.Count == 0) { return; } foreach (ItemData scratchItem in ScratchItems) { if (scratchItem == null || scratchItem.m_shared == null) { continue; } Character val2 = FirstEater(hungry, scratchItem.m_shared.m_name); if ((Object)(object)val2 == (Object)null || (Object)(object)scratchItem.m_dropPrefab == (Object)null) { continue; } ItemDrop val3 = ItemDrop.DropItem(scratchItem, 1, ServePoint(val2), Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f)); if ((Object)(object)val3 == (Object)null) { continue; } ZNetView component = ((Component)val3).GetComponent(); if ((Object)(object)component != (Object)null && component.GetZDO() != null) { stone.Set("afh_serving", component.GetZDO().m_uid); } val.RemoveOneItem(scratchItem); if (Configuration.verboseLogging.Value) { Plugin.Log.LogInfo((object)$"AwayFromHome: the Keeper Stone put out one {scratchItem.m_shared.m_name} for {hungry.Count} hungry animal(s)."); } break; } } private Vector3 ServePoint(Character target) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) Vector3 at = ((Component)this).transform.position + ((Component)this).transform.forward * 1.6f; if ((Object)(object)target == (Object)null) { return OnGround(at); } Vector3 position = ((Component)target).transform.position; Vector3 val = position - ((Component)this).transform.position; if (((Vector3)(ref val)).sqrMagnitude <= 64f) { return OnGround(at); } Vector3 val2 = ((Component)this).transform.position - position; val2.y = 0f; val2 = ((((Vector3)(ref val2)).sqrMagnitude > 0.01f) ? ((Vector3)(ref val2)).normalized : Vector3.forward); return OnGround(position + val2 * 1.2f); } private static Vector3 OnGround(Vector3 at) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) float y = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(at, ref y, 1000)) { at.y = y; } return at; } private bool AnyHungryNearby(float leash, out List hungry) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) hungry = null; float num = leash * leash; Vector3 position = ((Component)this).transform.position; foreach (BaseAI baseAIInstance in BaseAI.BaseAIInstances) { if ((Object)(object)baseAIInstance == (Object)null) { continue; } Character component = ((Component)baseAIInstance).GetComponent(); if ((Object)(object)component == (Object)null) { continue; } Vector3 val = ((Component)component).transform.position - position; if (((Vector3)(ref val)).sqrMagnitude > num) { continue; } Tameable component2 = ((Component)baseAIInstance).GetComponent(); if (!((Object)(object)component2 == (Object)null)) { ZNetView component3 = ((Component)baseAIInstance).GetComponent(); ZDO val2 = (((Object)(object)component3 != (Object)null) ? component3.GetZDO() : null); if (val2 != null && IsHungryByZdo(val2, component2)) { (hungry ?? (hungry = new List())).Add(component); } } } return hungry != null && hungry.Count > 0; } private static bool IsHungryByZdo(ZDO zdo, Tameable tame) { if ((Object)(object)ZNet.instance == (Object)null) { return false; } long num = zdo.GetLong(ZDOVars.s_tameLastFeeding, 0L); if (num <= 0) { return true; } DateTime dateTime = new DateTime(num); return (ZNet.instance.GetTime() - dateTime).TotalSeconds > (double)tame.m_fedDuration; } private static bool AnyoneEats(List hungry, string itemName) { return (Object)(object)FirstEater(hungry, itemName) != (Object)null; } private static Character FirstEater(List hungry, string itemName) { foreach (Character item in hungry) { MonsterAI component = ((Component)item).GetComponent(); if ((Object)(object)component == (Object)null || component.m_consumeItems == null) { continue; } foreach (ItemDrop consumeItem in component.m_consumeItems) { if ((Object)(object)consumeItem == (Object)null || consumeItem.m_itemData == null || consumeItem.m_itemData.m_shared == null || !(consumeItem.m_itemData.m_shared.m_name == itemName)) { continue; } return item; } } return null; } private void WidenPerception(float leash) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_0057: 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) float num = leash + 2f; float num2 = leash * leash; Vector3 position = ((Component)this).transform.position; foreach (BaseAI baseAIInstance in BaseAI.BaseAIInstances) { MonsterAI val = (MonsterAI)(object)((baseAIInstance is MonsterAI) ? baseAIInstance : null); if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - position; if (!(((Vector3)(ref val2)).sqrMagnitude > num2) && !((Object)(object)((Component)val).GetComponent() == (Object)null) && val.m_consumeSearchRange < num) { val.m_consumeSearchRange = num; } } } } } public static class KeeperManager { private const int SettledPolls = 6; private const float PollInterval = 0.25f; private const float SmelterSafeCycleSeconds = 3300f; private static Coroutine _routine; private static volatile bool _wantRunning; private static float _lastCycleWarnAt = float.NegativeInfinity; private static bool _saidStandingDown; private static float _lastCapWarnAt = float.NegativeInfinity; public static ZDOID ActiveSiteId { get; private set; } = ZDOID.None; public static string ActiveSiteLabel { get; private set; } public static string ActiveSiteOwner { get; private set; } public static string ActivePhase { get; private set; } = "Idle"; public static void Start() { if (_routine == null) { _wantRunning = true; _routine = ((MonoBehaviour)Plugin.Instance).StartCoroutine(Loop()); Plugin.Log.LogInfo((object)"AwayFromHome: keeper started."); } } public static void Stop() { _wantRunning = false; if (_routine != null) { ((MonoBehaviour)Plugin.Instance).StopCoroutine(_routine); _routine = null; } TearDownActiveSite(); Plugin.Log.LogInfo((object)"AwayFromHome: keeper stopped."); } private static void TearDownActiveSite() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) LivestockPin.RestoreAndClear(); LivestockPin.Release(); ZoneAnchor.Clear(); if (IsRemoteClient()) { SectorSubscription.Release(); } ActiveSiteId = ZDOID.None; ActiveSiteLabel = null; ActiveSiteOwner = null; ActivePhase = "Idle"; } private static bool IsRemoteClient() { return (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer(); } private static IEnumerator Loop() { while (_wantRunning) { if (!Configuration.enabled.Value || (Object)(object)ZNet.instance == (Object)null) { yield return (object)new WaitForSeconds(2f); continue; } if (!ZNet.instance.IsServer()) { if (!_saidStandingDown) { _saidStandingDown = true; Plugin.Log.LogInfo((object)"AwayFromHome: connected to a server, so the keeper is standing down here - the server tends every site, including yours."); } TearDownActiveSite(); yield return (object)new WaitForSeconds(5f); continue; } _saidStandingDown = false; IReadOnlyList sites = null; try { List all = SiteRegistry.GetAllKnownSites().ToList(); AdminSites.Refresh(all); sites = ApplyPerPlayerCap(all); } catch (Exception ex) { Exception ex2 = ex; Plugin.Log.LogError((object)$"AwayFromHome: reading the site list failed (non-fatal). Reason: {ex2}"); } if (sites == null) { yield return (object)new WaitForSeconds(5f); continue; } if (sites.Count == 0) { yield return (object)new WaitForSeconds(5f); continue; } WarnIfCycleUnsafe(sites.Count); float lapStarted = Time.realtimeSinceStartup; foreach (SiteRecord site in sites) { if (!_wantRunning || !Configuration.enabled.Value) { break; } yield return HoldSite(site); } float lapTook = Time.realtimeSinceStartup - lapStarted; float rest = Configuration.minimumCycleSeconds.Value - lapTook; if (rest > 0f) { if (Configuration.verboseLogging.Value) { Plugin.Log.LogInfo((object)$"AwayFromHome: rotation finished in {lapTook:0}s; resting {rest:0}s to keep the cycle at {Configuration.minimumCycleSeconds.Value:0}s and spare the sites needless load/unload churn."); } float wakeAt = Time.realtimeSinceStartup + rest; while (Time.realtimeSinceStartup < wakeAt && _wantRunning && Configuration.enabled.Value) { yield return (object)new WaitForSeconds(1f); } } } _routine = null; } private static IReadOnlyList ApplyPerPlayerCap(List sites) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) int value = Configuration.maxSitesPerPlayer.Value; if (value <= 0 || sites.Count == 0) { return sites; } List list = new List(sites.Count); List list2 = new List(sites.Count); int num = 0; foreach (SiteRecord site in sites) { if (site.OwnerId == 0L || AdminSites.IsAdminOwned(site.Id, site.OwnerId)) { list.Add(site); } else { list2.Add(site); } } foreach (IGrouping item in from s in list2 group s by s.OwnerId) { List list3 = item.OrderBy((SiteRecord s) => ((ZDOID)(ref s.Id)).ID).ToList(); list.AddRange(list3.Take(value)); num += Mathf.Max(0, list3.Count - value); } if (num > 0 && Time.realtimeSinceStartup - _lastCapWarnAt > 600f) { _lastCapWarnAt = Time.realtimeSinceStartup; Plugin.Log.LogWarning((object)$"AwayFromHome: {num} Keeper Stone(s) are past the per-player limit of {value} and are not being tended. They are still standing - raise MaxSitesPerPlayer or have the owner remove the extras."); } return list; } private static void WarnIfCycleUnsafe(int siteCount) { float num = (float)siteCount * (Configuration.dwellSeconds.Value + Configuration.settleTimeoutSeconds.Value * 0.25f); float num2 = Mathf.Max(num, Configuration.minimumCycleSeconds.Value); if (!(num2 <= 3300f) && !(Time.realtimeSinceStartup - _lastCycleWarnAt < 600f)) { _lastCycleWarnAt = Time.realtimeSinceStartup; Plugin.Log.LogWarning((object)$"AwayFromHome: with {siteCount} site(s) at {Configuration.dwellSeconds.Value:0}s dwell each and a {Configuration.minimumCycleSeconds.Value:0}s minimum cycle, a full rotation takes roughly {num2:0}s - over the ~1 hour a vanilla smelter/kiln can catch up in one gap. Any site with production buildings may lose progress between visits. Lower DwellSeconds or MinimumCycleSeconds (whichever is larger here), or split sites across a second keeper install, to bring the cycle under ~55 minutes."); } } private static bool IsSiteReady(Vector3 pos, int mark, int lastTotal, out SiteCensus.Result census) { //IL_0003: 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) try { census = SiteCensus.Take(pos, ZoneAnchor.Ring); return census.Valid && census.Total > 0 && census.Total == lastTotal && census.ZonesLoaded && census.Unbuilt == 0 && census.MeetsMark(mark) && !Heightmap.HaveQueuedRebuild(pos, Configuration.settleRadius.Value); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: checking whether a site had settled failed (non-fatal, treated as not-ready). Reason: {arg}"); census = default(SiteCensus.Result); return false; } } private static IEnumerator HoldSite(SiteRecord site) { ActiveSiteId = site.Id; ActiveSiteLabel = site.Label; ActiveSiteOwner = (string.IsNullOrEmpty(site.OwnerName) ? "unknown" : site.OwnerName); ActivePhase = "Loading"; ZoneAnchor.Set(site.Pos); bool remote = IsRemoteClient(); try { if (remote) { SectorSubscription.Renew(site.Pos); } float nextRenew = Time.realtimeSinceStartup + 5f; float deadline = Time.realtimeSinceStartup + Configuration.settleTimeoutSeconds.Value; int settledStreak = 0; int lastTotal = -1; int mark = SiteCensus.ReadMark(site.Id); SiteCensus.Result census = default(SiteCensus.Result); bool lastPollStable = false; while (Time.realtimeSinceStartup < deadline) { if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null) { yield return null; continue; } ZoneAnchor.PokeZones(); if (remote && Time.realtimeSinceStartup >= nextRenew) { SectorSubscription.Renew(site.Pos); nextRenew = Time.realtimeSinceStartup + 5f; } LivestockPin.HoldStill(site.Pos, ZoneAnchor.Ring); bool ready = IsSiteReady(site.Pos, mark, lastTotal, out census); lastPollStable = census.Valid && census.Total > 0 && census.Total == lastTotal; lastTotal = census.Total; settledStreak = (ready ? (settledStreak + 1) : 0); if (settledStreak >= 6) { break; } yield return (object)new WaitForSeconds(0.25f); } if (settledStreak < 6) { if (census.Valid && lastPollStable && census.ZonesLoaded && census.Unbuilt == 0 && !census.MeetsMark(mark)) { Plugin.Log.LogInfo((object)("AwayFromHome: the stone at " + site.Label + " is fully loaded but smaller than it was (" + census.Describe(mark) + ") - taking that as a rebuild and remembering the new size.")); SiteCensus.WriteMark(site.Id, census.Pieces); } else { Plugin.Log.LogWarning((object)$"AwayFromHome: the stone at {site.Label} did not settle within {Configuration.settleTimeoutSeconds.Value:0}s ({census.Describe(mark)}); skipping this pass, will retry next rotation."); } yield break; } ActivePhase = "Claiming"; LivestockPin.Snapshot(site.Pos, ZoneAnchor.Ring, site.Id); ClaimResult first = OwnershipClaim.ClaimPass(site.Pos, ZoneAnchor.Ring); LivestockPin.Release(); SiteCensus.WriteMark(site.Id, census.Pieces); ProductionCatchUp.Credit(site.Pos, ZoneAnchor.Ring); if (Configuration.verboseLogging.Value) { Plugin.Log.LogInfo((object)string.Format("AwayFromHome: the stone at {0} settled - {1}; claimed {2} object(s){3}.", site.Label, census.Describe(mark), first.Claimed, first.HasSmelter ? " (includes a smelter/kiln)" : "")); } ActivePhase = "Holding"; float holdDeadline = Time.realtimeSinceStartup + Configuration.dwellSeconds.Value; float nextClaim = Time.realtimeSinceStartup + Configuration.claimIntervalSeconds.Value; while (Time.realtimeSinceStartup < holdDeadline) { ZoneAnchor.PokeZones(); if (remote && Time.realtimeSinceStartup >= nextRenew) { SectorSubscription.Renew(site.Pos); nextRenew = Time.realtimeSinceStartup + 5f; } if (Time.realtimeSinceStartup >= nextClaim) { OwnershipClaim.ClaimPass(site.Pos, ZoneAnchor.Ring); LivestockPin.EnforceLeash(); nextClaim = Time.realtimeSinceStartup + Configuration.claimIntervalSeconds.Value; } yield return null; } SiteTelemetry.Report(site.Pos, ZoneAnchor.Ring, site.Label); } finally { TearDownActiveSite(); } } } public static class KeeperStone { public const string PrefabName = "AFH_KeeperStone"; public const string DisplayName = "Keeper Stone"; private const string DonorPrefab = "sign_notext"; private const string BundleName = "awayfromhome_kit"; private const string MeshAsset = "keeperstone"; private const string AlbedoAsset = "keeperstone_albedo"; private const string NormalAsset = "keeperstone_normal"; private const string EmissiveAsset = "keeperstone_emissive"; private const string HolderName = "AwayFromHome_PrefabContainer"; private const float StoneScale = 1f; private static GameObject _holder; private static GameObject _clone; private static AssetBundle _bundle; private static Mesh _mesh; private static Texture2D _albedo; private static Texture2D _normal; private static Texture2D _emissive; private static float GroundOffset { get { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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) float result; if (!IsAlive((Object)(object)_mesh)) { result = 0f; } else { Bounds bounds = _mesh.bounds; result = (0f - ((Bounds)(ref bounds)).min.y) * 1f; } return result; } } public static int PrefabHash { get; private set; } private static GameObject Holder { get { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown if (IsAlive((Object)(object)_holder)) { return _holder; } _holder = new GameObject("AwayFromHome_PrefabContainer"); _holder.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_holder); return _holder; } } public static GameObject PrefabHolder => Holder; private static bool IsAlive(Object o) { return o != null && o != (Object)null; } private static bool LoadBundle() { if (IsAlive((Object)(object)_bundle)) { return true; } try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string n) => n.EndsWith("awayfromhome_kit", StringComparison.Ordinal)); if (text == null) { Plugin.Log.LogError((object)"AwayFromHome: embedded asset bundle 'awayfromhome_kit' not found in the assembly. The Keeper Stone cannot be built."); return false; } using (Stream stream = executingAssembly.GetManifestResourceStream(text)) { _bundle = AssetBundle.LoadFromStream(stream); } if (!IsAlive((Object)(object)_bundle)) { Plugin.Log.LogError((object)"AwayFromHome: asset bundle failed to load."); return false; } _mesh = _bundle.LoadAsset("keeperstone"); _albedo = _bundle.LoadAsset("keeperstone_albedo"); _normal = _bundle.LoadAsset("keeperstone_normal"); _emissive = _bundle.LoadAsset("keeperstone_emissive"); if (!IsAlive((Object)(object)_mesh)) { Plugin.Log.LogError((object)"AwayFromHome: mesh 'keeperstone' missing from the bundle."); return false; } if (!IsAlive((Object)(object)_albedo)) { Plugin.Log.LogWarning((object)"AwayFromHome: albedo 'keeperstone_albedo' missing from the bundle - the stone will use the donor's own texture."); } if (!IsAlive((Object)(object)_normal)) { Plugin.Log.LogWarning((object)"AwayFromHome: normal map 'keeperstone_normal' missing from the bundle - the stone will render flat. Rebuild the bundle."); } if (!IsAlive((Object)(object)_emissive)) { Plugin.Log.LogInfo((object)"AwayFromHome: no emissive mask in the bundle - the rune pulse stays off, so the stone shows its own colours."); } return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: loading the asset bundle failed. Reason: {arg}"); return false; } } private static bool ObjectDbReady() { return (Object)(object)ObjectDB.instance != (Object)null && (Object)(object)ObjectDB.instance.GetItemPrefab("Wood") != (Object)null; } public static void TryRegister() { try { if (!((Object)(object)ZNetScene.instance == (Object)null) && ObjectDbReady() && EnsureClone()) { RegisterInZNetScene(); AddToHammer(); KeeperStore.HarvestChestLook(); KeeperStoneRing.HarvestTemplate(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: registering the Keeper Stone failed (the mod keeps working, the piece will be missing). Reason: {arg}"); } } private static bool EnsureClone() { if (IsAlive((Object)(object)_clone)) { return true; } if (!LoadBundle()) { return false; } GameObject prefab = ZNetScene.instance.GetPrefab("sign_notext"); if (!IsAlive((Object)(object)prefab)) { Plugin.Log.LogError((object)"AwayFromHome: donor prefab 'sign_notext' not found - cannot build the Keeper Stone."); return false; } if ("AFH_KeeperStone".IndexOf('(') >= 0 || "AFH_KeeperStone".IndexOf(' ') >= 0) { Plugin.Log.LogError((object)"AwayFromHome: prefab name 'AFH_KeeperStone' contains '(' or a space - refusing to register."); return false; } _clone = Object.Instantiate(prefab, Holder.transform, false); ((Object)_clone).name = "AFH_KeeperStone"; PrefabHash = StringExtensionMethods.GetStableHashCode("AFH_KeeperStone"); SwapVisual(_clone); ConfigurePiece(_clone); return true; } private static void SwapVisual(GameObject go) { //IL_004f: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_0390: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) MeshFilter componentInChildren = go.GetComponentInChildren(true); MeshRenderer componentInChildren2 = go.GetComponentInChildren(true); if (!IsAlive((Object)(object)componentInChildren) || !IsAlive((Object)(object)componentInChildren2)) { Plugin.Log.LogWarning((object)"AwayFromHome: donor has no MeshFilter/MeshRenderer - the Keeper Stone will look like a sign."); return; } Plugin.Log.LogInfo((object)$"AwayFromHome: donor renderer transform was pos={((Component)componentInChildren).transform.localPosition} rot={((Component)componentInChildren).transform.localEulerAngles} scale={((Component)componentInChildren).transform.localScale} (resetting)."); componentInChildren.sharedMesh = _mesh; ((Component)componentInChildren).transform.localPosition = new Vector3(0f, GroundOffset, 0f); ManualLogSource log = Plugin.Log; Bounds bounds = _mesh.bounds; log.LogInfo((object)$"AwayFromHome: mesh bounds min.y={((Bounds)(ref bounds)).min.y:0.00}, so lifting the stone {GroundOffset:0.00}m to stand its base on the ground."); ((Component)componentInChildren).transform.localRotation = Quaternion.identity; ((Component)componentInChildren).transform.localScale = Vector3.one * 1f; go.transform.localScale = Vector3.one; if (IsAlive((Object)(object)_albedo) || IsAlive((Object)(object)_normal) || IsAlive((Object)(object)_emissive)) { Material val = new Material(((Renderer)componentInChildren2).sharedMaterial) { name = "AFH_KeeperStone_mat" }; string text = Bind(val, _albedo, "_MainTex", "_MainTexture", "_BaseMap", "_BaseColorTexture"); text += Bind(val, _normal, "_BumpMap", "_NormalMap", "_NormalTex"); text += Bind(val, _emissive, "_EmissionMap", "_EmissiveTex", "_EmissionTex"); if (IsAlive((Object)(object)_normal) && val.HasProperty("_BumpScale")) { val.SetFloat("_BumpScale", 1f); } if (IsAlive((Object)(object)_emissive)) { val.EnableKeyword("_EMISSION"); if (val.HasProperty("_EmissionColor")) { val.SetColor("_EmissionColor", Color.white); } } NeutraliseDonorSurface(val); ((Renderer)componentInChildren2).sharedMaterials = (Material[])(object)new Material[1] { val }; ManualLogSource log2 = Plugin.Log; Shader shader = val.shader; log2.LogInfo((object)("AwayFromHome: Keeper Stone material '" + ((shader != null) ? ((Object)shader).name : null) + "' bound:" + (string.IsNullOrEmpty(text) ? " NOTHING - none of the expected texture slots exist on this shader" : text))); DumpMaterial(val); } BoxCollider componentInChildren3 = go.GetComponentInChildren(true); if (IsAlive((Object)(object)componentInChildren3) && IsAlive((Object)(object)_mesh)) { ((Component)componentInChildren3).transform.localScale = Vector3.one; bounds = _mesh.bounds; componentInChildren3.size = ((Bounds)(ref bounds)).size * 1f; bounds = _mesh.bounds; Vector3 val2 = ((Bounds)(ref bounds)).center * 1f; if ((Object)(object)((Component)componentInChildren3).transform != (Object)(object)((Component)componentInChildren).transform) { val2 += new Vector3(0f, GroundOffset, 0f); } componentInChildren3.center = val2; } go.AddComponent(); if ((Object)(object)go.GetComponent() == (Object)null) { go.AddComponent(); } if ((Object)(object)go.GetComponent() == (Object)null) { go.AddComponent(); } if ((Object)(object)go.GetComponent() == (Object)null) { go.AddComponent(); } if ((Object)(object)go.GetComponent() == (Object)null) { go.AddComponent(); } if ((Object)(object)go.GetComponent() == (Object)null) { go.AddComponent(); } } private static string Bind(Material m, Texture2D tex, params string[] props) { if (!IsAlive((Object)(object)tex)) { return ""; } foreach (string text in props) { if (m.HasProperty(text)) { m.SetTexture(text, (Texture)(object)tex); return " " + text + "=" + ((Object)tex).name; } } return " [" + ((Object)tex).name + " HAS NOWHERE TO GO - tried " + string.Join("/", props) + "]"; } private static void NeutraliseDonorSurface(Material m) { try { string[] array = new string[12] { "_MetallicGlossMap", "_MetalGlossMap", "_SpecGlossMap", "_GlossMap", "_OcclusionMap", "_ParallaxMap", "_DetailAlbedoMap", "_DetailNormalMap", "_DetailMask", "_StyleTex", "_PaintMask", "_MossTex" }; List list = new List(); string[] array2 = array; foreach (string text in array2) { if (m.HasProperty(text) && !((Object)(object)m.GetTexture(text) == (Object)null)) { m.SetTexture(text, (Texture)null); list.Add(text); } } List list2 = new List(); string[] array3 = new string[3] { "_Metallic", "_MetalGloss", "_Metallicness" }; foreach (string text2 in array3) { if (m.HasProperty(text2)) { m.SetFloat(text2, 0f); list2.Add(text2 + "=0"); } } string[] array4 = new string[3] { "_Glossiness", "_Smoothness", "_Gloss" }; foreach (string text3 in array4) { if (m.HasProperty(text3)) { m.SetFloat(text3, 0.15f); list2.Add(text3 + "=0.15"); } } Plugin.Log.LogInfo((object)("AwayFromHome: neutralised the donor's surface - cleared [" + ((list.Count == 0) ? "nothing" : string.Join(", ", list.ToArray())) + "], set [" + ((list2.Count == 0) ? "nothing" : string.Join(", ", list2.ToArray())) + "].")); } catch (Exception arg) { Plugin.Log.LogWarning((object)$"AwayFromHome: could not neutralise the donor surface (non-fatal, the stone may look glossy). Reason: {arg}"); } } private static void DumpMaterial(Material m) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 try { Shader shader = m.shader; if ((Object)(object)shader == (Object)null) { return; } List list = new List(); List list2 = new List(); int propertyCount = shader.GetPropertyCount(); for (int i = 0; i < propertyCount; i++) { string propertyName = shader.GetPropertyName(i); ShaderPropertyType propertyType = shader.GetPropertyType(i); ShaderPropertyType val = propertyType; if (val - 2 > 1) { if ((int)val == 4) { list.Add(propertyName + "=" + (((Object)(object)m.GetTexture(propertyName) != (Object)null) ? ((Object)m.GetTexture(propertyName)).name : "-")); } } else { list2.Add($"{propertyName}={m.GetFloat(propertyName):0.##}"); } } Plugin.Log.LogInfo((object)("AwayFromHome: shader '" + ((Object)shader).name + "' textures: " + string.Join(", ", list.ToArray()))); Plugin.Log.LogInfo((object)("AwayFromHome: shader '" + ((Object)shader).name + "' scalars: " + string.Join(", ", list2.ToArray()))); } catch (Exception arg) { Plugin.Log.LogWarning((object)$"AwayFromHome: could not dump the Keeper Stone material (non-fatal, diagnostics only). Reason: {arg}"); } } private static Requirement Req(string item, int amount) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item); if (!IsAlive((Object)(object)itemPrefab)) { Plugin.Log.LogWarning((object)("AwayFromHome: build material '" + item + "' not found in ObjectDB - dropping it from the Keeper Stone's cost.")); return null; } return new Requirement { m_resItem = itemPrefab.GetComponent(), m_amount = amount, m_recover = true }; } private static void ConfigurePiece(GameObject go) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) Piece val = go.GetComponent(); if (!IsAlive((Object)(object)val)) { val = go.AddComponent(); } val.m_name = "Keeper Stone"; val.m_description = "Marks this ground as a kept site. The keeper visits in turn, so the animals here keep breeding and the fires keep burning while you are elsewhere."; val.m_enabled = true; val.m_category = (PieceCategory)0; val.m_craftingStation = null; val.m_dlc = ""; val.m_resources = ((IEnumerable)(object)new Requirement[3] { Req("Mushroom", 10), Req("MushroomYellow", 10), Req("SurtlingCore", 5) }).Where((Requirement r) => r != null).ToArray(); val.m_groundPiece = true; val.m_allowedInDungeons = false; val.m_canRotate = true; Mesh mesh = _mesh; MeshRenderer componentInChildren = _clone.GetComponentInChildren(true); Sprite val2 = IconRenderer.Render(mesh, (componentInChildren != null) ? ((Renderer)componentInChildren).sharedMaterial : null); if (IsAlive((Object)(object)val2)) { val.m_icon = val2; } else { Plugin.Log.LogWarning((object)"AwayFromHome: could not render a build-menu icon - the Keeper Stone will wear the donor's sign icon."); } } private static void RegisterInZNetScene() { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_namedPrefabs == null) { return; } if (instance.m_namedPrefabs.TryGetValue(PrefabHash, out var value)) { if (IsAlive((Object)(object)value) && (Object)(object)value != (Object)(object)_clone) { Plugin.Log.LogError((object)"AwayFromHome: prefab hash collision on 'AFH_KeeperStone' - another mod or vanilla object owns it. Refusing to overwrite."); } return; } instance.m_namedPrefabs[PrefabHash] = _clone; if (!instance.m_prefabs.Contains(_clone)) { instance.m_prefabs.Add(_clone); } Plugin.Log.LogInfo((object)string.Format("AwayFromHome: registered '{0}' (hash {1}) in ZNetScene.", "AFH_KeeperStone", PrefabHash)); } private static void AddToHammer() { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer"); if (IsAlive((Object)(object)itemPrefab)) { PieceTable val = itemPrefab.GetComponent()?.m_itemData?.m_shared?.m_buildPieces; if (!((Object)(object)val == (Object)null) && !val.m_pieces.Contains(_clone)) { val.m_pieces.Add(_clone); Plugin.Log.LogInfo((object)"AwayFromHome: added 'Keeper Stone' to the hammer's build table."); } } } } [HarmonyPatch] public static class KeeperStoneRegistrationPatch { [HarmonyPatch(typeof(ObjectDB), "Awake")] [HarmonyPostfix] public static void ObjectDB_Awake_Postfix() { KeeperStone.TryRegister(); } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] [HarmonyPostfix] public static void ObjectDB_CopyOtherDB_Postfix() { KeeperStone.TryRegister(); } [HarmonyPatch(typeof(ZNetScene), "Awake")] [HarmonyPostfix] public static void ZNetScene_Awake_Postfix() { KeeperStone.TryRegister(); } } public class KeeperStoneHover : MonoBehaviour, Hoverable, Interactable { private ZNetView _nview; private KeeperStore _store; private KeeperStoneRing _ring; private KeeperSupply _supply; private void Awake() { _nview = ((Component)this).GetComponentInParent(); _store = ((Component)this).GetComponentInParent(); _ring = ((Component)this).GetComponentInParent(); _supply = ((Component)this).GetComponentInParent(); } public string GetHoverName() { return "Keeper Stone"; } public string GetHoverText() { //IL_0054: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_nview == (Object)null || _nview.GetZDO() == null) { return "Keeper Stone"; } string text = _nview.GetZDO().GetString(ZDOVars.s_creatorName, ""); Vector3 position = ((Component)this).transform.position; string text2 = $"({position.x:0}, {position.z:0})"; string text3 = ((!Configuration.enabled.Value) ? "dormant - Away From Home is switched off on this server" : ((!(KeeperManager.ActiveSiteId != ZDOID.None) || !(_nview.GetZDO().m_uid == KeeperManager.ActiveSiteId)) ? "waiting its turn in the keeper's rotation" : ("the keeper is here now - " + KeeperManager.ActivePhase.ToLowerInvariant() + ""))); string text4 = "Keeper Stone\n" + text2 + "\n" + text3; if (!string.IsNullOrEmpty(text)) { text4 = text4 + "\nraised by " + text; } PenArea pen = LivestockPin.PenFor(_nview.GetZDO()); int num = (((Object)(object)_store != (Object)null) ? _store.ItemCount() : 0); text4 = text4 + "\nstore: " + ((num > 0) ? $"{num} item(s)" : "empty") + " leash: " + pen.Describe(); int num2 = (((Object)(object)_supply != (Object)null) ? _supply.ProducerCount : 0); if (num2 > 0) { text4 += string.Format("\nsupplying {0} producer{1}", num2, (num2 == 1) ? "" : "s"); } text4 += "\n[E] open the store\n[Shift+E] set the leash"; if ((Object)(object)_ring != (Object)null) { _ring.Show(pen); } return text4; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: Keeper Stone hover text failed (non-fatal). Reason: {arg}"); return "Keeper Stone"; } } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold) { return false; } try { if ((Object)(object)_nview == (Object)null || !_nview.IsValid()) { return false; } if (alt) { OpenLeashPanel(user); return true; } if ((Object)(object)_store == (Object)null || (Object)(object)_store.Container == (Object)null) { ((Character)user).Message((MessageType)2, "This Keeper Stone has no store.", 0, (Sprite)null); return true; } return _store.Container.Interact(user, false, false); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: interacting with the Keeper Stone failed (non-fatal). Reason: {arg}"); return false; } } private void OpenLeashPanel(Humanoid user) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) ZDO zDO = _nview.GetZDO(); long num = zDO.GetLong(ZDOVars.s_creator, 0L); if ((!((Object)(object)Player.m_localPlayer != (Object)null) || num == 0L || num != Player.m_localPlayer.GetPlayerID()) && !Configuration.IsAdmin) { string text = zDO.GetString(ZDOVars.s_creatorName, ""); ((Character)user).Message((MessageType)2, string.IsNullOrEmpty(text) ? "This Keeper Stone is not yours." : ("This Keeper Stone belongs to " + text + "."), 0, (Sprite)null); } else { KeeperUIManager.ShowLeashPanel(zDO.m_uid, LivestockPin.PenFor(zDO)); } } public bool UseItem(Humanoid user, ItemData item) { return false; } } [HarmonyPatch] public static class KeeperStoneLifecyclePatch { [HarmonyPatch(typeof(Player), "PlacePiece")] [HarmonyPrefix] public static bool Player_PlacePiece_Prefix(Player __instance, Piece piece) { try { if ((Object)(object)piece == (Object)null) { return true; } if (Utils.GetPrefabName(((Component)piece).gameObject) != "AFH_KeeperStone") { return true; } int value = Configuration.maxSitesPerPlayer.Value; if (value <= 0) { return true; } if (Configuration.adminBypass.Value && Configuration.IsAdmin) { return true; } int count = SiteRegistry.GetMySites().Count; if (count < value) { return true; } ((Character)__instance).Message((MessageType)2, string.Format("You already have {0} Keeper Stone{1} standing (limit {2}). Knock one down to move it.", count, (count == 1) ? "" : "s", value), 0, (Sprite)null); return false; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: the Keeper Stone placement limit check failed, allowing the placement. Reason: {arg}"); return true; } } [HarmonyPatch(typeof(Piece), "SetCreator")] [HarmonyPostfix] public static void Piece_SetCreator_Postfix(Piece __instance) { try { if (!((Object)(object)__instance == (Object)null) && !(Utils.GetPrefabName(((Component)__instance).gameObject) != "AFH_KeeperStone")) { SiteRegistry.RequestImmediateRescan(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: reacting to a placed Keeper Stone failed (non-fatal). Reason: {arg}"); } } [HarmonyPatch(typeof(WearNTear), "Destroy")] [HarmonyPostfix] public static void WearNTear_Destroy_Postfix(WearNTear __instance) { try { if (!((Object)(object)__instance == (Object)null) && !(Utils.GetPrefabName(((Component)__instance).gameObject) != "AFH_KeeperStone")) { SiteRegistry.RequestImmediateRescan(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: reacting to a destroyed Keeper Stone failed (non-fatal). Reason: {arg}"); } } } public sealed class KeeperStoneRing : MonoBehaviour { private const int Segments = 64; private const float HideAfter = 0.5f; private static GameObject _segmentPrefab; private static LayerMask _mask; private static bool _searched; private GameObject _marker; private PenProjector _projector; private ZNetView _nview; private float _hideAt; private bool _shown; public static void HarvestTemplate() { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if (_searched) { return; } _searched = true; try { if ((Object)(object)ZNetScene.instance == (Object)null) { _searched = false; return; } foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } CraftingStation component = prefab.GetComponent(); GameObject val = (((Object)(object)component != (Object)null) ? component.m_areaMarker : null); if (!((Object)(object)val == (Object)null)) { CircleProjector component2 = val.GetComponent(); if (!((Object)(object)component2 == (Object)null) && !((Object)(object)component2.m_prefab == (Object)null)) { _segmentPrefab = component2.m_prefab; _mask = component2.m_mask; Plugin.Log.LogInfo((object)("AwayFromHome: took the leash outline's look from '" + ((Object)prefab).name + "'.")); return; } } } Plugin.Log.LogWarning((object)"AwayFromHome: found no vanilla ground-outline to copy - the leash will still work, it just will not be drawn."); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: harvesting the leash outline failed (non-fatal, cosmetic only). Reason: {arg}"); } } public void Show(PenArea pen) { try { if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } if (pen.IsNone) { Hide(); } else if (EnsureMarker()) { _projector.Pen = pen; if (!_shown) { _marker.SetActive(true); _shown = true; } _hideAt = Time.time + 0.5f; } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: drawing the leash outline failed (non-fatal, cosmetic only). Reason: {arg}"); } } public void Hide() { if (_shown && !((Object)(object)_marker == (Object)null)) { _marker.SetActive(false); _shown = false; } } private bool EnsureMarker() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //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) if ((Object)(object)_marker != (Object)null) { return true; } if ((Object)(object)_segmentPrefab == (Object)null) { return false; } _marker = new GameObject("AFH_LeashOutline"); _projector = _marker.AddComponent(); _projector.SegmentPrefab = _segmentPrefab; _projector.Mask = _mask; _projector.SegmentCount = 64; return true; } private void Update() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_nview == (Object)null) { _nview = ((Component)this).GetComponent(); } if ((Object)(object)_nview != (Object)null && _nview.IsValid() && KeeperUIManager.LeashPanelSite == _nview.GetZDO().m_uid) { Show(KeeperUIManager.LeashPanelPen(((Component)this).transform.position)); } else if (_shown && Time.time >= _hideAt) { Hide(); } } private void OnDestroy() { if ((Object)(object)_marker != (Object)null) { Object.Destroy((Object)(object)_marker); } } } internal sealed class PenProjector : MonoBehaviour { public GameObject SegmentPrefab; public LayerMask Mask; public int SegmentCount = 64; public PenArea Pen; private readonly List _segments = new List(); private void Update() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) try { if (Pen.IsNone || (Object)(object)SegmentPrefab == (Object)null || !EnsureSegments()) { return; } int count = _segments.Count; RaycastHit val2 = default(RaycastHit); for (int i = 0; i < count; i++) { Vector3 val = Pen.PerimeterPoint((float)i / (float)count); if (Physics.Raycast(val + Vector3.up * 500f, Vector3.down, ref val2, 1000f, ((LayerMask)(ref Mask)).value)) { val.y = ((RaycastHit)(ref val2)).point.y; } _segments[i].transform.position = val; } for (int j = 0; j < count; j++) { Vector3 position = _segments[(j - 1 + count) % count].transform.position; Vector3 position2 = _segments[(j + 1) % count].transform.position; Vector3 val3 = position2 - position; Vector3 normalized = ((Vector3)(ref val3)).normalized; if (((Vector3)(ref normalized)).sqrMagnitude > 0.0001f) { _segments[j].transform.rotation = Quaternion.LookRotation(normalized, Vector3.up); } } } catch (Exception arg) { ((Behaviour)this).enabled = false; Plugin.Log.LogError((object)$"AwayFromHome: the leash outline failed and has been switched off for this stone (non-fatal, cosmetic only). Reason: {arg}"); } } private bool EnsureSegments() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (_segments.Count == SegmentCount) { return true; } foreach (GameObject segment in _segments) { if ((Object)(object)segment != (Object)null) { Object.Destroy((Object)(object)segment); } } _segments.Clear(); for (int i = 0; i < SegmentCount; i++) { GameObject item = Object.Instantiate(SegmentPrefab, ((Component)this).transform.position, Quaternion.identity, ((Component)this).transform); _segments.Add(item); } return _segments.Count > 0; } } public sealed class KeeperStore : MonoBehaviour { public const int Width = 3; public const int Height = 2; private const string ChildName = "AFH_Store"; private static Sprite _bkg; private static EffectList _openEffects; private static EffectList _closeEffects; private static bool _harvested; private Container _container; private ZNetView _nview; public Container Container => _container; public static void HarvestChestLook() { if (_harvested) { return; } _harvested = true; try { if ((Object)(object)ZNetScene.instance == (Object)null) { _harvested = false; return; } foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if (!((Object)(object)prefab == (Object)null)) { Container component = prefab.GetComponent(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.m_bkg == (Object)null)) { _bkg = component.m_bkg; _openEffects = component.m_openEffects; _closeEffects = component.m_closeEffects; Plugin.Log.LogInfo((object)("AwayFromHome: took the Keeper Stone's inventory look from '" + ((Object)prefab).name + "'.")); return; } } } Plugin.Log.LogWarning((object)"AwayFromHome: found no vanilla container to copy an inventory background from - the Keeper Stone's slots will look plain, but they work."); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: harvesting the chest look failed (non-fatal, cosmetic only). Reason: {arg}"); } } private void Start() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) try { _nview = ((Component)this).GetComponent(); if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && !((Object)(object)((Component)this).transform.Find("AFH_Store") != (Object)null)) { GameObject val = new GameObject("AFH_Store"); val.SetActive(false); val.transform.SetParent(((Component)this).transform, false); Container val2 = val.AddComponent(); val2.m_rootObjectOverride = _nview; val2.m_name = "Keeper Stone"; val2.m_width = 3; val2.m_height = 2; val2.m_privacy = (PrivacySetting)2; val2.m_checkGuardStone = true; val2.m_autoDestroyEmpty = false; if ((Object)(object)_bkg != (Object)null) { val2.m_bkg = _bkg; } if (_openEffects != null) { val2.m_openEffects = _openEffects; } if (_closeEffects != null) { val2.m_closeEffects = _closeEffects; } val.SetActive(true); _container = val2; } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: building the Keeper Stone's store failed (non-fatal - the stone still keeps the site, it just has no slots). Reason: {arg}"); } } public static int StoredItemCount(ZDO stone) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown if (stone == null) { return 0; } try { string text = stone.GetString(ZDOVars.s_items, ""); if (string.IsNullOrEmpty(text)) { return 0; } ZPackage val = new ZPackage(text); val.ReadInt(); return Mathf.Max(0, val.ReadInt()); } catch { return 0; } } public int ItemCount() { try { Inventory val = (((Object)(object)_container != (Object)null) ? _container.GetInventory() : null); return (val != null) ? val.NrOfItems() : 0; } catch { return 0; } } } public sealed class KeeperSupply : MonoBehaviour { private const float TickSeconds = 2f; private const float RescanSeconds = 15f; private const int PerProducerPerTick = 2; private const int MaxTransfersPerTick = 12; private ZNetView _nview; private KeeperStore _store; private float _rescanAt; private int _cursor; private int _lastReported = -1; private readonly List _producers = new List(); private static readonly Dictionary ProducerByPrefab = new Dictionary(); private static readonly List Scratch = new List(); private static readonly HashSet Reserved = new HashSet(); public int ProducerCount => _producers.Count; private void Start() { _nview = ((Component)this).GetComponent(); _store = ((Component)this).GetComponent(); if (!((Object)(object)_nview == (Object)null) && _nview.IsValid()) { ((MonoBehaviour)this).InvokeRepeating("Tick", 2f, 2f); } } private void Tick() { //IL_0156: Unknown result type (might be due to invalid IL or missing references) try { if (!Configuration.restockProduction.Value || (Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner()) { return; } float value = Configuration.productionReachMeters.Value; if (value <= 0f) { return; } Inventory val = (((Object)(object)_store != (Object)null && (Object)(object)_store.Container != (Object)null) ? _store.Container.GetInventory() : null); if (val == null || val.NrOfItems() == 0) { if (Time.time >= _rescanAt) { Rescan(value); } return; } if (Time.time >= _rescanAt) { Rescan(value); } if (_producers.Count == 0) { return; } Reserved.Clear(); ZDO zDO = _nview.GetZDO(); if (zDO != null && Configuration.autoFeed.Value) { KeeperFeeder.CollectEdibleNames(((Component)this).transform.position, KeeperFeeder.FeedRange(zDO), Reserved); } int num = 12; int num2 = 0; for (int i = 0; i < _producers.Count; i++) { if (num <= 0) { break; } Smelter s = _producers[(_cursor + i) % _producers.Count]; int num3 = TopUp(s, val, num); num -= num3; if (num3 > 0) { num2++; } } _cursor++; if (num2 > 0 && Configuration.verboseLogging.Value) { Plugin.Log.LogInfo((object)$"AwayFromHome: the Keeper Stone restocked {num2} of {_producers.Count} producer(s) from its own slots."); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: the Keeper Stone's supply run failed a tick (non-fatal, it will try again). Reason: {arg}"); } } private int TopUp(Smelter s, Inventory inv, int budget) { if ((Object)(object)s == (Object)null) { return 0; } ZNetView nview = s.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return 0; } int num = 0; int num2 = s.GetQueueSize(); int num3 = 0; while (num2 < s.m_maxOre && num3 < 2 && num < budget) { ItemData val = FirstAcceptedOre(s, inv); if (val == null) { break; } string name = ((Object)val.m_dropPrefab).name; inv.RemoveItem(val, 1); nview.InvokeRPC("RPC_AddOre", new object[1] { name }); num2++; num3++; num++; } if ((Object)(object)s.m_fuelItem != (Object)null && s.m_fuelItem.m_itemData != null && s.m_fuelItem.m_itemData.m_shared != null) { string name2 = s.m_fuelItem.m_itemData.m_shared.m_name; if (!Reserved.Contains(name2)) { float num4 = s.GetFuel(); int num5 = 0; while (num4 <= (float)s.m_maxFuel - 1f && num5 < 2 && num < budget) { ItemData item = inv.GetItem(name2, -1, false); if (item == null) { break; } inv.RemoveOneItem(item); nview.InvokeRPC("RPC_AddFuel", Array.Empty()); num4 += 1f; num5++; num++; } } } return num; } private static ItemData FirstAcceptedOre(Smelter s, Inventory inv) { if (s.m_conversion == null) { return null; } foreach (ItemConversion item2 in s.m_conversion) { if (item2 == null || (Object)(object)item2.m_from == (Object)null || item2.m_from.m_itemData == null || item2.m_from.m_itemData.m_shared == null) { continue; } string name = item2.m_from.m_itemData.m_shared.m_name; if (!Reserved.Contains(name)) { ItemData item = inv.GetItem(name, -1, false); if (item != null && !((Object)(object)item.m_dropPrefab == (Object)null) && s.IsItemAllowed(((Object)item.m_dropPrefab).name)) { return item; } } } return null; } private void Rescan(float reach) { //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_006b: 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_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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) _rescanAt = Time.time + 15f; _producers.Clear(); if (ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return; } try { Vector3 position = ((Component)this).transform.position; float num = reach * reach; Scratch.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(position), 1, 0, Scratch, (List)null); foreach (ZDO item in Scratch) { if (item == null || !item.IsValid()) { continue; } Vector3 val = item.GetPosition() - position; if (((Vector3)(ref val)).sqrMagnitude > num || !IsProducerPrefab(item)) { continue; } ZNetView val2 = ZNetScene.instance.FindInstance(item); if (!((Object)(object)val2 == (Object)null)) { Smelter componentInChildren = ((Component)val2).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { _producers.Add(componentInChildren); } } } } catch (Exception arg) { _producers.Clear(); Plugin.Log.LogError((object)$"AwayFromHome: looking for producers near a Keeper Stone failed (non-fatal, nothing is restocked this pass). Reason: {arg}"); } finally { Scratch.Clear(); } if (Configuration.verboseLogging.Value && _producers.Count != _lastReported) { _lastReported = _producers.Count; Plugin.Log.LogInfo((object)($"AwayFromHome: the Keeper Stone at ({((Component)this).transform.position.x:0}, {((Component)this).transform.position.z:0}) " + $"can see {_producers.Count} producer(s) within {reach:0.#}m.")); } } private static bool IsProducerPrefab(ZDO zdo) { int prefab = zdo.GetPrefab(); if (ProducerByPrefab.TryGetValue(prefab, out var value)) { return value; } bool flag = false; try { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null); flag = (Object)(object)val != (Object)null && (Object)(object)val.GetComponentInChildren(true) != (Object)null; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: could not tell whether a prefab was a producer (treated as not). Reason: {arg}"); } ProducerByPrefab[prefab] = flag; return flag; } } [HarmonyPatch] public static class LifecyclePatch { [HarmonyPatch(typeof(ZNet), "Start")] [HarmonyPostfix] public static void ZNet_Start_Postfix() { try { PetPantryConflict.EnsureProbed(); SectorSubscription.OnZNetStart(); SiteRegistry.OnZNetStart(); Commands.OnZNetStart(); KeeperManager.Start(); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && (Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(SiteRegistry.ScanLoop()); } } catch (Exception arg) { Plugin.Log.LogError((object)string.Format("AwayFromHome: {0} failed. Reason: {1}", "ZNet_Start_Postfix", arg)); } } [HarmonyPatch(typeof(ZNet), "Shutdown")] [HarmonyPostfix] public static void ZNet_Shutdown_Postfix() { try { KeeperManager.Stop(); } catch (Exception arg) { Plugin.Log.LogError((object)string.Format("AwayFromHome: {0} failed (non-fatal). Reason: {1}", "ZNet_Shutdown_Postfix", arg)); } } } public static class LivestockPin { private sealed class Held { public BaseAI Ai; public bool AiWasEnabled; } private const string HomeKey = "afh_home"; public const string LeashKey = "afh_leash"; public const string ShapeKey = "afh_leash_shape"; public const string DepthKey = "afh_leash_z"; public const string YawKey = "afh_leash_yaw"; private const float ReturnRadius = 2f; private const float ReturnClearance = 1.1f; private const float StrayTolerance = 0.5f; private static PenArea _pen = PenArea.None; private static readonly Dictionary TameableByPrefab = new Dictionary(); private static readonly List Tracked = new List(); private static readonly List Scratch = new List(); private static readonly Dictionary LastSeen = new Dictionary(); private static float _lastSeenAt; private const float ImpossibleSpeed = 15f; private static readonly Dictionary Frozen = new Dictionary(); public static int TrackedCount => Tracked.Count; public static int FrozenCount => Frozen.Count; public static float LeashFor(ZDO stone) { float value = Configuration.maxLeashMeters.Value; float result = Mathf.Min(Configuration.livestockLeashMeters.Value, value); if (stone == null) { return result; } float num = stone.GetFloat("afh_leash", -1f); if (num < 0f) { return result; } if (num == 0f) { return 0f; } return Mathf.Min(num, value); } public static PenArea PenFor(ZDO stone) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (stone == null) { return PenArea.None; } float num = LeashFor(stone); if (num <= 0f) { return PenArea.None; } float value = Configuration.maxLeashMeters.Value; PenShape penShape = (PenShape)stone.GetInt("afh_leash_shape", 0); if (penShape != PenShape.Square && penShape != PenShape.Rectangle) { penShape = PenShape.Circle; } float halfZ = num; if (penShape == PenShape.Rectangle) { float num2 = stone.GetFloat("afh_leash_z", 0f); halfZ = ((num2 > 0f) ? Mathf.Min(num2, value) : num); } return new PenArea(penShape, stone.GetPosition(), num, halfZ, stone.GetFloat("afh_leash_yaw", 0f)); } public static PenArea PenForSite(ZDOID siteId) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) try { ZDOMan instance = ZDOMan.instance; return PenFor((instance != null) ? instance.GetZDO(siteId) : null); } catch { return PenArea.None; } } private static Vector3 FindReturnSpot(ZDOID moving) { //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) float[] array = new float[7] { 1.2f, 1.8f, 2f, 3.5f, 5f, 7f, 10f }; float num = (_pen.IsNone ? 2f : _pen.OuterRadius); float[] array2 = array; float y = default(float); foreach (float num2 in array2) { if (num2 > num && num2 > 2f) { break; } int num3 = Mathf.Max(6, Mathf.RoundToInt(num2 * 4f)); for (int j = 0; j < num3; j++) { float num4 = (float)j / (float)num3 * (float)Math.PI * 2f + num2; Vector3 val = _pen.Centre + new Vector3(Mathf.Cos(num4) * num2, 0f, Mathf.Sin(num4) * num2); if (_pen.Contains(val) && (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(val, ref y, 1000)) { val.y = y; if (IsSpotClear(val, moving)) { return val; } } } } Vector3 centre = _pen.Centre; float y2 = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(centre, ref y2, 1000)) { centre.y = y2; } return centre; } private static bool IsSpotClear(Vector3 at, ZDOID moving) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) float num = 1.21f; foreach (ZDOID item in Tracked) { if (item == moving) { continue; } ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(item) : null); if (val != null && val.IsValid()) { Vector3 val2 = val.GetPosition() - at; if (((Vector3)(ref val2)).sqrMagnitude < num) { return false; } } } return true; } internal static bool IsLivestock(ZDO zdo) { int prefab = zdo.GetPrefab(); if (TameableByPrefab.TryGetValue(prefab, out var value)) { return value; } bool flag = false; try { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null); flag = (Object)(object)val != (Object)null && (Object)(object)val.GetComponent() != (Object)null; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: could not tell whether a prefab was livestock (treated as not). Reason: {arg}"); } TameableByPrefab[prefab] = flag; return flag; } internal static bool IsKept(ZDO zdo) { if (zdo.GetBool(ZDOVars.s_tamed, false)) { return true; } return zdo.GetLong(ZDOVars.s_tameLastFeeding, 0L) > 0; } public static void HoldStill(Vector3 pos, int ring) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) if (ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return; } try { Scratch.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(pos), ring, 0, Scratch, (List)null); foreach (ZDO item in Scratch) { if (item == null || !item.Persistent || !IsLivestock(item) || !IsKept(item)) { continue; } ZNetView val = ZNetScene.instance.FindInstance(item); if ((Object)(object)val == (Object)null) { continue; } if (!Frozen.ContainsKey(item.m_uid)) { BaseAI componentInChildren = ((Component)val).GetComponentInChildren(); Frozen[item.m_uid] = new Held { Ai = componentInChildren, AiWasEnabled = ((Object)(object)componentInChildren != (Object)null && ((Behaviour)componentInChildren).enabled) }; if ((Object)(object)componentInChildren != (Object)null) { ((Behaviour)componentInChildren).enabled = false; } } Rigidbody componentInChildren2 = ((Component)val).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null && !componentInChildren2.isKinematic) { componentInChildren2.velocity = Vector3.zero; componentInChildren2.angularVelocity = Vector3.zero; } } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: holding animals still while a site loaded failed (non-fatal, they simply are not held this visit). Reason: {arg}"); } } public static void Release() { if (Frozen.Count == 0) { return; } int num = 0; try { foreach (KeyValuePair item in Frozen) { BaseAI ai = item.Value.Ai; if (!((Object)(object)ai == (Object)null)) { ((Behaviour)ai).enabled = item.Value.AiWasEnabled; num++; } } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: releasing held animals failed (non-fatal). Reason: {arg}"); } finally { Frozen.Clear(); } if (num > 0 && Configuration.verboseLogging.Value) { Plugin.Log.LogInfo((object)$"AwayFromHome: released {num} animal(s) that were held still while the site loaded."); } } public static void Snapshot(Vector3 pos, int ring, ZDOID siteId) { //IL_000c: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) Tracked.Clear(); _pen = PenForSite(siteId); LastSeen.Clear(); _lastSeenAt = 0f; if (ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null) { return; } try { Scratch.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(pos), ring, 0, Scratch, (List)null); foreach (ZDO item in Scratch) { if (item != null && item.Persistent && IsLivestock(item) && IsKept(item)) { Tracked.Add(item.m_uid); } } if (Configuration.verboseLogging.Value && Tracked.Count > 0) { Plugin.Log.LogInfo((object)$"AwayFromHome: watching {Tracked.Count} animal(s) inside a {_pen.Describe()} leash around this site's stone."); } } catch (Exception arg) { Tracked.Clear(); Plugin.Log.LogError((object)$"AwayFromHome: noting animal positions failed (non-fatal, they simply will not be pinned this visit). Reason: {arg}"); } } public static void EnforceLeash() { if (!_pen.IsNone && Tracked.Count != 0) { Sweep("strayed outside the leash", quiet: true); } } public static void RestoreAndClear() { try { if (Tracked.Count > 0 && !_pen.IsNone) { Sweep("drifted outside the leash", quiet: false); } } finally { Tracked.Clear(); } } private static void Sweep(string what, bool quiet) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) int num = 0; float num2 = 0f; string text = "?"; float realtimeSinceStartup = Time.realtimeSinceStartup; float num3 = ((_lastSeenAt > 0f) ? (realtimeSinceStartup - _lastSeenAt) : 0f); _lastSeenAt = realtimeSinceStartup; try { long sessionID = ZDOMan.GetSessionID(); foreach (ZDOID item in Tracked) { ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(item) : null); if (val == null || !val.IsValid() || val.GetOwner() != sessionID) { continue; } Vector3 position = val.GetPosition(); if (num3 > 0.1f && LastSeen.TryGetValue(item, out var value)) { float num4 = Vector3.Distance(position, value) / num3; if (num4 > 15f) { GameObject val2 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(val.GetPrefab()) : null); Plugin.Log.LogWarning((object)(string.Format("AwayFromHome: a {0} JUMPED {1:0.0}m in {2:0.0}s ", ((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "?", Vector3.Distance(position, value), num3) + $"({num4:0} m/s) - too fast to have walked, so it went THROUGH something or was re-seated. " + "This is the escape mechanism worth chasing, not ordinary wandering.")); } } float num5 = _pen.HowFarOutside(position); if (num5 <= 0.5f) { LastSeen[item] = position; continue; } Vector3 val3 = FindReturnSpot(item); LastSeen[item] = val3; if (num5 > num2) { num2 = num5; GameObject val4 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(val.GetPrefab()) : null); text = (((Object)(object)val4 != (Object)null) ? ((Object)val4).name : "?"); } ZNetView val5 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(val) : null); if ((Object)(object)val5 != (Object)null) { ((Component)val5).transform.position = val3; Rigidbody componentInChildren = ((Component)val5).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.position = val3; if (!componentInChildren.isKinematic) { componentInChildren.velocity = Vector3.zero; componentInChildren.angularVelocity = Vector3.zero; } } } val.SetPosition(val3); num++; } if (num > 0) { Plugin.Log.LogInfo((object)$"AwayFromHome: {num} of {Tracked.Count} animal(s) had {what} and were walked back to the stone (furthest was a {text} at {num2:0.0}m)."); } else if (!quiet && Configuration.verboseLogging.Value) { Plugin.Log.LogInfo((object)$"AwayFromHome: all {Tracked.Count} animal(s) were where they belong - nothing to put back."); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: putting animals back failed (non-fatal). Reason: {arg}"); } } public static void Forget() { Tracked.Clear(); } } public readonly struct ClaimResult { public readonly int Claimed; public readonly bool HasSmelter; public ClaimResult(int claimed, bool hasSmelter) { Claimed = claimed; HasSmelter = hasSmelter; } } public static class OwnershipClaim { private static readonly List Scratch = new List(); private static bool IsAbandoned(long owner, long myId) { if (owner == 0L || owner == myId) { return true; } if ((Object)(object)ZNet.instance == (Object)null) { return false; } List peers = ZNet.instance.GetPeers(); if (peers == null) { return false; } for (int i = 0; i < peers.Count; i++) { if (peers[i] != null && peers[i].m_uid == owner) { return false; } } return true; } public static ClaimResult ClaimPass(Vector3 pos, int ring) { //IL_003b: 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) if (ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null) { return default(ClaimResult); } try { Scratch.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(pos), ring, 0, Scratch, (List)null); long sessionID = ZDOMan.GetSessionID(); int num = 0; bool flag = false; foreach (ZDO item in Scratch) { if (item == null || !item.Persistent) { continue; } if (!flag && (Object)(object)ZNetScene.instance != (Object)null) { ZNetView val = ZNetScene.instance.FindInstance(item); if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponent() != (Object)null) { flag = true; } } if (!item.HasOwner() || IsAbandoned(item.GetOwner(), sessionID)) { item.SetOwner(sessionID); num++; } } return new ClaimResult(num, flag); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: claiming ownership at a held site failed (non-fatal). Reason: {arg}"); return default(ClaimResult); } } } public enum PenShape { Circle, Square, Rectangle } public readonly struct PenArea { public readonly PenShape Shape; public readonly Vector3 Centre; public readonly float HalfX; public readonly float HalfZ; public readonly float Yaw; public static readonly PenArea None = new PenArea(PenShape.Circle, Vector3.zero, 0f, 0f, 0f); public bool IsNone => HalfX <= 0f || HalfZ <= 0f; public float OuterRadius => (Shape == PenShape.Circle) ? HalfX : Mathf.Sqrt(HalfX * HalfX + HalfZ * HalfZ); public PenArea(PenShape shape, Vector3 centre, float halfX, float halfZ, float yaw) { //IL_0009: 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) Shape = shape; Centre = centre; HalfX = Mathf.Max(0f, halfX); HalfZ = ((shape == PenShape.Rectangle) ? Mathf.Max(0f, halfZ) : HalfX); Yaw = ((shape == PenShape.Circle) ? 0f : yaw); } public bool Contains(Vector3 p) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_0086: Unknown result type (might be due to invalid IL or missing references) if (IsNone) { return true; } Vector3 val = p - Centre; val.y = 0f; if (Shape == PenShape.Circle) { return ((Vector3)(ref val)).sqrMagnitude <= HalfX * HalfX; } Vector3 val2 = Quaternion.Euler(0f, 0f - Yaw, 0f) * val; return Mathf.Abs(val2.x) <= HalfX && Mathf.Abs(val2.z) <= HalfZ; } public float HowFarOutside(Vector3 p) { //IL_0019: 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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_009c: Unknown result type (might be due to invalid IL or missing references) if (IsNone) { return 0f; } Vector3 val = p - Centre; val.y = 0f; if (Shape == PenShape.Circle) { return Mathf.Max(0f, ((Vector3)(ref val)).magnitude - HalfX); } Vector3 val2 = Quaternion.Euler(0f, 0f - Yaw, 0f) * val; float num = Mathf.Max(0f, Mathf.Abs(val2.x) - HalfX); float num2 = Mathf.Max(0f, Mathf.Abs(val2.z) - HalfZ); return Mathf.Sqrt(num * num + num2 * num2); } public Vector3 PerimeterPoint(float t) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_0157: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) t = Mathf.Repeat(t, 1f); if (Shape == PenShape.Circle) { float num = t * (float)Math.PI * 2f; return Centre + new Vector3(Mathf.Sin(num) * HalfX, 0f, Mathf.Cos(num) * HalfX); } float num2 = HalfX * 2f; float num3 = HalfZ * 2f; float num4 = t * (num2 + num3) * 2f; Vector3 val = default(Vector3); if (num4 < num2) { ((Vector3)(ref val))..ctor(0f - HalfX + num4, 0f, HalfZ); } else if (num4 < num2 + num3) { ((Vector3)(ref val))..ctor(HalfX, 0f, HalfZ - (num4 - num2)); } else if (num4 < num2 + num2 + num3) { ((Vector3)(ref val))..ctor(HalfX - (num4 - num2 - num3), 0f, 0f - HalfZ); } else { ((Vector3)(ref val))..ctor(0f - HalfX, 0f, 0f - HalfZ + (num4 - num2 - num2 - num3)); } return Centre + Quaternion.Euler(0f, Yaw, 0f) * val; } public string Describe() { if (IsNone) { return "none"; } return Shape switch { PenShape.Square => $"{HalfX * 2f:0.#}m square", PenShape.Rectangle => $"{HalfX * 2f:0.#} x {HalfZ * 2f:0.#}m pen", _ => $"{HalfX:0.#}m circle", }; } } [BepInPlugin("wubarrk.AwayFromHome", "AwayFromHome", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "wubarrk.AwayFromHome"; public const string PluginName = "AwayFromHome"; public const string PluginVersion = "1.0.0"; private readonly Harmony _harmony = new Harmony("wubarrk.AwayFromHome"); private static readonly Type[] PatchTypes = new Type[7] { typeof(LifecyclePatch), typeof(ZoneAnchor), typeof(SectorSubscription), typeof(InputFocusPatch), typeof(KeeperStoneRegistrationPatch), typeof(KeeperStoneLifecyclePatch), typeof(TerrainCompGuard) }; public static Plugin Instance; public static ManualLogSource Log; private void Awake() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; Configuration.Init(((BaseUnityPlugin)this).Config); SiteRegistry.Init(); KeeperUIManager.Init(); ApplyPatches(); Log.LogInfo((object)string.Format("{0} v{1} loaded successfully. Ranches breed, smelters smelt, kilns burn, wherever you aren't standing - build a Keeper Stone with the hammer, or press {2} for the admin panel.", "AwayFromHome", "1.0.0", Configuration.menuKey.Value)); } private void OnDestroy() { KeeperManager.Stop(); } private void ApplyPatches() { Type[] patchTypes = PatchTypes; foreach (Type type in patchTypes) { try { _harmony.PatchAll(type); Log.LogInfo((object)("AwayFromHome: patched " + type.Name + " successfully.")); } catch (Exception arg) { Log.LogError((object)$"AwayFromHome: failed to patch {type.Name} - related feature(s) will be disabled. Reason: {arg}"); } } } } public static class PluginLookup { public static bool TryFind(string guid, out PluginInfo info) { info = null; if (string.IsNullOrEmpty(guid)) { return false; } if (Chainloader.PluginInfos.TryGetValue(guid, out info) && info != null) { return true; } foreach (KeyValuePair pluginInfo in Chainloader.PluginInfos) { if (!string.Equals(pluginInfo.Key, guid, StringComparison.OrdinalIgnoreCase)) { continue; } info = pluginInfo.Value; return info != null; } info = null; return false; } public static bool IsLoaded(string guid) { PluginInfo info; return TryFind(guid, out info); } } public static class ProductionCatchUp { private const string LastTendKey = "afh_lastTend"; private const double MaxCreditSeconds = 3600.0; private static readonly List Scratch = new List(); private static readonly Dictionary SmelterByPrefab = new Dictionary(); private static bool IsProducer(ZDO zdo) { int prefab = zdo.GetPrefab(); if (SmelterByPrefab.TryGetValue(prefab, out var value)) { return value; } bool flag = false; try { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null); flag = (Object)(object)val != (Object)null && (Object)(object)val.GetComponent() != (Object)null; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: could not tell whether a prefab was a producer (treated as not). Reason: {arg}"); } SmelterByPrefab[prefab] = flag; return flag; } public static void Credit(Vector3 pos, int ring) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (!Configuration.creditOfflineProduction.Value || ZDOMan.instance == null || (Object)(object)ZNet.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null) { return; } try { Scratch.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(pos), ring, 0, Scratch, (List)null); long sessionID = ZDOMan.GetSessionID(); long ticks = DateTime.UtcNow.Ticks; DateTime time = ZNet.instance.GetTime(); int num = 0; double num2 = 0.0; foreach (ZDO item in Scratch) { if (item == null || !item.Persistent || !IsProducer(item) || item.GetOwner() != sessionID) { continue; } long num3 = item.GetLong("afh_lastTend", 0L); item.Set("afh_lastTend", ticks); if (num3 <= 0) { continue; } double num4 = (double)(ticks - num3) / 10000000.0; if (num4 <= 1.0) { continue; } long num5 = item.GetLong(ZDOVars.s_startTime, time.Ticks); double totalSeconds = (time - new DateTime(num5)).TotalSeconds; double num6 = num4 - totalSeconds; if (!(num6 <= 1.0)) { if (num6 > 3600.0) { num6 = 3600.0; } item.Set(ZDOVars.s_startTime, num5 - (long)(num6 * 10000000.0)); num++; if (num6 > num2) { num2 = num6; } } } if (num > 0 && Configuration.verboseLogging.Value) { Plugin.Log.LogInfo((object)($"AwayFromHome: gave {num} smelter/kiln the time the world clock owed them " + $"(up to {num2 / 60.0:0.0} min) - a dedicated server freezes its clock while empty, " + "so without this they would never advance at all.")); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: crediting production time failed (non-fatal, production simply will not catch up). Reason: {arg}"); } } } [DisallowMultipleComponent] public class RuneGlow : MonoBehaviour { private const float PeriodSeconds = 7f; private const float MaskedMin = 0.2f; private const float MaskedMax = 0.85f; private float _minIntensity = 0.2f; private float _maxIntensity = 0.85f; private static readonly Color RuneColour = new Color(0.45f, 0.75f, 1f, 1f); private static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor"); private static readonly int EmissiveColorId = Shader.PropertyToID("_EmissiveColor"); private Renderer _renderer; private MaterialPropertyBlock _block; private int _propId = -1; private float _phase; private void Awake() { //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) try { _renderer = ((Component)this).GetComponentInChildren(true); if ((Object)(object)_renderer == (Object)null) { ((Behaviour)this).enabled = false; return; } Material sharedMaterial = _renderer.sharedMaterial; if ((Object)(object)sharedMaterial == (Object)null) { ((Behaviour)this).enabled = false; return; } if (sharedMaterial.HasProperty(EmissionColorId)) { _propId = EmissionColorId; } else { if (!sharedMaterial.HasProperty(EmissiveColorId)) { ((Behaviour)this).enabled = false; return; } _propId = EmissiveColorId; } if (!sharedMaterial.HasProperty("_EmissionMap") || !((Object)(object)sharedMaterial.GetTexture("_EmissionMap") != (Object)null)) { MaterialPropertyBlock val = new MaterialPropertyBlock(); _renderer.GetPropertyBlock(val); val.SetColor(_propId, Color.black); _renderer.SetPropertyBlock(val); Plugin.Log.LogInfo((object)"AwayFromHome: no emissive mask on the Keeper Stone, so the rune pulse is off - an unmasked glow would light the whole model instead of the runes."); ((Behaviour)this).enabled = false; } else { sharedMaterial.EnableKeyword("_EMISSION"); _minIntensity = 0.2f; _maxIntensity = 0.85f; _block = new MaterialPropertyBlock(); Vector3 position = ((Component)this).transform.position; _phase = Mathf.Abs(position.x * 0.37f + position.z * 0.61f) % 7f; } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: rune glow setup failed (non-fatal, the stone just will not pulse). Reason: {arg}"); ((Behaviour)this).enabled = false; } } private void Update() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_renderer == (Object)null || _block == null) { return; } try { float num = (Time.time + _phase) * 0.8975979f; float num2 = (Mathf.Sin(num) + 1f) * 0.5f; float num3 = Mathf.Lerp(_minIntensity, _maxIntensity, num2); _renderer.GetPropertyBlock(_block); _block.SetColor(_propId, RuneColour * num3); _renderer.SetPropertyBlock(_block); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: rune glow update failed (non-fatal, disabling it). Reason: {arg}"); ((Behaviour)this).enabled = false; } } } [HarmonyPatch] public static class SectorSubscription { private sealed class Sub { public Vector3 Pos; public float Expires; } public const string RpcName = "AwayFromHome_AnchorSectors"; private const float SubscriptionSeconds = 15f; public const float RenewSeconds = 5f; private static readonly Dictionary Subs = new Dictionary(); private static readonly List TempSectorZdos = new List(); public static void OnZNetStart() { Subs.Clear(); if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("AwayFromHome_AnchorSectors", (Action)RPC_AnchorSectors); } } public static void Renew(Vector3 pos) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) try { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC("AwayFromHome_AnchorSectors", new object[1] { pos }); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: renewing the sector subscription failed (non-fatal). Reason: {arg}"); } } public static void Release() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Renew(Vector3.zero); } private static void RPC_AnchorSectors(long sender, Vector3 pos) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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_0079: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { if (pos == Vector3.zero) { Subs.Remove(sender); } else if (!Configuration.allowRemoteSites.Value) { Subs.Remove(sender); } else if (!float.IsNaN(pos.x) && !float.IsNaN(pos.y) && !float.IsNaN(pos.z) && !float.IsInfinity(pos.x) && !float.IsInfinity(pos.y) && !float.IsInfinity(pos.z)) { Subs[sender] = new Sub { Pos = pos, Expires = Time.realtimeSinceStartup + 15f }; } } } catch (Exception arg) { Plugin.Log.LogError((object)string.Format("AwayFromHome: {0} failed (non-fatal). Reason: {1}", "RPC_AnchorSectors", arg)); } } [HarmonyPatch(typeof(ZDOMan), "CreateSyncList")] [HarmonyPostfix] private static void ZDOMan_CreateSyncList_Postfix(ZDOMan __instance, ZDOPeer peer, List toSync) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: 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) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) if (Subs.Count == 0) { return; } try { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } if (!Configuration.allowRemoteSites.Value) { Subs.Clear(); } else { if (!Subs.TryGetValue(peer.m_peer.m_uid, out var value)) { return; } if (Time.realtimeSinceStartup > value.Expires) { Subs.Remove(peer.m_peer.m_uid); return; } Vector2i zone = ZoneSystem.GetZone(value.Pos); Vector2i zone2 = ZoneSystem.GetZone(peer.m_peer.GetRefPos()); int num = ZoneSystem.instance.m_activeArea + ZoneAnchor.Ring; if (Mathf.Abs(zone.x - zone2.x) <= num && Mathf.Abs(zone.y - zone2.y) <= num) { return; } Vector2i val = default(Vector2i); GameObject val2 = default(GameObject); for (int i = zone.y - ZoneAnchor.Ring; i <= zone.y + ZoneAnchor.Ring; i++) { bool flag = false; for (int j = zone.x - ZoneAnchor.Ring; j <= zone.x + ZoneAnchor.Ring; j++) { ((Vector2i)(ref val))..ctor(j, i); if (!ZoneSystem.instance.IsZoneGenerated(val) && ZoneSystem.instance.SpawnZone(val, (SpawnMode)2, ref val2)) { flag = true; break; } } if (flag) { break; } } TempSectorZdos.Clear(); __instance.FindSectorObjects(zone, ZoneAnchor.Ring, ZoneSystem.instance.m_activeDistantArea, TempSectorZdos, (List)null); { foreach (ZDO tempSectorZdo in TempSectorZdos) { if (peer.ShouldSend(tempSectorZdo)) { toSync.Add(tempSectorZdo); } } return; } } } catch (Exception arg) { Subs.Clear(); Plugin.Log.LogError((object)$"AwayFromHome: feeding subscribed sectors into a sync list failed (non-fatal, subscriptions dropped). Reason: {arg}"); } } } public static class SiteCensus { public readonly struct Result { public readonly int Total; public readonly int Pieces; public readonly int Unbuilt; public readonly bool ZonesLoaded; public readonly bool Valid; public Result(int total, int pieces, int unbuilt, bool zonesLoaded, bool valid) { Total = total; Pieces = pieces; Unbuilt = unbuilt; ZonesLoaded = zonesLoaded; Valid = valid; } public bool MeetsMark(int mark) { if (mark <= 0) { return true; } return Pieces >= Mathf.CeilToInt((float)mark * 0.9f); } public string Describe(int mark) { return string.Format("{0} piece(s) of an expected {1}, ", Pieces, (mark > 0) ? mark.ToString() : "unknown") + string.Format("{0} object(s) total, {1} not yet instantiated, zones {2}", Total, Unbuilt, ZonesLoaded ? "all loaded" : "still loading"); } } public const string PiecesKey = "afh_pieces"; private const float PresentFraction = 0.9f; private static readonly Dictionary PieceByPrefab = new Dictionary(); private static readonly List Scratch = new List(); private static bool IsPiece(int prefab) { if (PieceByPrefab.TryGetValue(prefab, out var value)) { return value; } bool flag = false; try { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null); flag = (Object)(object)val != (Object)null && (Object)(object)val.GetComponent() != (Object)null; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: could not tell whether a prefab was a build piece (treated as not). Reason: {arg}"); } PieceByPrefab[prefab] = flag; return flag; } public static Result Take(Vector3 pos, int ring) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return new Result(0, 0, 0, zonesLoaded: false, valid: false); } try { Vector2i zone = ZoneSystem.GetZone(pos); bool flag = true; for (int i = zone.y - ring; i <= zone.y + ring && flag; i++) { for (int j = zone.x - ring; j <= zone.x + ring; j++) { if (!ZoneSystem.instance.IsZoneLoaded(new Vector2i(j, i))) { flag = false; break; } } } Scratch.Clear(); ZDOMan.instance.FindSectorObjects(zone, ring, 0, Scratch, (List)null); int num = 0; int num2 = 0; foreach (ZDO item in Scratch) { if (item == null) { continue; } int prefab = item.GetPrefab(); if (prefab != 0 && !((Object)(object)ZNetScene.instance.GetPrefab(prefab) == (Object)null)) { if (IsPiece(prefab)) { num++; } if ((Object)(object)ZNetScene.instance.FindInstance(item) == (Object)null) { num2++; } } } return new Result(Scratch.Count, num, num2, flag, valid: true); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: taking a census of a site failed (non-fatal, treated as not-ready). Reason: {arg}"); return new Result(0, 0, 0, zonesLoaded: false, valid: false); } } public static int ReadMark(ZDOID siteId) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) try { ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(siteId) : null); return (val != null && val.IsValid()) ? val.GetInt("afh_pieces", 0) : 0; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: reading a site's expected piece count failed (non-fatal, treated as unknown). Reason: {arg}"); return 0; } } public static void WriteMark(ZDOID siteId, int pieces) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (pieces <= 0) { return; } try { ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(siteId) : null); if (val != null && val.IsValid() && val.GetInt("afh_pieces", 0) != pieces && val.GetOwner() == ZDOMan.GetSessionID()) { val.Set("afh_pieces", pieces); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: recording a site's piece count failed (non-fatal). Reason: {arg}"); } } } public sealed class SiteRecord { public ZDOID Id; public long OwnerId; public string OwnerName; public Vector3 Pos; public string Label => $"({Pos.x:0}, {Pos.z:0})"; } public static class SiteRegistry { private const byte FormatVersion = 3; private const string AdminRemoveRpc = "AwayFromHome_AdminRemoveSite"; private const string SetLeashRpc = "AwayFromHome_SetLeash"; private const string ReplyRpc = "AwayFromHome_SiteReply"; public static readonly CustomSyncedValue SyncedSites = new CustomSyncedValue(Configuration.configSync, "afh_sites", ""); private static readonly List ServerSites = new List(); private static readonly List _decoded = new List(); private static string _decodedFrom; private static bool _scanning; public static string LastReply { get; private set; } = ""; public static bool LastReplyOk { get; private set; } public static long LocalOwnerId => ((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerID() : 0; public static event Action OnReply; public static void Init() { Plugin.Log.LogDebug((object)("AwayFromHome: site index registered (" + SyncedSites.Identifier + ").")); } public static void OnZNetStart() { _decodedFrom = null; if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("AwayFromHome_AdminRemoveSite", (Action)RPC_AdminRemove); ZRoutedRpc.instance.Register("AwayFromHome_SetLeash", (Method)RPC_SetLeash); ZRoutedRpc.instance.Register("AwayFromHome_SiteReply", (Action)RPC_Reply); } } public static IReadOnlyList GetAllKnownSites() { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { return ServerSites; } string text = SyncedSites.Value ?? ""; if (text != _decodedFrom) { try { Decode(text, _decoded); } catch (Exception arg) { _decoded.Clear(); Plugin.Log.LogError((object)$"AwayFromHome: site index could not be read, list will be empty until the next server publish. Reason: {arg}"); } _decodedFrom = text; } return _decoded; } public static IReadOnlyList GetMySites() { long me = LocalOwnerId; if (me == 0) { return new List(); } return (from s in GetAllKnownSites() where s.OwnerId == me select s).ToList(); } public static IEnumerator ScanLoop() { while (true) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { yield return (object)new WaitForSeconds(5f); continue; } yield return Rescan(); yield return (object)new WaitForSeconds(Mathf.Max(5f, Configuration.scanIntervalSeconds.Value)); } } private static IEnumerator Rescan() { if (_scanning) { yield break; } _scanning = true; List found = new List(); int index = 0; bool done = false; int guard = 0; while (!done) { try { done = ZDOMan.instance.GetAllZDOsWithPrefabIterative("AFH_KeeperStone", found, ref index); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: scanning for Keeper Stones failed (non-fatal, keeping the previous list). Reason: {arg}"); _scanning = false; yield break; } int num = guard + 1; guard = num; if (num > 10000) { Plugin.Log.LogError((object)"AwayFromHome: Keeper Stone scan did not terminate - abandoning this pass."); _scanning = false; yield break; } if (!done) { yield return null; } } List sites = new List(found.Count); foreach (ZDO z in found) { if (z != null) { sites.Add(new SiteRecord { Id = z.m_uid, OwnerId = z.GetLong(ZDOVars.s_creator, 0L), OwnerName = z.GetString(ZDOVars.s_creatorName, ""), Pos = z.GetPosition() }); } } bool changed = sites.Count != ServerSites.Count || !sites.Select((SiteRecord s) => s.Id).SequenceEqual(ServerSites.Select((SiteRecord s) => s.Id)); ServerSites.Clear(); ServerSites.AddRange(sites); if (changed) { Publish(); if (Configuration.verboseLogging.Value) { Plugin.Log.LogInfo((object)$"AwayFromHome: {ServerSites.Count} Keeper Stone(s) standing."); } } _scanning = false; } private static void Publish() { try { SyncedSites.Value = Encode(ServerSites); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: publishing the site index failed (non-fatal, clients keep the previous list). Reason: {arg}"); } } public static void RequestImmediateRescan() { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !((Object)(object)Plugin.Instance == (Object)null)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(Rescan()); } } public static void RequestAdminRemove(ZDOID id) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) try { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC("AwayFromHome_AdminRemoveSite", new object[1] { id }); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: requesting an admin site removal failed (non-fatal). Reason: {arg}"); } } private static void RPC_AdminRemove(long sender, ZDOID id) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { if (!IsAuthorizedAdmin(sender)) { Plugin.Log.LogWarning((object)$"AwayFromHome: refused an admin site-removal from a non-admin sender ({sender})."); Reply(sender, ok: false, "You are not an admin on this server."); return; } ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(id) : null); if (val == null) { Reply(sender, ok: false, "That Keeper Stone is already gone."); return; } int num = KeeperStore.StoredItemCount(val); if (num > 0) { Reply(sender, ok: false, $"That Keeper Stone still holds {num} item(s) - empty it first, or knock it down in the world so the contents drop."); return; } val.SetOwner(ZDOMan.GetSessionID()); ZDOMan.instance.DestroyZDO(val); Reply(sender, ok: true, "Keeper Stone removed."); RequestImmediateRescan(); } catch (Exception arg) { Plugin.Log.LogError((object)string.Format("AwayFromHome: {0} failed (non-fatal). Reason: {1}", "RPC_AdminRemove", arg)); Reply(sender, ok: false, "Server error removing the stone."); } } public static void RequestSetLeash(ZDOID id, int shape, float halfX, float halfZ, float yaw) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) try { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC("AwayFromHome_SetLeash", new object[5] { id, shape, halfX, halfZ, yaw }); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: requesting a leash change failed (non-fatal). Reason: {arg}"); } } private static void RPC_SetLeash(long sender, ZDOID id, int shape, float halfX, float halfZ, float yaw) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(id) : null); if (val == null) { Reply(sender, ok: false, "That Keeper Stone is gone."); return; } if (!IsAuthorizedAdmin(sender) && !IsSenderTheCreator(sender, val)) { Reply(sender, ok: false, "That Keeper Stone is not yours."); return; } float value = Configuration.maxLeashMeters.Value; PenShape penShape = (PenShape)shape; if (penShape != PenShape.Square && penShape != PenShape.Rectangle) { penShape = PenShape.Circle; } float num = Mathf.Clamp(halfX, 0f, value); float num2 = ((penShape == PenShape.Rectangle) ? Mathf.Clamp(halfZ, 0f, value) : num); float num3 = ((penShape == PenShape.Circle) ? 0f : Mathf.Repeat(yaw, 180f)); val.SetOwner(ZDOMan.GetSessionID()); val.Set("afh_leash", num); val.Set("afh_leash_shape", (int)penShape); val.Set("afh_leash_z", num2); val.Set("afh_leash_yaw", num3); PenArea penArea = new PenArea(penShape, val.GetPosition(), num, num2, num3); Reply(sender, ok: true, penArea.IsNone ? "Leash removed from that Keeper Stone." : ("Leash set to a " + penArea.Describe() + ".")); } catch (Exception arg) { Plugin.Log.LogError((object)string.Format("AwayFromHome: {0} failed (non-fatal). Reason: {1}", "RPC_SetLeash", arg)); Reply(sender, ok: false, "Server error setting the leash."); } } private static bool IsSenderTheCreator(long sender, ZDO stone) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) long num = stone.GetLong(ZDOVars.s_creator, 0L); if (num == 0) { return false; } ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer == null || ((ZDOID)(ref peer.m_characterID)).IsNone()) { return false; } ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(peer.m_characterID) : null); if (val == null || !val.IsValid()) { return false; } return val.GetLong(ZDOVars.s_playerID, 0L) == num; } private static void RPC_Reply(long sender, bool ok, string message) { if (!IsFromServer(sender)) { return; } LastReplyOk = ok; LastReply = message ?? ""; try { SiteRegistry.OnReply?.Invoke(); } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: a site-reply listener threw (non-fatal). Reason: {arg}"); } } private static void Reply(long sender, bool ok, string message) { try { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(sender, "AwayFromHome_SiteReply", new object[2] { ok, message }); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: sending a site-reply failed (non-fatal). Reason: {arg}"); } } private static bool IsSelf(long sender) { return sender == ZDOMan.GetSessionID(); } private static bool IsFromServer(long sender) { if ((Object)(object)ZNet.instance == (Object)null) { return false; } if (ZNet.instance.IsServer()) { return IsSelf(sender); } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); return serverPeer != null && sender == serverPeer.m_uid; } public static bool IsAuthorizedAdmin(long sender) { if (IsSelf(sender)) { return true; } ZNetPeer peer = ZNet.instance.GetPeer(sender); string text = ((peer != null && peer.m_socket != null) ? peer.m_socket.GetHostName() : ""); return !string.IsNullOrEmpty(text) && ZNet.instance.IsAdmin(text); } private static string Encode(List sites) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write((byte)3); val.Write(sites.Count); foreach (SiteRecord site in sites) { val.Write(site.Id); val.Write(site.OwnerId); val.Write(site.OwnerName ?? ""); val.Write(site.Pos); } return val.GetBase64(); } private static void Decode(string blob, List into) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) into.Clear(); if (string.IsNullOrEmpty(blob)) { return; } ZPackage val = new ZPackage(blob); byte b = val.ReadByte(); if (b != 3) { Plugin.Log.LogWarning((object)$"AwayFromHome: site index format {b} is not the expected {(byte)3} - the server is running a different version of the mod."); return; } int num = val.ReadInt(); for (int i = 0; i < num; i++) { into.Add(new SiteRecord { Id = val.ReadZDOID(), OwnerId = val.ReadLong(), OwnerName = val.ReadString(), Pos = val.ReadVector3() }); } } } public static class SiteTelemetry { private static readonly List Scratch = new List(); public static void Report(Vector3 pos, int ring, string siteLabel) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (!Configuration.verboseLogging.Value || ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return; } try { Scratch.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(pos), ring, 0, Scratch, (List)null); List list = new List(); List list2 = new List(); foreach (ZDO item in Scratch) { if (item == null || !item.Persistent) { continue; } GameObject prefab = ZNetScene.instance.GetPrefab(item.GetPrefab()); if (!((Object)(object)prefab == (Object)null)) { Tameable component = prefab.GetComponent(); if ((Object)(object)component != (Object)null) { list.Add(DescribeAnimal(item, prefab, component)); } else if ((Object)(object)prefab.GetComponent() != (Object)null) { list2.Add(DescribeSmelter(item, prefab)); } } } if (list.Count == 0 && list2.Count == 0) { return; } Plugin.Log.LogInfo((object)("AwayFromHome: [" + siteLabel + "] end-of-visit state:")); foreach (string item2 in list) { Plugin.Log.LogInfo((object)(" " + item2)); } foreach (string item3 in list2) { Plugin.Log.LogInfo((object)(" " + item3)); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: reporting site state failed (non-fatal, diagnostics only). Reason: {arg}"); } } private static string DescribeAnimal(ZDO zdo, GameObject prefab, Tameable tameable) { if (zdo.GetBool(ZDOVars.s_tamed, false)) { return ((Object)prefab).name + ": TAMED"; } float num = Mathf.Max(1f, tameable.m_tamingTime); float num2 = zdo.GetFloat(ZDOVars.s_tameTimeLeft, num); float num3 = Mathf.Clamp01(1f - num2 / num) * 100f; long num4 = zdo.GetLong(ZDOVars.s_tameLastFeeding, 0L); string text; if (num4 <= 0) { text = "never fed - not taming (wild, or no food it can reach)"; } else if ((Object)(object)ZNet.instance == (Object)null) { text = "fed at some point"; } else { double totalSeconds = (ZNet.instance.GetTime() - new DateTime(num4)).TotalSeconds; text = ((totalSeconds > (double)tameable.m_fedDuration) ? $"HUNGRY (fed {totalSeconds / 60.0:0.0} min ago, goes hungry after {tameable.m_fedDuration / 60f:0.0}) - taming is stalled until it eats" : $"fed {totalSeconds / 60.0:0.0} min ago, still full"); } return $"{((Object)prefab).name}: taming {num3:0.0}% ({num2 / 60f:0.0} min of {num / 60f:0.0} left), {text}"; } private static string DescribeSmelter(ZDO zdo, GameObject prefab) { float num = zdo.GetFloat(ZDOVars.s_fuel, 0f); int num2 = zdo.GetInt(ZDOVars.s_queued, 0); float num3 = zdo.GetFloat(ZDOVars.s_bakeTimer, 0f); Smelter component = prefab.GetComponent(); float num4 = (((Object)(object)component != (Object)null) ? component.m_secPerProduct : 0f); string text = ((num4 > 0f) ? $"{Mathf.Clamp01(num3 / num4) * 100f:0}% toward the next unit ({num3:0}s of {num4:0}s)" : $"{num3:0}s baked"); string text2 = ((zdo.GetOwner() == ZDOMan.GetSessionID()) ? "keeper-owned" : (zdo.HasOwner() ? "OWNED BY SOMEONE ELSE - not simulating for us" : "UNOWNED - not simulating at all")); return $"{((Object)prefab).name}: {num2} ore queued, {num:0.#} fuel, {text}, {text2}"; } } [HarmonyPatch] public static class TerrainCompGuard { private static readonly FieldRef HmapRef = AccessTools.FieldRefAccess("m_hmap"); private static readonly FieldRef ModifiedHeightRef = AccessTools.FieldRefAccess("m_modifiedHeight"); private static int _suppressed; private static float _lastReportAt = float.NegativeInfinity; [HarmonyPatch(typeof(TerrainComp), "Load")] [HarmonyPrefix] public static bool TerrainComp_Load_Prefix(TerrainComp __instance, ref bool __result) { try { if ((Object)(object)__instance == (Object)null) { return true; } if ((Object)(object)HmapRef.Invoke(__instance) != (Object)null && ModifiedHeightRef.Invoke(__instance) != null) { return true; } __result = false; _suppressed++; if (Time.realtimeSinceStartup - _lastReportAt > 300f) { _lastReportAt = Time.realtimeSinceStartup; Plugin.Log.LogInfo((object)$"AwayFromHome: suppressed {_suppressed} vanilla TerrainComp.Load null-dereference(s) so far (zones whose heightmap had not arrived when they woke). Harmless - the terrain edits were already unreachable; this only stops the exception spam."); } return false; } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: the TerrainComp guard failed, letting vanilla run. Reason: {arg}"); return true; } } } internal static class AFHUITheme { private enum Edge { Top, Bottom, Left, Right } private sealed class Painter { public bool MirrorX; public bool MirrorY; public bool Transpose; private readonly int _w; private readonly int _h; private readonly float[] _a; private readonly float[] _z; public Painter(int w, int h) { _w = w; _h = h; _a = new float[w * h]; _z = new float[w * h]; } private void Put(float fx, float fy, float a, float z) { if (a <= 0f) { return; } if (Transpose) { float num = fx; fx = fy; fy = num; } int num2 = Mathf.RoundToInt(fx); int num3 = Mathf.RoundToInt(fy); if (MirrorX) { num2 = _w - 1 - num2; } if (MirrorY) { num3 = _h - 1 - num3; } if (num2 >= 0 && num3 >= 0 && num2 < _w && num3 < _h) { int num4 = num3 * _w + num2; if (a > _a[num4]) { _a[num4] = a; } if (z > _z[num4]) { _z[num4] = z; } } } public void Disc(float cx, float cy, float r) { if (r <= 0f) { return; } int num = Mathf.FloorToInt(cx - r - 1f); int num2 = Mathf.CeilToInt(cx + r + 1f); int num3 = Mathf.FloorToInt(cy - r - 1f); int num4 = Mathf.CeilToInt(cy + r + 1f); for (int i = num3; i <= num4; i++) { for (int j = num; j <= num2; j++) { float num5 = (float)j - cx; float num6 = (float)i - cy; float num7 = Mathf.Sqrt(num5 * num5 + num6 * num6); float num8 = Mathf.Clamp01(r + 0.5f - num7); if (!(num8 <= 0f)) { Put(j, i, num8, Mathf.Sqrt(Mathf.Max(0f, 1f - num7 / r * (num7 / r)))); } } } } public void Lozenge(float cx, float cy, float r) { int num = Mathf.FloorToInt(cx - r - 1f); int num2 = Mathf.CeilToInt(cx + r + 1f); int num3 = Mathf.FloorToInt(cy - r - 1f); int num4 = Mathf.CeilToInt(cy + r + 1f); for (int i = num3; i <= num4; i++) { for (int j = num; j <= num2; j++) { float num5 = Mathf.Abs((float)j - cx) + Mathf.Abs((float)i - cy); float num6 = Mathf.Clamp01(r + 0.5f - num5); if (!(num6 <= 0f)) { Put(j, i, num6, Mathf.Sqrt(Mathf.Max(0f, 1f - num5 / r * (num5 / r)))); } } } } public void Taper(Vector2 a, Vector2 b, float w0, float w1) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //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_002f: 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) int num = Mathf.Max(8, Mathf.CeilToInt(Vector2.Distance(a, b) * 3f)); for (int i = 0; i <= num; i++) { float num2 = (float)i / (float)num; Vector2 val = Vector2.Lerp(a, b, num2); Disc(val.x, val.y, Mathf.Lerp(w0, w1, num2)); } } public void Bezier(Vector2 a, Vector2 b, Vector2 c, float w0, float w1) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: 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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_0061: 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_006b: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(16, Mathf.CeilToInt((Vector2.Distance(a, b) + Vector2.Distance(b, c)) * 3f)); for (int i = 0; i <= num; i++) { float num2 = (float)i / (float)num; float num3 = 1f - num2; Vector2 val = num3 * num3 * a + 2f * num3 * num2 * b + num2 * num2 * c; Disc(val.x, val.y, Mathf.Lerp(w0, w1, num2)); } } public void Spiral(Vector2 eye, float r0, float growth, float t0, float t1, float phase, float w0, float w1, bool mirror = false) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(48, Mathf.CeilToInt((t1 - t0) * 40f)); for (int i = 0; i <= num; i++) { float num2 = (float)i / (float)num; float num3 = Mathf.Lerp(t0, t1, num2); float num4 = r0 * Mathf.Exp(growth * num3); float num5 = num3 + phase; float num6 = Mathf.Cos(num5) * num4; float num7 = Mathf.Sin(num5) * num4; if (mirror) { num6 = 0f - num6; } Disc(eye.x + num6, eye.y + num7, Mathf.Lerp(w0, w1, num2)); } } public void RailPixel(int x, int y, float d, float half) { float num = Mathf.Clamp01(half + 0.5f - d); if (!(num <= 0f)) { Put(x, y, num, Mathf.Sqrt(Mathf.Max(0f, 1f - d / half * (d / half)))); } } public void RailElbow(float mid, float half, float centre) { float num = centre - mid; for (int i = 0; i < _h; i++) { for (int j = 0; j < _w; j++) { float d; if (!((float)j <= centre) || !((float)i <= centre)) { d = ((!((float)i <= centre)) ? ((!((float)j <= centre)) ? Mathf.Min(Mathf.Abs((float)i - mid), Mathf.Abs((float)j - mid)) : Mathf.Abs((float)j - mid)) : Mathf.Abs((float)i - mid)); } else { float num2 = (float)j - centre; float num3 = (float)i - centre; d = Mathf.Abs(Mathf.Sqrt(num2 * num2 + num3 * num3) - num); } RailPixel(j, i, d, half); } } } public Texture2D Bake() { //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_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) Texture2D val = New(_w, _h, (TextureWrapMode)1, (FilterMode)1); Color[] array = (Color[])(object)new Color[_w * _h]; for (int i = 0; i < _h; i++) { for (int j = 0; j < _w; j++) { int num = i * _w + j; float num2 = _a[num]; int num3 = (_h - 1 - i) * _w + j; if (num2 <= 0f) { array[num3] = Color.clear; continue; } float num4 = Z(j - 1, i) - Z(j + 1, i) + (Z(j, i - 1) - Z(j, i + 1)); float num5 = Mathf.Clamp01(0.42f + 0.5f * num4 + 0.18f * _z[num]); Color val2 = ((num5 < 0.5f) ? Color.Lerp(GoldDeep, Gold, num5 * 2f) : Color.Lerp(Gold, GoldBright, (num5 - 0.5f) * 2f)); array[num3] = new Color(val2.r, val2.g, val2.b, num2); } } val.SetPixels(array); val.Apply(false); return val; } private float Z(int x, int y) { return _z[Mathf.Clamp(y, 0, _h - 1) * _w + Mathf.Clamp(x, 0, _w - 1)]; } } public static Color GoldDeep = new Color(0.26f, 0.17f, 0.05f, 1f); public static Color Gold = new Color(0.8f, 0.62f, 0.26f, 1f); public static Color GoldBright = new Color(1f, 0.94f, 0.72f, 1f); public static readonly Color Parchment = new Color(0.9f, 0.86f, 0.75f, 1f); public static readonly Color Muted = new Color(0.6f, 0.56f, 0.48f, 1f); public static readonly Color BadColour = new Color(0.88f, 0.3f, 0.26f, 1f); public static readonly Color GoodColour = new Color(0.53f, 0.82f, 0.47f, 1f); private static Color _builtGold = new Color(0.8f, 0.62f, 0.26f, 1f); private const float TextScale = 1f; private const int FontDelta = 0; public const float Band = 18f; public const float Pad = 22f; public const float TitleHeight = 54f; public const float FooterHeight = 30f; private const int CornerTile = 84; private const float Overhang = 10f; private const float CornerExtent = 74f; private const int CrestW = 56; private const int CrestH = 34; private const float CrestOverhang = 8f; private const float OuterMid = 4.4f; private const float OuterHalf = 2.7f; private const float InnerMid = 12.4f; private const float InnerHalf = 1.6f; private const float BendCentre = 32f; public static GUIStyle Title; public static GUIStyle SubTitle; public static GUIStyle Header; public static GUIStyle Key; public static GUIStyle Value; public static GUIStyle Note; public static GUIStyle Footer; public static GUIStyle Button; public static GUIStyle Primary; public static GUIStyle Row; public static GUIStyle Field; private static Texture2D _railTop; private static Texture2D _railBottom; private static Texture2D _railLeft; private static Texture2D _railRight; private static Texture2D _cornerTL; private static Texture2D _cornerTR; private static Texture2D _cornerBL; private static Texture2D _cornerBR; private static Texture2D _crestTop; private static Texture2D _crestBottom; private static Texture2D _diamond; private static Texture2D _dot; private static Texture2D _panel; private static Texture2D _white; private static Texture2D _btnNormal; private static Texture2D _btnHover; private static Texture2D _btnActive; private static Texture2D _rowNormal; private static Texture2D _rowHover; private static Texture2D _rowActive; private static Texture2D _selection; private static Texture2D _fieldTex; public static void EnsureBuilt() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Unknown result type (might be due to invalid IL or missing references) Color val = ((Configuration.uiGoldColour != null) ? Configuration.uiGoldColour.Value : _builtGold); if ((Object)(object)_railTop != (Object)null && val != _builtGold) { DestroyTextures(); } if (!((Object)(object)_railTop != (Object)null)) { _builtGold = val; Gold = new Color(val.r, val.g, val.b, 1f); if (Mathf.Approximately(Gold.r, 0.8f) && Mathf.Approximately(Gold.g, 0.62f) && Mathf.Approximately(Gold.b, 0.26f)) { GoldDeep = new Color(0.26f, 0.17f, 0.05f, 1f); GoldBright = new Color(1f, 0.94f, 0.72f, 1f); } else { GoldDeep = new Color(Gold.r * 0.325f, Gold.g * 0.274f, Gold.b * 0.192f, 1f); GoldBright = Color.Lerp(Gold, Color.white, 0.72f); } _railTop = BuildRail(Edge.Top); _railBottom = BuildRail(Edge.Bottom); _railLeft = BuildRail(Edge.Left); _railRight = BuildRail(Edge.Right); _cornerTL = BuildCorner(mirrorX: false, mirrorY: false); _cornerTR = BuildCorner(mirrorX: true, mirrorY: false); _cornerBL = BuildCorner(mirrorX: false, mirrorY: true); _cornerBR = BuildCorner(mirrorX: true, mirrorY: true); _crestTop = BuildCrest(flip: false); _crestBottom = BuildCrest(flip: true); _diamond = BuildDiamond(14); _dot = BuildDot(24); _panel = BuildPanel(64); _white = BuildSolid(Color.white); _btnNormal = BuildPatch(new Color(0.1f, 0.085f, 0.065f, 0.95f), GoldDeep); _btnHover = BuildPatch(new Color(0.19f, 0.15f, 0.09f, 0.97f), Gold); _btnActive = BuildPatch(new Color(0.3f, 0.23f, 0.11f, 0.98f), GoldBright); _rowNormal = BuildPatch(Color.clear, Color.clear); _rowHover = BuildPatch(new Color(Gold.r, Gold.g, Gold.b, 0.1f), new Color(Gold.r, Gold.g, Gold.b, 0.45f)); _rowActive = BuildPatch(new Color(Gold.r, Gold.g, Gold.b, 0.2f), Gold); _selection = BuildSolid(new Color(Gold.r, Gold.g, Gold.b, 0.15f)); _fieldTex = BuildPatch(new Color(0.03f, 0.03f, 0.025f, 0.95f), GoldDeep); BuildStyles(); } } private static void DestroyTextures() { Texture2D[] array = (Texture2D[])(object)new Texture2D[22] { _railTop, _railBottom, _railLeft, _railRight, _cornerTL, _cornerTR, _cornerBL, _cornerBR, _crestTop, _crestBottom, _diamond, _dot, _panel, _white, _btnNormal, _btnHover, _btnActive, _rowNormal, _rowHover, _rowActive, _selection, _fieldTex }; Texture2D[] array2 = array; foreach (Texture2D val in array2) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } _railTop = (_railBottom = (_railLeft = (_railRight = null))); _cornerTL = (_cornerTR = (_cornerBL = (_cornerBR = null))); _crestTop = (_crestBottom = (_diamond = (_dot = null))); _panel = (_white = null); _btnNormal = (_btnHover = (_btnActive = null)); _rowNormal = (_rowHover = (_rowActive = (_selection = null))); _fieldTex = null; Title = null; } public static void DrawWindow(Rect win, string title) { //IL_003b: 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) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) EnsureBuilt(); GUI.DrawTexture(new Rect(((Rect)(ref win)).x + 1f, ((Rect)(ref win)).y + 1f, ((Rect)(ref win)).width - 2f, ((Rect)(ref win)).height - 2f), (Texture)(object)_panel, (ScaleMode)0); DrawFrame(win); Rect r = default(Rect); ((Rect)(ref r))..ctor(((Rect)(ref win)).x + 74f, ((Rect)(ref win)).y + 18f + 8f, ((Rect)(ref win)).width - 148f, 34f); DrawShadowed(r, title.ToUpperInvariant(), Title); DrawRule(new Rect(((Rect)(ref win)).x + 18f + 22f, ((Rect)(ref win)).y + 18f + 54f - 12f, ((Rect)(ref win)).width - 80f, 1f)); } public static Rect Body(Rect win) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) return new Rect(((Rect)(ref win)).x + 18f + 22f, ((Rect)(ref win)).y + 18f + 54f, ((Rect)(ref win)).width - 80f, ((Rect)(ref win)).height - 36f - 54f - 30f); } public static Rect FooterLine(Rect win) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) return new Rect(((Rect)(ref win)).x + 18f + 22f, ((Rect)(ref win)).yMax - 18f - 30f, ((Rect)(ref win)).width - 80f, 18f); } private static void DrawFrame(Rect r) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) float num = ((Rect)(ref r)).width - 148f; float num2 = ((Rect)(ref r)).height - 148f; GUI.DrawTexture(new Rect(((Rect)(ref r)).x + 74f, ((Rect)(ref r)).y, num, 18f), (Texture)(object)_railTop); GUI.DrawTexture(new Rect(((Rect)(ref r)).x + 74f, ((Rect)(ref r)).yMax - 18f, num, 18f), (Texture)(object)_railBottom); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 74f, 18f, num2), (Texture)(object)_railLeft); GUI.DrawTexture(new Rect(((Rect)(ref r)).xMax - 18f, ((Rect)(ref r)).y + 74f, 18f, num2), (Texture)(object)_railRight); float num3 = 74f; GUI.DrawTexture(new Rect(((Rect)(ref r)).x - 10f, ((Rect)(ref r)).y - 10f, 84f, 84f), (Texture)(object)_cornerTL); GUI.DrawTexture(new Rect(((Rect)(ref r)).xMax - num3, ((Rect)(ref r)).y - 10f, 84f, 84f), (Texture)(object)_cornerTR); GUI.DrawTexture(new Rect(((Rect)(ref r)).x - 10f, ((Rect)(ref r)).yMax - num3, 84f, 84f), (Texture)(object)_cornerBL); GUI.DrawTexture(new Rect(((Rect)(ref r)).xMax - num3, ((Rect)(ref r)).yMax - num3, 84f, 84f), (Texture)(object)_cornerBR); float num4 = ((Rect)(ref r)).center.x - 28f; GUI.DrawTexture(new Rect(num4, ((Rect)(ref r)).y - 8f, 56f, 34f), (Texture)(object)_crestTop); GUI.DrawTexture(new Rect(num4, ((Rect)(ref r)).yMax + 8f - 34f, 56f, 34f), (Texture)(object)_crestBottom); DrawOutline(new Rect(((Rect)(ref r)).x + 18f, ((Rect)(ref r)).y + 18f, ((Rect)(ref r)).width - 36f, ((Rect)(ref r)).height - 36f), new Color(Gold.r, Gold.g, Gold.b, 0.45f), 1f); } public static void DrawOutline(Rect r, Color c, float t) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = c; GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width, t), (Texture)(object)_white); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).yMax - t, ((Rect)(ref r)).width, t), (Texture)(object)_white); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, t, ((Rect)(ref r)).height), (Texture)(object)_white); GUI.DrawTexture(new Rect(((Rect)(ref r)).xMax - t, ((Rect)(ref r)).y, t, ((Rect)(ref r)).height), (Texture)(object)_white); GUI.color = color; } public static void DrawFill(Rect r, Color c) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = c; GUI.DrawTexture(r, (Texture)(object)_white); GUI.color = color; } public static void DrawInset(Rect r) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) //IL_0045: Unknown result type (might be due to invalid IL or missing references) DrawFill(r, new Color(0f, 0f, 0f, 0.45f)); DrawOutline(r, new Color(Gold.r, Gold.g, Gold.b, 0.32f), 1f); } public static void DrawSelection(Rect r) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(r, (Texture)(object)_selection); } public static void DrawDot(Rect r, Color c) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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) Color color = GUI.color; GUI.color = c; GUI.DrawTexture(r, (Texture)(object)_dot, (ScaleMode)2); GUI.color = color; } public static void DrawRule(Rect r) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_005f: Unknown result type (might be due to invalid IL or missing references) DrawFill(r, new Color(Gold.r, Gold.g, Gold.b, 0.4f)); GUI.DrawTexture(new Rect(((Rect)(ref r)).center.x - 7f, ((Rect)(ref r)).y - 7f + 0.5f, 14f, 14f), (Texture)(object)_diamond); } public static void DrawShadowed(Rect r, string text, GUIStyle style) { //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) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) Color textColor = style.normal.textColor; style.normal.textColor = new Color(0f, 0f, 0f, 0.7f); GUI.Label(new Rect(((Rect)(ref r)).x + 1.5f, ((Rect)(ref r)).y + 1.5f, ((Rect)(ref r)).width, ((Rect)(ref r)).height), text, style); style.normal.textColor = textColor; GUI.Label(r, text, style); } private static void BuildStyles() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown //IL_018c: 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_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Expected O, but got Unknown //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Expected O, but got Unknown //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Expected O, but got Unknown //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) Font font = FindFont(); Title = Text(font, Pt(25), (FontStyle)1, GoldBright, (TextAnchor)4); SubTitle = Text(font, Pt(18), (FontStyle)1, Gold, (TextAnchor)3); Header = Text(font, Pt(13), (FontStyle)1, Gold, (TextAnchor)3); Key = Text(font, Pt(13), (FontStyle)0, Muted, (TextAnchor)3); Value = Text(font, Pt(13), (FontStyle)1, Parchment, (TextAnchor)3); Note = Text(font, Pt(13), (FontStyle)2, Muted, (TextAnchor)4); Note.wordWrap = true; Footer = Text(font, Pt(11), (FontStyle)0, Muted, (TextAnchor)4); Button = Patch(font, Pt(13), (FontStyle)1, (TextAnchor)4, _btnNormal, _btnHover, _btnActive); Button.padding = new RectOffset(12, 12, 6, 6); Primary = Patch(font, Pt(16), (FontStyle)1, (TextAnchor)4, _btnNormal, _btnHover, _btnActive); Primary.padding = new RectOffset(12, 12, 8, 8); Primary.normal.textColor = Gold; Row = Patch(font, Pt(14), (FontStyle)0, (TextAnchor)3, _rowNormal, _rowHover, _rowActive); Row.padding = new RectOffset(12, 12, 4, 4); Row.normal.textColor = Parchment; Field = new GUIStyle { font = font, fontSize = Pt(13), alignment = (TextAnchor)3, padding = new RectOffset(8, 8, 4, 4), border = new RectOffset(3, 3, 3, 3), clipping = (TextClipping)1 }; Field.normal.background = _fieldTex; Field.focused.background = _fieldTex; Field.hover.background = _fieldTex; Field.active.background = _fieldTex; Field.normal.textColor = Parchment; Field.focused.textColor = GoldBright; Field.hover.textColor = Parchment; Field.active.textColor = Parchment; } private static int Pt(int designSize) { return Mathf.Max(8, Mathf.RoundToInt((float)designSize * 1f)); } private static GUIStyle Text(Font font, int size, FontStyle fs, Color colour, TextAnchor anchor) { //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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0040: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) GUIStyle val = new GUIStyle { font = font, fontSize = size, fontStyle = fs, alignment = anchor, richText = true, wordWrap = false, clipping = (TextClipping)1 }; val.normal.textColor = colour; return val; } private static GUIStyle Patch(Font font, int size, FontStyle fs, TextAnchor anchor, Texture2D normal, Texture2D hover, Texture2D active) { //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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_008d: 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_00af: 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) GUIStyle val = new GUIStyle { font = font, fontSize = size, fontStyle = fs, alignment = anchor, richText = true, wordWrap = false, border = new RectOffset(3, 3, 3, 3), clipping = (TextClipping)1 }; val.normal.background = normal; val.hover.background = hover; val.active.background = active; val.focused.background = normal; val.normal.textColor = Parchment; val.hover.textColor = GoldBright; val.active.textColor = GoldBright; val.focused.textColor = Parchment; return val; } private static Font FindFont() { Font[] array = Resources.FindObjectsOfTypeAll(); string[] array2 = new string[3] { "AveriaSerifLibre", "Norsebold", "Norse" }; string[] array3 = array2; foreach (string value in array3) { Font[] array4 = array; foreach (Font val in array4) { if ((Object)(object)val != (Object)null && ((Object)val).name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { return val; } } } return null; } private static Texture2D BuildRail(Edge edge) { bool flag = edge == Edge.Top || edge == Edge.Bottom; int num = (flag ? 4 : 18); int num2 = (flag ? 18 : 4); Painter painter = new Painter(num, num2); for (int i = 0; i < num2; i++) { for (int j = 0; j < num; j++) { float num3 = edge switch { Edge.Top => i, Edge.Bottom => num2 - 1 - i, Edge.Left => j, _ => num - 1 - j, }; painter.RailPixel(j, i, Mathf.Abs(num3 - 4.4f), 2.7f); painter.RailPixel(j, i, Mathf.Abs(num3 - 12.4f), 1.6f); } } return painter.Bake(); } private static Texture2D BuildCorner(bool mirrorX, bool mirrorY) { //IL_0066: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) Painter painter = new Painter(84, 84) { MirrorX = mirrorX, MirrorY = mirrorY }; painter.RailElbow(14.4f, 2.7f, 32f); painter.RailElbow(22.4f, 1.6f, 32f); painter.Lozenge(11f, 11f, 4.5f); painter.Taper(new Vector2(13.5f, 13.5f), new Vector2(19.3f, 19.3f), 1.2f, 2.2f); painter.Lozenge(36f, 36f, 3.4f); for (int i = 0; i < 2; i++) { painter.Transpose = i == 1; painter.Taper(new Vector2(38.5f, 35f), new Vector2(47f, 32f), 1.4f, 0.45f); painter.Spiral(new Vector2(50f, 31f), 1.3f, 0.33f, 0f, 6.8f, 5.371f, 0.55f, 2.4f); painter.Bezier(new Vector2(56f, 36f), new Vector2(68f, 41f), new Vector2(78f, 30f), 2.2f, 0.35f); painter.Spiral(new Vector2(76f, 32f), 0.9f, 0.32f, 0f, 4.6f, 1f, 1.1f, 0.3f); painter.Disc(58f, 34f, 1.8f); } painter.Transpose = false; return painter.Bake(); } private static Texture2D BuildCrest(bool flip) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) Painter painter = new Painter(56, 34) { MirrorY = flip }; painter.Disc(28f, 2.5f, 1.8f); painter.Lozenge(28f, 9f, 6.5f); painter.Taper(new Vector2(28f, 14f), new Vector2(28f, 24f), 2.2f, 1.2f); painter.Spiral(new Vector2(17f, 25f), 1.2f, 0.33f, 0f, 5.4f, 2.6f, 2f, 0.35f); painter.Spiral(new Vector2(39f, 25f), 1.2f, 0.33f, 0f, 5.4f, 2.6f, 2f, 0.35f, mirror: true); painter.Bezier(new Vector2(23f, 19f), new Vector2(13f, 24f), new Vector2(4f, 16f), 1.6f, 0.3f); painter.Bezier(new Vector2(33f, 19f), new Vector2(43f, 24f), new Vector2(52f, 16f), 1.6f, 0.3f); painter.Disc(3f, 14f, 1.5f); painter.Disc(53f, 14f, 1.5f); return painter.Bake(); } private static Texture2D BuildDot(int size) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) Texture2D val = New(size, size, (TextureWrapMode)1, (FilterMode)1); Color[] array = (Color[])(object)new Color[size * size]; float num = (float)(size - 1) * 0.5f; float num2 = (float)size * 0.42f; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { float num3 = 0f; for (int k = 0; k < 2; k++) { for (int l = 0; l < 2; l++) { float num4 = (float)j + ((float)l + 0.5f) * 0.5f - 0.5f - num; float num5 = (float)i + ((float)k + 0.5f) * 0.5f - 0.5f - num; if (Mathf.Sqrt(num4 * num4 + num5 * num5) <= num2) { num3 += 0.25f; } } } array[i * size + j] = new Color(1f, 1f, 1f, num3); } } val.SetPixels(array); val.Apply(false); return val; } private static Texture2D BuildDiamond(int size) { Painter painter = new Painter(size, size); float num = (float)(size - 1) * 0.5f; painter.Lozenge(num, num, (float)size * 0.36f); return painter.Bake(); } private static Texture2D BuildPanel(int size) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) Texture2D val = New(size, size, (TextureWrapMode)1, (FilterMode)1); Color[] array = (Color[])(object)new Color[size * size]; float num = (float)(size - 1) * 0.5f; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { float num2 = ((float)j - num) / num; float num3 = ((float)i - num) / num; float num4 = Mathf.Clamp01(1f - 0.55f * Mathf.Sqrt(0.6f * (num2 * num2 + num3 * num3))); array[i * size + j] = new Color(0.055f * num4 + 0.02f, 0.048f * num4 + 0.017f, 0.038f * num4 + 0.013f, 0.955f); } } val.SetPixels(array); val.Apply(false); return val; } private static Texture2D BuildPatch(Color fill, Color border) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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) Texture2D val = New(12, 12, (TextureWrapMode)1, (FilterMode)0); Color[] array = (Color[])(object)new Color[144]; for (int i = 0; i < 12; i++) { for (int j = 0; j < 12; j++) { int num = Mathf.Min(Mathf.Min(j, i), Mathf.Min(11 - j, 11 - i)); array[i * 12 + j] = ((num == 0) ? border : fill); } } val.SetPixels(array); val.Apply(false); return val; } private static Texture2D BuildSolid(Color c) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) Texture2D val = New(1, 1, (TextureWrapMode)1, (FilterMode)0); val.SetPixel(0, 0, c); val.Apply(false); return val; } private static Texture2D New(int w, int h, TextureWrapMode wrap, FilterMode filter) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown return new Texture2D(w, h, (TextureFormat)4, false) { wrapMode = wrap, filterMode = filter, hideFlags = (HideFlags)61 }; } } [HarmonyPatch] public static class InputFocusPatch { [HarmonyPatch(typeof(Chat), "HasFocus")] [HarmonyPostfix] public static void Chat_HasFocus_Postfix(ref bool __result) { try { if (KeeperUIManager.BlocksGameInput() || KeeperUIManager.IsTextInputActive()) { __result = true; } } catch (Exception arg) { Plugin.Log.LogError((object)string.Format("AwayFromHome: {0} failed (non-fatal, keyboard may leak into gameplay while typing). Reason: {1}", "Chat_HasFocus_Postfix", arg)); } } [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] [HarmonyPrefix] public static bool GameCamera_UpdateMouseCapture_Prefix() { try { if (!KeeperUIManager.WantsCursor()) { return true; } Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; return false; } catch (Exception arg) { Plugin.Log.LogError((object)string.Format("AwayFromHome: {0} failed (non-fatal, falling back to vanilla cursor handling). Reason: {1}", "GameCamera_UpdateMouseCapture_Prefix", arg)); return true; } } } public class KeeperUIManager : MonoBehaviour { private static KeeperUIManager _instance; private bool _isVisible; private Vector2 _scroll; private float _statusUntil; private string _statusText = ""; private bool _statusOk; private bool _leashVisible; private ZDOID _leashSite = ZDOID.None; private PenShape _penShape = PenShape.Circle; private float _penHalfX; private float _penHalfZ; private float _penYaw; public static ZDOID LeashPanelSite => ((Object)(object)_instance != (Object)null && _instance._leashVisible) ? _instance._leashSite : ZDOID.None; public static void Init() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("AwayFromHome.KeeperUIManager"); _instance = val.AddComponent(); Object.DontDestroyOnLoad((Object)(object)val); SiteRegistry.OnReply += OnServerReply; } } private static void OnServerReply() { if (!((Object)(object)_instance == (Object)null)) { _instance._statusText = SiteRegistry.LastReply; _instance._statusOk = SiteRegistry.LastReplyOk; _instance._statusUntil = Time.unscaledTime + 6f; } } public static bool BlocksGameInput() { return (Object)(object)_instance != (Object)null && (_instance._isVisible || _instance._leashVisible); } public static bool IsTextInputActive() { return false; } public static bool WantsCursor() { return (Object)(object)_instance != (Object)null && (_instance._isVisible || _instance._leashVisible); } public static void ShowLeashPanel(ZDOID site, PenArea current) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance == (Object)null)) { _instance._leashSite = site; _instance._penShape = current.Shape; _instance._penHalfX = current.HalfX; _instance._penHalfZ = current.HalfZ; _instance._penYaw = current.Yaw; _instance._leashVisible = true; } } public static void HideLeashPanel() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance == (Object)null)) { _instance._leashVisible = false; _instance._leashSite = ZDOID.None; } } public static PenArea LeashPanelPen(Vector3 centre) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_instance == (Object)null) { return PenArea.None; } return new PenArea(_instance._penShape, centre, _instance._penHalfX, _instance._penHalfZ, _instance._penYaw); } private void Update() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return; } bool flag = _isVisible || _leashVisible; if (((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus() && !flag) || (!flag && (Console.IsVisible() || TextInput.IsVisible() || Menu.IsVisible()))) { return; } if (Input.GetKeyDown(Configuration.menuKey.Value)) { if (!Configuration.IsAdmin) { if (!_isVisible && (Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Away From Home: place a Keeper Stone to mark a site - the panel is for admins.", 0, (Sprite)null); } return; } _isVisible = !_isVisible; } if (_isVisible && Input.GetKeyDown((KeyCode)27)) { _isVisible = false; } if (_leashVisible) { if (Input.GetKeyDown((KeyCode)27)) { HideLeashPanel(); } else { ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(_leashSite) : null); float num = LeashPanelPen(Vector3.zero).OuterRadius + 16f; if (val == null || !val.IsValid() || Vector3.Distance(val.GetPosition(), ((Component)Player.m_localPlayer).transform.position) > num) { HideLeashPanel(); } } } if (_isVisible && !Configuration.IsAdmin) { _isVisible = false; } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: menu Update failed (non-fatal). Reason: {arg}"); } } private void OnGUI() { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } if (_leashVisible) { try { AFHUITheme.EnsureBuilt(); DrawLeashPanel(); } catch (Exception arg) { _leashVisible = false; Plugin.Log.LogError((object)$"AwayFromHome: the leash panel failed (non-fatal, closing it). Reason: {arg}"); } } if (!_isVisible) { return; } try { AFHUITheme.EnsureBuilt(); float num = 620f; float num2 = 480f; Rect win = default(Rect); ((Rect)(ref win))..ctor(((float)Screen.width - num) * 0.5f, ((float)Screen.height - num2) * 0.5f, num, num2); AFHUITheme.DrawWindow(win, "Away From Home — Keeper Stones"); DrawHeader(win); DrawBody(win); DrawFooter(win); } catch (Exception arg2) { Plugin.Log.LogError((object)$"AwayFromHome: menu OnGUI failed (non-fatal, closing the menu). Reason: {arg2}"); _isVisible = false; } } private void DrawHeader(Rect win) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) Rect val = AFHUITheme.Body(win); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, 28f); GUI.Label(new Rect(((Rect)(ref val2)).x, ((Rect)(ref val2)).y, ((Rect)(ref val2)).width - 90f, ((Rect)(ref val2)).height), "Every Keeper Stone standing on this server.", AFHUITheme.Note); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(((Rect)(ref win)).xMax - 18f - 22f - 70f, ((Rect)(ref val2)).y, 70f, ((Rect)(ref val2)).height); if (GUI.Button(val3, "CLOSE", AFHUITheme.Button)) { _isVisible = false; } } private void DrawBody(Rect win) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: 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_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) Rect val = AFHUITheme.Body(win); Rect r = default(Rect); ((Rect)(ref r))..ctor(((Rect)(ref val)).x, ((Rect)(ref val)).y + 34f, ((Rect)(ref val)).width, ((Rect)(ref val)).height - 34f); AFHUITheme.DrawInset(r); IReadOnlyList allKnownSites = SiteRegistry.GetAllKnownSites(); float num = 30f; float num2 = Mathf.Max(((Rect)(ref r)).height, (float)allKnownSites.Count * num + 8f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref r)).x + 4f, ((Rect)(ref r)).y + 4f, ((Rect)(ref r)).width - 8f, ((Rect)(ref r)).height - 8f); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(0f, 0f, ((Rect)(ref val2)).width - 16f, num2); _scroll = GUI.BeginScrollView(val2, _scroll, val3); float num3 = 4f; List list = allKnownSites.ToList(); Rect r2 = default(Rect); foreach (SiteRecord item in list) { ((Rect)(ref r2))..ctor(0f, num3, ((Rect)(ref val3)).width, num); bool flag = KeeperManager.ActiveSiteId != ZDOID.None && item.Id == KeeperManager.ActiveSiteId; if (flag) { AFHUITheme.DrawSelection(r2); } string text = (string.IsNullOrEmpty(item.OwnerName) ? "unknown" : item.OwnerName); float num4 = Vector3.Distance(((Component)Player.m_localPlayer).transform.position, item.Pos); GUI.Label(new Rect(((Rect)(ref r2)).x + 6f, ((Rect)(ref r2)).y, 150f, ((Rect)(ref r2)).height), text, AFHUITheme.Key); GUI.Label(new Rect(((Rect)(ref r2)).x + 160f, ((Rect)(ref r2)).y, ((Rect)(ref r2)).width - 160f - 200f, ((Rect)(ref r2)).height), flag ? (item.Label + " (" + KeeperManager.ActivePhase + ")") : item.Label, AFHUITheme.Row); GUI.Label(new Rect(((Rect)(ref r2)).xMax - 196f, ((Rect)(ref r2)).y, 100f, ((Rect)(ref r2)).height), $"{num4:0}m away", AFHUITheme.Key); if (GUI.Button(new Rect(((Rect)(ref r2)).xMax - 84f, ((Rect)(ref r2)).y + 2f, 80f, ((Rect)(ref r2)).height - 4f), "REMOVE", AFHUITheme.Button)) { SiteRegistry.RequestAdminRemove(item.Id); } num3 += num; } if (list.Count == 0) { GUI.Label(new Rect(4f, 4f, ((Rect)(ref val3)).width - 8f, 40f), "No Keeper Stones standing anywhere on this server yet.", AFHUITheme.Note); } GUI.EndScrollView(); } private void DrawLeashPanel() { //IL_0079: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) int num = ((_penShape == PenShape.Rectangle) ? 3 : ((_penShape != PenShape.Square) ? 1 : 2)); float num2 = 82f + (float)num * 58f + 20f + 16f + 30f; float num3 = num2 + 36f + 54f + 30f; float num4 = 480f; Rect win = default(Rect); ((Rect)(ref win))..ctor(((float)Screen.width - num4) * 0.5f, (float)Screen.height * 0.52f, num4, num3); AFHUITheme.DrawWindow(win, "Keeper Stone — leash"); Rect body = AFHUITheme.Body(win); float value = Configuration.maxLeashMeters.Value; float y = ((Rect)(ref body)).y; GUI.Label(new Rect(((Rect)(ref body)).x, y, ((Rect)(ref body)).width, 40f), "How far animals may get from this stone before the keeper walks them back. The outline on the ground is this pen - stand the stone in the MIDDLE of it.", AFHUITheme.Note); y += 48f; y = DrawShapeRow(body, y, 26f, value) + 8f; if (_penShape == PenShape.Circle) { y = DrawDimension(body, y, 26f, 24f, 8f, "radius", ref _penHalfX, 1f, value); } else { y = DrawDimension(body, y, 26f, 24f, 8f, (_penShape == PenShape.Square) ? "side" : "width", ref _penHalfX, 0.5f, value * 2f); if (_penShape == PenShape.Rectangle) { y = DrawDimension(body, y, 26f, 24f, 8f, "depth", ref _penHalfZ, 0.5f, value * 2f); } else { _penHalfZ = _penHalfX; } y = DrawRotation(body, y, 26f, 24f, 8f); } GUI.Label(new Rect(((Rect)(ref body)).x, y, ((Rect)(ref body)).width, 20f), $"server maximum {value:0.#} m from the stone", AFHUITheme.Note); y += 36f; float num5 = (((Rect)(ref body)).width - 8f) * 0.5f; if (GUI.Button(new Rect(((Rect)(ref body)).x, y, num5, 30f), "Apply", AFHUITheme.Primary)) { SiteRegistry.RequestSetLeash(_leashSite, (int)_penShape, _penHalfX, _penHalfZ, _penYaw); HideLeashPanel(); } if (GUI.Button(new Rect(((Rect)(ref body)).x + num5 + 8f, y, num5, 30f), "Cancel", AFHUITheme.Button)) { HideLeashPanel(); } } private float DrawShapeRow(Rect body, float y, float shapeH, float cap) { //IL_0020: 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_0072: Unknown result type (might be due to invalid IL or missing references) float num = (((Rect)(ref body)).width - 8f) / 3f; DrawShapeButton(new Rect(((Rect)(ref body)).x, y, num, shapeH), PenShape.Circle, "Circle", cap); DrawShapeButton(new Rect(((Rect)(ref body)).x + num + 4f, y, num, shapeH), PenShape.Square, "Square", cap); DrawShapeButton(new Rect(((Rect)(ref body)).x + (num + 4f) * 2f, y, num, shapeH), PenShape.Rectangle, "Rectangle", cap); return y + shapeH; } private void DrawShapeButton(Rect r, PenShape shape, string label, float cap) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) bool flag = _penShape == shape; if (GUI.Button(r, label, flag ? AFHUITheme.Primary : AFHUITheme.Button) && !flag) { _penShape = shape; if (_penHalfX <= 0f) { _penHalfX = Mathf.Min(Configuration.livestockLeashMeters.Value, cap); } if (shape != PenShape.Rectangle || _penHalfZ <= 0f) { _penHalfZ = _penHalfX; } if (shape == PenShape.Circle) { _penYaw = 0f; } } } private float DrawDimension(Rect body, float y, float valueH, float sliderH, float gap, string label, ref float half, float toHalf, float sliderMax) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) float num = half / toHalf; GUI.Label(new Rect(((Rect)(ref body)).x, y, ((Rect)(ref body)).width, valueH), (num <= 0f) ? "No leash on this stone" : $"{label} {num:0.#} m", AFHUITheme.Value); y += valueH; num = GUI.HorizontalSlider(new Rect(((Rect)(ref body)).x, y + (sliderH - 12f) * 0.5f, ((Rect)(ref body)).width, 12f), num, 0f, sliderMax); num = Mathf.Round(num * 2f) * 0.5f; half = num * toHalf; return y + sliderH + gap; } private float DrawRotation(Rect body, float y, float valueH, float sliderH, float gap) { //IL_0011: 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) GUI.Label(new Rect(((Rect)(ref body)).x, y, ((Rect)(ref body)).width, valueH), $"rotation {_penYaw:0} deg", AFHUITheme.Value); y += valueH; _penYaw = GUI.HorizontalSlider(new Rect(((Rect)(ref body)).x, y + (sliderH - 12f) * 0.5f, ((Rect)(ref body)).width, 12f), _penYaw, 0f, 180f); _penYaw = Mathf.Round(_penYaw / 5f) * 5f; return y + sliderH + gap; } private void DrawFooter(Rect win) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) Rect val = AFHUITheme.FooterLine(win); if (Time.unscaledTime < _statusUntil && !string.IsNullOrEmpty(_statusText)) { GUIStyle val2 = new GUIStyle(AFHUITheme.Footer); val2.normal.textColor = (_statusOk ? AFHUITheme.GoodColour : AFHUITheme.BadColour); GUI.Label(val, _statusText, val2); return; } string text = ((KeeperManager.ActiveSiteId != ZDOID.None) ? ("Keeper: holding " + KeeperManager.ActiveSiteLabel + " (" + KeeperManager.ActiveSiteOwner + ") - " + KeeperManager.ActivePhase) : "Keeper: idle"); GUI.Label(val, text, AFHUITheme.Footer); } } [HarmonyPatch] public static class ZoneAnchor { private static bool _active; private static Vector3 _position; public static int Ring => Configuration.ring.Value; public static bool IsActive => _active; public static Vector3 Position => _position; public static void Set(Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _position = position; _active = true; } public static void Clear() { _active = false; } public static void PokeZones() { //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) //IL_0031: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (!_active || (Object)(object)ZoneSystem.instance == (Object)null) { return; } try { int ring = Ring; Vector2i zone = ZoneSystem.GetZone(_position); for (int i = zone.y - ring; i <= zone.y + ring; i++) { for (int j = zone.x - ring; j <= zone.x + ring; j++) { if (ZoneSystem.instance.PokeLocalZone(new Vector2i(j, i))) { return; } } } } catch (Exception arg) { Plugin.Log.LogError((object)$"AwayFromHome: poking zones for the active site failed (non-fatal). Reason: {arg}"); _active = false; } } [HarmonyPatch(typeof(ZNetScene), "CreateObjects")] [HarmonyPrefix] public static void ZNetScene_CreateObjects_Prefix(List currentNearObjects) { //IL_0041: 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_004b: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) if (!_active) { return; } try { if (ZDOMan.instance != null && !((Object)(object)ZoneSystem.instance == (Object)null) && !((Object)(object)ZNet.instance == (Object)null)) { Vector2i zone = ZoneSystem.GetZone(_position); Vector2i zone2 = ZoneSystem.GetZone(ZNet.instance.GetReferencePosition()); int num = ZoneSystem.instance.m_activeArea + Ring; if (Mathf.Abs(zone.x - zone2.x) > num || Mathf.Abs(zone.y - zone2.y) > num) { ZDOMan.instance.FindSectorObjects(zone, Ring, ZoneSystem.instance.m_activeDistantArea, currentNearObjects, (List)null); } } } catch (Exception arg) { _active = false; Plugin.Log.LogError((object)$"AwayFromHome: feeding anchored objects to ZNetScene failed (non-fatal). Reason: {arg}"); } } } } namespace AwayFromHome.Compat { public static class PetPantryConflict { private const string PluginGuid = "Azumatt.PetPantry"; private static bool _probed; private static bool _isInstalled; public static bool IsInstalled => _isInstalled; public static void EnsureProbed() { if (_probed) { return; } _probed = true; try { _isInstalled = PluginLookup.IsLoaded("Azumatt.PetPantry"); if (_isInstalled) { Plugin.Log.LogWarning((object)"AwayFromHome: Pet Pantry is installed, and the Keeper Stone feeds animals itself. Both will now feed the same tames from different stores - nothing breaks, but food is consumed out of two places for one feeding's worth of effect. Pick one: leave the Keeper Stone's slots empty to keep using Pet Pantry's chests, or remove Pet Pantry and stock the stone instead."); } } catch (Exception arg) { _isInstalled = false; Plugin.Log.LogWarning((object)$"AwayFromHome: could not check for Pet Pantry (assuming it is absent, nothing breaks either way). Reason: {arg}"); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }